{
  "version": 3,
  "sources": ["../../src/lib/TLSocketRoom.ts"],
  "sourcesContent": ["import type { SerializedSchema, StoreSchema, UnknownRecord } from '@tldraw/store'\nimport { createTLSchema, TLInstancePresence, TLStoreSnapshot } from '@tldraw/tlschema'\nimport { getOwnProperty, hasOwnProperty, isEqual, structuredClone } from '@tldraw/utils'\nimport { JsonChunkAssembler, MessageTooLargeError } from './chunk'\nimport { DEFAULT_INITIAL_SNAPSHOT, InMemorySyncStorage } from './InMemorySyncStorage'\nimport { TLObjectStoreAccess, TLSocketServerSentEvent } from './protocol'\nimport { RoomSessionState } from './RoomSession'\nimport { ServerSocketAdapter, WebSocketMinimal } from './ServerSocketAdapter'\nimport { TLSyncErrorCloseEventReason } from './TLSyncClient'\nimport { RoomSnapshot, TLRecordAuthorizers, TLSyncRoom } from './TLSyncRoom'\nimport {\n\tconvertStoreSnapshotToRoomSnapshot,\n\tloadSnapshotIntoStorage,\n\tTLSyncForwardDiff,\n\tTLSyncStorage,\n} from './TLSyncStorage'\n\n/**\n * Strip potentially large fields from a tldraw instance_presence record so the\n * snapshot stays small when stored in WebSocket attachments (e.g. for hibernation).\n * Keeps cursor, selection, page, and user identity; clears scribbles, chatMessage, brush.\n */\nfunction stripPresenceForSnapshot(record: UnknownRecord): UnknownRecord {\n\tif (record.typeName !== 'instance_presence') return record\n\tconst stripped = { ...record } as TLInstancePresence\n\tstripped.scribbles = []\n\tstripped.chatMessage = ''\n\tstripped.selectedShapeIds = []\n\tstripped.brush = null\n\n\treturn stripped as unknown as UnknownRecord\n}\n\n/**\n * Logging interface for TLSocketRoom operations. Provides optional methods\n * for warning and error logging during synchronization operations.\n *\n * @example\n * ```ts\n * const logger: TLSyncLog = {\n *   warn: (...args) => console.warn('[SYNC]', ...args),\n *   error: (...args) => console.error('[SYNC]', ...args)\n * }\n *\n * const room = new TLSocketRoom({ log: logger })\n * ```\n *\n * @public\n */\nexport interface TLSyncLog {\n\t/**\n\t * Optional warning logger for non-fatal sync issues\n\t * @param args - Arguments to log\n\t */\n\twarn?(...args: any[]): void\n\t/**\n\t * Optional error logger for sync errors and failures\n\t * @param args - Arguments to log\n\t */\n\terror?(...args: any[]): void\n}\n\n/**\n * A snapshot of per-session state that can be persisted and used to resume a session\n * after the server restarts (e.g., after Cloudflare Durable Object hibernation).\n *\n * Obtain via {@link TLSocketRoom.getSessionSnapshot} and restore via\n * {@link TLSocketRoom.handleSocketResume}.\n *\n * @public\n */\nexport interface SessionStateSnapshot {\n\tserializedSchema: SerializedSchema\n\tisReadonly: boolean\n\t/**\n\t * Write access for the record types listed in `objectTypes`. Optional so snapshots persisted\n\t * before this field existed resume cleanly (they fail closed to 'read').\n\t */\n\tobjectAccess?: TLObjectStoreAccess\n\tpresenceId: string | null\n\tpresenceRecord: UnknownRecord | null\n\trequiresLegacyRejection: boolean\n\tsupportsStringAppend: boolean\n}\n\n/**\n * Base options for TLSocketRoom.\n * @public\n */\nexport interface TLSocketRoomOptions<R extends UnknownRecord, SessionMeta> {\n\tstorage?: TLSyncStorage<R>\n\t/**\n\t * @deprecated use the storage option instead\n\t */\n\tinitialSnapshot?: RoomSnapshot | TLStoreSnapshot\n\t/**\n\t * @deprecated use the storage option with an onChange callback instead\n\t */\n\tonDataChange?(): void\n\tschema?: StoreSchema<R, any>\n\t// how long to wait for a client to communicate before disconnecting them\n\tclientTimeout?: number\n\tlog?: TLSyncLog\n\t// a callback that is called when a client is disconnected\n\t// eslint-disable-next-line tldraw/method-signature-style\n\tonSessionRemoved?: (\n\t\troom: TLSocketRoom<R, SessionMeta>,\n\t\targs: { sessionId: string; numSessionsRemaining: number; meta: SessionMeta }\n\t) => void\n\t// a callback that is called whenever a message is sent\n\t// eslint-disable-next-line tldraw/method-signature-style\n\tonBeforeSendMessage?: (args: {\n\t\tsessionId: string\n\t\t/** @internal keep the protocol private for now */\n\t\tmessage: TLSocketServerSentEvent<R>\n\t\tstringified: string\n\t\tmeta: SessionMeta\n\t}) => void\n\t// eslint-disable-next-line tldraw/method-signature-style\n\tonAfterReceiveMessage?: (args: {\n\t\tsessionId: string\n\t\t/** @internal keep the protocol private for now */\n\t\tmessage: TLSocketServerSentEvent<R>\n\t\tstringified: string\n\t\tmeta: SessionMeta\n\t}) => void\n\t/** @internal */\n\tonPresenceChange?(): void\n\t/**\n\t * Called once after a client push commits, with the committed document diff. Use to react to\n\t * document changes as soon as they commit \u2014 e.g. project certain record types to an external\n\t * store. Best-effort: do not throw and do not block.\n\t *\n\t * Only fires for client pushes. Server-initiated changes \u2014 `updateStore`, `loadSnapshot`, or\n\t * writing to the storage directly \u2014 do not trigger it, so anything mirroring room state through\n\t * this callback must handle those paths itself.\n\t */\n\t// eslint-disable-next-line tldraw/method-signature-style\n\tonCommittedChanges?: (args: { diff: TLSyncForwardDiff<R>; documentClock: number }) => void\n\t/**\n\t * Record type names to serve through the object-store lane instead of the document lane.\n\t * Each must be a document-scoped type registered in the schema. Object-lane writes are gated\n\t * per session by `objectAccess` (see {@link TLSocketRoom.handleSocketConnect}) rather than\n\t * `isReadonly`, so e.g. a session can be allowed to comment without being allowed to edit.\n\t */\n\tobjectTypes?: readonly string[]\n\t/**\n\t * Per-type authorizers for client record writes (create, update, delete): veto writes the\n\t * session isn't allowed to make, or rewrite the record on create \u2014 e.g. force a comment's\n\t * `authorId` to the signed-in user. See {@link TLRecordAuthorizers}.\n\t */\n\tauthorizeRecord?: TLRecordAuthorizers<R, SessionMeta>\n\t/**\n\t * When set, the room will call {@link TLSocketRoom.getSessionSnapshot} after\n\t * no message activity for a session for 5s and pass the result to this callback.\n\t * Use for persisting snapshots to WebSocket attachments (e.g. Cloudflare hibernation).\n\t * The room clears any pending snapshot when the session closes.\n\t */\n\t// eslint-disable-next-line tldraw/method-signature-style\n\tonSessionSnapshot?: (sessionId: string, snapshot: SessionStateSnapshot) => void\n}\n\n/**\n * A server-side room that manages WebSocket connections and synchronizes tldraw document state\n * between multiple clients in real-time. Each room represents a collaborative document space\n * where users can work together on drawings with automatic conflict resolution.\n *\n * TLSocketRoom handles:\n * - WebSocket connection lifecycle management\n * - Real-time synchronization of document changes\n * - Session management and presence tracking\n * - Message chunking for large payloads\n * - Automatic client timeout and cleanup\n *\n * @example\n * ```ts\n * // Basic room setup\n * const room = new TLSocketRoom({\n *   onSessionRemoved: (room, { sessionId, numSessionsRemaining }) => {\n *     console.log(`Client ${sessionId} disconnected, ${numSessionsRemaining} remaining`)\n *     if (numSessionsRemaining === 0) {\n *       room.close()\n *     }\n *   },\n *   onDataChange: () => {\n *     console.log('Document data changed, consider persisting')\n *   }\n * })\n *\n * // Handle new client connections\n * room.handleSocketConnect({\n *   sessionId: 'user-session-123',\n *   socket: webSocket,\n *   isReadonly: false\n * })\n * ```\n *\n * @example\n * ```ts\n * // Room with initial snapshot and schema\n * const room = new TLSocketRoom({\n *   initialSnapshot: existingSnapshot,\n *   schema: myCustomSchema,\n *   clientTimeout: 30000,\n *   log: {\n *     warn: (...args) => logger.warn('SYNC:', ...args),\n *     error: (...args) => logger.error('SYNC:', ...args)\n *   }\n * })\n *\n * // Update document programmatically\n * await room.updateStore(store => {\n *   const shape = store.get('shape:abc123')\n *   if (shape) {\n *     shape.x = 100\n *     store.put(shape)\n *   }\n * })\n * ```\n *\n * @public\n */\nexport class TLSocketRoom<R extends UnknownRecord = UnknownRecord, SessionMeta = void> {\n\tprivate room: TLSyncRoom<R, SessionMeta>\n\tprivate readonly sessions = new Map<\n\t\tstring,\n\t\t// eslint-disable-next-line tldraw/method-signature-style\n\t\t{ assembler: JsonChunkAssembler; socket: WebSocketMinimal; unlisten: () => void }\n\t>()\n\treadonly log?: TLSyncLog\n\n\tpublic storage: TLSyncStorage<R>\n\n\tprivate disposables = new Set<() => void>()\n\tprivate readonly snapshotTimers = new Map<string, ReturnType<typeof setTimeout>>()\n\n\t/**\n\t * Creates a new TLSocketRoom instance for managing collaborative document synchronization.\n\t *\n\t * opts - Configuration options for the room\n\t *   - initialSnapshot - Optional initial document state to load\n\t *   - schema - Store schema defining record types and validation\n\t *   - clientTimeout - Milliseconds to wait before disconnecting inactive clients\n\t *   - log - Optional logger for warnings and errors\n\t *   - onSessionRemoved - Called when a client session is removed\n\t *   - onBeforeSendMessage - Called before sending messages to clients\n\t *   - onAfterReceiveMessage - Called after receiving messages from clients\n\t *   - onDataChange - Called when document data changes\n\t *   - onPresenceChange - Called when presence data changes\n\t */\n\tconstructor(public readonly opts: TLSocketRoomOptions<R, SessionMeta>) {\n\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\tif (opts.storage && opts.initialSnapshot) {\n\t\t\tthrow new Error('Cannot provide both storage and initialSnapshot options')\n\t\t}\n\t\tconst storage = opts.storage\n\t\t\t? opts.storage\n\t\t\t: new InMemorySyncStorage<R>({\n\t\t\t\t\tsnapshot: convertStoreSnapshotToRoomSnapshot(\n\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t\t\t\topts.initialSnapshot ?? DEFAULT_INITIAL_SNAPSHOT\n\t\t\t\t\t),\n\t\t\t\t\t// keep the storage partition in step with the room's object lane\n\t\t\t\t\tobjectTypes: opts.objectTypes,\n\t\t\t\t})\n\n\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\tif ('onDataChange' in opts && opts.onDataChange) {\n\t\t\tthis.disposables.add(\n\t\t\t\tstorage.onChange(() => {\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t\t\topts.onDataChange?.()\n\t\t\t\t})\n\t\t\t)\n\t\t}\n\t\t// The default logger belongs to both layers: TLSyncRoom is where authorizer throws are\n\t\t// logged, and a host that passes no `log` would otherwise never see them.\n\t\tthis.log = 'log' in opts ? opts.log : { error: console.error }\n\t\tthis.room = new TLSyncRoom<R, SessionMeta>({\n\t\t\tonPresenceChange: opts.onPresenceChange,\n\t\t\tonCommittedChanges: opts.onCommittedChanges,\n\t\t\tobjectTypes: opts.objectTypes,\n\t\t\tauthorizeRecord: opts.authorizeRecord,\n\t\t\tschema: opts.schema ?? (createTLSchema() as any),\n\t\t\tlog: this.log,\n\t\t\tstorage,\n\t\t\tclientTimeout: opts.clientTimeout,\n\t\t})\n\t\tthis.storage = storage\n\t\tthis.room.events.on('session_removed', (args) => {\n\t\t\tthis.clearSnapshotTimer(args.sessionId)\n\t\t\tthis.sessions.delete(args.sessionId)\n\t\t\tif (this.opts.onSessionRemoved) {\n\t\t\t\tthis.opts.onSessionRemoved(this, {\n\t\t\t\t\tsessionId: args.sessionId,\n\t\t\t\t\tnumSessionsRemaining: this.room.sessions.size,\n\t\t\t\t\tmeta: args.meta,\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Returns the number of active sessions.\n\t * Note that this is not the same as the number of connected sockets!\n\t * Sessions time out a few moments after sockets close, to smooth over network hiccups.\n\t *\n\t * @returns the number of active sessions\n\t */\n\tgetNumActiveSessions() {\n\t\treturn this.room.sessions.size\n\t}\n\n\t/**\n\t * Handles a new client WebSocket connection, creating a session within the room.\n\t * This should be called whenever a client establishes a WebSocket connection to join\n\t * the collaborative document.\n\t *\n\t * @param opts - Connection options\n\t *   - sessionId - Unique identifier for the client session (typically from browser tab)\n\t *   - socket - WebSocket-like object for client communication\n\t *   - isReadonly - Whether the client can modify the document (defaults to false)\n\t *   - objectAccess - Write access for object-store lane record types (defaults to 'write').\n\t *     Independent of isReadonly, so a session can be allowed to write objects (e.g. comments)\n\t *     without being allowed to edit the document.\n\t *   - meta - Additional session metadata (required if SessionMeta is not void)\n\t *\n\t * @example\n\t * ```ts\n\t * // Handle new WebSocket connection\n\t * room.handleSocketConnect({\n\t *   sessionId: 'user-session-abc123',\n\t *   socket: webSocketConnection,\n\t *   isReadonly: !userHasEditPermission\n\t * })\n\t * ```\n\t *\n\t * @example\n\t * ```ts\n\t * // With session metadata\n\t * room.handleSocketConnect({\n\t *   sessionId: 'session-xyz',\n\t *   socket: ws,\n\t *   meta: { userId: 'user-123', name: 'Alice' }\n\t * })\n\t * ```\n\t */\n\thandleSocketConnect(\n\t\topts: {\n\t\t\tsessionId: string\n\t\t\tsocket: WebSocketMinimal\n\t\t\tisReadonly?: boolean\n\t\t\tobjectAccess?: TLObjectStoreAccess\n\t\t} & (SessionMeta extends void ? object : { meta: SessionMeta })\n\t) {\n\t\tconst { sessionId, socket, isReadonly = false, objectAccess = 'write' } = opts\n\t\tconst handleSocketMessage = (event: MessageEvent) =>\n\t\t\tthis.handleSocketMessage(sessionId, event.data)\n\t\tconst handleSocketError = this.handleSocketError.bind(this, sessionId)\n\t\tconst handleSocketClose = this.handleSocketClose.bind(this, sessionId)\n\n\t\tthis.sessions.set(sessionId, {\n\t\t\tassembler: new JsonChunkAssembler(),\n\t\t\tsocket,\n\t\t\tunlisten: () => {\n\t\t\t\tsocket.removeEventListener?.('message', handleSocketMessage)\n\t\t\t\tsocket.removeEventListener?.('close', handleSocketClose)\n\t\t\t\tsocket.removeEventListener?.('error', handleSocketError)\n\t\t\t},\n\t\t})\n\n\t\tthis.room.handleNewSession({\n\t\t\tsessionId,\n\t\t\tisReadonly,\n\t\t\tobjectAccess,\n\t\t\tsocket: new ServerSocketAdapter({\n\t\t\t\tws: socket,\n\t\t\t\tonBeforeSendMessage: this.opts.onBeforeSendMessage\n\t\t\t\t\t? (message, stringified) =>\n\t\t\t\t\t\t\tthis.opts.onBeforeSendMessage!({\n\t\t\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\t\t\tmessage,\n\t\t\t\t\t\t\t\tstringified,\n\t\t\t\t\t\t\t\tmeta: this.room.sessions.get(sessionId)?.meta as SessionMeta,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t: undefined,\n\t\t\t}),\n\t\t\tmeta: 'meta' in opts ? (opts.meta as any) : undefined,\n\t\t})\n\n\t\tsocket.addEventListener?.('message', handleSocketMessage)\n\t\tsocket.addEventListener?.('close', handleSocketClose)\n\t\tsocket.addEventListener?.('error', handleSocketError)\n\t}\n\n\tprivate clearSnapshotTimer(sessionId: string) {\n\t\tconst t = this.snapshotTimers.get(sessionId)\n\t\tif (t) {\n\t\t\tclearTimeout(t)\n\t\t\tthis.snapshotTimers.delete(sessionId)\n\t\t}\n\t}\n\n\tprivate scheduleDebouncedSnapshot(sessionId: string) {\n\t\tif (!this.opts.onSessionSnapshot) return\n\t\tthis.clearSnapshotTimer(sessionId)\n\t\tthis.snapshotTimers.set(\n\t\t\tsessionId,\n\t\t\tsetTimeout(() => {\n\t\t\t\tthis.snapshotTimers.delete(sessionId)\n\t\t\t\tconst snapshot = this.getSessionSnapshot(sessionId)\n\t\t\t\tif (snapshot) this.opts.onSessionSnapshot!(sessionId, snapshot)\n\t\t\t}, 5000)\n\t\t)\n\t}\n\n\t/**\n\t * Processes a message received from a client WebSocket. Use this method in server\n\t * environments where WebSocket event listeners cannot be attached directly to socket\n\t * instances (e.g., Bun.serve, Cloudflare Workers with WebSocket hibernation).\n\t *\n\t * The method handles message chunking/reassembly and forwards complete messages\n\t * to the underlying sync room for processing.\n\t *\n\t * @param sessionId - Session identifier matching the one used in handleSocketConnect\n\t * @param message - Raw message data from the client (string or binary)\n\t *\n\t * @example\n\t * ```ts\n\t * // In a Bun.serve handler\n\t * server.upgrade(req, {\n\t *   data: { sessionId, room },\n\t *   upgrade(res, req) {\n\t *     // Connection established\n\t *   },\n\t *   message(ws, message) {\n\t *     const { sessionId, room } = ws.data\n\t *     room.handleSocketMessage(sessionId, message)\n\t *   }\n\t * })\n\t * ```\n\t */\n\thandleSocketMessage(sessionId: string, message: string | AllowSharedBufferSource) {\n\t\tconst assembler = this.sessions.get(sessionId)?.assembler\n\t\tif (!assembler) {\n\t\t\tthis.log?.warn?.('Received message from unknown session', sessionId)\n\t\t\treturn\n\t\t}\n\n\t\ttry {\n\t\t\tconst messageString =\n\t\t\t\ttypeof message === 'string' ? message : new TextDecoder().decode(message)\n\t\t\tconst res = assembler.handleMessage(messageString)\n\t\t\tif (!res) {\n\t\t\t\t// not enough chunks yet\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif ('data' in res) {\n\t\t\t\t// need to do this first in case the session gets removed as a result of handling the message\n\t\t\t\tif (this.opts.onAfterReceiveMessage) {\n\t\t\t\t\tconst session = this.room.sessions.get(sessionId)\n\t\t\t\t\tif (session) {\n\t\t\t\t\t\tthis.opts.onAfterReceiveMessage({\n\t\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\t\tmessage: res.data as any,\n\t\t\t\t\t\t\tstringified: res.stringified,\n\t\t\t\t\t\t\tmeta: session.meta,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// handleMessage is async but does not await internally; catch rejections\n\t\t\t\t// so an error thrown while handling the message still rejects the session\n\t\t\t\tthis.room.handleMessage(sessionId, res.data as any).catch((error) => {\n\t\t\t\t\tthis.log?.error?.(error)\n\t\t\t\t\tthis.room.rejectSession(sessionId, TLSyncErrorCloseEventReason.UNKNOWN_ERROR)\n\t\t\t\t})\n\t\t\t\tthis.room.pruneSessions()\n\t\t\t\tthis.scheduleDebouncedSnapshot(sessionId)\n\t\t\t} else if (res.error instanceof MessageTooLargeError) {\n\t\t\t\tthis.log?.error?.('Error assembling message', res.error)\n\t\t\t\t// Resetting the connection would have the client reconnect and re-send the same\n\t\t\t\t// oversized message, so reject the session instead and let the client surface it.\n\t\t\t\tthis.room.rejectSession(sessionId, TLSyncErrorCloseEventReason.MESSAGE_TOO_LARGE)\n\t\t\t} else {\n\t\t\t\tthis.log?.error?.('Error assembling message', res.error)\n\t\t\t\t// close the socket to reset the connection\n\t\t\t\tthis.handleSocketError(sessionId)\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tthis.log?.error?.(e)\n\t\t\t// here we use rejectSession rather than removeSession to support legacy clients\n\t\t\t// that use the old incompatibility_error close event\n\t\t\tthis.room.rejectSession(sessionId, TLSyncErrorCloseEventReason.UNKNOWN_ERROR)\n\t\t}\n\t}\n\n\t/**\n\t * Handles a WebSocket error for the specified session. Use this in server environments\n\t * where socket event listeners cannot be attached directly. This will initiate cleanup\n\t * and session removal for the affected client.\n\t *\n\t * @param sessionId - Session identifier matching the one used in handleSocketConnect\n\t *\n\t * @example\n\t * ```ts\n\t * // In a custom WebSocket handler\n\t * socket.addEventListener('error', () => {\n\t *   room.handleSocketError(sessionId)\n\t * })\n\t * ```\n\t */\n\thandleSocketError(sessionId: string) {\n\t\tthis.clearSnapshotTimer(sessionId)\n\t\tthis.room.handleClose(sessionId)\n\t}\n\n\t/**\n\t * Handles a WebSocket close event for the specified session. Use this in server\n\t * environments where socket event listeners cannot be attached directly. This will\n\t * initiate cleanup and session removal for the disconnected client.\n\t *\n\t * @param sessionId - Session identifier matching the one used in handleSocketConnect\n\t *\n\t * @example\n\t * ```ts\n\t * // In a custom WebSocket handler\n\t * socket.addEventListener('close', () => {\n\t *   room.handleSocketClose(sessionId)\n\t * })\n\t * ```\n\t */\n\thandleSocketClose(sessionId: string) {\n\t\tthis.clearSnapshotTimer(sessionId)\n\t\tthis.room.handleClose(sessionId)\n\t}\n\n\t/**\n\t * Resumes a previously-connected session directly into `Connected` state, bypassing\n\t * the connect handshake. Use this after server hibernation (e.g., Cloudflare Durable\n\t * Object hibernation) when WebSocket connections survived but all in-memory state was lost.\n\t *\n\t * The session is restored using a {@link SessionStateSnapshot} previously obtained\n\t * via {@link TLSocketRoom.getSessionSnapshot}. The client is unaware the server restarted and\n\t * continues sending messages normally.\n\t *\n\t * Unlike {@link TLSocketRoom.handleSocketConnect}, this method does NOT attach WebSocket event\n\t * listeners. In hibernation environments, events are delivered via class methods\n\t * (e.g., `webSocketMessage`) rather than `addEventListener`.\n\t *\n\t * @param opts - Resume options\n\t *   - sessionId - Unique identifier for the client session\n\t *   - socket - WebSocket-like object for client communication\n\t *   - snapshot - Session state snapshot from {@link TLSocketRoom.getSessionSnapshot}\n\t *   - meta - Additional session metadata (required if SessionMeta is not void)\n\t *\n\t * @example\n\t * ```ts\n\t * // After Cloudflare DO hibernation wake\n\t * for (const ws of ctx.getWebSockets()) {\n\t *   const data = ws.deserializeAttachment()\n\t *   room.handleSocketResume({\n\t *     sessionId: data.sessionId,\n\t *     socket: ws,\n\t *     snapshot: data.snapshot,\n\t *   })\n\t * }\n\t * ```\n\t */\n\thandleSocketResume(\n\t\topts: {\n\t\t\tsessionId: string\n\t\t\tsocket: WebSocketMinimal\n\t\t\tsnapshot: SessionStateSnapshot\n\t\t} & (SessionMeta extends void ? object : { meta: SessionMeta })\n\t) {\n\t\tconst { sessionId, socket, snapshot } = opts\n\n\t\tthis.sessions.set(sessionId, {\n\t\t\tassembler: new JsonChunkAssembler(),\n\t\t\tsocket,\n\t\t\tunlisten: () => {\n\t\t\t\t// no-op: hibernation environments use class methods, not addEventListener\n\t\t\t},\n\t\t})\n\n\t\tthis.room.handleResumedSession({\n\t\t\tsessionId,\n\t\t\tisReadonly: snapshot.isReadonly,\n\t\t\t// snapshots persisted before this field existed fail closed \u2014 those sessions predate\n\t\t\t// the record types gated by objectAccess, so they have nothing to write anyway\n\t\t\tobjectAccess: snapshot.objectAccess ?? 'read',\n\t\t\tserializedSchema: snapshot.serializedSchema,\n\t\t\tpresenceId: snapshot.presenceId,\n\t\t\tpresenceRecord: snapshot.presenceRecord,\n\t\t\trequiresLegacyRejection: snapshot.requiresLegacyRejection,\n\t\t\tsupportsStringAppend: snapshot.supportsStringAppend,\n\t\t\tsocket: new ServerSocketAdapter({\n\t\t\t\tws: socket,\n\t\t\t\tonBeforeSendMessage: this.opts.onBeforeSendMessage\n\t\t\t\t\t? (message, stringified) =>\n\t\t\t\t\t\t\tthis.opts.onBeforeSendMessage!({\n\t\t\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\t\t\tmessage,\n\t\t\t\t\t\t\t\tstringified,\n\t\t\t\t\t\t\t\tmeta: this.room.sessions.get(sessionId)?.meta as SessionMeta,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t: undefined,\n\t\t\t}),\n\t\t\tmeta: 'meta' in opts ? (opts.meta as any) : undefined,\n\t\t})\n\t}\n\n\t/**\n\t * Returns a snapshot of a connected session's state that can be persisted and later\n\t * used with {@link TLSocketRoom.handleSocketResume} to restore the session after hibernation.\n\t *\n\t * Returns `null` if the session doesn't exist or isn't in the `Connected` state.\n\t *\n\t * @param sessionId - The session to snapshot\n\t *\n\t * @example\n\t * ```ts\n\t * // Store snapshot in a Cloudflare WebSocket attachment\n\t * const snapshot = room.getSessionSnapshot(sessionId)\n\t * if (snapshot) {\n\t *   ws.serializeAttachment({ sessionId, snapshot })\n\t * }\n\t * ```\n\t */\n\tgetSessionSnapshot(sessionId: string): SessionStateSnapshot | null {\n\t\tconst session = this.room.sessions.get(sessionId)\n\t\tif (!session || session.state !== RoomSessionState.Connected) {\n\t\t\treturn null\n\t\t}\n\n\t\tlet presenceRecord: UnknownRecord | null = null\n\t\tif (session.presenceId) {\n\t\t\tconst record = this.room.presenceStore.get(session.presenceId)\n\t\t\tif (record) {\n\t\t\t\tpresenceRecord = stripPresenceForSnapshot(record as UnknownRecord)\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tserializedSchema: session.serializedSchema,\n\t\t\tisReadonly: session.isReadonly,\n\t\t\tobjectAccess: session.objectAccess,\n\t\t\tpresenceId: session.presenceId,\n\t\t\tpresenceRecord,\n\t\t\trequiresLegacyRejection: session.requiresLegacyRejection,\n\t\t\tsupportsStringAppend: session.supportsStringAppend,\n\t\t}\n\t}\n\n\t/**\n\t * Returns the current document clock value. The clock is a monotonically increasing\n\t * integer that increments with each document change, providing a consistent ordering\n\t * of changes across the distributed system.\n\t *\n\t * @returns The current document clock value\n\t *\n\t * @example\n\t * ```ts\n\t * const clock = room.getCurrentDocumentClock()\n\t * console.log(`Document is at version ${clock}`)\n\t * ```\n\t */\n\tgetCurrentDocumentClock() {\n\t\treturn this.storage.getClock()\n\t}\n\n\t/**\n\t * Retrieves a deeply cloned copy of a record from the document store.\n\t * Returns undefined if the record doesn't exist. The returned record is\n\t * safe to mutate without affecting the original store data.\n\t *\n\t * @param id - Unique identifier of the record to retrieve\n\t * @returns Deep clone of the record, or undefined if not found\n\t *\n\t * @example\n\t * ```ts\n\t * const shape = room.getRecord('shape:abc123')\n\t * if (shape) {\n\t *   console.log('Shape position:', shape.x, shape.y)\n\t *   // Safe to modify without affecting store\n\t *   shape.x = 100\n\t * }\n\t * ```\n\t */\n\tgetRecord(id: string) {\n\t\treturn this.storage.transaction((txn) => {\n\t\t\treturn structuredClone(txn.get(id)) as any\n\t\t}).result as R\n\t}\n\n\t/**\n\t * Returns information about all active sessions in the room. Each session\n\t * represents a connected client with their current connection status and metadata.\n\t *\n\t * @returns Array of session information objects containing:\n\t *   - sessionId - Unique session identifier\n\t *   - isConnected - Whether the session has an active WebSocket connection\n\t *   - isReadonly - Whether the session can modify the document\n\t *   - meta - Custom session metadata\n\t *\n\t * @example\n\t * ```ts\n\t * const sessions = room.getSessions()\n\t * console.log(`Room has ${sessions.length} active sessions`)\n\t *\n\t * for (const session of sessions) {\n\t *   console.log(`${session.sessionId}: ${session.isConnected ? 'online' : 'offline'}`)\n\t *   if (session.isReadonly) {\n\t *     console.log('  (read-only access)')\n\t *   }\n\t * }\n\t * ```\n\t */\n\tgetSessions(): Array<{\n\t\tsessionId: string\n\t\tisConnected: boolean\n\t\tisReadonly: boolean\n\t\tobjectAccess: TLObjectStoreAccess\n\t\tmeta: SessionMeta\n\t}> {\n\t\treturn [...this.room.sessions.values()].map((session) => {\n\t\t\treturn {\n\t\t\t\tsessionId: session.sessionId,\n\t\t\t\tisConnected: session.state === RoomSessionState.Connected,\n\t\t\t\tisReadonly: session.isReadonly,\n\t\t\t\tobjectAccess: session.objectAccess,\n\t\t\t\tmeta: session.meta,\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Creates a complete snapshot of the current document state, including all records\n\t * and synchronization metadata. This snapshot can be persisted to storage and used\n\t * to restore the room state later or revert to a previous version.\n\t *\n\t * @returns Complete room snapshot including documents, clock values, and tombstones\n\t * @deprecated if you need to do this use\n\t *\n\t * @example\n\t * ```ts\n\t * // Capture current state for persistence\n\t * const snapshot = room.getCurrentSnapshot()\n\t * await saveToDatabase(roomId, JSON.stringify(snapshot))\n\t *\n\t * // Later, restore from snapshot\n\t * const savedSnapshot = JSON.parse(await loadFromDatabase(roomId))\n\t * const newRoom = new TLSocketRoom({ initialSnapshot: savedSnapshot })\n\t * ```\n\t */\n\tgetCurrentSnapshot() {\n\t\tif (this.storage.getSnapshot) {\n\t\t\treturn this.storage.getSnapshot()\n\t\t}\n\t\tthrow new Error('getCurrentSnapshot is not supported for this storage type')\n\t}\n\n\t/**\n\t * Returns a snapshot of the object-store lane (see `objectTypes`), for persisting\n\t * object-lane records separately from the document. Same entry shape as\n\t * {@link RoomSnapshot.documents}.\n\t */\n\tgetCurrentObjectsSnapshot(): RoomSnapshot['documents'] {\n\t\tif (this.storage.getObjectsSnapshot) {\n\t\t\treturn this.storage.getObjectsSnapshot()\n\t\t}\n\t\tthrow new Error('getCurrentObjectsSnapshot is not supported for this storage type')\n\t}\n\n\t/**\n\t * Retrieves all presence records from the document store. Presence records\n\t * contain ephemeral user state like cursor positions and selections.\n\t *\n\t * @returns Object mapping record IDs to presence record data\n\t * @internal\n\t */\n\tgetPresenceRecords() {\n\t\tconst result = {} as Record<string, UnknownRecord>\n\t\tfor (const presence of this.room.presenceStore.values()) {\n\t\t\tresult[presence.id] = presence\n\t\t}\n\t\treturn result\n\t}\n\n\t/**\n\t * Loads a document snapshot, completely replacing the current room state.\n\t * This will disconnect all current clients and update the document to match\n\t * the provided snapshot. Use this for restoring from backups or implementing\n\t * document versioning.\n\t *\n\t * @param snapshot - Room or store snapshot to load\n\t *\n\t * @example\n\t * ```ts\n\t * // Restore from a saved snapshot\n\t * const backup = JSON.parse(await loadBackup(roomId))\n\t * room.loadSnapshot(backup)\n\t *\n\t * // All clients will be disconnected and need to reconnect\n\t * // to see the restored document state\n\t * ```\n\t */\n\tloadSnapshot(snapshot: RoomSnapshot | TLStoreSnapshot) {\n\t\tthis.storage.transaction((txn) => {\n\t\t\tloadSnapshotIntoStorage(txn, this.room.schema, snapshot)\n\t\t})\n\t}\n\n\t/**\n\t * Executes a transaction to modify the document store. Changes made within the\n\t * transaction are atomic and will be synchronized to all connected clients.\n\t * The transaction provides isolation from concurrent changes until it commits.\n\t *\n\t * @param updater - Function that receives store methods to make changes\n\t *   - store.get(id) - Retrieve a record (safe to mutate, but must call put() to commit)\n\t *   - store.put(record) - Save a modified record\n\t *   - store.getAll() - Get all records in the store\n\t *   - store.delete(id) - Remove a record from the store\n\t * @returns Promise that resolves when the transaction completes\n\t *\n\t * @example\n\t * ```ts\n\t * // Update multiple shapes in a single transaction\n\t * await room.updateStore(store => {\n\t *   const shape1 = store.get('shape:abc123')\n\t *   const shape2 = store.get('shape:def456')\n\t *\n\t *   if (shape1) {\n\t *     shape1.x = 100\n\t *     store.put(shape1)\n\t *   }\n\t *\n\t *   if (shape2) {\n\t *     shape2.meta.approved = true\n\t *     store.put(shape2)\n\t *   }\n\t * })\n\t * ```\n\t *\n\t * @example\n\t * ```ts\n\t * // Async transaction with external API call\n\t * await room.updateStore(async store => {\n\t *   const doc = store.get('document:main')\n\t *   if (doc) {\n\t *     doc.lastModified = await getCurrentTimestamp()\n\t *     store.put(doc)\n\t *   }\n\t * })\n\t * ```\n\t * @deprecated use the storage.transaction method instead\n\t */\n\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\tasync updateStore(updater: (store: RoomStoreMethods<R>) => void | Promise<void>) {\n\t\tif (this.isClosed()) {\n\t\t\tthrow new Error('Cannot update store on a closed room')\n\t\t}\n\t\t// read through the transaction rather than getSnapshot(): the document snapshot excludes the\n\t\t// object-store lane, which would make object-lane records invisible (and undeletable) here\n\t\tconst records = this.storage.transaction((txn) => Object.fromEntries(txn.entries())).result\n\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\tconst ctx = new StoreUpdateContext<R>(records, this.room.schema)\n\t\ttry {\n\t\t\tawait updater(ctx)\n\t\t} finally {\n\t\t\tctx.close()\n\t\t}\n\t\tthis.storage.transaction((txn) => {\n\t\t\tfor (const [id, record] of Object.entries(ctx.updates.puts)) {\n\t\t\t\ttxn.set(id, record as R)\n\t\t\t}\n\t\t\tfor (const id of ctx.updates.deletes) {\n\t\t\t\ttxn.delete(id)\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Sends a custom message to a specific client session. This allows sending\n\t * application-specific data that doesn't modify the document state, such as\n\t * notifications, chat messages, or custom commands.\n\t *\n\t * @param sessionId - Target session identifier\n\t * @param data - Custom payload to send (will be JSON serialized)\n\t *\n\t * @example\n\t * ```ts\n\t * // Send a notification to a specific user\n\t * room.sendCustomMessage('session-123', {\n\t *   type: 'notification',\n\t *   message: 'Your changes have been saved'\n\t * })\n\t *\n\t * // Send a chat message\n\t * room.sendCustomMessage('session-456', {\n\t *   type: 'chat',\n\t *   from: 'Alice',\n\t *   text: 'Great work on this design!'\n\t * })\n\t * ```\n\t */\n\tsendCustomMessage(sessionId: string, data: any) {\n\t\tthis.room.sendCustomMessage(sessionId, data)\n\t}\n\n\t/**\n\t * Immediately removes a session from the room and closes its WebSocket connection.\n\t * The client will attempt to reconnect automatically unless a fatal reason is provided.\n\t *\n\t * @param sessionId - Session identifier to remove\n\t * @param fatalReason - Optional fatal error reason that prevents reconnection. WebSocket close\n\t * reasons are capped at 123 UTF-8 bytes, so a longer reason is truncated and ends with `... (+N bytes)`.\n\t *\n\t * @example\n\t * ```ts\n\t * // Kick a user (they can reconnect)\n\t * room.closeSession('session-troublemaker')\n\t *\n\t * // Permanently ban a user\n\t * room.closeSession('session-banned', 'PERMISSION_DENIED')\n\t *\n\t * // Close session due to inactivity\n\t * room.closeSession('session-idle', 'TIMEOUT')\n\t * ```\n\t */\n\tcloseSession(sessionId: string, fatalReason?: TLSyncErrorCloseEventReason | string) {\n\t\tthis.room.rejectSession(sessionId, fatalReason)\n\t}\n\n\t/**\n\t * Closes the room and disconnects all connected clients. This should be called\n\t * when shutting down the room permanently, such as during server shutdown or\n\t * when the room is no longer needed. Once closed, the room cannot be reopened.\n\t *\n\t * @example\n\t * ```ts\n\t * // Clean shutdown when no users remain\n\t * if (room.getNumActiveSessions() === 0) {\n\t *   await persistSnapshot(room.getCurrentSnapshot())\n\t *   room.close()\n\t * }\n\t *\n\t * // Server shutdown\n\t * process.on('SIGTERM', () => {\n\t *   for (const room of activeRooms.values()) {\n\t *     room.close()\n\t *   }\n\t * })\n\t * ```\n\t */\n\tclose() {\n\t\tthis.room.close()\n\t\tfor (const sessionId of this.snapshotTimers.keys()) {\n\t\t\tthis.clearSnapshotTimer(sessionId)\n\t\t}\n\t\tthis.disposables.forEach((d) => d())\n\t\tthis.disposables.clear()\n\t}\n\n\t/**\n\t * Checks whether the room has been permanently closed. Closed rooms cannot\n\t * accept new connections or process further changes.\n\t *\n\t * @returns True if the room is closed, false if still active\n\t *\n\t * @example\n\t * ```ts\n\t * if (room.isClosed()) {\n\t *   console.log('Room has been shut down')\n\t *   // Create a new room or redirect users\n\t * } else {\n\t *   // Room is still accepting connections\n\t *   room.handleSocketConnect({ sessionId, socket })\n\t * }\n\t * ```\n\t */\n\tisClosed() {\n\t\treturn this.room.isClosed()\n\t}\n}\n\n/**\n * Utility type that removes properties with void values from an object type.\n * This is used internally to conditionally require session metadata based on\n * whether SessionMeta extends void.\n *\n * @example\n * ```ts\n * type Example = { a: string, b: void, c: number }\n * type Result = OmitVoid<Example> // { a: string, c: number }\n * ```\n *\n * @public\n */\nexport type OmitVoid<T, KS extends keyof T = keyof T> = {\n\t[K in KS extends any ? (void extends T[KS] ? never : KS) : never]: T[K]\n}\n\n/**\n * Interface for making transactional changes to room store data. Used within\n * updateStore transactions to modify documents atomically.\n *\n * @example\n * ```ts\n * await room.updateStore((store) => {\n *   const shape = store.get('shape:123')\n *   if (shape) {\n *     store.put({ ...shape, x: shape.x + 10 })\n *   }\n *   store.delete('shape:456')\n * })\n * ```\n *\n * @public\n * @deprecated use the storage.transaction method instead\n */\nexport interface RoomStoreMethods<R extends UnknownRecord = UnknownRecord> {\n\t/**\n\t * Add or update a record in the store.\n\t *\n\t * @param record - The record to store\n\t */\n\tput(record: R): void\n\t/**\n\t * Delete a record from the store.\n\t *\n\t * @param recordOrId - The record or record ID to delete\n\t */\n\tdelete(recordOrId: R | string): void\n\t/**\n\t * Get a record by its ID.\n\t *\n\t * @param id - The record ID\n\t * @returns The record or null if not found\n\t */\n\tget(id: string): R | null\n\t/**\n\t * Get all records in the store.\n\t *\n\t * @returns Array of all records\n\t */\n\tgetAll(): R[]\n}\n\n/**\n * @deprecated use the storage.transaction method instead\n */\n// eslint-disable-next-line @typescript-eslint/no-deprecated\nclass StoreUpdateContext<R extends UnknownRecord> implements RoomStoreMethods<R> {\n\tconstructor(\n\t\tprivate readonly snapshot: Record<string, UnknownRecord>,\n\t\tprivate readonly schema: StoreSchema<R, any>\n\t) {}\n\treadonly updates = {\n\t\tputs: {} as Record<string, UnknownRecord>,\n\t\tdeletes: new Set<string>(),\n\t}\n\tput(record: R): void {\n\t\tif (this._isClosed) throw new Error('StoreUpdateContext is closed')\n\t\tconst recordType = getOwnProperty(this.schema.types, record.typeName)\n\t\tif (!recordType) {\n\t\t\tthrow new Error(`Missing definition for record type ${record.typeName}`)\n\t\t}\n\t\tconst recordBefore = this.snapshot[record.id] ?? undefined\n\t\trecordType.validate(record, recordBefore as R)\n\n\t\tif (record.id in this.snapshot && isEqual(this.snapshot[record.id], record)) {\n\t\t\tdelete this.updates.puts[record.id]\n\t\t} else {\n\t\t\tthis.updates.puts[record.id] = structuredClone(record)\n\t\t}\n\t\tthis.updates.deletes.delete(record.id)\n\t}\n\tdelete(recordOrId: R | string): void {\n\t\tif (this._isClosed) throw new Error('StoreUpdateContext is closed')\n\t\tconst id = typeof recordOrId === 'string' ? recordOrId : recordOrId.id\n\t\tdelete this.updates.puts[id]\n\t\tif (this.snapshot[id]) {\n\t\t\tthis.updates.deletes.add(id)\n\t\t}\n\t}\n\tget(id: string): R | null {\n\t\tif (this._isClosed) throw new Error('StoreUpdateContext is closed')\n\t\tif (hasOwnProperty(this.updates.puts, id)) {\n\t\t\treturn structuredClone(this.updates.puts[id]) as R\n\t\t}\n\t\tif (this.updates.deletes.has(id)) {\n\t\t\treturn null\n\t\t}\n\t\treturn structuredClone(this.snapshot[id] ?? null) as R\n\t}\n\n\tgetAll(): R[] {\n\t\tif (this._isClosed) throw new Error('StoreUpdateContext is closed')\n\t\tconst result = Object.values(this.updates.puts)\n\t\tfor (const [id, record] of Object.entries(this.snapshot)) {\n\t\t\tif (!this.updates.deletes.has(id) && !hasOwnProperty(this.updates.puts, id)) {\n\t\t\t\tresult.push(record)\n\t\t\t}\n\t\t}\n\t\treturn structuredClone(result) as R[]\n\t}\n\n\tprivate _isClosed = false\n\tclose() {\n\t\tthis._isClosed = true\n\t}\n}\n"],
  "mappings": "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",
  "names": []
}
