@pluv/client 3.2.2 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +14 -16
- package/CHANGELOG.md +19 -0
- package/dist/index.d.mts +285 -283
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1739 -1866
- package/dist/index.mjs.map +1 -0
- package/package.json +17 -13
- package/tsdown.config.ts +14 -0
- package/dist/index.d.ts +0 -354
- package/dist/index.js +0 -1946
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["connectionId","ConnectionState","#_storage","presence","addons","addon","other","others","encodedState","remaining","options","ConnectionState","#_storage","presence","type","data"],"sources":["../src/AbstractStorageStore.ts","../src/constants.ts","../src/PluvProcedure.ts","../src/CrdtManager.ts","../src/CrdtNotifier.ts","../src/EventNotifier.ts","../src/ListenerManager.ts","../src/PluvRouter.ts","../src/StateNotifier.ts","../src/StorageStore.ts","../src/utils/debounce.ts","../src/utils/pickBy.ts","../src/UsersManager.ts","../src/UsersNotifier.ts","../src/PluvRoom.ts","../src/PluvClient.ts","../src/createClient.ts","../src/infer.ts","../src/MockedRoom.ts","../src/register.ts"],"sourcesContent":["export abstract class AbstractStorageStore {\n public room: string;\n\n constructor(room: string) {\n this.room = room;\n }\n\n public abstract addUpdate(update: string): Promise<void>;\n\n public abstract destroy(): Promise<void>;\n\n public abstract flatten(encodedState: string): Promise<void>;\n\n public abstract getShouldFlatten(): Promise<boolean>;\n\n public abstract getUpdates(start?: number): Promise<readonly string[]>;\n\n public abstract initialize(): Promise<void>;\n}\n","export const MAX_PRESENCE_SIZE_BYTES = 512;\nexport const PLUV_PRESENCE_META_KEY = \"__pluv\";\n","import type { AbstractCrdtDocFactory, InferDocLike } from \"@pluv/crdt\";\nimport type { EventRecord, IOLike, InputZodLike, JsonObject, ProcedureLike } from \"@pluv/types\";\nimport type { EventResolver, MergeEventRecords } from \"./types\";\n\nexport interface PluvProcedureConfig<\n TIO extends IOLike,\n TInput extends JsonObject,\n TOutput extends EventRecord<string, any>,\n TPresence extends Record<string, any>,\n TCrdt extends AbstractCrdtDocFactory<any, any>,\n> {\n broadcast?: EventResolver<TIO, TInput, TOutput, TPresence, InferDocLike<TCrdt>> | null;\n input?: InputZodLike<TInput>;\n}\n\nexport class PluvProcedure<\n TIO extends IOLike,\n TInput extends JsonObject,\n TOutput extends EventRecord<string, any>,\n TPresence extends Record<string, any>,\n TCrdt extends AbstractCrdtDocFactory<any, any>,\n TFilled extends \"input\" | \"broadcast\" | \"\",\n> implements ProcedureLike<TInput, TOutput>\n{\n private _broadcast: EventResolver<\n TIO,\n TInput,\n Partial<TOutput>,\n TPresence,\n InferDocLike<TCrdt>\n > | null = null;\n private _input: InputZodLike<TInput> | 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 } as ProcedureLike<TInput, TOutput>[\"config\"];\n }\n\n constructor(config: PluvProcedureConfig<TIO, TInput, TOutput, TPresence, TCrdt> = {}) {\n const { broadcast, input } = config;\n\n this._broadcast = broadcast ?? null;\n this._input = input ?? null;\n }\n\n public broadcast<TResult extends EventRecord<string, any> = {}>(\n resolver: EventResolver<TIO, TInput, TResult, TPresence, InferDocLike<TCrdt>>,\n ): Omit<\n PluvProcedure<\n TIO,\n TInput,\n MergeEventRecords<[TOutput, TResult]>,\n TPresence,\n TCrdt,\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 TIO,\n TInput,\n MergeEventRecords<[TOutput, TResult]>,\n TPresence,\n TCrdt,\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<PluvProcedure<TIO, TData, {}, TPresence, TCrdt, TFilled | \"input\">, TFilled | \"input\"> {\n return new PluvProcedure<TIO, TData, {}, TPresence, TCrdt, TFilled | \"input\">({ input });\n }\n\n private _resolver(): EventResolver<TIO, TInput, TOutput, TPresence, InferDocLike<TCrdt>> {\n return (data, context) => this._broadcast?.(data, context) as TOutput;\n }\n}\n","import type { AbstractCrdtDocFactory, InferDocLike, InferStorage } from \"@pluv/crdt\";\nimport { noop } from \"@pluv/crdt\";\n\nexport type CrdtManagerOptions<TCrdt extends AbstractCrdtDocFactory<any, any>> = {\n initialStorage?: TCrdt;\n};\n\ninterface CrdtManagerInitializeParams {\n origin?: any;\n update: string | readonly string[];\n}\n\ninterface ApplyUpdateParams {\n origin?: any;\n update: string | readonly string[];\n}\n\nexport class CrdtManager<TCrdt extends AbstractCrdtDocFactory<any, any>> {\n public doc: InferDocLike<TCrdt>;\n public initialized: boolean = false;\n\n private readonly _docFactory: TCrdt;\n\n constructor(options: CrdtManagerOptions<TCrdt>) {\n const { initialStorage = noop.doc({}) as any } = options;\n\n this._docFactory = initialStorage;\n this.doc = this._docFactory.getEmpty() as InferDocLike<TCrdt>;\n }\n\n /**\n * !HACK\n * @description If this is called before the doc is initialized. Omit the\n * update. This unfortunately means updates can be dropped entirely\n * @date June 13, 2023\n */\n public applyUpdate(params: ApplyUpdateParams): this {\n const { origin, update } = params;\n\n if (!this.initialized) return this;\n\n const updates: readonly string[] = typeof update === \"string\" ? [update] : update;\n\n this._applyDocUpdates(this.doc, updates, origin);\n\n return this;\n }\n\n public destroy(): void {\n this.initialized = false;\n this.doc.destroy();\n this.doc = this._docFactory.getEmpty() as InferDocLike<TCrdt>;\n }\n\n public get<TKey extends keyof InferStorage<TCrdt>>(key: TKey): InferStorage<TCrdt>[TKey] {\n return this.doc.get(key);\n }\n\n public getInitialState(): string {\n return this._docFactory.getInitialized().getEncodedState();\n }\n\n public initialize(params: CrdtManagerInitializeParams): this {\n const { origin, update } = params;\n\n /**\n * !HACK\n * @description If the doc was already initialized, we don't want to re-initialize\n * the doc and overwrite what's already there. Skip the initialization and log a\n * warning.\n * @date May 7, 2025\n */\n if (this.initialized) {\n console.warn(\"Attempted to initialize storage multiple times\");\n return this;\n }\n\n /**\n * !HACK\n * @description If the doc is already populated, somehow we're attempting to overwrite\n * a doc that was presumably initialized but not identified as initialized. Skip the\n * initialization and log a warning.\n * @date May 7, 2025\n */\n if (!this.doc.isEmpty()) {\n console.warn(\"Attempted to initialize over a non-empty storage\");\n return this;\n }\n\n const updates = typeof update === \"string\" ? [update] : update;\n\n if (!updates.length) return this;\n\n const initialized = this._docFactory.getInitialized();\n const reference = initialized.get();\n\n this.doc = this._applyDocUpdates(this.doc, updates, origin).rebuildStorage(reference);\n this.initialized = true;\n initialized.destroy();\n\n return this;\n }\n\n public resolveEncodedState(updates: string | string[] | readonly string[]): string | null {\n return this._docFactory.resolveEncodedState(updates);\n }\n\n private _applyDocUpdates(\n doc: InferDocLike<TCrdt>,\n updates: readonly string[],\n origin?: string,\n ): InferDocLike<TCrdt> {\n doc.transact(() => {\n doc.batchApplyEncodedState({ origin, updates });\n }, origin);\n\n return doc;\n }\n}\n","import type { CrdtType, InferCrdtJson } from \"@pluv/crdt\";\nimport type { StorageSubscriptionCallback } from \"@pluv/types\";\nimport type { Subject } from \"wonka\";\nimport { makeSubject, subscribe } from \"wonka\";\n\nexport class CrdtNotifier<TStorage extends Record<string, CrdtType<any, any>>> {\n public rootSubject = makeSubject<{\n [P in keyof TStorage]: InferCrdtJson<TStorage[P]>;\n }>();\n\n public subjects = new Map<\n keyof TStorage,\n Subject<InferCrdtJson<TStorage[keyof TStorage]>> | null\n >();\n\n public subject<TKey extends keyof TStorage>(key: TKey): Subject<InferCrdtJson<TStorage[TKey]>> {\n const subject = this.subjects.get(key);\n\n if (subject) {\n return subject as Subject<InferCrdtJson<TStorage[TKey]>>;\n }\n\n const newSubject = makeSubject<InferCrdtJson<TStorage[TKey]>>();\n\n this.subjects.set(key, newSubject);\n\n return newSubject as Subject<InferCrdtJson<TStorage[TKey]>>;\n }\n\n public subcribeRoot(\n callback: (value: {\n [P in keyof TStorage]: InferCrdtJson<TStorage[P]>;\n }) => void,\n ): () => void {\n return subscribe(callback)(this.rootSubject.source).unsubscribe;\n }\n\n public subscribe<TKey extends keyof TStorage>(\n key: TKey,\n callback: StorageSubscriptionCallback<TStorage, TKey>,\n ): () => void {\n const subject = this.subject(key);\n\n return subscribe(callback)(subject.source).unsubscribe;\n }\n}\n","import type {\n EventNotifierSubscriptionCallback,\n Id,\n InferIOOutput,\n IOEventMessage,\n IOLike,\n} from \"@pluv/types\";\nimport type { Subject } from \"wonka\";\nimport { makeSubject, subscribe } from \"wonka\";\n\nexport class EventNotifier<TIO extends IOLike> {\n public subjects = new Map<\n keyof InferIOOutput<TIO>,\n Subject<Id<IOEventMessage<TIO, keyof InferIOOutput<TIO>>>>\n >();\n\n public subject<TEvent extends keyof InferIOOutput<TIO>>(\n name: TEvent,\n ): Subject<Id<IOEventMessage<TIO, TEvent>>> {\n const subject = this.subjects.get(name);\n\n if (subject) return subject;\n\n const newSubject = makeSubject<Id<IOEventMessage<TIO, TEvent>>>();\n\n this.subjects.set(name, newSubject);\n\n return newSubject;\n }\n\n public subscribe<TEvent extends keyof InferIOOutput<TIO>>(\n name: TEvent,\n callback: EventNotifierSubscriptionCallback<TIO, TEvent>,\n ): () => void {\n const subject = this.subject(name);\n\n return subscribe(callback)(subject.source).unsubscribe;\n }\n}\n","import { makeSubject, subscribe, TypeOfSource } from \"wonka\";\n\nexport class ListenerManager {\n public subjects = {\n close: makeSubject<CloseEvent>(),\n error: makeSubject<Event>(),\n message: makeSubject<MessageEvent<any>>(),\n open: makeSubject<Event>(),\n };\n\n public subscribe<TKind extends keyof typeof this.subjects>(\n kind: TKind,\n callback: (event: TypeOfSource<(typeof this.subjects)[TKind][\"source\"]>) => void,\n ): () => void {\n const source = this.subjects[kind]?.source;\n\n if (!source) return () => undefined;\n\n return subscribe(callback)(source as any).unsubscribe;\n }\n}\n","import type { AbstractCrdtDocFactory } from \"@pluv/crdt\";\nimport type { IOLike, IORouterLike, JsonObject } from \"@pluv/types\";\nimport type { PluvProcedure } from \"./PluvProcedure\";\n\nexport type PluvRouterEventConfig<\n TIO extends IOLike,\n TPresence extends Record<string, any>,\n TCrdt extends AbstractCrdtDocFactory<any, any>,\n> = { [P: string]: Pick<PluvProcedure<TIO, any, any, TPresence, TCrdt, \"\">, \"config\"> };\n\nexport type MergedRouter<\n TRouters extends PluvRouter<TIO, TPresence, TCrdt, any>[],\n TIO extends IOLike,\n TPresence extends Record<string, any>,\n TCrdt extends AbstractCrdtDocFactory<any, any>,\n TRoot extends TRouters[0][\"_defs\"][\"events\"],\n> = TRouters extends [\n infer IHead extends PluvRouter<TIO, TPresence, TCrdt, any>,\n ...infer ITail extends PluvRouter<TIO, TPresence, TCrdt, any>[],\n]\n ? MergedRouter<ITail, TIO, TPresence, TCrdt, TRoot & IHead[\"_defs\"][\"events\"]>\n : PluvRouter<TIO, TPresence, TCrdt, TRoot>;\n\nexport class PluvRouter<\n TIO extends IOLike,\n TPresence extends Record<string, any>,\n TCrdt extends AbstractCrdtDocFactory<any, any>,\n TEvents extends PluvRouterEventConfig<TIO, TPresence, TCrdt> = {},\n> implements IORouterLike<TEvents>\n{\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<\n TRouters extends PluvRouter<TIO, TPresence, TCrdt, any>[],\n TIO extends IOLike,\n TPresence extends Record<string, any>,\n TCrdt extends AbstractCrdtDocFactory<any, any>,\n >(...routers: TRouters): MergedRouter<TRouters, TIO, TPresence, TCrdt, {}> {\n const events = Object.assign(\n Object.create(null),\n ...routers.map((router) => router._defs.events),\n );\n\n if (Object.keys(events).some((key) => key.startsWith(\"$\"))) {\n throw new Error('Procedures may not start with \"$\"');\n }\n\n return new PluvRouter<any, any, any, any>(events) as MergedRouter<\n TRouters,\n TIO,\n TPresence,\n TCrdt,\n {}\n >;\n }\n\n private _isValidEventName(name: string): boolean {\n return /^[a-z_$][a-z0-9_$]*$/gi.test(name);\n }\n}\n","import type {\n Id,\n IOLike,\n JsonObject,\n StateNotifierSubjects,\n SubscriptionCallback,\n UserInfo,\n WebSocketState,\n} from \"@pluv/types\";\nimport type { Subject } from \"wonka\";\nimport { makeSubject, subscribe } from \"wonka\";\n\ntype InferSubjectValue<\n TIO extends IOLike,\n TPresence extends Record<string, any>,\n TSubject extends keyof StateNotifierSubjects<TIO, TPresence>,\n> =\n StateNotifierSubjects<TIO, TPresence>[TSubject] extends Subject<infer IValue>\n ? Id<IValue>\n : never;\n\nexport class StateNotifier<TIO extends IOLike, TPresence extends Record<string, any> = {}> {\n public subjects: StateNotifierSubjects<TIO, TPresence> = {\n connection: makeSubject<Id<WebSocketState<TIO>>>(),\n \"my-presence\": makeSubject<TPresence>(),\n myself: makeSubject<Readonly<Id<UserInfo<TIO, TPresence>>> | null>(),\n others: makeSubject<readonly Id<UserInfo<TIO, TPresence>>[]>(),\n \"storage-loaded\": makeSubject<true>(),\n };\n\n public subscribe<TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(\n name: TSubject,\n callback: SubscriptionCallback<TIO, TPresence, TSubject>,\n ): () => void {\n const source = this.subjects[name]?.source as any;\n\n if (!source) throw new Error(`Could not subscribe to source: ${name}`);\n\n return subscribe(callback)(source).unsubscribe;\n }\n}\n","import { AbstractStorageStore } from \"./AbstractStorageStore\";\n\nexport class StorageStore extends AbstractStorageStore {\n public addUpdate() {\n return Promise.resolve();\n }\n\n public applyUpdates() {\n return Promise.resolve();\n }\n\n public destroy() {\n return Promise.resolve();\n }\n\n public flatten() {\n return Promise.resolve();\n }\n\n public getShouldFlatten() {\n return Promise.resolve(false);\n }\n\n public getUpdates() {\n return Promise.resolve([]);\n }\n\n public initialize() {\n return Promise.resolve();\n }\n}\n","export const debounce = <TFunc extends (...args: any[]) => any>(\n fn: TFunc,\n options?: { wait: number; immediate?: boolean },\n): ((...args: Parameters<TFunc>) => void) => {\n const { wait, immediate = false } = options ?? {};\n\n let prevResult: ReturnType<TFunc> | undefined;\n let timeout: number | null = null;\n\n return (...args: Parameters<TFunc>): void => {\n if (typeof timeout === \"number\") clearTimeout(timeout);\n\n timeout = setTimeout(() => {\n timeout = null;\n\n if (!immediate) prevResult = fn(args);\n }, wait) as number;\n\n if (immediate && !timeout) {\n fn(args);\n\n return;\n }\n\n return prevResult;\n };\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 { InputZodLike, IOLike, JsonObject, OptionalProps, UserInfo } from \"@pluv/types\";\nimport { PLUV_PRESENCE_META_KEY } from \"./constants\";\nimport type { PluvClientLimits } from \"./types\";\nimport { pickBy } from \"./utils\";\n\nexport type Presence = Record<string, unknown>;\n\nexport type UsersManagerConfig<TPresence extends Record<string, any> = {}> = {\n initialPresence?: TPresence;\n limits: PluvClientLimits;\n presence?: InputZodLike<TPresence>;\n};\n\nexport type AddConnectionResult<TIO extends IOLike, TPresence extends Record<string, any> = {}> = {\n clientId: string;\n data: UserInfo<TIO, TPresence>;\n isMyself: boolean;\n remaining: number;\n};\n\nexport type DeleteConnectionResult<\n TIO extends IOLike,\n TPresence extends Record<string, any> = {},\n> = {\n clientId: string;\n data: UserInfo<TIO, TPresence>;\n remaining: number;\n};\n\nexport class UsersManager<TIO extends IOLike, TPresence extends Record<string, any> = {}> {\n public readonly initialPresence: TPresence;\n\n private readonly _idMap = {\n fromConnectionId: new Map<[connectionId: string][0], [clientId: string][0]>(),\n fromClientId: new Map<[clientId: string][0], Set<[connectionId: string][0]>>(),\n };\n private _limits: PluvClientLimits;\n /**\n * @description This presence can be updated while the user is not\n * connected.\n */\n private _myPresence: TPresence;\n /**\n * @description This is only set when the user is connected.\n */\n private _myself: UserInfo<TIO, TPresence> | null = null;\n private _others = new Map<[clientId: string][0], UserInfo<TIO, TPresence>>();\n private _presence: InputZodLike<TPresence> | null = null;\n\n constructor(config: UsersManagerConfig<TPresence>) {\n const { initialPresence, limits, presence = null } = config;\n\n this.initialPresence = initialPresence as TPresence;\n\n this._limits = limits;\n this._myPresence = initialPresence as TPresence;\n this._presence = presence;\n }\n\n public get myPresence(): TPresence {\n return this._myPresence;\n }\n\n public get myself(): UserInfo<TIO, TPresence> | null {\n return this._myself;\n }\n\n public addConnection(\n userInfo: OptionalProps<UserInfo<TIO, TPresence>, \"presence\">,\n ): AddConnectionResult<TIO, TPresence> {\n /**\n * @description Ensure that the presence is complete, so that it does not fail schema\n * validation\n * @date April 20, 2025\n */\n const cleaned = pickBy(userInfo.presence ?? {}, (value) => typeof value !== \"undefined\");\n const presence = { ...this.initialPresence, ...cleaned };\n const data: UserInfo<TIO, TPresence> = { ...userInfo, presence };\n\n const myClientId = !!this._myself ? this.getClientId(this._myself) : null;\n const clientId = this.getClientId(data);\n const remaining = this._setConnectionId(userInfo.connectionId, clientId).size;\n\n if (myClientId === clientId) {\n const result: AddConnectionResult<TIO, TPresence> = {\n clientId,\n data,\n isMyself: true,\n remaining,\n };\n\n return result;\n }\n\n const other = this._others.get(clientId);\n\n /**\n * !HACK\n * @description Don't override the other user, so that we ensure the user's presence isn't\n * overwritten by whatever incoming connection.\n * @date April 16, 2025\n */\n if (!other) this._others.set(clientId, data);\n\n const result: AddConnectionResult<TIO, TPresence> = {\n clientId,\n data,\n isMyself: false,\n remaining,\n };\n\n return result;\n }\n\n public clearConnections(): void {\n this.removeMyself();\n this._others.clear();\n this._idMap.fromClientId.clear();\n this._idMap.fromConnectionId.clear();\n }\n\n public deleteConnection(connectionId: string): DeleteConnectionResult<TIO, TPresence> | null {\n const clientId = this.getClientId(connectionId);\n\n this._deleteConnectionId(connectionId);\n\n // We expect that a clientId should always exist here, but we'll just check this anyways\n if (!clientId) return null;\n\n const userInfo = this._others.get(clientId) ?? null;\n\n // We also expect this should always exist here, but we'll just check this anyways\n if (!userInfo) return null;\n\n const remaining = this._getConnectionIds(clientId)?.size ?? 0;\n const result: DeleteConnectionResult<TIO, TPresence> = {\n clientId,\n remaining,\n data: userInfo,\n };\n\n /**\n * @description A user may have multiple websocket connections open. If one connection is\n * dropped, the user may still be connected via another websocket (e.g. on anther browser\n * tab). In this case, we just want to remove the connection mapping for the connection\n * that is dropped, but keep the remaining connections and the user.\n * @date April 16, 2025\n */\n if (!remaining) this._others.delete(clientId);\n\n return result;\n }\n\n /**\n * @description The client id is either the user's id (if authorized) or the connection id\n * otherwise. This is so that, despite having multiple connections, the user will only have one\n * presence to all other users.\n * @date April 16, 2025\n */\n public getClientId(connectionId: string): string | null;\n public getClientId(userInfo: UserInfo<TIO, TPresence>): string;\n public getClientId(input: string | UserInfo<TIO, TPresence>): string | null {\n if (typeof input !== \"string\") {\n const userInfo = input;\n const connectionId = userInfo.connectionId;\n const userId = userInfo.user?.id ?? null;\n\n return userId ?? connectionId;\n }\n\n const connectionId = input;\n\n if (this._myself?.connectionId === connectionId) {\n return this.getClientId(this._myself);\n }\n\n const clientId = this._idMap.fromConnectionId.get(connectionId) ?? null;\n\n return clientId;\n }\n\n public getOther(connectionId: string): UserInfo<TIO, TPresence> | null {\n const clientId = this.getClientId(connectionId);\n\n if (!clientId) return null;\n\n return this._others.get(clientId) ?? null;\n }\n\n public getOthers(): readonly UserInfo<TIO, TPresence>[] {\n return Array.from(this._others.values());\n }\n\n public patchPresence(connectionId: string, patch: Partial<TPresence>): TPresence | null {\n const clientId = this.getClientId(connectionId);\n const myClientId = !!this._myself ? this.getClientId(this._myself) : null;\n\n if (!clientId) return null;\n if (myClientId === clientId) return this.updateMyPresence(patch);\n\n const other = this._others.get(clientId);\n\n if (!other) return null;\n\n const cleanedPatch = pickBy(patch ?? {}, (value) => typeof value !== \"undefined\");\n const cleanedPresence = pickBy(\n other.presence ?? {},\n (value) => typeof value !== \"undefined\",\n );\n const presence = {\n ...this.initialPresence,\n ...cleanedPresence,\n ...cleanedPatch,\n } as TPresence;\n const validated = this._presence ? this._presence.parse(presence) : presence;\n\n /**\n * !HACK\n * @description We're patching internal metadata back into the presence field so it doesn't\n * get stripped from validation\n * @date May 13, 2025\n */\n if (!!presence[PLUV_PRESENCE_META_KEY]) {\n (validated as any)[PLUV_PRESENCE_META_KEY] = presence[PLUV_PRESENCE_META_KEY];\n }\n\n this._others.set(clientId, { ...other, presence: validated });\n\n return validated;\n }\n\n public removeMyself(): void {\n if (!this._myself) return;\n\n const connectionId = this._myself.connectionId;\n\n this._deleteConnectionId(connectionId);\n this._myself = null;\n }\n\n public setMyself(userInfo: OptionalProps<UserInfo<TIO, TPresence>, \"presence\">): void {\n const connectionId = userInfo.connectionId;\n const presence = userInfo.presence ?? this.initialPresence;\n\n this._myself = { ...userInfo, presence };\n this._myPresence = presence;\n\n const clientId = this.getClientId(this._myself);\n\n this._setConnectionId(connectionId, clientId);\n }\n\n public setPresence(connectionId: string, presence: TPresence): void {\n const clientId = this.getClientId(connectionId);\n const myClientId = !!this._myself ? this.getClientId(this._myself) : null;\n\n if (!clientId) return;\n\n if (myClientId === clientId) {\n if (!this._myself) return;\n\n this._myself.presence = presence;\n this._myPresence = presence;\n\n return;\n }\n\n const other = this._others.get(clientId);\n\n if (!other) return;\n\n this._others.set(clientId, { ...other, presence });\n }\n\n /**\n * @description This method need not care about being connected. This is because a user should\n * be able to view and update their own presence without being online\n * @date April 20, 2025\n */\n public updateMyPresence(patch: Partial<TPresence>): TPresence {\n const cleanedPatch = pickBy(patch, (value) => typeof value !== \"undefined\");\n const cleanedPresence = pickBy(this._myPresence, (value) => typeof value !== \"undefined\");\n const updated = {\n ...this.initialPresence,\n ...cleanedPresence,\n ...cleanedPatch,\n } as TPresence;\n const bytes = new TextEncoder().encode(JSON.stringify(updated)).length;\n\n if (!!this._limits.presenceMaxSize && bytes > this._limits.presenceMaxSize) {\n throw new Error(\n `Large presence. Presence must be at most 512 bytes. Current size: ${bytes.toLocaleString()}`,\n );\n }\n\n this._myPresence = updated;\n\n if (!this._myself) return updated;\n\n this._myself.presence = updated;\n\n return updated;\n }\n\n private _deleteConnectionId(connectionId: string): void {\n const clientId = this.getClientId(connectionId);\n\n this._idMap.fromConnectionId.delete(connectionId);\n\n if (!clientId) return;\n\n const set = this._idMap.fromClientId.get(clientId) ?? null;\n\n if (!set) return;\n\n set.delete(connectionId);\n\n if (!!set.size) return;\n\n this._idMap.fromClientId.delete(clientId);\n }\n\n private _getConnectionIds(clientId: string): Set<[connectionId: string][0]> | null {\n const set = this._idMap.fromClientId.get(clientId);\n\n return set ?? null;\n }\n\n private _setConnectionId(\n connectionId: string,\n clientId: string,\n ): Set<[connectionId: string][0]> {\n const set = this._idMap.fromClientId.get(clientId) ?? new Set<string>();\n const updated = set.add(connectionId);\n\n this._idMap.fromConnectionId.set(connectionId, clientId);\n this._idMap.fromClientId.set(clientId, updated);\n\n return updated;\n }\n}\n","import type {\n Id,\n IOLike,\n JsonObject,\n OtherSubscriptionCallback,\n OthersSubscriptionCallback,\n OthersSubscriptionEvent,\n UserInfo,\n} from \"@pluv/types\";\nimport type { Subject } from \"wonka\";\nimport { makeSubject, subscribe, TypeOfSource } from \"wonka\";\n\nexport class UsersNotifier<TIO extends IOLike, TPresence extends Record<string, any> = {}> {\n public readonly others = makeSubject<{\n others: readonly Id<UserInfo<TIO, TPresence>>[];\n event: OthersSubscriptionEvent<TIO, TPresence>;\n }>();\n\n private _otherSubjects = new Map<\n [clientId: string][0],\n Subject<Id<UserInfo<TIO, TPresence>> | null>\n >();\n\n public clear(): void {\n this._otherSubjects.forEach((subject) => {\n subject.next(null);\n });\n\n this._otherSubjects.clear();\n }\n\n public delete(clientId: string): void {\n this.other(clientId).next(null);\n this._otherSubjects.delete(clientId);\n }\n\n public other(clientId: string): Subject<Id<UserInfo<TIO, TPresence>> | null> {\n const subject = this._otherSubjects.get(clientId);\n\n if (subject) return subject;\n\n const newSubject = makeSubject<Id<UserInfo<TIO, TPresence>>>();\n\n this._otherSubjects.set(clientId, newSubject);\n\n return newSubject;\n }\n\n public subscribeOther(\n clientId: string,\n callback: OtherSubscriptionCallback<TIO, TPresence>,\n ): () => void {\n const source = this.other(clientId).source;\n const subscription = subscribe(callback)(source);\n\n return subscription.unsubscribe;\n }\n\n public subscribeOthers(callback: OthersSubscriptionCallback<TIO, TPresence>): () => void {\n const subscription = subscribe<TypeOfSource<typeof this.others.source>>(\n ({ others, event }) => callback(others, event),\n )(this.others.source);\n\n return subscription.unsubscribe;\n }\n}\n","import type {\n AbstractCrdtDocFactory,\n InferCrdtJson,\n InferDoc,\n InferDocLike,\n InferStorage,\n NoopCrdtDocFactory,\n} from \"@pluv/crdt\";\nimport type {\n BaseIOEventRecord,\n BaseUser,\n BroadcastProxy,\n CrdtDocLike,\n EventMessage,\n EventNotifierSubscriptionCallback,\n EventProxy,\n IOEventMessage,\n IOLike,\n Id,\n InferIOAuthorize,\n InferIOAuthorizeUser,\n InferIOInput,\n InferIOOutput,\n InputZodLike,\n JsonObject,\n MergeEvents,\n OptionalProps,\n OtherSubscriptionCallback,\n OthersSubscriptionCallback,\n RoomEventListenerMap,\n RoomLike,\n StateNotifierSubjects,\n StorageProxy,\n StorageRootSubscriptionCallback,\n StorageSubscriptionCallback,\n SubscribeProxy,\n SubscriptionCallback,\n UpdateMyPresenceAction,\n UserInfo,\n WebSocketConnection,\n WebSocketState,\n} from \"@pluv/types\";\nimport { ConnectionState, StorageState } from \"@pluv/types\";\nimport type { AbstractStorageStore } from \"./AbstractStorageStore\";\nimport type { CrdtManagerOptions } from \"./CrdtManager\";\nimport { CrdtManager } from \"./CrdtManager\";\nimport { CrdtNotifier } from \"./CrdtNotifier\";\nimport { EventNotifier } from \"./EventNotifier\";\nimport { ListenerManager } from \"./ListenerManager\";\nimport { PluvProcedure } from \"./PluvProcedure\";\nimport type { PluvRouterEventConfig } from \"./PluvRouter\";\nimport { PluvRouter } from \"./PluvRouter\";\nimport { StateNotifier } from \"./StateNotifier\";\nimport { StorageStore } from \"./StorageStore\";\nimport type {\n AuthorizationState,\n EventResolver,\n EventResolverContext,\n InternalSubscriptions,\n PluvClientLimits,\n PublicKey,\n WithMetadata,\n} from \"./types\";\nimport type { UsersManagerConfig } from \"./UsersManager\";\nimport { UsersManager } from \"./UsersManager\";\nimport { UsersNotifier } from \"./UsersNotifier\";\nimport { debounce } from \"./utils\";\n\nconst ADD_TO_STORAGE_STATE_DEBOUNCE_MS = 1_000;\nconst HEARTBEAT_INTERVAL_MS = 10_000;\nconst PONG_TIMEOUT_MS = 2_000;\n\nconst RECONNECT_TIMEOUT_MS = 5_000;\nconst MIN_RECONNECT_TIMEOUT_MS = 1_000;\nconst MAX_RECONNECT_TIMEOUT_MS = 60_000;\n\nconst ORIGIN_INITIALIZED = \"$initialized\";\nconst ORIGIN_STORAGE_UPDATED = \"$storageUpdated\";\n\ndeclare global {\n var process: {\n env: {\n [key: string]: string | undefined;\n };\n };\n}\n\nexport const DEFAULT_PLUV_CLIENT_ADDON = <\n TIO extends IOLike,\n TMetadata extends JsonObject,\n TPresence extends Record<string, any>,\n TCrdt extends AbstractCrdtDocFactory<any, any>,\n>(\n input: PluvRoomAddonInput<TIO, TMetadata, TPresence, TCrdt>,\n): PluvRoomAddonResult => ({\n storage: new StorageStore(input.room.id),\n});\n\ninterface WindowListeners {\n onNavigatorOffline: () => void;\n onNavigatorOnline: () => void;\n onVisibilityChange: () => void;\n onWindowFocus: () => void;\n}\n\ninterface WebSocketListeners {\n onClose: (event: CloseEvent) => void;\n onError: (event: Event) => void;\n onMessage: (message: MessageEvent<string>) => void;\n onOpen: (event: Event) => void;\n}\n\ninterface IntervalIds {\n heartbeat: number | null;\n}\n\ninterface TimeoutIds {\n pong: number | null;\n reconnect: number | null;\n}\n\nexport interface EndpointParams<TMetadata extends JsonObject> {\n metadata: TMetadata;\n room: string;\n}\nexport type AuthEndpoint<TMetadata extends JsonObject> =\n | string\n | ((params: EndpointParams<TMetadata>) => string | FetchOptions)\n | true;\nexport type WsEndpoint<TMetadata extends JsonObject> =\n | string\n | ((params: EndpointParams<TMetadata>) => string);\n\ntype FetchOptions = { url: string; options?: RequestInit };\n\nexport type RoomEndpoints<TIO extends IOLike<any, any, any>, TMetadata extends JsonObject> = {\n wsEndpoint?: WsEndpoint<TMetadata>;\n} & (InferIOAuthorizeUser<InferIOAuthorize<TIO>> extends BaseUser\n ? { authEndpoint: AuthEndpoint<TMetadata> }\n : { authEndpoint?: undefined });\n\ninterface InternalListeners {\n onAuthorizationFail: (error: Error) => void;\n}\n\nexport type PluvRoomAddon<\n TIO extends IOLike,\n TMetadata extends JsonObject,\n TPresence extends Record<string, any>,\n TCrdt extends AbstractCrdtDocFactory<any, any>,\n> = (input: PluvRoomAddonInput<TIO, TMetadata, TPresence, TCrdt>) => Partial<PluvRoomAddonResult>;\n\nexport interface PluvRoomAddonInput<\n TIO extends IOLike,\n TMetadata extends JsonObject,\n TPresence extends Record<string, any>,\n TCrdt extends AbstractCrdtDocFactory<any, any>,\n> {\n room: PluvRoom<TIO, TMetadata, TPresence, TCrdt>;\n}\n\nexport interface PluvRoomAddonResult {\n storage: AbstractStorageStore;\n}\n\nexport type PluvRoomDebug<TIO extends IOLike> = Id<{\n output: readonly (keyof InferIOOutput<TIO>)[];\n input: readonly (keyof InferIOInput<TIO>)[];\n}>;\n\ntype GetPublickKeyParams<TMetadata extends JsonObject = {}> = WithMetadata<TMetadata>;\ntype GetAuthEndpointParams<TMetadata extends JsonObject = {}> = WithMetadata<TMetadata>;\ntype GetWsEndpointParams<TMetadata extends JsonObject = {}> = WithMetadata<TMetadata>;\n\nexport type RoomConnectParams<TMetadata extends JsonObject = {}> = keyof TMetadata extends never\n ? []\n : [WithMetadata<TMetadata>];\n\nexport interface ReconnectTimeoutMsParams {\n attempts: number;\n}\nexport type ReconnectTimeoutMs = number | ((params: ReconnectTimeoutMsParams) => number);\n\nexport type RoomConfig<\n TIO extends IOLike,\n TMetadata extends JsonObject,\n TPresence extends Record<string, any>,\n TCrdt extends AbstractCrdtDocFactory<any, any>,\n TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>>,\n> = Id<\n {\n addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TCrdt>[];\n debug?: boolean | PluvRoomDebug<TIO>;\n limits: PluvClientLimits;\n onAuthorizationFail?: (error: Error) => void;\n metadata?: InputZodLike<TMetadata>;\n publicKey?: PublicKey<TMetadata>;\n reconnectTimeoutMs?: ReconnectTimeoutMs;\n router?: PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents>;\n } & RoomEndpoints<TIO, TMetadata> &\n Pick<CrdtManagerOptions<TCrdt>, \"initialStorage\"> &\n UsersManagerConfig<TPresence>\n>;\n\nexport class PluvRoom<\n TIO extends IOLike<any, any, any>,\n TMetadata extends JsonObject = {},\n TPresence extends Record<string, any> = {},\n TCrdt extends AbstractCrdtDocFactory<any, any> = NoopCrdtDocFactory,\n TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {},\n> implements RoomLike<TIO, InferDoc<TCrdt>, TPresence, InferStorage<TCrdt>>\n{\n readonly _endpoints: RoomEndpoints<TIO, TMetadata>;\n\n public readonly id: string;\n public readonly metadata?: InputZodLike<TMetadata>;\n\n private readonly _crdtManager: CrdtManager<TCrdt>;\n private readonly _crdtNotifier = new CrdtNotifier<InferStorage<TCrdt>>();\n private readonly _debug: boolean | PluvRoomDebug<TIO>;\n private readonly _eventNotifier = new EventNotifier<MergeEvents<TEvents, TIO>>();\n private readonly _intervals: IntervalIds = {\n heartbeat: null,\n };\n private readonly _limits: PluvClientLimits;\n private readonly _listenerManager = new ListenerManager();\n private readonly _listeners: InternalListeners;\n private readonly _publicKey: PublicKey<TMetadata> | null = null;\n private readonly _reconnectTimeoutMs: ReconnectTimeoutMs;\n private readonly _router: PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents>;\n private readonly _stateNotifier = new StateNotifier<TIO, TPresence>();\n private readonly _storageStore: AbstractStorageStore;\n private readonly _subscriptions: InternalSubscriptions = {\n observeCrdt: null,\n };\n private readonly _timeouts: TimeoutIds = {\n pong: null,\n reconnect: null,\n };\n private readonly _usersManager: UsersManager<TIO, TPresence>;\n private readonly _usersNotifier = new UsersNotifier<TIO, TPresence>();\n\n private _lastMetadata: TMetadata | null = null;\n private _state: WebSocketState<TIO> = {\n authorization: {\n token: null,\n user: null,\n },\n connection: {\n attempts: 0,\n id: null,\n state: ConnectionState.Untouched,\n },\n storage: {\n state: StorageState.Unavailable,\n },\n webSocket: null,\n };\n private _windowListeners: WindowListeners | null = null;\n private _wsListeners: WebSocketListeners | null = null;\n\n constructor(room: string, options: RoomConfig<TIO, TMetadata, TPresence, TCrdt, TEvents>) {\n const {\n addons = [],\n authEndpoint,\n debug = false,\n initialPresence,\n initialStorage,\n limits,\n metadata,\n onAuthorizationFail,\n presence,\n publicKey,\n reconnectTimeoutMs = RECONNECT_TIMEOUT_MS,\n router,\n wsEndpoint,\n } = options;\n\n this.id = room;\n this.metadata = metadata;\n\n const addon = this._getAddon(addons);\n const { storage } = {\n ...DEFAULT_PLUV_CLIENT_ADDON({ room: this }),\n ...addon({ room: this }),\n };\n this._storageStore = storage;\n\n this._debug = debug;\n this._endpoints = { authEndpoint, wsEndpoint } as RoomEndpoints<TIO, TMetadata>;\n this._limits = limits;\n this._reconnectTimeoutMs = reconnectTimeoutMs;\n\n if (!!publicKey) this._publicKey = publicKey;\n\n this._listeners = {\n onAuthorizationFail: (error) => {\n onAuthorizationFail?.(error);\n },\n };\n\n this._router =\n router ??\n (new PluvRouter({}) as PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents>);\n this._usersManager = new UsersManager<TIO, TPresence>({\n initialPresence,\n limits: this._limits,\n presence,\n });\n this._crdtManager = new CrdtManager<TCrdt>({ initialStorage });\n }\n\n public get webSocket(): WebSocket | null {\n return this._state.webSocket;\n }\n\n public addEventListener<TKind extends keyof RoomEventListenerMap>(\n kind: TKind,\n handler: RoomEventListenerMap[TKind],\n ): () => void {\n return this._listenerManager.subscribe(kind, handler as any);\n }\n\n public broadcast = new Proxy(\n async <TEvent extends keyof InferIOInput<MergeEvents<TEvents, TIO>>>(\n event: TEvent,\n data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[TEvent]>,\n ): Promise<void> => {\n if (!this._state.webSocket) return;\n if (this._state.connection.state !== ConnectionState.Open) return;\n\n const type = event.toString();\n\n const procedure = this._router._defs.events[type] as PluvProcedure<\n TIO,\n any,\n any,\n TPresence,\n TCrdt,\n \"\"\n > | null;\n\n if (!procedure?.config.broadcast) {\n this._sendMessage({ data, type });\n\n return;\n }\n\n const myself = this._usersManager.myself;\n\n if (!myself) return;\n\n const parsed = procedure.config.input ? procedure.config.input.parse(data) : data;\n const context: EventResolverContext<TIO, TPresence, InferDocLike<TCrdt>> = {\n doc: this._crdtManager.doc,\n others: this._usersManager.getOthers(),\n room: this.id,\n user: myself,\n };\n\n const output = await (\n procedure.config.broadcast as EventResolver<\n TIO,\n any,\n any,\n TPresence,\n InferStorage<TCrdt>\n >\n )(parsed, context);\n\n Object.entries(output).forEach(([_type, _data]) => {\n this._sendMessage({ data: _data, type: _type });\n });\n },\n {\n get(fn, prop) {\n return async (\n data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[any]>,\n ): Promise<void> => {\n return await fn(prop, data);\n };\n },\n },\n ) as BroadcastProxy<TIO, TEvents>;\n\n public canRedo = (): boolean => {\n return !!this._crdtManager.doc.canRedo();\n };\n\n public canUndo = (): boolean => {\n return !!this._crdtManager.doc.canUndo();\n };\n\n public async connect(...args: RoomConnectParams<TMetadata>): Promise<void> {\n const params = (args[0] ?? {}) as WithMetadata<TMetadata>;\n const metadata = params.metadata as TMetadata;\n\n this._setMetadata(metadata);\n\n const canConnect = [\n ConnectionState.Closed,\n ConnectionState.Unavailable,\n ConnectionState.Untouched,\n ].some((state) => this._state.connection.state === state);\n\n if (!canConnect) return;\n\n this._clearTimeout(this._timeouts.reconnect);\n this._updateState((oldState) => {\n oldState.connection.state = ConnectionState.Connecting;\n\n return oldState;\n });\n\n await this._storageStore.initialize();\n\n const wsEndpoint = this._getWsEndpoint(this.id, params);\n const url = new URL(wsEndpoint);\n\n let authToken: string | null = null;\n let webSocket: WebSocket;\n\n try {\n const publicKey = this._getPublicKey(params);\n\n authToken = await this._getAuthorization(this.id, params);\n\n if (authToken) url.searchParams.set(\"token\", encodeURIComponent(authToken));\n if (publicKey) url.searchParams.set(\"public_key\", encodeURIComponent(publicKey));\n\n webSocket = new WebSocket(url.toString());\n } catch (err) {\n this._updateState((oldState) => {\n oldState.connection.attempts += 1;\n\n return oldState;\n });\n\n await this._storageStore.destroy();\n this._pollReconnect();\n await Promise.resolve(this._onAuthorizationFail(err));\n\n return;\n }\n\n this._updateState((oldState) => {\n oldState.connection.attempts = 0;\n oldState.authorization.token = authToken;\n oldState.storage.state = StorageState.Loading;\n oldState.webSocket = webSocket;\n\n return oldState;\n });\n\n this._attachWindowListeners();\n this._attachWsListeners();\n }\n\n public async disconnect(): Promise<void> {\n if (!this._state.webSocket) return;\n\n const canDisconnect = [ConnectionState.Connecting, ConnectionState.Open].some(\n (state) => this._state.connection.state === state,\n );\n\n if (!canDisconnect) return;\n\n this._closeWs();\n this._clearInterval(this._intervals.heartbeat);\n this._clearTimeout(this._timeouts.reconnect);\n this._updateState((oldState) => {\n oldState.authorization.token = null;\n oldState.connection.state = ConnectionState.Closed;\n oldState.storage.state =\n oldState.storage.state === StorageState.Synced\n ? StorageState.Offline\n : StorageState.Unavailable;\n oldState.webSocket = null;\n\n return oldState;\n });\n\n await this._storageStore.destroy();\n this._crdtManager.destroy();\n this._stateNotifier.subjects[\"my-presence\"].next(null);\n }\n\n public getConnection = (): WebSocketConnection => {\n // Create a read-only clone of the connection state\n return Object.freeze(JSON.parse(JSON.stringify(this._state.connection)));\n };\n\n public getDoc = (): CrdtDocLike<InferDoc<TCrdt>, InferStorage<TCrdt>> => {\n return this._crdtManager.doc;\n };\n\n public getMyPresence = (): TPresence => {\n return this._usersManager.myPresence;\n };\n\n public getMyself = (): Id<UserInfo<TIO, TPresence>> | null => {\n return this._usersManager.myself;\n };\n\n public getOther = (connectionId: string): Id<UserInfo<TIO, TPresence>> | null => {\n return this._usersManager.getOther(connectionId);\n };\n\n public getOthers = (): readonly Id<UserInfo<TIO, TPresence>>[] => {\n return this._usersManager.getOthers();\n };\n\n public getStorage = <TKey extends keyof InferStorage<TCrdt>>(\n type: TKey,\n ): InferStorage<TCrdt>[TKey] | null => {\n const sharedType = this._crdtManager.get(type);\n\n if (typeof sharedType === \"undefined\") return null;\n\n return sharedType;\n };\n\n public getStorageJson(): InferCrdtJson<InferStorage<TCrdt>> | null;\n public getStorageJson<TKey extends keyof InferStorage<TCrdt>>(\n type: TKey,\n ): InferCrdtJson<InferStorage<TCrdt>[TKey]> | null;\n public getStorageJson<TKey extends keyof InferStorage<TCrdt>>(type?: TKey) {\n if (this._state.connection.id === null) return null;\n if (typeof type === \"undefined\") return this._crdtManager.doc.toJson();\n\n const sharedType = this._crdtManager.get(type);\n\n if (typeof sharedType === \"undefined\") return null;\n\n return this._crdtManager.doc.toJson(type);\n }\n\n public getStorageLoaded = (): boolean => {\n return (\n this._state.storage.state === StorageState.Offline ||\n this._state.storage.state === StorageState.Synced\n );\n };\n\n public redo = (): void => {\n this._crdtManager.doc.redo();\n };\n\n public subscribe = new Proxy(\n <TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(\n name: TSubject,\n callback: SubscriptionCallback<TIO, TPresence, TSubject>,\n ): (() => void) => this._stateNotifier.subscribe(name, callback),\n {\n get: (fn, prop) => {\n if (prop === \"connection\") {\n return (callback: SubscriptionCallback<TIO, TPresence, \"connection\">) => {\n return fn(\"connection\", callback);\n };\n }\n\n if (prop === \"myPresence\") {\n return (callback: SubscriptionCallback<TIO, TPresence, \"my-presence\">) => {\n return fn(\"my-presence\", callback);\n };\n }\n\n if (prop === \"myself\") {\n return (callback: SubscriptionCallback<TIO, TPresence, \"myself\">) => {\n return fn(\"myself\", callback);\n };\n }\n\n if (prop === \"others\") {\n return (callback: OthersSubscriptionCallback<TIO, TPresence>) => {\n return this._usersNotifier.subscribeOthers(callback);\n };\n }\n\n if (prop === \"storageLoaded\") {\n return (callback: SubscriptionCallback<TIO, TPresence, \"storage-loaded\">) => {\n return fn(\"storage-loaded\", callback);\n };\n }\n\n if (prop === \"event\") return this._event;\n if (prop === \"other\") return this._other;\n if (prop === \"storage\") return this.#_storage;\n },\n },\n ) as SubscribeProxy<TIO, TPresence, InferStorage<TCrdt>, TEvents>;\n\n public transact = (fn: (storage: InferStorage<TCrdt>) => void, origin?: string): void => {\n const _origin = origin ?? this._state.connection.id;\n\n /**\n * !HACK\n * @description Don't transact anything if there is no origin, because\n * that means the user isn't connected yet. This will mean events are\n * lost unfortunately.\n * @date September 23, 2023\n */\n if (typeof _origin !== \"string\") return;\n\n this._crdtManager.doc.transact(() => {\n const storage = this._crdtManager.doc.get();\n\n fn(storage);\n }, _origin);\n };\n\n public undo = (): void => {\n this._crdtManager.doc.undo();\n };\n\n public updateMyPresence = (presence: UpdateMyPresenceAction<TPresence>): void => {\n const newPresence =\n typeof presence === \"function\" ? presence(this.getMyPresence()) : presence;\n\n this._usersManager.updateMyPresence(newPresence);\n\n const myPresence = this._usersManager.myPresence;\n const myself = this._usersManager.myself ?? null;\n\n this._stateNotifier.subjects[\"my-presence\"].next(myPresence);\n if (!!myself) this._stateNotifier.subjects.myself.next(myself);\n\n this.broadcast(\n \"$updatePresence\" as keyof InferIOInput<MergeEvents<TEvents, TIO>>,\n { presence: newPresence } as any,\n );\n };\n\n private async _addToStorageStore(update: string): Promise<void> {\n await this._storageStore.addUpdate(update);\n\n this._flattenStorageStore();\n }\n\n private _attachWindowListeners(): void {\n if (typeof window === \"undefined\") return;\n if (typeof document === \"undefined\") return;\n\n this._windowListeners = this._windowListeners || {\n onNavigatorOffline: this._onNavigatorOffline.bind(this),\n onNavigatorOnline: this._onNavigatorOnline.bind(this),\n onVisibilityChange: this._onVisibilityChange.bind(this),\n onWindowFocus: this._onWindowFocus.bind(this),\n };\n\n window.addEventListener(\"offline\", this._windowListeners.onNavigatorOffline);\n window.addEventListener(\"online\", this._windowListeners.onNavigatorOnline);\n document.addEventListener(\"visibilitychange\", this._windowListeners.onVisibilityChange);\n window.addEventListener(\"focus\", this._windowListeners.onWindowFocus);\n }\n\n private _attachWsListeners(): void {\n if (!this._state.webSocket) return;\n\n this._wsListeners = this._wsListeners || {\n onClose: this._onClose.bind(this),\n onError: this._onError.bind(this),\n onMessage: this._onMessage.bind(this),\n onOpen: this._onOpen.bind(this),\n };\n\n this._state.webSocket.addEventListener(\"close\", this._wsListeners.onClose);\n this._state.webSocket.addEventListener(\"error\", this._wsListeners.onError);\n this._state.webSocket.addEventListener(\"message\", this._wsListeners.onMessage);\n this._state.webSocket.addEventListener(\"open\", this._wsListeners.onOpen);\n }\n\n private _clearInterval(interval: number | null) {\n if (Number.isNaN(interval)) return;\n\n clearInterval(interval as number);\n }\n\n private _clearTimeout(timeout: number | null) {\n if (Number.isNaN(timeout)) return;\n\n clearTimeout(timeout as number);\n }\n\n private _closeWs(): void {\n this._subscriptions.observeCrdt?.();\n\n this._clearInterval(this._intervals.heartbeat);\n this._clearTimeout(this._timeouts.pong);\n this._detachWindowListeners();\n this._usersManager.removeMyself();\n this._usersManager.clearConnections();\n this._usersNotifier.clear();\n this._stateNotifier.subjects.myself.next(null);\n this._stateNotifier.subjects.others.next([]);\n this._usersNotifier.others.next({ others: [], event: { kind: \"clear\" } });\n this._state.webSocket?.close();\n\n this._detachWsListeners();\n }\n\n private _detachWindowListeners(): void {\n if (!this._windowListeners) return;\n if (typeof window === \"undefined\") return;\n if (typeof document === \"undefined\") return;\n\n window.removeEventListener(\"offline\", this._windowListeners.onNavigatorOffline);\n window.removeEventListener(\"online\", this._windowListeners.onNavigatorOnline);\n document.removeEventListener(\"visibilitychange\", this._windowListeners.onVisibilityChange);\n window.removeEventListener(\"focus\", this._windowListeners.onWindowFocus);\n\n this._windowListeners = null;\n }\n\n private _detachWsListeners(): void {\n if (!this._state.webSocket || !this._wsListeners) return;\n\n this._state.webSocket.removeEventListener(\"close\", this._wsListeners.onClose);\n this._state.webSocket.removeEventListener(\"error\", this._wsListeners.onError);\n this._state.webSocket.removeEventListener(\"message\", this._wsListeners.onMessage);\n this._state.webSocket.removeEventListener(\"open\", this._wsListeners.onOpen);\n\n this._wsListeners = null;\n }\n\n private _emitSharedTypes(): void {\n const sharedTypes = this._crdtManager.doc.get();\n\n const storageRoot = Object.keys(sharedTypes).reduce(\n (acc, prop) => {\n const serialized = this._crdtManager.doc.toJson(prop);\n\n this._crdtNotifier.subject(prop).next(serialized);\n\n return { ...acc, [prop]: serialized };\n },\n {} as { [P in keyof InferStorage<TCrdt>]: InferCrdtJson<InferStorage<TCrdt>[P]> },\n );\n\n this._crdtNotifier.rootSubject.next(storageRoot);\n }\n\n private _event = new Proxy(\n <TEvent extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(\n event: TEvent,\n callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, any>,\n ): (() => void) => this._eventNotifier.subscribe(event, callback),\n {\n get(fn, prop) {\n return (\n callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, any>,\n ): (() => void) => fn(prop as any, callback);\n },\n },\n ) as EventProxy<TIO, TEvents>;\n\n private _flattenStorageStore = debounce(\n async (): Promise<void> => {\n const shouldFlatten = await this._storageStore.getShouldFlatten();\n\n if (!shouldFlatten) return;\n\n const updates = await this._storageStore.getUpdates();\n\n this._crdtManager.applyUpdate({ update: updates, origin: this });\n\n const encodedState = this._crdtManager.doc.getEncodedState();\n\n await this._storageStore.flatten(encodedState);\n },\n { wait: ADD_TO_STORAGE_STATE_DEBOUNCE_MS },\n );\n\n private _getAddon = (\n addons: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TCrdt>[],\n ): PluvRoomAddon<TIO, TMetadata, TPresence, TCrdt> => {\n return addons.reduce<PluvRoomAddon<TIO, TMetadata, TPresence, TCrdt>>(\n (acc, addon) => () => ({\n ...acc({ room: this }),\n ...addon({ room: this }),\n }),\n DEFAULT_PLUV_CLIENT_ADDON,\n );\n };\n\n private _getAuthFetchOptions(\n room: string,\n params: GetAuthEndpointParams<TMetadata>,\n ): FetchOptions | null {\n const metadata = params.metadata as TMetadata;\n\n if (typeof this._endpoints.authEndpoint === \"undefined\") return null;\n\n if (this._endpoints.authEndpoint === true) {\n return {\n url: `/api/pluv/authorize?room=${room}`,\n options: {},\n };\n }\n\n if (typeof this._endpoints.authEndpoint === \"string\") {\n return {\n url: this._endpoints.authEndpoint,\n options: {},\n };\n }\n\n const result = this._endpoints.authEndpoint({ metadata, room });\n\n return typeof result === \"string\" ? { url: result, options: {} } : result;\n }\n\n private async _getAuthorization(\n room: string,\n params: GetAuthEndpointParams<TMetadata>,\n ): Promise<string | null> {\n const fetchOptions = this._getAuthFetchOptions(room, params);\n\n if (!fetchOptions) return null;\n\n const { url, options } = fetchOptions;\n const res = await fetch(url, options);\n\n if (!res.ok || res.status !== 200) throw new Error(\"Room is unauthorized\");\n\n try {\n return await res.text().then((text) => text.trim());\n } catch (err) {\n throw new Error(err instanceof Error ? err.message : \"Room is unauthorized\");\n }\n }\n\n private _getPublicKey(params: GetPublickKeyParams<TMetadata>): string | null {\n const metadata = params.metadata as TMetadata;\n\n if (!this._publicKey) return null;\n if (typeof this._publicKey === \"string\") return this._publicKey;\n\n return this._publicKey({ metadata });\n }\n\n private _getWsEndpoint(room: string, params: GetWsEndpointParams<TMetadata>): string {\n const metadata = params.metadata as TMetadata;\n\n switch (typeof this._endpoints.wsEndpoint) {\n case \"undefined\":\n return !!this._getPublicKey(params)\n ? `wss://rooms.pluv.io/api/room/${room}`\n : `/api/pluv/room/${room}`;\n case \"string\":\n return this._endpoints.wsEndpoint;\n default:\n return this._endpoints.wsEndpoint({ metadata, room });\n }\n }\n\n private _handleExit(message: IOEventMessage<TIO>): void {\n const { connectionId } = message;\n\n if (!connectionId) return;\n // Should not reach here\n if (!this._state.webSocket) throw new Error(\"Could not find WebSocket\");\n\n const clientId = this._usersManager.getClientId(connectionId);\n const deleted = this._usersManager.deleteConnection(connectionId);\n const others = this._usersManager.getOthers();\n\n this._stateNotifier.subjects.others.next(others);\n\n // Should not reach here\n if (!deleted) {\n console.warn(\"Could not identify exited connection\");\n return;\n }\n\n const { data: user, remaining } = deleted;\n\n this._usersNotifier.others.next({\n others,\n event: { kind: \"leave\", user },\n });\n\n /**\n * @description A single user can have multiple connections. So we're checking that\n * there isn't a remaining connection before choosing to delete that user for other\n * connected participants.\n * @date April 16, 2025\n */\n if (!remaining && !!clientId) this._usersNotifier.delete(clientId);\n }\n\n private _handlePresenceUpdatedMessage(message: IOEventMessage<TIO>): void {\n const { connectionId } = message;\n\n if (!connectionId) return;\n // Should not reach here\n if (!this._state.webSocket) throw new Error(\"Could not find WebSocket\");\n\n const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>[\"$presenceUpdated\"];\n const myself = this._usersManager.myself ?? null;\n\n /**\n * !HACK\n * @description We're going to have the user's own presence be patched only via local calls\n * to this.updateMyPresence. So we'll not update the user's presence in this handler to\n * avoid weird update delays to the user's own presence.\n *\n * However, we will patch the user's own presence if the user has updated their own\n * presence via another connection that is not this one (e.g. if the user has opened\n * another connection in another browser tab/window somewhere).\n * @date April 2, 2025\n */\n if (myself?.connectionId === connectionId) return;\n\n const updated = this._usersManager.patchPresence(connectionId, data.presence as TPresence);\n const myClientId = !!myself ? this._usersManager.getClientId(myself) : null;\n const clientId = this._usersManager.getClientId(connectionId);\n\n if (!!clientId && myClientId === clientId) {\n this._stateNotifier.subjects[\"my-presence\"].next(updated);\n this._stateNotifier.subjects.myself.next(this._usersManager.myself);\n\n return;\n }\n\n if (!!clientId) {\n const other = this._usersManager.getOther(connectionId);\n const others = this._usersManager.getOthers();\n\n this._usersNotifier.other(clientId).next(other);\n this._stateNotifier.subjects.others.next(others);\n\n if (!!other) {\n this._usersNotifier.others.next({\n others,\n event: { kind: \"update\", user: other },\n });\n }\n\n return;\n }\n\n /**\n * !HACK\n * @description User could not be found. Add the connection to keep others up-to-date\n * @date April 19, 2025\n */\n const added = this._usersManager.addConnection({\n connectionId,\n presence: data.presence as TPresence,\n user: message.user as Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>,\n });\n const other = this._usersManager.getOther(connectionId);\n const others = this._usersManager.getOthers();\n\n this._usersNotifier.other(added.clientId).next(other);\n this._stateNotifier.subjects.others.next(others);\n\n this._usersNotifier.others.next({\n others,\n event: { kind: \"update\", user: added.data },\n });\n }\n\n private _handleReceiveOthers(message: IOEventMessage<TIO>): void {\n if (!message.connectionId) return;\n // Should not reach here\n if (!this._state.webSocket) throw new Error(\"Could not find WebSocket\");\n\n const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>[\"$othersReceived\"];\n\n Object.keys(data.others).forEach((connectionId) => {\n const { presence, user } = data.others[connectionId];\n\n const result = this._usersManager.addConnection({\n connectionId,\n presence: presence as TPresence,\n user,\n });\n\n if (!!presence) this._usersManager.patchPresence(connectionId, presence as TPresence);\n if (result.isMyself) return;\n\n const clientId = result.clientId;\n const other = this._usersManager.getOther(connectionId);\n\n this._usersNotifier.other(clientId).next(other);\n });\n\n const others = this._usersManager.getOthers();\n\n this._stateNotifier.subjects.others.next(others);\n this._usersNotifier.others.next({\n others,\n event: { kind: \"sync\", users: others },\n });\n }\n\n private async _handleRegisteredMessage(message: IOEventMessage<TIO>): Promise<void> {\n const { connectionId } = message;\n const user = message.user as Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>;\n\n if (!connectionId) return;\n // Should not reach here\n if (!this._state.webSocket) throw new Error(\"Could not find WebSocket\");\n\n const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>[\"$registered\"];\n const state = data.state;\n\n this._updateState((oldState) => {\n oldState.connection.id = connectionId;\n oldState.authorization.user = user ?? null;\n\n return oldState;\n });\n\n const userInfo: OptionalProps<UserInfo<TIO, TPresence>, \"presence\"> = {\n connectionId,\n presence: (data.presence as TPresence | null) ?? undefined,\n user,\n };\n this._usersManager.setMyself(userInfo);\n\n const presence = this._usersManager.myPresence;\n const myself = this._usersManager.myself ?? null;\n\n this._stateNotifier.subjects[\"my-presence\"].next(presence);\n this._stateNotifier.subjects.myself.next(myself);\n\n const update = await (async () => {\n if (!!state) {\n this._logDebug(\"Retrieving initial state\");\n return this._crdtManager.getInitialState();\n }\n\n const resolved = this._crdtManager.resolveEncodedState(\n await this._storageStore.getUpdates(),\n );\n\n if (!!resolved) {\n this._logDebug(\"Retrieving storage store state\");\n return resolved;\n }\n\n this._logDebug(\"Retrieving initial state\");\n return this._crdtManager.getInitialState();\n })();\n\n this._sendMessage({\n type: \"$initializeSession\",\n data: { presence, update },\n });\n }\n\n private async _handleStorageReceivedMessage(message: IOEventMessage<TIO>): Promise<void> {\n const { connectionId } = message;\n\n if (!connectionId) return;\n // Should not reach here\n if (!this._state.webSocket) throw new Error(\"Could not find WebSocket\");\n\n const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>[\"$storageReceived\"];\n const changeKind = data.changeKind;\n const state = data.state;\n\n const updates = await this._storageStore.getUpdates();\n const origin = ORIGIN_INITIALIZED;\n\n const updateStorage = (update: string | readonly string[]) => {\n return this._crdtManager.initialized\n ? this._crdtManager.applyUpdate({ update, origin })\n : this._crdtManager.initialize({ update, origin });\n };\n\n if (changeKind === \"empty\") {\n this._crdtManager.destroy();\n this._crdtManager.initialize({ origin, update: updates });\n } else if (changeKind === \"initialized\") {\n if (!!updates.length) updateStorage(updates);\n else {\n const encodedState = updateStorage(state).doc.getEncodedState();\n\n this._addToStorageStore(encodedState);\n }\n } else {\n const encodedState = updateStorage(state).doc.getEncodedState();\n\n if (!!updates.length) this._crdtManager.applyUpdate({ update: updates, origin });\n\n await this._addToStorageStore(encodedState);\n }\n\n const encodedState = this._crdtManager.doc.getEncodedState();\n\n this._emitSharedTypes();\n this._observeCrdt();\n this._updateState((oldState) => {\n oldState.storage.state = StorageState.Synced;\n\n return oldState;\n });\n this._stateNotifier.subjects[\"storage-loaded\"].next(true);\n\n this._sendMessage({\n type: \"$updateStorage\",\n data: { origin, update: encodedState },\n });\n }\n\n private _handleStorageUpdatedMessage(message: IOEventMessage<TIO>): void {\n const { connectionId } = message;\n\n if (!connectionId) return;\n // Should not reach here\n if (!this._state.webSocket) throw new Error(\"Could not find WebSocket\");\n\n const data = message.data as BaseIOEventRecord<\n InferIOAuthorize<TIO>\n >[typeof ORIGIN_STORAGE_UPDATED];\n\n this._crdtManager.doc.applyEncodedState({\n origin: ORIGIN_STORAGE_UPDATED,\n update: data.state,\n });\n\n const sharedTypes = this._crdtManager.doc.get();\n\n const storageRoot = Object.keys(sharedTypes).reduce(\n (acc, prop) => {\n const serialized = this._crdtManager.doc.toJson(prop);\n\n this._crdtNotifier.subject(prop).next(serialized);\n\n return { ...acc, [prop]: serialized };\n },\n {} as {\n [P in keyof InferStorage<TCrdt>]: InferCrdtJson<InferStorage<TCrdt>[P]>;\n },\n );\n\n this._crdtNotifier.rootSubject.next(storageRoot);\n }\n\n private _handleSyncStateReceived(message: IOEventMessage<TIO>): void {\n const { connectionId } = message;\n\n if (!connectionId) return;\n // Should not reach here\n if (!this._state.webSocket) throw new Error(\"Could not find WebSocket\");\n if (!this._usersManager.myself) return;\n\n const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>[\"$syncStateReceived\"];\n const active = new Set(data?.connectionIds ?? []);\n\n const quitters = this._usersManager\n .getOthers()\n .filter((other) => !active.has(other.connectionId));\n\n quitters.forEach((quitter) => {\n this._usersManager.deleteConnection(quitter.connectionId);\n\n const remaining = this._usersManager.getOthers();\n\n this._usersNotifier.others.next({\n others: remaining,\n event: { kind: \"leave\", user: quitter },\n });\n });\n\n const remaining = this._usersManager.getOthers();\n\n this._stateNotifier.subjects.others.next(remaining);\n }\n\n private _handleUserJoinedMessage(message: IOEventMessage<TIO>): void {\n const { connectionId } = message;\n\n if (!connectionId) return;\n // Should not reach here\n if (!this._state.webSocket) throw new Error(\"Could not find WebSocket\");\n if (!this._usersManager.myself) return;\n\n const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>[\"$userJoined\"];\n const myself = this._usersManager.myself;\n\n if (myself.connectionId === connectionId) {\n this._sendMessage({ type: \"$getOthers\", data: {} });\n return;\n }\n\n const added = this._usersManager.addConnection({\n connectionId,\n presence: data.presence as TPresence,\n user: data.user,\n });\n\n const other = this._usersManager.getOther(connectionId);\n const others = this._usersManager.getOthers();\n\n this._usersNotifier.other(added.clientId).next(other);\n this._stateNotifier.subjects.others.next(others);\n\n this._usersNotifier.others.next({\n others,\n event: { kind: \"enter\", user: added.data },\n });\n }\n\n private _heartbeat(): void {\n this._clearTimeout(this._timeouts.pong);\n this._timeouts.pong = setTimeout(\n this._reconnect.bind(this),\n PONG_TIMEOUT_MS,\n ) as unknown as number;\n\n /**\n * !HACK\n * @description Send data as a stable string so that the server can react to this in a\n * consistent way.\n * @date July 8, 2024\n */\n this._sendMessage('{\"type\":\"$ping\",\"data\":{}}');\n }\n\n private _logDebug(...data: any[]): void {\n if (typeof process === \"undefined\") return;\n if (process.env?.NODE_ENV === \"production\") return;\n if (this._debug) console.log(...data);\n }\n\n private _observeCrdt(): void {\n this._subscriptions.observeCrdt?.();\n\n const unsubscribe = this._crdtManager.doc.subscribe((event) => {\n const origin = event.origin ?? null;\n\n this._emitSharedTypes();\n\n if (origin === ORIGIN_INITIALIZED || origin === ORIGIN_STORAGE_UPDATED) {\n return;\n }\n\n this._addToStorageStore(event.update);\n\n if (!this._state.webSocket) return;\n if (!this._state.connection.id) return;\n if (this._state.webSocket.readyState !== WebSocket.OPEN) return;\n\n this._sendMessage({\n type: \"$updateStorage\",\n data: { origin, update: event.update },\n });\n });\n\n this._subscriptions.observeCrdt = unsubscribe;\n }\n\n private _onAuthorizationFail(error: unknown): void {\n const authError = error instanceof Error ? error : new Error(\"Authorization failed\");\n\n this._listeners.onAuthorizationFail(authError);\n\n this._updateState((oldState) => {\n oldState.connection.state = ConnectionState.Unavailable;\n oldState.storage.state = StorageState.Unavailable;\n\n return oldState;\n });\n }\n\n private async _onClose(event: CloseEvent): Promise<void> {\n this._listenerManager.subjects.close.next(event);\n\n this._logDebug(\"WebSocket closed\");\n if (!!event.reason) this._logDebug(event.reason);\n\n const shouldRetry = [\n // Going away: Client/server is shutting down or navigating\n 1001,\n // Abnormal closure: Usually network error\n 1006,\n // Internal error: may be a larger problem, but try reconnecting for now\n 1011,\n // Service restart: Server restarted\n 1012,\n // Try again later: server overloaded\n 1013,\n // Bad gateway\n 1014,\n ].some((okToRetryCode) => event.code === okToRetryCode);\n\n this._updateState((oldState) => {\n oldState.authorization.token = null;\n oldState.connection.state = shouldRetry\n ? ConnectionState.Unavailable\n : /**\n * !HACK\n * @description Assume these are not recoverable. We're just going to mark this\n * as Closed to avoid reconnect attempts.\n * @date April 28, 2025\n */\n ConnectionState.Closed;\n oldState.storage.state =\n oldState.storage.state === StorageState.Synced\n ? StorageState.Offline\n : StorageState.Unavailable;\n oldState.webSocket = null;\n\n return oldState;\n });\n\n if (shouldRetry) {\n await this._reconnect();\n return;\n }\n\n this._closeWs();\n this._clearTimeout(this._timeouts.reconnect);\n }\n\n /**\n * TODO\n * @description Handle websocket errors in a meaningful way, should they occur\n * @date August 19, 2022\n */\n private _onError(event: Event): void {\n this._listenerManager.subjects.error.next(event);\n }\n\n private _onMessage(event: MessageEvent<string>): void {\n this._listenerManager.subjects.message.next(event);\n\n const message = this._parseMessage(event);\n\n if (!message) return;\n\n const shouldLog =\n typeof this._debug === \"boolean\"\n ? this._debug\n : this._debug.output.find((value) => value === message.type);\n\n if (shouldLog) {\n this._logDebug(\"WebSocket event received: \", message.type, message);\n }\n\n this._eventNotifier.subject(message.type as keyof InferIOOutput<TIO>).next(message as any);\n\n switch (message.type) {\n case \"$exit\": {\n this._handleExit(message);\n return;\n }\n case \"$othersReceived\": {\n this._handleReceiveOthers(message);\n return;\n }\n case \"$pong\": {\n this._clearTimeout(this._timeouts.pong);\n return;\n }\n case \"$presenceUpdated\": {\n this._handlePresenceUpdatedMessage(message);\n return;\n }\n case \"$registered\": {\n this._handleRegisteredMessage(message);\n return;\n }\n case \"$storageReceived\": {\n this._handleStorageReceivedMessage(message);\n return;\n }\n case \"$storageUpdated\": {\n this._handleStorageUpdatedMessage(message);\n return;\n }\n case \"$syncStateReceived\": {\n this._handleSyncStateReceived(message);\n return;\n }\n case \"$userJoined\": {\n this._handleUserJoinedMessage(message);\n return;\n }\n default:\n }\n }\n\n private _onOpen(event: Event): void {\n this._listenerManager.subjects.open.next(event);\n\n if (this._state.connection.state !== ConnectionState.Connecting) return;\n\n this._logDebug(\"WebSocket connected\");\n this._updateState((oldState) => {\n oldState.connection.state = ConnectionState.Open;\n\n return oldState;\n });\n\n this._clearInterval(this._intervals.heartbeat);\n this._intervals.heartbeat = setInterval(\n this._heartbeat.bind(this),\n HEARTBEAT_INTERVAL_MS,\n ) as unknown as number;\n }\n\n private async _onNavigatorOffline(): Promise<void> {\n if (this._state.connection.state === ConnectionState.Closed) return;\n\n this._updateState((oldState) => {\n oldState.connection.state = ConnectionState.Unavailable;\n oldState.storage.state =\n oldState.storage.state === StorageState.Synced\n ? StorageState.Offline\n : StorageState.Unavailable;\n\n return oldState;\n });\n }\n\n private async _onNavigatorOnline(): Promise<void> {\n if (this._state.connection.state !== ConnectionState.Unavailable) return;\n\n await this._reconnect();\n }\n\n private async _onVisibilityChange(): Promise<void> {\n if (typeof document === \"undefined\") return;\n if (document.visibilityState !== \"visible\") return;\n\n switch (this._state.connection.state) {\n case ConnectionState.Open: {\n this._clearInterval(this._intervals.heartbeat);\n setInterval(this._heartbeat.bind(this), HEARTBEAT_INTERVAL_MS);\n\n return;\n }\n case ConnectionState.Unavailable: {\n await this._reconnect();\n\n return;\n }\n default:\n }\n }\n\n private async _onWindowFocus(): Promise<void> {\n return await this._onVisibilityChange();\n }\n\n private _other = (\n connectionId: string,\n callback: OtherSubscriptionCallback<TIO, TPresence>,\n ): (() => void) => {\n const clientId = this._usersManager.getClientId(connectionId);\n\n if (!clientId) return () => undefined;\n\n return this._usersNotifier.subscribeOther(clientId, callback);\n };\n\n private _parseMessage(message: { data: string | ArrayBuffer }): IOEventMessage<TIO> | null {\n /**\n * !HACK\n * @description We'll only handle stringified JSONs for now\n * @date August 9, 2022\n */\n if (typeof message.data !== \"string\") return null;\n\n try {\n const config = JSON.parse(message.data);\n\n if (typeof config !== \"object\") return null;\n if (typeof config.type !== \"string\") return null;\n if (typeof config.data !== \"object\") return null;\n\n return config as IOEventMessage<TIO>;\n } catch {\n return null;\n }\n }\n\n private _pollReconnect(): void {\n this._clearTimeout(this._timeouts.reconnect);\n\n const timeoutMs =\n typeof this._reconnectTimeoutMs === \"number\"\n ? this._reconnectTimeoutMs\n : this._reconnectTimeoutMs({ attempts: this._state.connection.attempts });\n const clampedMs = Math.max(\n Math.min(MIN_RECONNECT_TIMEOUT_MS, timeoutMs),\n MAX_RECONNECT_TIMEOUT_MS,\n );\n\n this._timeouts.reconnect = setTimeout(\n this._reconnect.bind(this),\n clampedMs,\n ) as unknown as number;\n }\n\n private async _reconnect(): Promise<void> {\n this._closeWs();\n\n /**\n * @description User has manually closed this connection. We're not going to retry, since\n * the user probably intends for this to stay closed. They will need to invoke connect\n * themselves.\n * @date April 28, 2025\n */\n if (this._state.connection.state === ConnectionState.Closed) return;\n\n const metadata = this._lastMetadata;\n const params = (\n typeof metadata === \"undefined\" ? [] : [{ metadata }]\n ) as RoomConnectParams<TMetadata>;\n\n await this.connect(...params);\n }\n\n private _sendMessage(data: string): void;\n private _sendMessage<TMessage extends EventMessage<string, any> = EventMessage<string, any>>(\n data: TMessage,\n ): void;\n private _sendMessage(data: any): void {\n const webSocket = this._state.webSocket;\n\n if (webSocket?.readyState !== WebSocket.OPEN) return;\n\n const dataType: string | null = ((): string | null => {\n if (typeof data === \"object\") return data.type ?? null;\n if (typeof data !== \"string\") return null;\n\n try {\n return JSON.parse(data)?.type ?? null;\n } catch {\n return null;\n }\n })();\n\n const shouldLog: boolean =\n typeof this._debug === \"boolean\"\n ? this._debug\n : !dataType\n ? false\n : this._debug.input.some((value) => value === data.type);\n\n if (shouldLog) this._logDebug(\"WebSocket event sent: \", data.type, data);\n\n const message = typeof data === \"string\" ? data : JSON.stringify(data);\n\n webSocket.send(message);\n }\n\n private _setMetadata(metadata: TMetadata): TMetadata {\n const parsed = this.metadata ? this.metadata.parse(metadata) : metadata;\n\n this._lastMetadata = parsed;\n\n return parsed;\n }\n\n #_storage = new Proxy(\n <TKey extends keyof InferStorage<TCrdt>>(\n key: TKey,\n callback: StorageSubscriptionCallback<InferStorage<TCrdt>, TKey>,\n ): (() => void) => this._crdtNotifier.subscribe(key, callback),\n {\n get: (fn, prop) => {\n type _Storage = InferStorage<TCrdt>;\n\n if (!!prop) {\n return (callback: StorageRootSubscriptionCallback<_Storage>) => {\n return this._crdtNotifier.subcribeRoot(callback);\n };\n }\n\n return (callback: StorageSubscriptionCallback<_Storage, keyof _Storage>) => {\n return fn(prop as keyof InferStorage<TCrdt>, callback);\n };\n },\n },\n ) as StorageProxy<InferStorage<TCrdt>>;\n\n private _updateState(\n updater: (oldState: WebSocketState<TIO>) => WebSocketState<TIO>,\n ): WebSocketState<TIO> {\n let authorization: AuthorizationState<TIO>;\n\n try {\n authorization = JSON.parse(JSON.stringify(this._state.authorization));\n } catch {\n throw new Error(\"User is not JSON serializable\");\n }\n\n const oldState: WebSocketState<TIO> = {\n ...this._state,\n authorization,\n connection: JSON.parse(JSON.stringify(this._state.connection)),\n storage: JSON.parse(JSON.stringify(this._state.storage)),\n webSocket: this._state.webSocket,\n };\n\n const newState = updater(oldState);\n this._state = newState;\n\n this._logDebug(\"WebSocket new state: \", newState);\n this._stateNotifier.subjects.connection.next(newState);\n\n return newState;\n }\n}\n","import type { AbstractCrdtDocFactory, InferStorage, NoopCrdtDocFactory } from \"@pluv/crdt\";\nimport type { InferIOCrdtKind, InputZodLike, IOLike, JsonObject } from \"@pluv/types\";\nimport { MAX_PRESENCE_SIZE_BYTES } from \"./constants\";\nimport type { InferCallback } from \"./infer\";\nimport { PluvProcedure } from \"./PluvProcedure\";\nimport type {\n AuthEndpoint,\n PluvRoomAddon,\n PluvRoomDebug,\n ReconnectTimeoutMs,\n RoomConfig,\n RoomEndpoints,\n WsEndpoint,\n} from \"./PluvRoom\";\nimport { PluvRoom } from \"./PluvRoom\";\nimport type { PluvRouterEventConfig } from \"./PluvRouter\";\nimport { PluvRouter } from \"./PluvRouter\";\nimport type { PluvClientLimits, PublicKey, WithMetadata } from \"./types\";\n\nexport type PluvClientOptions<\n TIO extends IOLike<any, any, any>,\n TPresence extends Record<string, any>,\n TCrdt extends InferIOCrdtKind<TIO>,\n TMetadata extends JsonObject,\n> = RoomEndpoints<TIO, TMetadata> & {\n debug?: boolean;\n /**\n * @description Configurable limits defined for client-side validation. You should only set\n * this if you control the server and have changed the limits there.\n */\n limits?: PluvClientLimits;\n metadata?: InputZodLike<TMetadata>;\n presence?: InputZodLike<TPresence>;\n publicKey?: PublicKey<TMetadata>;\n types: InferCallback<TIO>;\n} & (InferIOCrdtKind<TIO> extends NoopCrdtDocFactory\n ? { initialStorage?: \"[ERROR]: Must provide crdt to createIO to use storage\" }\n : { initialStorage?: TCrdt });\n\nexport type CreateRoomOptions<\n TIO extends IOLike<any, any, any>,\n TPresence extends Record<string, any>,\n TCrdt extends AbstractCrdtDocFactory<any, any>,\n TMetadata extends JsonObject,\n TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {},\n> = {\n addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, InferStorage<TCrdt>>[];\n debug?: boolean | PluvRoomDebug<TIO>;\n initialPresence?: TPresence;\n initialStorage?: TCrdt;\n onAuthorizationFail?: (error: Error) => void;\n reconnectTimeoutMs?: ReconnectTimeoutMs;\n router?: PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents>;\n};\n\nexport type EnterRoomParams<TMetadata extends JsonObject = {}> = keyof TMetadata extends never\n ? []\n : [WithMetadata<TMetadata>];\n\nexport class PluvClient<\n TIO extends IOLike<any, any, any>,\n TPresence extends Record<string, any> = {},\n TCrdt extends InferIOCrdtKind<TIO> = InferIOCrdtKind<TIO>,\n TMetadata extends JsonObject = {},\n> {\n public readonly metadata?: InputZodLike<TMetadata>;\n\n private readonly _authEndpoint: AuthEndpoint<TMetadata> | undefined;\n private readonly _debug: boolean;\n private readonly _initialStorage?: TCrdt;\n private readonly _limits: PluvClientLimits;\n private readonly _presence?: InputZodLike<TPresence>;\n private readonly _publicKey: PublicKey<TMetadata> | null = null;\n private readonly _rooms = new Map<string, PluvRoom<TIO, TMetadata, TPresence, TCrdt, any>>();\n private readonly _wsEndpoint: WsEndpoint<TMetadata> | undefined;\n\n public get _defs() {\n return {\n initialStorage: this._initialStorage,\n };\n }\n\n public get procedure(): PluvProcedure<TIO, {}, {}, TPresence, TCrdt, \"\"> {\n return new PluvProcedure<TIO, {}, {}, TPresence, TCrdt, \"\">();\n }\n\n constructor(options: PluvClientOptions<TIO, TPresence, TCrdt, TMetadata>) {\n const {\n authEndpoint,\n debug = false,\n initialStorage,\n limits,\n metadata,\n presence,\n publicKey,\n wsEndpoint,\n } = options;\n\n this.metadata = metadata;\n\n this._authEndpoint = authEndpoint as AuthEndpoint<TMetadata>;\n this._debug = debug;\n this._initialStorage = initialStorage as TCrdt;\n this._limits = {\n presenceMaxSize: MAX_PRESENCE_SIZE_BYTES,\n ...limits,\n };\n this._presence = presence;\n this._wsEndpoint = wsEndpoint;\n\n if (!!publicKey) this._publicKey = publicKey;\n }\n\n public createRoom = <\n TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {},\n >(\n room: string,\n options: CreateRoomOptions<TIO, TPresence, TCrdt, TMetadata, TEvents> = {},\n ): PluvRoom<TIO, TMetadata, TPresence, TCrdt, TEvents> => {\n const oldRoom = this.getRoom(room);\n\n if (oldRoom) return oldRoom;\n\n const newRoom = new PluvRoom<TIO, TMetadata, TPresence, TCrdt, TEvents>(room, {\n addons: options.addons,\n authEndpoint: this._authEndpoint,\n debug: options.debug,\n initialPresence: options.initialPresence,\n initialStorage: options.initialStorage ?? this._initialStorage,\n limits: this._limits,\n metadata: this.metadata,\n onAuthorizationFail: options.onAuthorizationFail,\n presence: this._presence,\n publicKey: this._publicKey ?? undefined,\n reconnectTimeoutMs: options.reconnectTimeoutMs,\n router: options.router,\n wsEndpoint: this._wsEndpoint,\n } as RoomConfig<TIO, TMetadata, TPresence, TCrdt, TEvents>);\n\n this._rooms.set(room, newRoom);\n\n this._logDebug(`New room was created: ${room}`);\n\n return newRoom;\n };\n\n public enter = async (\n room: string | PluvRoom<TIO, TMetadata, any, any, any>,\n ...args: EnterRoomParams<TMetadata>\n ): Promise<PluvRoom<TIO, TMetadata, TPresence, TCrdt, any>> => {\n const toEnter = typeof room === \"string\" ? this.getRoom(room) : room;\n\n if (!toEnter) throw new Error(`Could not find room: ${room}.`);\n\n this._rooms.set(toEnter.id, toEnter);\n await toEnter.connect(...args);\n\n this._logDebug(`Entered room: ${room}`);\n\n return toEnter;\n };\n\n public getRoom = (room: string): PluvRoom<TIO, TMetadata, TPresence, TCrdt, any> | null => {\n const found = this._rooms.get(room) as\n | PluvRoom<TIO, TMetadata, TPresence, TCrdt, any>\n | undefined;\n\n return found ?? null;\n };\n\n public getRooms = (): readonly PluvRoom<TIO, TMetadata, TPresence, TCrdt, any>[] => {\n return Array.from(this._rooms.values());\n };\n\n public leave = async (room: string | PluvRoom<TIO, any, any, any>): Promise<void> => {\n const toLeave = typeof room === \"string\" ? this.getRoom(room) : room;\n\n if (!toLeave) return;\n\n await toLeave.disconnect();\n\n this._rooms.delete(toLeave.id);\n\n this._logDebug(`Left and deleted room: ${room}`);\n };\n\n public router<TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {}>(\n events: TEvents,\n ): PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents> {\n const invalidName = Object.keys(events).find((name) => name.includes(\"$\"));\n\n if (typeof invalidName === \"string\") {\n throw new Error(`Invalid event name. Event names must not contain $: \"${invalidName}\"`);\n }\n\n return new PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents>(events);\n }\n\n private _logDebug(...data: any[]): void {\n if (typeof process === \"undefined\") return;\n if (process.env?.NODE_ENV === \"production\") return;\n\n if (this._debug) console.log(...data);\n }\n}\n","import type { InferIOCrdtKind, IOLike, JsonObject } from \"@pluv/types\";\nimport type { PluvClientOptions } from \"./PluvClient\";\nimport { PluvClient } from \"./PluvClient\";\n\nexport const createClient = <\n TIO extends IOLike<any, any, any>,\n TPresence extends Record<string, any> = {},\n TCrdt extends InferIOCrdtKind<TIO> = InferIOCrdtKind<TIO>,\n TMetadata extends JsonObject = {},\n>(\n options: PluvClientOptions<TIO, TPresence, TCrdt, TMetadata>,\n): PluvClient<TIO, TPresence, TCrdt, TMetadata> => {\n return new PluvClient<TIO, TPresence, TCrdt, TMetadata>(options);\n};\n","import type { InferIOAuthorize, InferIOCrdt, InferIOEvents, IOLike } from \"@pluv/types\";\nimport type { identity } from \"./utils\";\n\nexport type InferCallback<TIO extends IOLike<any, any, any>> = (i: typeof identity) => {\n io: (io: TIO) => TIO;\n};\n\nexport const infer = <TIO extends IOLike<any, any, any>>(\n callback: InferCallback<TIO>,\n): InferCallback<IOLike<InferIOAuthorize<TIO>, InferIOCrdt<TIO>, InferIOEvents<TIO>>> => {\n return callback as any;\n};\n","import type {\n AbstractCrdtDocFactory,\n InferCrdtJson,\n InferDoc,\n InferDocLike,\n InferStorage,\n} from \"@pluv/crdt\";\nimport type {\n BroadcastProxy,\n CrdtDocLike,\n EventNotifierSubscriptionCallback,\n EventProxy,\n IOLike,\n Id,\n InferIOInput,\n InferIOOutput,\n MergeEvents,\n OtherSubscriptionCallback,\n OthersSubscriptionCallback,\n RoomEventListenerMap,\n RoomLike,\n StateNotifierSubjects,\n StorageProxy,\n StorageRootSubscriptionCallback,\n StorageSubscriptionCallback,\n SubscribeProxy,\n SubscriptionCallback,\n UpdateMyPresenceAction,\n UserInfo,\n WebSocketConnection,\n WebSocketState,\n} from \"@pluv/types\";\nimport { ConnectionState, StorageState } from \"@pluv/types\";\nimport type { CrdtManagerOptions } from \"./CrdtManager\";\nimport { CrdtManager } from \"./CrdtManager\";\nimport { CrdtNotifier } from \"./CrdtNotifier\";\nimport { EventNotifier } from \"./EventNotifier\";\nimport { PluvProcedure } from \"./PluvProcedure\";\nimport type { PluvRouterEventConfig } from \"./PluvRouter\";\nimport { PluvRouter } from \"./PluvRouter\";\nimport { StateNotifier } from \"./StateNotifier\";\nimport type { UsersManagerConfig } from \"./UsersManager\";\nimport { UsersManager } from \"./UsersManager\";\nimport { UsersNotifier } from \"./UsersNotifier\";\nimport { MAX_PRESENCE_SIZE_BYTES } from \"./constants\";\nimport type {\n EventResolver,\n EventResolverContext,\n InternalSubscriptions,\n PluvClientLimits,\n} from \"./types\";\n\nexport type MockedRoomEvents<TIO extends IOLike> = Partial<{\n [P in keyof InferIOInput<TIO>]: (data: Id<InferIOInput<TIO>[P]>) => Partial<InferIOOutput<TIO>>;\n}>;\n\nexport type MockedRoomConfig<\n TIO extends IOLike,\n TPresence extends Record<string, any>,\n TCrdt extends AbstractCrdtDocFactory<any, any>,\n TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>>,\n> = {\n events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;\n limits?: PluvClientLimits;\n router?: PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents>;\n} & Pick<CrdtManagerOptions<TCrdt>, \"initialStorage\"> &\n Omit<UsersManagerConfig<TPresence>, \"limits\">;\n\nexport class MockedRoom<\n TIO extends IOLike,\n TPresence extends Record<string, any>,\n TCrdt extends AbstractCrdtDocFactory<any, any>,\n TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>>,\n> implements RoomLike<TIO, InferDoc<TCrdt>, TPresence, InferStorage<TCrdt>>\n{\n public readonly id: string;\n\n private readonly _crdtManager: CrdtManager<TCrdt>;\n private readonly _crdtNotifier = new CrdtNotifier<InferStorage<TCrdt>>();\n private readonly _eventNotifier = new EventNotifier<MergeEvents<TEvents, TIO>>();\n private readonly _events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;\n private readonly _limits: PluvClientLimits;\n private readonly _usersNotifier = new UsersNotifier<TIO, TPresence>();\n private readonly _router: PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents>;\n private _state: WebSocketState<TIO> = {\n authorization: {\n token: null,\n user: null,\n },\n connection: {\n attempts: 0,\n id: null,\n state: ConnectionState.Untouched,\n },\n storage: {\n state: StorageState.Unavailable,\n },\n webSocket: null,\n };\n private readonly _stateNotifier = new StateNotifier<TIO, TPresence>();\n private readonly _subscriptions: InternalSubscriptions = {\n observeCrdt: null,\n };\n private readonly _usersManager: UsersManager<TIO, TPresence>;\n\n constructor(room: string, options: MockedRoomConfig<TIO, TPresence, TCrdt, TEvents>) {\n const { events, initialPresence, initialStorage, limits, presence, router } = options;\n\n this.id = room;\n\n this._events = events;\n this._limits = {\n presenceMaxSize: MAX_PRESENCE_SIZE_BYTES,\n ...limits,\n };\n this._router =\n router ??\n (new PluvRouter({}) as PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents>);\n this._usersManager = new UsersManager<TIO, TPresence>({\n initialPresence,\n limits: this._limits,\n presence,\n });\n\n this._crdtManager = new CrdtManager<TCrdt>({\n initialStorage,\n });\n\n this._observeCrdt();\n }\n\n public addEventListener<TKind extends keyof RoomEventListenerMap>(\n kind: TKind,\n handler: RoomEventListenerMap[TKind],\n ): () => void {\n return () => undefined;\n }\n\n public broadcast = new Proxy(\n async <TEvent extends keyof InferIOInput<MergeEvents<TEvents, TIO>>>(\n event: TEvent,\n data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[TEvent]>,\n ): Promise<void> => {\n if (!this._state.webSocket) return;\n if (this._state.connection.state !== ConnectionState.Open) return;\n\n const type = event.toString();\n\n const procedure = this._router._defs.events[type] as PluvProcedure<\n TIO,\n any,\n any,\n TPresence,\n TCrdt,\n \"\"\n > | null;\n\n if (!procedure?.config.broadcast) {\n this._simulateEvent(type as TEvent, data);\n\n return;\n }\n\n const myself = this._usersManager.myself;\n\n if (!myself) return;\n\n const parsed = procedure.config.input ? procedure.config.input.parse(data) : data;\n const context: EventResolverContext<TIO, TPresence, InferDocLike<TCrdt>> = {\n doc: this._crdtManager.doc,\n others: this._usersManager.getOthers(),\n room: this.id,\n user: myself,\n };\n\n const output = await (\n procedure.config.broadcast as EventResolver<\n TIO,\n any,\n any,\n TPresence,\n InferStorage<TCrdt>\n >\n )(parsed, context);\n\n Object.entries(output).forEach(([_type, _data]) => {\n this._simulateEvent(_type as TEvent, _data as any);\n });\n },\n {\n get(fn, prop) {\n return async (\n data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[any]>,\n ): Promise<void> => {\n return await fn(prop, data);\n };\n },\n },\n ) as BroadcastProxy<TIO, TEvents>;\n\n public canRedo = (): boolean => {\n return !!this._crdtManager.doc.canRedo();\n };\n\n public canUndo = (): boolean => {\n return !!this._crdtManager.doc.canUndo();\n };\n\n public getConnection = (): WebSocketConnection => {\n // Create a read-only clone of the connection state\n return Object.freeze(JSON.parse(JSON.stringify(this._state.connection)));\n };\n\n public getDoc(): CrdtDocLike<InferDoc<TCrdt>, InferStorage<TCrdt>> {\n return this._crdtManager.doc;\n }\n\n public getMyPresence = (): TPresence => {\n return this._usersManager.myPresence;\n };\n\n public getMyself = (): Id<UserInfo<TIO, TPresence>> | null => {\n return this._usersManager.myself;\n };\n\n public getOther = (connectionId: string): Id<UserInfo<TIO, TPresence>> | null => {\n return this._usersManager.getOther(connectionId);\n };\n\n public getOthers = (): readonly Id<UserInfo<TIO, TPresence>>[] => {\n return this._usersManager.getOthers();\n };\n\n public getStorage = <TKey extends keyof InferStorage<TCrdt>>(\n type: TKey,\n ): InferStorage<TCrdt>[TKey] | null => {\n const sharedType = this._crdtManager.get(type);\n\n if (typeof sharedType === \"undefined\") return null;\n\n return sharedType;\n };\n\n public getStorageJson(): InferCrdtJson<InferStorage<TCrdt>> | null;\n public getStorageJson<TKey extends keyof InferStorage<TCrdt>>(\n type: TKey,\n ): InferCrdtJson<InferStorage<TCrdt>[TKey]> | null;\n public getStorageJson<TKey extends keyof InferStorage<TCrdt>>(type?: TKey) {\n if (this._state.connection.id === null) return null;\n if (typeof type === \"undefined\") return this._crdtManager.doc.toJson();\n\n return this._crdtManager.doc.toJson(type);\n }\n\n public getStorageLoaded(): boolean {\n return true;\n }\n\n public redo = (): void => {\n this._crdtManager.doc.redo();\n };\n\n public storageRoot = (\n fn: (value: {\n [P in keyof InferStorage<TCrdt>]: InferCrdtJson<InferStorage<TCrdt>[P]>;\n }) => void,\n ): (() => void) => {\n return this._crdtNotifier.subcribeRoot(fn);\n };\n\n public subscribe = new Proxy(\n <TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(\n name: TSubject,\n callback: SubscriptionCallback<TIO, TPresence, TSubject>,\n ): (() => void) => {\n return this._stateNotifier.subscribe(name, callback);\n },\n {\n get: (fn, prop) => {\n if (prop === \"connection\") {\n return (callback: SubscriptionCallback<TIO, TPresence, \"connection\">) => {\n return fn(\"connection\", callback);\n };\n }\n\n if (prop === \"myPresence\") {\n return (callback: SubscriptionCallback<TIO, TPresence, \"my-presence\">) => {\n return fn(\"my-presence\", callback);\n };\n }\n\n if (prop === \"myself\") {\n return (callback: SubscriptionCallback<TIO, TPresence, \"myself\">) => {\n return fn(\"myself\", callback);\n };\n }\n\n if (prop === \"others\") {\n return (callback: OthersSubscriptionCallback<TIO, TPresence>) => {\n return this._usersNotifier.subscribeOthers(callback);\n };\n }\n\n if (prop === \"storageLoaded\") {\n return (callback: SubscriptionCallback<TIO, TPresence, \"storage-loaded\">) => {\n return fn(\"storage-loaded\", callback);\n };\n }\n\n if (prop === \"event\") return this._event;\n if (prop === \"other\") return this._other;\n if (prop === \"storage\") return this.#_storage;\n },\n },\n ) as SubscribeProxy<TIO, TPresence, InferStorage<TCrdt>, TEvents>;\n\n public transact = (fn: (storage: InferStorage<TCrdt>) => void, origin?: string): void => {\n const _origin = origin ?? this._state.connection.id;\n const crdtManager = this._crdtManager;\n\n /**\n * !HACK\n * @description Don't transact anything if there is no origin, because\n * that means the user isn't connected yet. This will mean events are\n * lost unfortunately.\n * @date September 23, 2023\n */\n if (typeof _origin !== \"string\") return;\n if (!crdtManager) return;\n\n crdtManager.doc.transact(() => {\n const storage = crdtManager.doc.get();\n\n fn(storage);\n }, _origin);\n };\n\n public undo = (): void => {\n this._crdtManager.doc.undo();\n };\n\n public updateMyPresence = (presence: UpdateMyPresenceAction<TPresence>): void => {\n const newPresence =\n typeof presence === \"function\" ? presence(this.getMyPresence()) : presence;\n\n this._usersManager.updateMyPresence(newPresence);\n\n const myPresence = this._usersManager.myPresence;\n const myself = this._usersManager.myself ?? null;\n\n this._stateNotifier.subjects[\"my-presence\"].next(myPresence);\n if (!!myself) this._stateNotifier.subjects[\"myself\"].next(myself);\n };\n\n private _event = new Proxy(\n <TEvent extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(\n event: TEvent,\n callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, any>,\n ): (() => void) => this._eventNotifier.subscribe(event, callback),\n {\n get(fn, prop) {\n return (\n callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, any>,\n ): (() => void) => fn(prop as any, callback);\n },\n },\n ) as EventProxy<TIO, TEvents>;\n\n private _observeCrdt(): void {\n this._subscriptions.observeCrdt?.();\n\n if (!this._crdtManager) return;\n\n const unsubscribe = this._crdtManager.doc.subscribe((event) => {\n const origin = event.origin ?? null;\n\n if (!this._crdtManager) return;\n if (origin === \"$storageUpdated\") return;\n\n const sharedTypes = this._crdtManager.doc.get();\n\n const storageRoot = Object.keys(sharedTypes).reduce(\n (acc, prop) => {\n if (!this._crdtManager) return acc;\n\n const serialized = this._crdtManager.doc.toJson(prop);\n\n this._crdtNotifier.subject(prop).next(serialized);\n\n return { ...acc, [prop]: serialized };\n },\n {} as {\n [P in keyof InferStorage<TCrdt>]: InferCrdtJson<InferStorage<TCrdt>[P]>;\n },\n );\n\n this._crdtNotifier.rootSubject.next(storageRoot);\n });\n\n this._subscriptions.observeCrdt = unsubscribe;\n }\n\n private _other = (\n connectionId: string,\n callback: OtherSubscriptionCallback<TIO, TPresence>,\n ): (() => void) => {\n const clientId = this._usersManager.getClientId(connectionId);\n\n if (!clientId) return () => undefined;\n\n return this._usersNotifier.subscribeOther(clientId, callback);\n };\n\n private _simulateEvent<TEvent extends keyof InferIOInput<MergeEvents<TEvents, TIO>>>(\n event: TEvent,\n data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[TEvent]>,\n ) {\n if (!this._events) return;\n\n const type = event.toString();\n const resolver = this._events[type as TEvent];\n\n if (!resolver) return;\n\n const result = resolver(data);\n\n Object.keys(result).forEach((type) => {\n const _type = type.toString() as keyof Partial<InferIOOutput<TIO>>;\n const data = result[_type] as any;\n\n if (!data) return;\n\n this._eventNotifier.subject(_type).next(data);\n });\n }\n\n #_storage = new Proxy(\n <TKey extends keyof InferStorage<TCrdt>>(\n key: TKey,\n callback: StorageSubscriptionCallback<InferStorage<TCrdt>, TKey>,\n ): (() => void) => this._crdtNotifier.subscribe(key, callback),\n {\n get: (fn, prop) => {\n type _Storage = InferStorage<TCrdt>;\n\n if (!!prop) {\n return (callback: StorageRootSubscriptionCallback<_Storage>) => {\n return this._crdtNotifier.subcribeRoot(callback);\n };\n }\n\n return (callback: StorageSubscriptionCallback<_Storage, keyof _Storage>) => {\n return fn(prop as keyof InferStorage<TCrdt>, callback);\n };\n },\n },\n ) as StorageProxy<InferStorage<TCrdt>>;\n}\n","import type { JsonObject } from \"@pluv/types\";\nimport type { AuthEndpoint, WsEndpoint } from \"./PluvRoom\";\n\nexport interface RegisterParams<TMetadata extends JsonObject> {\n authEndpoint: AuthEndpoint<TMetadata>;\n publicKey: string;\n}\n\nexport const register = <TMetadata extends JsonObject>(params: RegisterParams<TMetadata>) => {\n const { authEndpoint, publicKey } = params;\n\n const wsEndpoint: WsEndpoint<TMetadata> = ({ room }) =>\n `wss://connect.pluv.io/api/room?room=${room}`;\n\n return {\n authEndpoint,\n publicKey,\n wsEndpoint,\n };\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/client",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "@pluv/io frontend client",
|
|
5
5
|
"author": {
|
|
6
6
|
"email": "david@pluv.io",
|
|
@@ -14,28 +14,32 @@
|
|
|
14
14
|
"url": "git+https://github.com/pluv-io/pluv.git",
|
|
15
15
|
"directory": "packages/client"
|
|
16
16
|
},
|
|
17
|
-
"
|
|
18
|
-
"
|
|
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
|
"wonka": "^6.3.5",
|
|
25
|
-
"@pluv/crdt": "^
|
|
26
|
-
"@pluv/types": "^
|
|
24
|
+
"@pluv/crdt": "^4.0.0",
|
|
25
|
+
"@pluv/types": "^4.0.0"
|
|
27
26
|
},
|
|
28
27
|
"devDependencies": {
|
|
29
|
-
"eslint": "^9.
|
|
30
|
-
"
|
|
31
|
-
"typescript": "^5.
|
|
32
|
-
"@pluv/tsconfig": "^
|
|
33
|
-
"eslint-config-pluv": "^
|
|
28
|
+
"eslint": "^9.39.2",
|
|
29
|
+
"tsdown": "0.20.0-beta.4",
|
|
30
|
+
"typescript": "^5.9.3",
|
|
31
|
+
"@pluv/tsconfig": "^4.0.0",
|
|
32
|
+
"eslint-config-pluv": "^4.0.0"
|
|
33
|
+
},
|
|
34
|
+
"exports": {
|
|
35
|
+
".": "./dist/index.mjs",
|
|
36
|
+
"./*": "./*"
|
|
34
37
|
},
|
|
35
38
|
"scripts": {
|
|
36
|
-
"build": "
|
|
37
|
-
"dev": "
|
|
39
|
+
"build": "tsdown",
|
|
40
|
+
"dev": "tsdown --watch",
|
|
38
41
|
"lint": "eslint \"src/**/*.ts*\" --fix --max-warnings 0",
|
|
42
|
+
"typecheck": "tsc --noEmit",
|
|
39
43
|
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
|
|
40
44
|
}
|
|
41
45
|
}
|
package/tsdown.config.ts
ADDED