{"version":3,"file":"hocuspocus-server.cjs","names":["AuthMessageType","messageYjsSyncStep1","messageYjsSyncStep2","Y","decoding","messageYjsUpdate","WsReadyStates","ResetConnection","Doc","Mutex","Awareness","crypto","ConnectionTimeout","Unauthorized","WsReadyStates","SocketIncomingMessage","decoding","Forbidden","ResetConnection","URLSearchParams","meta.version","ResetConnection","Doc","SkipFurtherHooksError","crypto","meta.version"],"sources":["../src/IncomingMessage.ts","../src/types.ts","../src/OutgoingMessage.ts","../src/MessageReceiver.ts","../src/Connection.ts","../src/Document.ts","../package.json","../src/util/getParameters.ts","../src/ClientConnection.ts","../src/DirectConnection.ts","../src/util/debounce.ts","../src/Hocuspocus.ts","../src/Server.ts"],"sourcesContent":["import type { Decoder } from \"lib0/decoding\";\nimport {\n\tcreateDecoder,\n\treadVarString,\n\treadVarUint,\n\treadVarUint8Array,\n} from \"lib0/decoding\";\nimport type { Encoder } from \"lib0/encoding\";\nimport {\n\tcreateEncoder,\n\tlength,\n\ttoUint8Array,\n\twriteVarString,\n\twriteVarUint,\n} from \"lib0/encoding\";\nimport type { MessageType } from \"./types.ts\";\n\nexport class IncomingMessage {\n\t/**\n\t * Access to the received message.\n\t */\n\tdecoder: Decoder;\n\n\t/**\n\t * Private encoder; can be undefined.\n\t *\n\t * Lazy creation of the encoder speeds up IncomingMessages that need only a decoder.\n\t */\n\tprivate encoderInternal?: Encoder;\n\n\tconstructor(input: any) {\n\t\tif (!(input instanceof Uint8Array)) {\n\t\t\tinput = new Uint8Array(input);\n\t\t}\n\n\t\tthis.decoder = createDecoder(input);\n\t}\n\n\tget encoder() {\n\t\tif (!this.encoderInternal) {\n\t\t\tthis.encoderInternal = createEncoder();\n\t\t}\n\t\treturn this.encoderInternal;\n\t}\n\n\treadVarUint8Array() {\n\t\treturn readVarUint8Array(this.decoder);\n\t}\n\n\tpeekVarUint8Array() {\n\t\tconst { pos } = this.decoder;\n\t\tconst result = readVarUint8Array(this.decoder);\n\t\tthis.decoder.pos = pos;\n\t\treturn result;\n\t}\n\n\treadVarUint() {\n\t\treturn readVarUint(this.decoder);\n\t}\n\n\treadVarString() {\n\t\treturn readVarString(this.decoder);\n\t}\n\n\ttoUint8Array() {\n\t\treturn toUint8Array(this.encoder);\n\t}\n\n\twriteVarUint(type: MessageType) {\n\t\twriteVarUint(this.encoder, type);\n\t}\n\n\twriteVarString(string: string) {\n\t\twriteVarString(this.encoder, string);\n\t}\n\n\tget length(): number {\n\t\treturn length(this.encoder);\n\t}\n}\n","import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type { Awareness } from \"y-protocols/awareness\";\nimport type Connection from \"./Connection.ts\";\nimport type Document from \"./Document.ts\";\nimport type { Hocuspocus } from \"./Hocuspocus.ts\";\n\nexport interface ConnectionTransactionOrigin {\n\tsource: \"connection\";\n\tconnection: Connection;\n}\n\nexport interface RedisTransactionOrigin {\n\tsource: \"redis\";\n}\n\nexport interface LocalTransactionOrigin {\n\tsource: \"local\";\n\tskipStoreHooks?: boolean;\n\tcontext?: any;\n}\n\nexport type TransactionOrigin =\n\t| ConnectionTransactionOrigin\n\t| RedisTransactionOrigin\n\t| LocalTransactionOrigin;\n\nexport function isTransactionOrigin(\n\torigin: unknown,\n): origin is TransactionOrigin {\n\treturn (\n\t\ttypeof origin === \"object\" &&\n\t\torigin !== null &&\n\t\t\"source\" in origin &&\n\t\t((origin as any).source === \"connection\" ||\n\t\t\t(origin as any).source === \"redis\" ||\n\t\t\t(origin as any).source === \"local\")\n\t);\n}\n\nexport function shouldSkipStoreHooks(origin: unknown): boolean {\n\tif (!isTransactionOrigin(origin)) return false;\n\tswitch (origin.source) {\n\t\tcase \"connection\":\n\t\t\treturn false;\n\t\tcase \"redis\":\n\t\t\treturn true;\n\t\tcase \"local\":\n\t\t\treturn origin.skipStoreHooks ?? false;\n\t}\n}\n\n/**\n * Minimal interface for any WebSocket-like object for WebSocket, Bun's ServerWebSocket, ws, Deno, etc.\n */\nexport interface WebSocketLike {\n\tsend(data: string | ArrayBufferLike | Blob | ArrayBufferView): void;\n\tclose(code?: number, reason?: string): void;\n\treadyState: number;\n}\n\nexport enum MessageType {\n\tUnknown = -1,\n\tSync = 0,\n\tAwareness = 1,\n\tAuth = 2,\n\tQueryAwareness = 3,\n\tSyncReply = 4, // same as Sync, but won't trigger another 'SyncStep1'\n\tStateless = 5,\n\tBroadcastStateless = 6,\n\tCLOSE = 7,\n\tSyncStatus = 8,\n\tPing = 9,\n\tPong = 10,\n}\n\nexport interface AwarenessUpdate {\n\tadded: Array<any>;\n\tupdated: Array<any>;\n\tremoved: Array<any>;\n}\n\nexport interface ConnectionConfiguration {\n\treadOnly: boolean;\n\tisAuthenticated: boolean;\n}\n\nexport interface Extension<Context = any> {\n\tpriority?: number;\n\textensionName?: string;\n\tonConfigure?(data: onConfigurePayload): Promise<any>;\n\tonListen?(data: onListenPayload): Promise<any>;\n\tonUpgrade?(data: onUpgradePayload): Promise<any>;\n\tonConnect?(data: onConnectPayload<Context>): Promise<any>;\n\tconnected?(data: connectedPayload<Context>): Promise<any>;\n\tonAuthenticate?(data: onAuthenticatePayload<Context>): Promise<any>;\n\tonTokenSync?(data: onTokenSyncPayload<Context>): Promise<any>;\n\tonCreateDocument?(data: onCreateDocumentPayload<Context>): Promise<any>;\n\tonLoadDocument?(data: onLoadDocumentPayload<Context>): Promise<any>;\n\tafterLoadDocument?(data: afterLoadDocumentPayload<Context>): Promise<any>;\n\tbeforeHandleMessage?(data: beforeHandleMessagePayload<Context>): Promise<any>;\n\tbeforeSync?(data: beforeSyncPayload<Context>): Promise<any>;\n\tbeforeBroadcastStateless?(\n\t\tdata: beforeBroadcastStatelessPayload,\n\t): Promise<any>;\n\tonStateless?(payload: onStatelessPayload): Promise<any>;\n\tonChange?(data: onChangePayload<Context>): Promise<any>;\n\tonStoreDocument?(data: onStoreDocumentPayload<Context>): Promise<any>;\n\tafterStoreDocument?(data: afterStoreDocumentPayload<Context>): Promise<any>;\n\tonAwarenessUpdate?(data: onAwarenessUpdatePayload<Context>): Promise<any>;\n\tonRequest?(data: onRequestPayload): Promise<any>;\n\tonDisconnect?(data: onDisconnectPayload<Context>): Promise<any>;\n\tbeforeUnloadDocument?(data: beforeUnloadDocumentPayload): Promise<any>;\n\tafterUnloadDocument?(data: afterUnloadDocumentPayload): Promise<any>;\n\tonDestroy?(data: onDestroyPayload): Promise<any>;\n}\n\nexport type HookName =\n\t| \"onConfigure\"\n\t| \"onListen\"\n\t| \"onUpgrade\"\n\t| \"onConnect\"\n\t| \"connected\"\n\t| \"onAuthenticate\"\n\t| \"onTokenSync\"\n\t| \"onCreateDocument\"\n\t| \"onLoadDocument\"\n\t| \"afterLoadDocument\"\n\t| \"beforeHandleMessage\"\n\t| \"beforeBroadcastStateless\"\n\t| \"beforeSync\"\n\t| \"onStateless\"\n\t| \"onChange\"\n\t| \"onStoreDocument\"\n\t| \"afterStoreDocument\"\n\t| \"onAwarenessUpdate\"\n\t| \"onRequest\"\n\t| \"onDisconnect\"\n\t| \"beforeUnloadDocument\"\n\t| \"afterUnloadDocument\"\n\t| \"onDestroy\";\n\nexport type HookPayloadByName<Context = any> = {\n\tonConfigure: onConfigurePayload;\n\tonListen: onListenPayload;\n\tonUpgrade: onUpgradePayload;\n\tonConnect: onConnectPayload<Context>;\n\tconnected: connectedPayload<Context>;\n\tonAuthenticate: onAuthenticatePayload<Context>;\n\tonTokenSync: onTokenSyncPayload<Context>;\n\tonCreateDocument: onCreateDocumentPayload<Context>;\n\tonLoadDocument: onLoadDocumentPayload<Context>;\n\tafterLoadDocument: afterLoadDocumentPayload<Context>;\n\tbeforeHandleMessage: beforeHandleMessagePayload<Context>;\n\tbeforeBroadcastStateless: beforeBroadcastStatelessPayload;\n\tbeforeSync: beforeSyncPayload<Context>;\n\tonStateless: onStatelessPayload;\n\tonChange: onChangePayload<Context>;\n\tonStoreDocument: onStoreDocumentPayload<Context>;\n\tafterStoreDocument: afterStoreDocumentPayload<Context>;\n\tonAwarenessUpdate: onAwarenessUpdatePayload<Context>;\n\tonRequest: onRequestPayload;\n\tonDisconnect: onDisconnectPayload<Context>;\n\tafterUnloadDocument: afterUnloadDocumentPayload;\n\tbeforeUnloadDocument: beforeUnloadDocumentPayload;\n\tonDestroy: onDestroyPayload;\n};\n\nexport interface Configuration<Context = any> extends Extension<Context> {\n\t/**\n\t * A name for the instance, used for logging.\n\t */\n\tname: string | null;\n\t/**\n\t * A list of hocuspocus extensions.\n\t */\n\textensions: Array<Extension>;\n\t/**\n\t * Defines in which interval the server sends a ping, and closes the connection when no pong is sent back.\n\t */\n\ttimeout: number;\n\t/**\n\t * Debounces the call of the `onStoreDocument` hook for the given amount of time in ms.\n\t * Otherwise every single update would be persisted.\n\t */\n\tdebounce: number;\n\t/**\n\t * Makes sure to call `onStoreDocument` at least in the given amount of time (ms).\n\t */\n\tmaxDebounce: number;\n\t/**\n\t * By default, the servers show a start screen. If passed false, the server will start quietly.\n\t */\n\tquiet: boolean;\n\t/**\n\t * If set to false, respects the debounce time of `onStoreDocument` before unloading a document.\n\t * Otherwise, the document will be unloaded immediately.\n\t *\n\t * This prevents a client from DOSing the server by repeatedly connecting and disconnecting when\n\t * your onStoreDocument is rate-limited.\n\t */\n\tunloadImmediately: boolean;\n\n\t/**\n\t * options to pass to the ydoc document\n\t */\n\tyDocOptions: {\n\t\tgc: boolean; // enable or disable garbage collection (see https://github.com/yjs/yjs/blob/main/INTERNALS.md#deletions)\n\t\tgcFilter: () => boolean; // will be called before garbage collecting ; return false to keep it\n\t};\n}\n\nexport interface onStatelessPayload {\n\tconnection: Connection;\n\tdocumentName: string;\n\tdocument: Document;\n\tpayload: string;\n}\n\nexport interface onAuthenticatePayload<Context = any> {\n\tcontext: Context;\n\tdocumentName: string;\n\tinstance: Hocuspocus;\n\trequestHeaders: Headers;\n\trequestParameters: URLSearchParams;\n\trequest: Request;\n\tsocketId: string;\n\ttoken: string;\n\tconnectionConfig: ConnectionConfiguration;\n\tproviderVersion: string | null;\n}\n\nexport interface onTokenSyncPayload<Context = any> {\n\tcontext: Context;\n\tdocument: Document;\n\tdocumentName: string;\n\tinstance: Hocuspocus;\n\trequestHeaders: Headers;\n\trequestParameters: URLSearchParams;\n\tsocketId: string;\n\ttoken: string;\n\tconnectionConfig: ConnectionConfiguration;\n\tconnection: Connection<Context>;\n}\n\nexport interface onCreateDocumentPayload<Context = any> {\n\tcontext: Context;\n\tdocumentName: string;\n\tinstance: Hocuspocus;\n\trequestHeaders: Headers;\n\trequestParameters: URLSearchParams;\n\tsocketId: string;\n\tconnectionConfig: ConnectionConfiguration;\n}\n\nexport interface onConnectPayload<Context = any> {\n\tcontext: Context;\n\tdocumentName: string;\n\tinstance: Hocuspocus;\n\trequest: Request;\n\trequestHeaders: Headers;\n\trequestParameters: URLSearchParams;\n\tsocketId: string;\n\tconnectionConfig: ConnectionConfiguration;\n\tproviderVersion: string | null;\n}\n\nexport interface connectedPayload<Context = any> {\n\tcontext: Context;\n\tdocumentName: string;\n\tinstance: Hocuspocus;\n\trequest: Request;\n\trequestHeaders: Headers;\n\trequestParameters: URLSearchParams;\n\tsocketId: string;\n\tconnectionConfig: ConnectionConfiguration;\n\tconnection: Connection<Context>;\n\tproviderVersion: string | null;\n}\n\nexport interface onLoadDocumentPayload<Context = any> {\n\tcontext: Context;\n\tdocument: Document;\n\tdocumentName: string;\n\tinstance: Hocuspocus;\n\trequestHeaders: Headers;\n\trequestParameters: URLSearchParams;\n\tsocketId: string;\n\tconnectionConfig: ConnectionConfiguration;\n}\n\nexport interface afterLoadDocumentPayload<Context = any> {\n\tcontext: Context;\n\tdocument: Document;\n\tdocumentName: string;\n\tinstance: Hocuspocus;\n\trequestHeaders: Headers;\n\trequestParameters: URLSearchParams;\n\tsocketId: string;\n\tconnectionConfig: ConnectionConfiguration;\n}\n\nexport interface onChangePayload<Context = any> {\n\tclientsCount: number;\n\tcontext: Context;\n\tdocument: Document;\n\tdocumentName: string;\n\tinstance: Hocuspocus;\n\trequestHeaders: Headers;\n\trequestParameters: URLSearchParams;\n\tupdate: Uint8Array;\n\tsocketId: string;\n\ttransactionOrigin: unknown;\n\tconnection?: Connection<Context>;\n}\n\nexport interface beforeHandleMessagePayload<Context = any> {\n\tclientsCount: number;\n\tcontext: Context;\n\tdocument: Document;\n\tdocumentName: string;\n\tinstance: Hocuspocus;\n\trequestHeaders: Headers;\n\trequestParameters: URLSearchParams;\n\tupdate: Uint8Array;\n\tsocketId: string;\n\tconnection: Connection<Context>;\n}\n\nexport interface beforeSyncPayload<Context = any> {\n\tclientsCount: number;\n\tcontext: Context;\n\tdocument: Document;\n\tdocumentName: string;\n\tconnection: Connection<Context>;\n\t/**\n\t * The y-protocols/sync message type\n\t * @example\n\t * 0: SyncStep1\n\t * 1: SyncStep2\n\t * 2: YjsUpdate\n\t *\n\t * @see https://github.com/yjs/y-protocols/blob/master/sync.js#L13-L40\n\t */\n\ttype: number;\n\t/**\n\t * The payload of the y-sync message.\n\t */\n\tpayload: Uint8Array;\n}\n\nexport interface beforeBroadcastStatelessPayload {\n\tdocument: Document;\n\tdocumentName: string;\n\tpayload: string;\n}\n\nexport interface onStoreDocumentPayload<Context = any> {\n\tclientsCount: number;\n\tdocument: Document;\n\tlastContext: Context;\n\tlastTransactionOrigin: unknown;\n\tdocumentName: string;\n\tinstance: Hocuspocus;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-empty-object-type\nexport interface afterStoreDocumentPayload<Context = any>\n\textends onStoreDocumentPayload<Context> {}\n\nexport interface onAwarenessUpdatePayload<Context = any> {\n\tdocument: Document;\n\tdocumentName: string;\n\tinstance: Hocuspocus;\n\ttransactionOrigin: unknown;\n\tconnection?: Connection<Context>;\n\tadded: number[];\n\tupdated: number[];\n\tremoved: number[];\n\tawareness: Awareness;\n\tstates: StatesArray;\n}\n\nexport type StatesArray = { clientId: number; [key: string | number]: any }[];\n\nexport interface fetchPayload<Context = any> {\n\tcontext: Context;\n\tdocument: Document;\n\tdocumentName: string;\n\tinstance: Hocuspocus;\n\trequestHeaders: Headers;\n\trequestParameters: URLSearchParams;\n\tsocketId: string;\n\tconnectionConfig: ConnectionConfiguration;\n}\n\nexport interface storePayload<Context = any>\n\textends onStoreDocumentPayload<Context> {\n\tstate: Buffer;\n}\n\nexport interface onDisconnectPayload<Context = any> {\n\tclientsCount: number;\n\tcontext: Context;\n\tdocument: Document;\n\tdocumentName: string;\n\tinstance: Hocuspocus;\n\trequestHeaders: Headers;\n\trequestParameters: URLSearchParams;\n\tsocketId: string;\n}\n\nexport interface onRequestPayload {\n\trequest: IncomingMessage;\n\tresponse: ServerResponse;\n\tinstance: Hocuspocus;\n}\n\nexport interface onUpgradePayload {\n\trequest: IncomingMessage;\n\tsocket: any;\n\thead: any;\n\tinstance: Hocuspocus;\n}\n\nexport interface onListenPayload {\n\tinstance: Hocuspocus;\n\tconfiguration: Configuration;\n\tport: number;\n}\n\nexport interface onDestroyPayload {\n\tinstance: Hocuspocus;\n}\n\nexport interface onConfigurePayload {\n\tinstance: Hocuspocus;\n\tconfiguration: Configuration;\n\tversion: string;\n}\n\nexport interface afterUnloadDocumentPayload {\n\tinstance: Hocuspocus;\n\tdocumentName: string;\n}\n\nexport interface beforeUnloadDocumentPayload {\n\tinstance: Hocuspocus;\n\tdocumentName: string;\n\tdocument: Document;\n}\n\nexport interface DirectConnection {\n\ttransact(transaction: (document: Document) => void): Promise<void>;\n\tdisconnect(): void;\n}\n","import type { Encoder } from \"lib0/encoding\";\nimport {\n\tcreateEncoder,\n\ttoUint8Array,\n\twriteVarString,\n\twriteVarUint,\n\twriteVarUint8Array,\n} from \"lib0/encoding\";\nimport type { Awareness } from \"y-protocols/awareness\";\nimport { encodeAwarenessUpdate } from \"y-protocols/awareness\";\nimport { writeSyncStep1, writeUpdate } from \"y-protocols/sync\";\n\nimport { writeAuthenticated, writePermissionDenied, writeTokenSyncRequest } from \"@hocuspocus/common\";\nimport type Document from \"./Document.ts\";\nimport { MessageType } from \"./types.ts\";\n\nexport class OutgoingMessage {\n\tencoder: Encoder;\n\n\ttype?: number;\n\n\tcategory?: string;\n\n\tconstructor(documentName: string) {\n\t\tthis.encoder = createEncoder();\n\n\t\twriteVarString(this.encoder, documentName);\n\t}\n\n\tcreateSyncMessage(): OutgoingMessage {\n\t\tthis.type = MessageType.Sync;\n\n\t\twriteVarUint(this.encoder, MessageType.Sync);\n\n\t\treturn this;\n\t}\n\n\tcreateSyncReplyMessage(): OutgoingMessage {\n\t\tthis.type = MessageType.SyncReply;\n\n\t\twriteVarUint(this.encoder, MessageType.SyncReply);\n\n\t\treturn this;\n\t}\n\n\tcreateAwarenessUpdateMessage(\n\t\tawareness: Awareness,\n\t\tchangedClients?: Array<any>,\n\t): OutgoingMessage {\n\t\tthis.type = MessageType.Awareness;\n\t\tthis.category = \"Update\";\n\n\t\tconst message = encodeAwarenessUpdate(\n\t\t\tawareness,\n\t\t\tchangedClients || Array.from(awareness.getStates().keys()),\n\t\t);\n\n\t\twriteVarUint(this.encoder, MessageType.Awareness);\n\t\twriteVarUint8Array(this.encoder, message);\n\n\t\treturn this;\n\t}\n\n\twriteQueryAwareness(): OutgoingMessage {\n\t\tthis.type = MessageType.QueryAwareness;\n\t\tthis.category = \"Update\";\n\n\t\twriteVarUint(this.encoder, MessageType.QueryAwareness);\n\n\t\treturn this;\n\t}\n\n\twriteTokenSyncRequest(): OutgoingMessage {\n\t\tthis.type = MessageType.Auth;\n\t\tthis.category = \"TokenSync\";\n\n\t\twriteVarUint(this.encoder, MessageType.Auth);\n\t\twriteTokenSyncRequest(this.encoder);\n\n\t\treturn this;\n\t}\n\n\twriteAuthenticated(readonly: boolean): OutgoingMessage {\n\t\tthis.type = MessageType.Auth;\n\t\tthis.category = \"Authenticated\";\n\n\t\twriteVarUint(this.encoder, MessageType.Auth);\n\t\twriteAuthenticated(this.encoder, readonly ? \"readonly\" : \"read-write\");\n\n\t\treturn this;\n\t}\n\n\twritePermissionDenied(reason: string): OutgoingMessage {\n\t\tthis.type = MessageType.Auth;\n\t\tthis.category = \"PermissionDenied\";\n\n\t\twriteVarUint(this.encoder, MessageType.Auth);\n\t\twritePermissionDenied(this.encoder, reason);\n\n\t\treturn this;\n\t}\n\n\twriteFirstSyncStepFor(document: Document): OutgoingMessage {\n\t\tthis.category = \"SyncStep1\";\n\n\t\twriteSyncStep1(this.encoder, document);\n\n\t\treturn this;\n\t}\n\n\twriteUpdate(update: Uint8Array): OutgoingMessage {\n\t\tthis.category = \"Update\";\n\n\t\twriteUpdate(this.encoder, update);\n\n\t\treturn this;\n\t}\n\n\twriteStateless(payload: string): OutgoingMessage {\n\t\tthis.category = \"Stateless\";\n\n\t\twriteVarUint(this.encoder, MessageType.Stateless);\n\t\twriteVarString(this.encoder, payload);\n\n\t\treturn this;\n\t}\n\n\twriteBroadcastStateless(payload: string): OutgoingMessage {\n\t\tthis.category = \"Stateless\";\n\n\t\twriteVarUint(this.encoder, MessageType.BroadcastStateless);\n\t\twriteVarString(this.encoder, payload);\n\n\t\treturn this;\n\t}\n\n\t// TODO: should this be write* or create* as method name?\n\twriteSyncStatus(updateSaved: boolean): OutgoingMessage {\n\t\tthis.category = \"SyncStatus\";\n\n\t\twriteVarUint(this.encoder, MessageType.SyncStatus);\n\t\twriteVarUint(this.encoder, updateSaved ? 1 : 0);\n\n\t\treturn this;\n\t}\n\n\twriteCloseMessage(reason: string): OutgoingMessage {\n\t\tthis.type = MessageType.CLOSE;\n\n\t\twriteVarUint(this.encoder, MessageType.CLOSE);\n\t\twriteVarString(this.encoder, reason);\n\n\t\treturn this;\n\t}\n\n\ttoUint8Array(): Uint8Array {\n\t\treturn toUint8Array(this.encoder);\n\t}\n}\n","import { AuthMessageType } from \"@hocuspocus/common\";\nimport * as decoding from \"lib0/decoding\";\nimport { readVarString } from \"lib0/decoding\";\nimport { applyAwarenessUpdate } from \"y-protocols/awareness\";\nimport {\n\tmessageYjsSyncStep1,\n\tmessageYjsSyncStep2,\n\tmessageYjsUpdate,\n\treadSyncStep1,\n\treadSyncStep2,\n\treadUpdate,\n} from \"y-protocols/sync\";\nimport * as Y from \"yjs\";\nimport type Connection from \"./Connection.ts\";\nimport type Document from \"./Document.ts\";\nimport type { IncomingMessage } from \"./IncomingMessage.ts\";\nimport { OutgoingMessage } from \"./OutgoingMessage.ts\";\nimport { MessageType, type TransactionOrigin } from \"./types.ts\";\n\nexport class MessageReceiver {\n\tmessage: IncomingMessage;\n\n\tdefaultTransactionOrigin?: TransactionOrigin;\n\n\tconstructor(\n\t\tmessage: IncomingMessage,\n\t\tdefaultTransactionOrigin?: TransactionOrigin,\n\t) {\n\t\tthis.message = message;\n\t\tthis.defaultTransactionOrigin = defaultTransactionOrigin;\n\t}\n\n\tpublic async apply(\n\t\tdocument: Document,\n\t\tconnection?: Connection,\n\t\treply?: (message: Uint8Array) => void,\n\t) {\n\t\tconst { message } = this;\n\t\tconst type = message.readVarUint();\n\t\tconst emptyMessageLength = message.length;\n\n\t\tswitch (type) {\n\t\t\tcase MessageType.Sync:\n\t\t\tcase MessageType.SyncReply: {\n\t\t\t\tmessage.writeVarUint(MessageType.Sync);\n\t\t\t\tawait this.readSyncMessage(\n\t\t\t\t\tmessage,\n\t\t\t\t\tdocument,\n\t\t\t\t\tconnection,\n\t\t\t\t\treply,\n\t\t\t\t\ttype !== MessageType.SyncReply,\n\t\t\t\t);\n\n\t\t\t\tif (message.length > emptyMessageLength + 1) {\n\t\t\t\t\tif (reply) {\n\t\t\t\t\t\treply(message.toUint8Array());\n\t\t\t\t\t} else if (connection) {\n\t\t\t\t\t\tconnection.send(message.toUint8Array());\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase MessageType.Awareness: {\n\t\t\t\tapplyAwarenessUpdate(\n\t\t\t\t\tdocument.awareness,\n\t\t\t\t\tmessage.readVarUint8Array(),\n\t\t\t\t\tconnection ?? null,\n\t\t\t\t);\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase MessageType.QueryAwareness: {\n\t\t\t\tthis.applyQueryAwarenessMessage(document, connection, reply);\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase MessageType.Stateless: {\n\t\t\t\tconnection?.callbacks.statelessCallback({\n\t\t\t\t\tconnection,\n\t\t\t\t\tdocumentName: document.name,\n\t\t\t\t\tdocument,\n\t\t\t\t\tpayload: readVarString(message.decoder),\n\t\t\t\t});\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase MessageType.BroadcastStateless: {\n\t\t\t\tconst msg = message.readVarString();\n\t\t\t\tdocument.getConnections().forEach((connection) => {\n\t\t\t\t\tconnection.sendStateless(msg);\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MessageType.CLOSE: {\n\t\t\t\tconnection?.close({\n\t\t\t\t\tcode: 1000,\n\t\t\t\t\treason: \"provider_initiated\",\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MessageType.Auth: {\n\t\t\t\tconst authType = message.readVarUint();\n\t\t\t\tif (authType === AuthMessageType.Token) {\n\t\t\t\t\tconnection?.callbacks.onTokenSyncCallback({\n\t\t\t\t\t\ttoken: message.readVarString(),\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"Received an authentication message on a connection that is already fully authenticated. Probably your provider has been destroyed + recreated really fast.\",\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\tconsole.error(\n\t\t\t\t\t`Unable to handle message of type ${type}: no handler defined! Are your provider/server versions aligned?`,\n\t\t\t\t);\n\t\t\t// Do nothing\n\t\t}\n\t}\n\n\tasync readSyncMessage(\n\t\tmessage: IncomingMessage,\n\t\tdocument: Document,\n\t\tconnection?: Connection,\n\t\treply?: (message: Uint8Array) => void,\n\t\trequestFirstSync = true,\n\t) {\n\t\tconst type = message.readVarUint();\n\t\tconst messageAddress = connection?.messageAddress ?? document.name;\n\n\t\tif (connection) {\n\t\t\tawait connection.callbacks.beforeSync(connection, {\n\t\t\t\ttype,\n\t\t\t\tpayload: message.peekVarUint8Array(),\n\t\t\t});\n\t\t}\n\n\t\tswitch (type) {\n\t\t\tcase messageYjsSyncStep1: {\n\t\t\t\treadSyncStep1(message.decoder, message.encoder, document);\n\n\t\t\t\t// When the server receives SyncStep1, it should reply with SyncStep2 immediately followed by SyncStep1.\n\t\t\t\tif (reply && requestFirstSync) {\n\t\t\t\t\tconst syncMessage = new OutgoingMessage(messageAddress)\n\t\t\t\t\t\t.createSyncReplyMessage()\n\t\t\t\t\t\t.writeFirstSyncStepFor(document);\n\n\t\t\t\t\treply(syncMessage.toUint8Array());\n\t\t\t\t} else if (connection) {\n\t\t\t\t\tconst syncMessage = new OutgoingMessage(messageAddress)\n\t\t\t\t\t\t.createSyncMessage()\n\t\t\t\t\t\t.writeFirstSyncStepFor(document);\n\n\t\t\t\t\tconnection.send(syncMessage.toUint8Array());\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase messageYjsSyncStep2: {\n\t\t\t\tif (connection?.readOnly) {\n\t\t\t\t\t// We're in read-only mode, so we can't apply the update.\n\t\t\t\t\t// Let's use snapshotContainsUpdate to see if the update actually contains changes.\n\t\t\t\t\t// If not, we can still ack the update\n\t\t\t\t\tconst snapshot = Y.snapshot(document);\n\t\t\t\t\tconst update = decoding.readVarUint8Array(message.decoder);\n\t\t\t\t\tif (Y.snapshotContainsUpdate(snapshot, update)) {\n\t\t\t\t\t\t// no new changes in update\n\t\t\t\t\t\tconst ackMessage = new OutgoingMessage(\n\t\t\t\t\t\t\tmessageAddress,\n\t\t\t\t\t\t).writeSyncStatus(true);\n\n\t\t\t\t\t\tconnection.send(ackMessage.toUint8Array());\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// new changes in update that we can't apply, because readOnly\n\t\t\t\t\t\tconst ackMessage = new OutgoingMessage(\n\t\t\t\t\t\t\tmessageAddress,\n\t\t\t\t\t\t).writeSyncStatus(false);\n\n\t\t\t\t\t\tconnection.send(ackMessage.toUint8Array());\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\treadSyncStep2(\n\t\t\t\t\tmessage.decoder,\n\t\t\t\t\tdocument,\n\t\t\t\t\tconnection\n\t\t\t\t\t\t? { source: \"connection\" as const, connection }\n\t\t\t\t\t\t: (this.defaultTransactionOrigin ?? { source: \"local\" as const }),\n\t\t\t\t);\n\n\t\t\t\tif (connection) {\n\t\t\t\t\tconnection.send(\n\t\t\t\t\t\tnew OutgoingMessage(messageAddress)\n\t\t\t\t\t\t\t.writeSyncStatus(true)\n\t\t\t\t\t\t\t.toUint8Array(),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase messageYjsUpdate: {\n\t\t\t\tif (connection?.readOnly) {\n\t\t\t\t\tconnection.send(\n\t\t\t\t\t\tnew OutgoingMessage(messageAddress)\n\t\t\t\t\t\t\t.writeSyncStatus(false)\n\t\t\t\t\t\t\t.toUint8Array(),\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\treadUpdate(\n\t\t\t\t\tmessage.decoder,\n\t\t\t\t\tdocument,\n\t\t\t\t\tconnection\n\t\t\t\t\t\t? { source: \"connection\" as const, connection }\n\t\t\t\t\t\t: (this.defaultTransactionOrigin ?? { source: \"local\" as const }),\n\t\t\t\t);\n\t\t\t\tif (connection) {\n\t\t\t\t\tconnection.send(\n\t\t\t\t\t\tnew OutgoingMessage(messageAddress)\n\t\t\t\t\t\t\t.writeSyncStatus(true)\n\t\t\t\t\t\t\t.toUint8Array(),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Received a message with an unknown type: ${type}`);\n\t\t}\n\n\t\treturn type;\n\t}\n\n\tapplyQueryAwarenessMessage(\n\t\tdocument: Document,\n\t\tconnection?: Connection,\n\t\treply?: (message: Uint8Array) => void,\n\t) {\n\t\tconst message = new OutgoingMessage(\n\t\t\tconnection?.messageAddress ?? document.name,\n\t\t).createAwarenessUpdateMessage(document.awareness);\n\n\t\tif (reply) {\n\t\t\treply(message.toUint8Array());\n\t\t}\n\t}\n}\n","import {\n\ttype CloseEvent,\n\tResetConnection,\n\tWsReadyStates,\n} from \"@hocuspocus/common\";\nimport type Document from \"./Document.ts\";\nimport { IncomingMessage } from \"./IncomingMessage.ts\";\nimport { MessageReceiver } from \"./MessageReceiver.ts\";\nimport { OutgoingMessage } from \"./OutgoingMessage.ts\";\nimport type {\n\tWebSocketLike,\n\tbeforeSyncPayload,\n\tonStatelessPayload,\n} from \"./types.ts\";\n\nexport class Connection<Context = any> {\n\twebSocket: WebSocketLike;\n\n\tcontext: Context;\n\n\tdocument: Document;\n\n\trequest: Request;\n\n\tcallbacks = {\n\t\tonClose: [(document: Document, event?: CloseEvent) => {}],\n\t\tbeforeHandleMessage: (connection: Connection, update: Uint8Array) =>\n\t\t\tPromise.resolve(),\n\t\tbeforeSync: (\n\t\t\tconnection: Connection,\n\t\t\tpayload: Pick<beforeSyncPayload, \"type\" | \"payload\">,\n\t\t) => Promise.resolve(),\n\t\tstatelessCallback: (payload: onStatelessPayload) => Promise.resolve(),\n\t\tonTokenSyncCallback: (payload: { token: string }) => Promise.resolve(),\n\t};\n\n\tsocketId: string;\n\n\treadOnly: boolean;\n\n\tsessionId: string | null;\n\n\tproviderVersion: string | null;\n\n\t/**\n\t * The address string prefixed to outgoing messages.\n\t * Session-aware clients get `documentName\\0sessionId`; legacy clients get plain `documentName`.\n\t */\n\tget messageAddress(): string {\n\t\treturn this.sessionId\n\t\t\t? `${this.document.name}\\0${this.sessionId}`\n\t\t\t: this.document.name;\n\t}\n\n\tprivate messageQueue: Uint8Array[] = [];\n\n\tprivate processingPromise: Promise<void> = Promise.resolve();\n\n\t/**\n\t * Constructor.\n\t */\n\tconstructor(\n\t\tconnection: WebSocketLike,\n\t\trequest: Request,\n\t\tdocument: Document,\n\t\tsocketId: string,\n\t\tcontext: Context,\n\t\treadOnly = false,\n\t\tsessionId?: string | null,\n\t\tproviderVersion?: string | null,\n\t) {\n\t\tthis.webSocket = connection;\n\t\tthis.context = context;\n\t\tthis.document = document;\n\t\tthis.request = request;\n\t\tthis.socketId = socketId;\n\t\tthis.readOnly = readOnly;\n\t\tthis.sessionId = sessionId ?? null;\n\t\tthis.providerVersion = providerVersion ?? null;\n\n\t\tthis.document.addConnection(this);\n\n\t\tthis.sendCurrentAwareness();\n\t}\n\n\t/**\n\t * Set a callback that will be triggered when the connection is closed\n\t */\n\tonClose(\n\t\tcallback: (document: Document, event?: CloseEvent) => void,\n\t): Connection {\n\t\tthis.callbacks.onClose.push(callback);\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set a callback that will be triggered when an stateless message is received\n\t */\n\tonStatelessCallback(\n\t\tcallback: (payload: onStatelessPayload) => Promise<void>,\n\t): Connection {\n\t\tthis.callbacks.statelessCallback = callback;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set a callback that will be triggered before an message is handled\n\t */\n\tbeforeHandleMessage(\n\t\tcallback: (connection: Connection, update: Uint8Array) => Promise<any>,\n\t): Connection {\n\t\tthis.callbacks.beforeHandleMessage = callback;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set a callback that will be triggered before a sync message is handled\n\t */\n\tbeforeSync(\n\t\tcallback: (\n\t\t\tconnection: Connection,\n\t\t\tpayload: Pick<beforeSyncPayload, \"type\" | \"payload\">,\n\t\t) => Promise<any>,\n\t): Connection {\n\t\tthis.callbacks.beforeSync = callback;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set a callback that will be triggered when on token sync message is received\n\t */\n\tonTokenSyncCallback(\n\t\tcallback: (payload: { token: string }) => Promise<void>,\n\t): Connection {\n\t\tthis.callbacks.onTokenSyncCallback = callback;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Returns a promise that resolves when all queued messages have been processed.\n\t */\n\twaitForPendingMessages(): Promise<void> {\n\t\treturn this.processingPromise;\n\t}\n\n\t/**\n\t * Send the given message\n\t */\n\tsend(message: Uint8Array): void {\n\t\tif (\n\t\t\tthis.webSocket.readyState === WsReadyStates.Closing ||\n\t\t\tthis.webSocket.readyState === WsReadyStates.Closed\n\t\t) {\n\t\t\tthis.close();\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.webSocket.send(message);\n\t\t} catch (exception) {\n\t\t\tthis.close();\n\t\t}\n\t}\n\n\t/**\n\t * Send a stateless message with payload\n\t */\n\tpublic sendStateless(payload: string): void {\n\t\tconst message = new OutgoingMessage(this.messageAddress).writeStateless(\n\t\t\tpayload,\n\t\t);\n\n\t\tthis.send(message.toUint8Array());\n\t}\n\n\t/**\n\t * Request current token from the client\n\t */\n\tpublic requestToken(): void {\n\t\tconst message = new OutgoingMessage(\n\t\t\tthis.messageAddress,\n\t\t).writeTokenSyncRequest();\n\n\t\tthis.send(message.toUint8Array());\n\t}\n\n\t/**\n\t * Graceful wrapper around the WebSocket close method.\n\t */\n\tclose(event?: CloseEvent): void {\n\t\tif (this.document.hasConnection(this)) {\n\t\t\tthis.document.removeConnection(this);\n\t\t\tthis.callbacks.onClose.forEach(\n\t\t\t\t(callback: (arg0: Document, arg1?: CloseEvent) => any) =>\n\t\t\t\t\tcallback(this.document, event),\n\t\t\t);\n\n\t\t\tconst closeMessage = new OutgoingMessage(this.messageAddress);\n\t\t\tcloseMessage.writeCloseMessage(\n\t\t\t\tevent?.reason ?? \"Server closed the connection\",\n\t\t\t);\n\t\t\tthis.send(closeMessage.toUint8Array());\n\t\t}\n\t}\n\n\t/**\n\t * Send the current document awareness to the client, if any\n\t * @private\n\t */\n\tprivate sendCurrentAwareness(): void {\n\t\tif (!this.document.hasAwarenessStates()) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst awarenessMessage = new OutgoingMessage(\n\t\t\tthis.messageAddress,\n\t\t).createAwarenessUpdateMessage(this.document.awareness);\n\n\t\tthis.send(awarenessMessage.toUint8Array());\n\t}\n\n\t/**\n\t * Handle an incoming message\n\t * @public\n\t */\n\tpublic handleMessage(data: Uint8Array): void {\n\t\tthis.messageQueue.push(data);\n\n\t\tif (this.messageQueue.length === 1) {\n\t\t\tthis.processingPromise = this.processMessages();\n\t\t}\n\t}\n\n\tprivate async processMessages() {\n\t\twhile (this.messageQueue.length > 0) {\n\t\t\tconst rawUpdate = this.messageQueue.at(0) as Uint8Array;\n\n\t\t\tconst message = new IncomingMessage(rawUpdate);\n\t\t\tconst rawKey = message.readVarString();\n\n\t\t\t// Accept messages addressed with either the plain documentName or documentName\\0sessionId\n\t\t\tconst sepIdx = rawKey.indexOf('\\0');\n\t\t\tconst documentName = sepIdx === -1 ? rawKey : rawKey.substring(0, sepIdx);\n\t\t\tif (documentName !== this.document.name) {\n\t\t\t\tthis.messageQueue.shift();\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Write the correct address so replies reach the right provider\n\t\t\tmessage.writeVarString(this.messageAddress);\n\n\t\t\ttry {\n\t\t\t\tawait this.callbacks.beforeHandleMessage(this, rawUpdate);\n\t\t\t\tconst receiver = new MessageReceiver(message);\n\n\t\t\t\tawait receiver.apply(this.document, this);\n\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: <explanation>\n\t\t\t} catch (e: any) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`closing connection ${this.socketId} (while handling ${documentName}) because of exception`,\n\t\t\t\t\te,\n\t\t\t\t);\n\t\t\t\tthis.close({\n\t\t\t\t\tcode: \"code\" in e && typeof e.code === 'number' ? e.code : ResetConnection.code,\n\t\t\t\t\treason: \"reason\" in e ? e.reason : ResetConnection.reason,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tthis.messageQueue.shift();\n\t\t}\n\t}\n}\n\nexport default Connection;\n","import { Mutex } from \"async-mutex\";\nimport {\n\tAwareness,\n\tapplyAwarenessUpdate,\n\tremoveAwarenessStates,\n} from \"y-protocols/awareness\";\nimport { Doc, applyUpdate, encodeStateAsUpdate } from \"yjs\";\nimport type Connection from \"./Connection.ts\";\nimport { OutgoingMessage } from \"./OutgoingMessage.ts\";\nimport type { AwarenessUpdate } from \"./types.ts\";\n\nexport class Document extends Doc {\n\tawareness: Awareness;\n\n\tcallbacks = {\n\t\t// eslint-disable-next-line @typescript-eslint/no-empty-function\n\t\tonUpdate: (document: Document, origin: unknown, update: Uint8Array) => {},\n\t\tbeforeBroadcastStateless: (document: Document, stateless: string) => {},\n\t};\n\n\tconnections: Map<\n\t\tConnection,\n\t\t{\n\t\t\tclients: Set<any>;\n\t\t}\n\t> = new Map();\n\n\t// The number of direct (non-websocket) connections to this document\n\tdirectConnectionsCount = 0;\n\n\tname: string;\n\n\tisLoading: boolean;\n\n\tisDestroyed = false;\n\n\tsaveMutex = new Mutex();\n\n\tlastChangeTime = 0;\n\n\t/**\n\t * Constructor.\n\t */\n\tconstructor(name: string, yDocOptions?: object) {\n\t\tsuper(yDocOptions);\n\n\t\tthis.name = name;\n\n\t\tthis.awareness = new Awareness(this);\n\t\tthis.awareness.setLocalState(null);\n\n\t\tthis.awareness.on(\"update\", this.handleAwarenessUpdate.bind(this));\n\t\tthis.on(\"update\", this.handleUpdate.bind(this));\n\n\t\tthis.isLoading = true;\n\t}\n\n\t/**\n\t * Check if the Document (XMLFragment or Map) is empty\n\t */\n\tisEmpty(fieldName: string): boolean {\n\t\treturn !this.get(fieldName)._start && !this.get(fieldName)._map.size;\n\t}\n\n\t/**\n\t * Merge the given document(s) into this one\n\t */\n\tmerge(documents: Doc | Array<Doc>): Document {\n\t\t(Array.isArray(documents) ? documents : [documents]).forEach((document) => {\n\t\t\tapplyUpdate(this, encodeStateAsUpdate(document));\n\t\t});\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set a callback that will be triggered when the document is updated\n\t */\n\tonUpdate(\n\t\tcallback: (document: Document, origin: unknown, update: Uint8Array) => void,\n\t): Document {\n\t\tthis.callbacks.onUpdate = callback;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set a callback that will be triggered before a stateless message is broadcasted\n\t */\n\tbeforeBroadcastStateless(\n\t\tcallback: (document: Document, stateless: string) => void,\n\t): Document {\n\t\tthis.callbacks.beforeBroadcastStateless = callback;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Register a connection and a set of clients on this document keyed by the\n\t * underlying websocket connection\n\t */\n\taddConnection(connection: Connection): Document {\n\t\tthis.connections.set(connection, {\n\t\t\tclients: new Set(),\n\t\t});\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Is the given connection registered on this document\n\t */\n\thasConnection(connection: Connection): boolean {\n\t\treturn this.connections.has(connection);\n\t}\n\n\t/**\n\t * Remove the given connection from this document\n\t */\n\tremoveConnection(connection: Connection): Document {\n\t\tconst entry = this.connections.get(connection);\n\t\tif (entry) {\n\t\t\tremoveAwarenessStates(\n\t\t\t\tthis.awareness,\n\t\t\t\tArray.from(entry.clients),\n\t\t\t\tnull,\n\t\t\t);\n\t\t}\n\n\t\tthis.connections.delete(connection);\n\n\t\treturn this;\n\t}\n\n\taddDirectConnection(): Document {\n\t\tthis.directConnectionsCount += 1;\n\n\t\treturn this;\n\t}\n\n\tremoveDirectConnection(): Document {\n\t\tif (this.directConnectionsCount > 0) {\n\t\t\tthis.directConnectionsCount -= 1;\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Get the number of active connections for this document\n\t */\n\tgetConnectionsCount(): number {\n\t\treturn this.connections.size + this.directConnectionsCount;\n\t}\n\n\t/**\n\t * Get an array of registered connections\n\t */\n\tgetConnections(): Array<Connection> {\n\t\treturn Array.from(this.connections.keys());\n\t}\n\n\t/**\n\t * Get the client ids for the given connection instance\n\t */\n\tgetClients(connection: Connection): Set<any> {\n\t\tconst entry = this.connections.get(connection);\n\n\t\treturn entry?.clients === undefined ? new Set() : entry.clients;\n\t}\n\n\t/**\n\t * Has the document awareness states\n\t */\n\thasAwarenessStates(): boolean {\n\t\treturn this.awareness.getStates().size > 0;\n\t}\n\n\t/**\n\t * Apply the given awareness update\n\t */\n\tapplyAwarenessUpdate(connection: Connection, update: Uint8Array): Document {\n\t\tapplyAwarenessUpdate(this.awareness, update, connection);\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Handle an awareness update and sync changes to clients\n\t * @private\n\t */\n\tprivate handleAwarenessUpdate(\n\t\t{ added, updated, removed }: AwarenessUpdate,\n\t\toriginConnection: Connection | null,\n\t): Document {\n\t\tconst changedClients = added.concat(updated, removed);\n\n\t\tif (originConnection !== null) {\n\t\t\tconst entry = this.connections.get(originConnection);\n\n\t\t\tif (entry) {\n\t\t\t\tadded.forEach((clientId: any) => entry.clients.add(clientId));\n\t\t\t\tremoved.forEach((clientId: any) => entry.clients.delete(clientId));\n\t\t\t}\n\t\t}\n\n\t\tfor (const connection of this.getConnections()) {\n\t\t\tconst awarenessMessage = new OutgoingMessage(\n\t\t\t\tconnection.messageAddress,\n\t\t\t).createAwarenessUpdateMessage(this.awareness, changedClients);\n\n\t\t\tconnection.send(awarenessMessage.toUint8Array());\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Handle an updated document and sync changes to clients\n\t */\n\tprivate handleUpdate(update: Uint8Array, origin: unknown): Document {\n\t\tthis.callbacks.onUpdate(this, origin, update);\n\n\t\tfor (const connection of this.getConnections()) {\n\t\t\tconst message = new OutgoingMessage(connection.messageAddress)\n\t\t\t\t.createSyncMessage()\n\t\t\t\t.writeUpdate(update);\n\n\t\t\tconnection.send(message.toUint8Array());\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Broadcast stateless message to all connections\n\t */\n\tpublic broadcastStateless(\n\t\tpayload: string,\n\t\tfilter?: (conn: Connection) => boolean,\n\t): void {\n\t\tthis.callbacks.beforeBroadcastStateless(this, payload);\n\n\t\tconst connections = filter\n\t\t\t? this.getConnections().filter(filter)\n\t\t\t: this.getConnections();\n\n\t\tconnections.forEach((connection) => {\n\t\t\tconnection.sendStateless(payload);\n\t\t});\n\t}\n\n\tdestroy() {\n\t\tsuper.destroy();\n\t\tthis.isDestroyed = true;\n\t}\n}\n\nexport default Document;\n","","/**\n * Get parameters by the given request\n */\nexport function getParameters(request?: { url?: string }): URLSearchParams {\n\tconst url = request?.url;\n\tif (!url) {\n\t\treturn new URLSearchParams();\n\t}\n\t// Handle both full URLs (web Request) and path-only URLs (Node.js IncomingMessage)\n\tconst query = url.includes(\"://\")\n\t\t? new URL(url).searchParams\n\t\t: new URLSearchParams(url.split(\"?\")[1] || \"\");\n\treturn query;\n}\n","import crypto from \"node:crypto\";\nimport {\n\ttype CloseEvent,\n\tConnectionTimeout,\n\tForbidden,\n\tResetConnection,\n\tUnauthorized,\n\tWsReadyStates,\n} from \"@hocuspocus/common\";\nimport * as decoding from \"lib0/decoding\";\nimport Connection from \"./Connection.ts\";\nimport type Document from \"./Document.ts\";\nimport type { Hocuspocus } from \"./Hocuspocus.ts\";\nimport { IncomingMessage as SocketIncomingMessage } from \"./IncomingMessage.ts\";\nimport { OutgoingMessage } from \"./OutgoingMessage.ts\";\nimport type {\n\tConnectionConfiguration,\n\tWebSocketLike,\n\tbeforeHandleMessagePayload,\n\tbeforeSyncPayload,\n\tonDisconnectPayload,\n} from \"./types.ts\";\nimport { MessageType } from \"./types.ts\";\nimport { getParameters } from \"./util/getParameters.ts\";\n\n/**\n * The `ClientConnection` class is responsible for handling an incoming WebSocket\n *\n * TODO-refactor:\n * - use event handlers instead of calling hooks directly, hooks should probably be called from Hocuspocus.ts\n */\nexport class ClientConnection<Context = any> {\n\t// Map of established document connections, keyed by rawKey (composite or plain)\n\tprivate readonly documentConnections: Record<string, Connection<Context>> =\n\t\t{};\n\n\t// While the connection will be establishing messages will\n\t// be queued and handled later.\n\tprivate readonly incomingMessageQueue: Record<string, Uint8Array[]> = {};\n\n\t// While the connection is establishing, keep track of which documents have received auth\n\tprivate readonly documentConnectionsEstablished = new Set<string>();\n\n\t// Hook payloads keyed by rawKey (composite or plain)\n\tprivate readonly hookPayloads: Record<\n\t\tstring,\n\t\t{\n\t\t\tinstance: Hocuspocus;\n\t\t\trequest: Request;\n\t\t\trequestHeaders: Headers;\n\t\t\trequestParameters: URLSearchParams;\n\t\t\tsocketId: string;\n\t\t\tconnectionConfig: ConnectionConfiguration;\n\t\t\tcontext: Context;\n\t\t\tproviderVersion: string | null;\n\t\t}\n\t> = {};\n\n\tprivate readonly callbacks = {\n\t\tonClose: [(document: Document, payload: onDisconnectPayload) => {}],\n\t};\n\n\t// Every new connection gets a unique identifier.\n\tprivate readonly socketId = crypto.randomUUID();\n\n\ttimeout: number;\n\n\tpingInterval: NodeJS.Timeout;\n\n\tlastMessageReceivedAt = Date.now();\n\n\t/**\n\t * The `ClientConnection` class receives incoming WebSocket connections,\n\t * runs all hooks:\n\t *\n\t *  - onConnect for all connections\n\t *  - onAuthenticate only if required\n\t *\n\t * … and if nothings fails it'll fully establish the connection and\n\t * load the Document then.\n\t */\n\tconstructor(\n\t\tprivate readonly websocket: WebSocketLike,\n\t\tprivate readonly request: Request,\n\t\tprivate readonly documentProvider: {\n\t\t\tcreateDocument: Hocuspocus[\"createDocument\"];\n\t\t},\n\t\t// TODO: change to events\n\t\tprivate readonly hooks: Hocuspocus[\"hooks\"],\n\t\tprivate readonly opts: {\n\t\t\ttimeout: number;\n\t\t},\n\t\tprivate readonly defaultContext: Context = {} as Context,\n\t) {\n\t\tthis.timeout = opts.timeout;\n\t\tthis.pingInterval = setInterval(this.check, this.timeout);\n\t}\n\n\t/**\n\t * Handle WebSocket close event. Call this from your integration\n\t * when the WebSocket connection closes.\n\t */\n\thandleClose(event?: CloseEvent) {\n\t\tthis.close(event);\n\t\tclearInterval(this.pingInterval);\n\t}\n\n\tprivate close(event?: CloseEvent) {\n\t\tObject.values(this.documentConnections).forEach((connection) =>\n\t\t\tconnection.close(event),\n\t\t);\n\t}\n\n\t/**\n\t * Close the connection if no messages have been received within the timeout period.\n\t * This replaces application-level ping/pong to maintain backward compatibility\n\t * with older provider versions that don't understand Ping/Pong message types.\n\t * Awareness updates (~every 30s) keep active connections alive.\n\t */\n\tprivate check = () => {\n\t\tif (Date.now() - this.lastMessageReceivedAt > this.timeout) {\n\t\t\tthis.close(ConnectionTimeout);\n\t\t}\n\t};\n\n\t/**\n\t * Set a callback that will be triggered when the connection is closed\n\t */\n\tpublic onClose(\n\t\tcallback: (document: Document, payload: onDisconnectPayload) => void,\n\t): ClientConnection {\n\t\tthis.callbacks.onClose.push(callback);\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Create a new connection by the given request and document\n\t */\n\tprivate createConnection(\n\t\tconnection: WebSocketLike,\n\t\tdocument: Document,\n\t\thookPayload: (typeof this.hookPayloads)[string],\n\t\tsessionId: string | null,\n\t\tproviderVersion: string | null,\n\t): Connection {\n\t\tconst instance = new Connection(\n\t\t\tconnection,\n\t\t\thookPayload.request,\n\t\t\tdocument,\n\t\t\thookPayload.socketId,\n\t\t\thookPayload.context,\n\t\t\thookPayload.connectionConfig.readOnly,\n\t\t\tsessionId,\n\t\t\tproviderVersion,\n\t\t);\n\n\t\tinstance.onClose(async (document, event) => {\n\t\t\t// Wait for any pending message processing to complete before running\n\t\t\t// disconnect hooks. This ensures that document updates from queued messages\n\t\t\t// are applied (and their debounced onStoreDocument scheduled) before the\n\t\t\t// disconnect handler checks whether to call executeNow.\n\t\t\tawait instance.waitForPendingMessages();\n\n\t\t\tconst disconnectHookPayload: onDisconnectPayload = {\n\t\t\t\tinstance: this.documentProvider as Hocuspocus, // TODO, this will be removed when we use events instead of hooks for this class\n\t\t\t\tclientsCount: document.getConnectionsCount(),\n\t\t\t\tcontext: hookPayload.context,\n\t\t\t\tdocument,\n\t\t\t\tsocketId: hookPayload.socketId,\n\t\t\t\tdocumentName: document.name,\n\t\t\t\trequestHeaders: hookPayload.request.headers,\n\t\t\t\trequestParameters: getParameters(hookPayload.request),\n\t\t\t};\n\n\t\t\tawait this.hooks(\"onDisconnect\", disconnectHookPayload);\n\t\t\tthis.callbacks.onClose.forEach((callback) =>\n\t\t\t\tcallback(document, disconnectHookPayload),\n\t\t\t);\n\t\t});\n\n\t\tinstance.onStatelessCallback(async (payload) => {\n\t\t\ttry {\n\t\t\t\treturn await this.hooks(\"onStateless\", payload);\n\t\t\t} catch (error: any) {\n\t\t\t\tif (error?.message) {\n\t\t\t\t\t// if a hook rejects and the error is empty, do nothing\n\t\t\t\t\t// this is only meant to prevent later hooks and the\n\t\t\t\t\t// default handler to do something. if an error is present\n\t\t\t\t\t// just rethrow it\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tinstance.beforeHandleMessage((connection, update) => {\n\t\t\tconst beforeHandleMessagePayload: beforeHandleMessagePayload = {\n\t\t\t\tinstance: this.documentProvider as Hocuspocus, // TODO, this will be removed when we use events instead of hooks for this class\n\t\t\t\tclientsCount: document.getConnectionsCount(),\n\t\t\t\tcontext: hookPayload.context,\n\t\t\t\tdocument,\n\t\t\t\tsocketId: hookPayload.socketId,\n\t\t\t\tconnection,\n\t\t\t\tdocumentName: document.name,\n\t\t\t\trequestHeaders: hookPayload.request.headers,\n\t\t\t\trequestParameters: getParameters(hookPayload.request),\n\t\t\t\tupdate,\n\t\t\t};\n\n\t\t\treturn this.hooks(\"beforeHandleMessage\", beforeHandleMessagePayload);\n\t\t});\n\n\t\tinstance.beforeSync((connection, payload) => {\n\t\t\tconst beforeSyncPayload: beforeSyncPayload = {\n\t\t\t\tclientsCount: document.getConnectionsCount(),\n\t\t\t\tcontext: hookPayload.context,\n\t\t\t\tdocument,\n\t\t\t\tdocumentName: document.name,\n\t\t\t\tconnection,\n\t\t\t\ttype: payload.type,\n\t\t\t\tpayload: payload.payload,\n\t\t\t};\n\n\t\t\treturn this.hooks(\"beforeSync\", beforeSyncPayload);\n\t\t});\n\n\t\treturn instance;\n\t}\n\n\t// Once all hooks are run, we'll fully establish the connection:\n\tprivate setUpNewConnection = async (rawKey: string, documentName: string, sessionId: string | null) => {\n\t\tconst hookPayload = this.hookPayloads[rawKey];\n\t\t// If no hook interrupts, create a document and connection\n\t\tconst document = await this.documentProvider.createDocument(\n\t\t\tdocumentName,\n\t\t\thookPayload.request,\n\t\t\thookPayload.socketId,\n\t\t\thookPayload.connectionConfig,\n\t\t\thookPayload.context,\n\t\t);\n\t\tconst connection = this.createConnection(this.websocket, document, hookPayload, sessionId, hookPayload.providerVersion);\n\n\t\tconnection.onClose((document, event) => {\n\t\t\tdelete this.hookPayloads[rawKey];\n\t\t\tdelete this.documentConnections[rawKey];\n\t\t\tdelete this.incomingMessageQueue[rawKey];\n\t\t\tthis.documentConnectionsEstablished.delete(rawKey);\n\t\t});\n\n\t\tconnection.onTokenSyncCallback(async (payload) => {\n\t\t\ttry {\n\t\t\t\treturn await this.hooks(\n\t\t\t\t\t\"onTokenSync\",\n\t\t\t\t\t{\n\t\t\t\t\t\t...hookPayload,\n\t\t\t\t\t\t...payload,\n\t\t\t\t\t\tdocument,\n\t\t\t\t\t\tconnection,\n\t\t\t\t\t\tdocumentName,\n\t\t\t\t\t},\n\t\t\t\t\t(contextAdditions: Partial<Context>) => {\n\t\t\t\t\t\thookPayload.context = {\n\t\t\t\t\t\t\t...hookPayload.context,\n\t\t\t\t\t\t\t...contextAdditions,\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t} catch (err: any) {\n\t\t\t\tconsole.error(err);\n\t\t\t\tconst error = { ...Unauthorized, ...err };\n\t\t\t\tconnection.close({ code: error.code, reason: error.reason });\n\t\t\t}\n\t\t});\n\n\t\tthis.documentConnections[rawKey] = connection;\n\n\t\t// If the WebSocket has already disconnected (wow, that was fast) – then\n\t\t// immediately call close to cleanup the connection and document in memory.\n\t\tif (\n\t\t\tthis.websocket.readyState === WsReadyStates.Closing ||\n\t\t\tthis.websocket.readyState === WsReadyStates.Closed\n\t\t) {\n\t\t\tthis.close();\n\t\t\treturn;\n\t\t}\n\n\t\t// Drain queued messages to the Connection.\n\t\tthis.incomingMessageQueue[rawKey]?.forEach((input) => {\n\t\t\tconnection.handleMessage(input);\n\t\t});\n\n\t\tawait this.hooks(\"connected\", {\n\t\t\t...hookPayload,\n\t\t\tdocumentName,\n\t\t\tcontext: hookPayload.context,\n\t\t\tconnection,\n\t\t});\n\t};\n\n\t// This listener handles authentication messages and queues everything else.\n\tprivate handleQueueingMessage = async (data: Uint8Array, rawKey: string, documentName: string) => {\n\t\ttry {\n\t\t\tconst tmpMsg = new SocketIncomingMessage(data);\n\n\t\t\tdecoding.readVarString(tmpMsg.decoder); // skip the message address (already extracted)\n\t\t\tconst type = decoding.readVarUint(tmpMsg.decoder);\n\n\t\t\tif (\n\t\t\t\t!(\n\t\t\t\t\ttype === MessageType.Auth &&\n\t\t\t\t\t!this.documentConnectionsEstablished.has(rawKey)\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthis.incomingMessageQueue[rawKey].push(data);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Okay, we've got the authentication message we're waiting for:\n\t\t\tthis.documentConnectionsEstablished.add(rawKey);\n\n\t\t\t// The 2nd integer contains the submessage type\n\t\t\t// which will always be authentication when sent from client -> server\n\t\t\tdecoding.readVarUint(tmpMsg.decoder);\n\t\t\tconst token = decoding.readVarString(tmpMsg.decoder);\n\n\t\t\t// Try to read providerVersion (new protocol field)\n\t\t\tlet providerVersion: string | null = null;\n\t\t\tif (decoding.hasContent(tmpMsg.decoder)) {\n\t\t\t\tproviderVersion = decoding.readVarString(tmpMsg.decoder);\n\t\t\t}\n\n\t\t\t// Extract sessionId from the rawKey (documentName\\0sessionId) if present\n\t\t\tconst sepIdx = rawKey.indexOf('\\0');\n\t\t\tconst sessionId = sepIdx === -1 ? null : rawKey.substring(sepIdx + 1);\n\n\t\t\t// Response uses rawKey so session-aware clients can route correctly\n\t\t\tconst responseAddress = rawKey;\n\n\t\t\ttry {\n\t\t\t\tconst hookPayload = this.hookPayloads[rawKey];\n\t\t\t\thookPayload.providerVersion = providerVersion;\n\n\t\t\t\tawait this.hooks(\n\t\t\t\t\t\"onConnect\",\n\t\t\t\t\t{ ...hookPayload, documentName },\n\t\t\t\t\t(contextAdditions: Partial<Context>) => {\n\t\t\t\t\t\thookPayload.context = {\n\t\t\t\t\t\t\t...hookPayload.context,\n\t\t\t\t\t\t\t...contextAdditions,\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\tawait this.hooks(\n\t\t\t\t\t\"onAuthenticate\",\n\t\t\t\t\t{\n\t\t\t\t\t\ttoken,\n\t\t\t\t\t\t...hookPayload,\n\t\t\t\t\t\tdocumentName,\n\t\t\t\t\t},\n\t\t\t\t\t(contextAdditions: Partial<Context>) => {\n\t\t\t\t\t\thookPayload.context = {\n\t\t\t\t\t\t\t...hookPayload.context,\n\t\t\t\t\t\t\t...contextAdditions,\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\t// All `onAuthenticate` hooks passed.\n\t\t\t\thookPayload.connectionConfig.isAuthenticated = true;\n\n\t\t\t\t// Let the client know that authentication was successful.\n\t\t\t\tconst message = new OutgoingMessage(responseAddress).writeAuthenticated(\n\t\t\t\t\thookPayload.connectionConfig.readOnly,\n\t\t\t\t);\n\n\t\t\t\tthis.websocket.send(message.toUint8Array());\n\n\t\t\t\t// Time to actually establish the connection.\n\t\t\t\tawait this.setUpNewConnection(rawKey, documentName, sessionId);\n\t\t\t} catch (err: any) {\n\t\t\t\tconst error = err || Forbidden;\n\t\t\t\tconst message = new OutgoingMessage(responseAddress).writePermissionDenied(\n\t\t\t\t\terror.reason ?? \"permission-denied\",\n\t\t\t\t);\n\n\t\t\t\tthis.websocket.send(message.toUint8Array());\n\n\t\t\t\t// Clean up all state for this document so a retry is treated\n\t\t\t\t// as a fresh first connection attempt.\n\t\t\t\tthis.documentConnectionsEstablished.delete(rawKey);\n\t\t\t\tdelete this.hookPayloads[rawKey];\n\t\t\t\tdelete this.incomingMessageQueue[rawKey];\n\t\t\t}\n\n\t\t\t// Catch errors due to failed decoding of data\n\t\t} catch (error) {\n\t\t\tconsole.error(error);\n\t\t\tthis.websocket.close(ResetConnection.code, ResetConnection.reason);\n\t\t}\n\t};\n\n\t/**\n\t * Handle an incoming WebSocket message. Call this from your integration\n\t * when the WebSocket receives a binary message.\n\t */\n\thandleMessage = (data: Uint8Array) => {\n\t\tthis.lastMessageReceivedAt = Date.now();\n\n\t\ttry {\n\t\t\tconst tmpMsg = new SocketIncomingMessage(data);\n\n\t\t\tconst rawKey = decoding.readVarString(tmpMsg.decoder);\n\n\t\t\t// Extract the plain documentName (the raw key may be documentName\\0sessionId)\n\t\t\tconst sepIdx = rawKey.indexOf('\\0');\n\t\t\tconst documentName = sepIdx === -1 ? rawKey : rawKey.substring(0, sepIdx);\n\n\t\t\t// Look up by rawKey first (session-aware providers), then fall back\n\t\t\t// to plain documentName for backward compatibility with old providers\n\t\t\tconst connection = this.documentConnections[rawKey]\n\t\t\t\t?? this.documentConnections[documentName];\n\t\t\tif (connection) {\n\t\t\t\tconnection.handleMessage(data);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst isFirst = this.incomingMessageQueue[rawKey] === undefined;\n\t\t\tif (isFirst) {\n\t\t\t\tthis.incomingMessageQueue[rawKey] = [];\n\t\t\t\tif (this.hookPayloads[rawKey]) {\n\t\t\t\t\tthrow new Error(\"first message, but hookPayloads exists\");\n\t\t\t\t}\n\n\t\t\t\tthis.hookPayloads[rawKey] = {\n\t\t\t\t\tinstance: this.documentProvider as Hocuspocus,\n\t\t\t\t\trequest: this.request,\n\t\t\t\t\tconnectionConfig: {\n\t\t\t\t\t\treadOnly: false,\n\t\t\t\t\t\tisAuthenticated: false,\n\t\t\t\t\t},\n\t\t\t\t\trequestHeaders: this.request.headers,\n\t\t\t\t\trequestParameters: getParameters(this.request),\n\t\t\t\t\tsocketId: this.socketId,\n\t\t\t\t\tcontext: {\n\t\t\t\t\t\t...this.defaultContext,\n\t\t\t\t\t},\n\t\t\t\t\tproviderVersion: null as string | null,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tthis.handleQueueingMessage(data, rawKey, documentName);\n\t\t} catch (closeError) {\n\t\t\t// catch is needed in case an invalid payload crashes the parsing of the Uint8Array\n\t\t\tconsole.error(closeError);\n\t\t\tthis.websocket.close(Unauthorized.code, Unauthorized.reason);\n\t\t}\n\t};\n}\n","import { URLSearchParams } from \"node:url\";\nimport type Document from \"./Document.ts\";\nimport type { Hocuspocus } from \"./Hocuspocus.ts\";\nimport type {\n\tDirectConnection as DirectConnectionInterface,\n\tLocalTransactionOrigin,\n} from \"./types.ts\";\n\nexport class DirectConnection<Context = any>\n\timplements DirectConnectionInterface\n{\n\tdocument: Document | null = null;\n\n\tinstance!: Hocuspocus;\n\n\tcontext: Context;\n\n\t/**\n\t * Constructor.\n\t */\n\tconstructor(document: Document, instance: Hocuspocus, context?: Context) {\n\t\tthis.document = document;\n\t\tthis.instance = instance;\n\t\tthis.context = (context ?? {}) as Context;\n\n\t\tthis.document.addDirectConnection();\n\t}\n\n\tasync transact(transaction: (document: Document) => void) {\n\t\tif (!this.document) {\n\t\t\tthrow new Error(\"direct connection closed\");\n\t\t}\n\n\t\tthis.document.transact(\n\t\t\t(x) => {\n\t\t\t\t// biome-ignore lint/style/noNonNullAssertion: <explanation>\n\t\t\t\ttransaction(this.document!);\n\t\t\t},\n\t\t\t{\n\t\t\t\tsource: \"local\",\n\t\t\t\tcontext: this.context,\n\t\t\t} satisfies LocalTransactionOrigin,\n\t\t);\n\t}\n\n\tasync disconnect() {\n\t\tif (this.document) {\n\t\t\tthis.document?.removeDirectConnection();\n\n\t\t\tawait this.instance.storeDocumentHooks(\n\t\t\t\tthis.document,\n\t\t\t\t{\n\t\t\t\t\tclientsCount: this.document.getConnectionsCount(),\n\t\t\t\t\tlastContext: this.context,\n\t\t\t\t\tlastTransactionOrigin: {\n\t\t\t\t\t\tsource: \"local\",\n\t\t\t\t\t\tcontext: this.context,\n\t\t\t\t\t} satisfies LocalTransactionOrigin,\n\t\t\t\t\tdocument: this.document,\n\t\t\t\t\tdocumentName: this.document.name,\n\t\t\t\t\tinstance: this.instance,\n\t\t\t\t},\n\t\t\t\ttrue,\n\t\t\t);\n\n\t\t\t// If the direct connection was the only connection to the document\n\t\t\t// then we should trigger the onDisconnect hook for\n\t\t\t// this doc and unload the document\n\t\t\tif (\n\t\t\t\tthis.document.getConnectionsCount() === 0 &&\n\t\t\t\t!this.document.saveMutex.isLocked()\n\t\t\t) {\n\t\t\t\tawait this.instance.hooks(\"onDisconnect\", {\n\t\t\t\t\tinstance: this.instance,\n\t\t\t\t\tclientsCount: this.document.getConnectionsCount(),\n\t\t\t\t\tcontext: this.context,\n\t\t\t\t\tdocument: this.document,\n\t\t\t\t\tsocketId: \"server\",\n\t\t\t\t\tdocumentName: this.document.name,\n\t\t\t\t\trequestHeaders: new Headers(),\n\t\t\t\t\trequestParameters: new URLSearchParams(),\n\t\t\t\t});\n\n\t\t\t\tawait this.instance.unloadDocument(this.document);\n\t\t\t}\n\n\t\t\tthis.document = null;\n\t\t}\n\t}\n}\n","export const useDebounce = () => {\n\tconst timers: Map<\n\t\tstring,\n\t\t{\n\t\t\ttimeout: NodeJS.Timeout;\n\t\t\tstart: number;\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n\t\t\tfunc: () => any | Promise<() => any>;\n\t\t}\n\t> = new Map();\n\n\tconst runningExecutions: Map<string, Promise<any>> = new Map();\n\n\tconst debounce = async (\n\t\tid: string,\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n\t\tfunc: () => any | Promise<() => any>,\n\t\tdebounce: number,\n\t\tmaxDebounce: number,\n\t) => {\n\t\tconst old = timers.get(id);\n\t\tconst start = old?.start || Date.now();\n\n\t\tconst run = async () => {\n\t\t\tif (runningExecutions.has(id)) {\n\t\t\t\t// wait for previous execution to finish\n\t\t\t\tawait runningExecutions.get(id);\n\t\t\t}\n\n\t\t\ttimers.delete(id);\n\n\t\t\tconst execution = func();\n\n\t\t\trunningExecutions.set(id, execution);\n\t\t\tconst executionResult = await execution;\n\t\t\trunningExecutions.delete(id);\n\n\t\t\treturn executionResult;\n\t\t};\n\n\t\tif (old?.timeout) {\n\t\t\tclearTimeout(old.timeout);\n\t\t}\n\n\t\tif (debounce === 0) {\n\t\t\treturn run();\n\t\t}\n\n\t\tif (Date.now() - start >= maxDebounce) {\n\t\t\treturn run();\n\t\t}\n\n\t\ttimers.set(id, {\n\t\t\tstart,\n\t\t\ttimeout: setTimeout(run, debounce),\n\t\t\tfunc: run,\n\t\t});\n\t};\n\n\tconst executeNow = (id: string) => {\n\t\tconst old = timers.get(id);\n\t\tif (old) {\n\t\t\tclearTimeout(old.timeout);\n\t\t\treturn old.func();\n\t\t}\n\t};\n\n\tconst isDebounced = (id: string): boolean => {\n\t\treturn timers.has(id);\n\t};\n\n\tconst isCurrentlyExecuting = (id: string): boolean => {\n\t\treturn runningExecutions.has(id);\n\t};\n\n\treturn { debounce, isDebounced, isCurrentlyExecuting, executeNow };\n};\n","import crypto from \"node:crypto\";\nimport { awarenessStatesToArray, ResetConnection, SkipFurtherHooksError } from \"@hocuspocus/common\";\nimport { applyUpdate, Doc, encodeStateAsUpdate } from \"yjs\";\nimport meta from \"../package.json\" with { type: \"json\" };\n\nimport { ClientConnection } from \"./ClientConnection.ts\";\nimport { DirectConnection } from \"./DirectConnection.ts\";\nimport Document from \"./Document.ts\";\nimport type { Server } from \"./Server.ts\";\nimport type {\n\tAwarenessUpdate,\n\tbeforeBroadcastStatelessPayload,\n\tConfiguration,\n\tConnectionConfiguration,\n\tHookName,\n\tHookPayloadByName,\n\tonChangePayload,\n\tonDisconnectPayload,\n\tonStoreDocumentPayload,\n\tWebSocketLike,\n} from \"./types.ts\";\nimport { isTransactionOrigin, shouldSkipStoreHooks } from \"./types.ts\";\nimport { useDebounce } from \"./util/debounce.ts\";\nimport { getParameters } from \"./util/getParameters.ts\";\n\nexport const defaultConfiguration = {\n\tname: null,\n\ttimeout: 60_000,\n\tdebounce: 2_000,\n\tmaxDebounce: 10_000,\n\tquiet: false,\n\tyDocOptions: {\n\t\tgc: true,\n\t\tgcFilter: () => true,\n\t},\n\tunloadImmediately: true,\n};\n\nexport class Hocuspocus<Context = any> {\n\tconfiguration: Configuration<Context> = {\n\t\t...defaultConfiguration,\n\t\textensions: [],\n\t\tonConfigure: () => new Promise((r) => r(null)),\n\t\tonListen: () => new Promise((r) => r(null)),\n\t\tonUpgrade: () => new Promise((r) => r(null)),\n\t\tonConnect: () => new Promise((r) => r(null)),\n\t\tconnected: () => new Promise((r) => r(null)),\n\t\tbeforeHandleMessage: () => new Promise((r) => r(null)),\n\t\tbeforeSync: () => new Promise((r) => r(null)),\n\t\tbeforeBroadcastStateless: () => new Promise((r) => r(null)),\n\t\tonStateless: () => new Promise((r) => r(null)),\n\t\tonChange: () => new Promise((r) => r(null)),\n\t\tonCreateDocument: () => new Promise((r) => r(null)),\n\t\tonLoadDocument: () => new Promise((r) => r(null)),\n\t\tonStoreDocument: () => new Promise((r) => r(null)),\n\t\tafterStoreDocument: () => new Promise((r) => r(null)),\n\t\tonAwarenessUpdate: () => new Promise((r) => r(null)),\n\t\tonRequest: () => new Promise((r) => r(null)),\n\t\tonDisconnect: () => new Promise((r) => r(null)),\n\t\tonDestroy: () => new Promise((r) => r(null)),\n\t};\n\n\tloadingDocuments: Map<string, Promise<Document>> = new Map();\n\tunloadingDocuments: Map<string, Promise<void>> = new Map();\n\n\tdocuments: Map<string, Document> = new Map();\n\n\tserver?: Server;\n\n\tdebouncer = useDebounce();\n\n\tconstructor(configuration?: Partial<Configuration<Context>>) {\n\t\tif (configuration) {\n\t\t\tthis.configure(configuration);\n\t\t}\n\t}\n\n\t/**\n\t * Configure Hocuspocus\n\t */\n\tconfigure(\n\t\tconfiguration: Partial<Configuration<Context>>,\n\t): Hocuspocus<Context> {\n\t\tthis.configuration = {\n\t\t\t...this.configuration,\n\t\t\t...configuration,\n\t\t};\n\n\t\tthis.configuration.extensions.sort((a, b) => {\n\t\t\tconst one = typeof a.priority === \"undefined\" ? 100 : a.priority;\n\t\t\tconst two = typeof b.priority === \"undefined\" ? 100 : b.priority;\n\n\t\t\tif (one > two) {\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tif (one < two) {\n\t\t\t\treturn 1;\n\t\t\t}\n\n\t\t\treturn 0;\n\t\t});\n\n\t\tthis.configuration.extensions.push({\n\t\t\tonConfigure: this.configuration.onConfigure,\n\t\t\tonListen: this.configuration.onListen,\n\t\t\tonUpgrade: this.configuration.onUpgrade,\n\t\t\tonConnect: this.configuration.onConnect,\n\t\t\tconnected: this.configuration.connected,\n\t\t\tonAuthenticate: this.configuration.onAuthenticate,\n\t\t\tonTokenSync: this.configuration.onTokenSync,\n\t\t\tonLoadDocument: this.configuration.onLoadDocument,\n\t\t\tafterLoadDocument: this.configuration.afterLoadDocument,\n\t\t\tbeforeHandleMessage: this.configuration.beforeHandleMessage,\n\t\t\tbeforeBroadcastStateless: this.configuration.beforeBroadcastStateless,\n\t\t\tbeforeSync: this.configuration.beforeSync,\n\t\t\tonStateless: this.configuration.onStateless,\n\t\t\tonChange: this.configuration.onChange,\n\t\t\tonStoreDocument: this.configuration.onStoreDocument,\n\t\t\tafterStoreDocument: this.configuration.afterStoreDocument,\n\t\t\tonAwarenessUpdate: this.configuration.onAwarenessUpdate,\n\t\t\tonRequest: this.configuration.onRequest,\n\t\t\tbeforeUnloadDocument: this.configuration.beforeUnloadDocument,\n\t\t\tafterUnloadDocument: this.configuration.afterUnloadDocument,\n\t\t\tonDisconnect: this.configuration.onDisconnect,\n\t\t\tonDestroy: this.configuration.onDestroy,\n\t\t});\n\n\t\tthis.hooks(\"onConfigure\", {\n\t\t\tconfiguration: this.configuration,\n\t\t\tversion: meta.version,\n\t\t\tinstance: this,\n\t\t});\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Get the total number of active documents\n\t */\n\tgetDocumentsCount(): number {\n\t\treturn this.documents.size;\n\t}\n\n\t/**\n\t * Get the total number of active connections\n\t */\n\tgetConnectionsCount(): number {\n\t\tconst uniqueSocketIds = new Set<string>();\n\t\tconst totalDirectConnections = Array.from(this.documents.values()).reduce(\n\t\t\t(acc, document) => {\n\t\t\t\t// Accumulate unique socket IDs\n\t\t\t\tdocument.getConnections().forEach(({ socketId }) => {\n\t\t\t\t\tuniqueSocketIds.add(socketId);\n\t\t\t\t});\n\t\t\t\t// Accumulate direct connections\n\t\t\t\treturn acc + document.directConnectionsCount;\n\t\t\t},\n\t\t\t0,\n\t\t);\n\t\t// Return the sum of unique socket IDs and direct connections\n\t\treturn uniqueSocketIds.size + totalDirectConnections;\n\t}\n\n\t/**\n\t * Immediately execute all pending debounced onStoreDocument calls.\n\t * Useful during shutdown to ensure documents are persisted and unloaded\n\t * before the server exits, even when unloadImmediately is false.\n\t */\n\tflushPendingStores() {\n\t\tthis.documents.forEach((document: Document) => {\n\t\t\tconst debounceId = `onStoreDocument-${document.name}`;\n\t\t\tif (!document.isLoading && this.debouncer.isDebounced(debounceId)) {\n\t\t\t\tthis.debouncer.executeNow(debounceId);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Force close one or more connections\n\t */\n\tcloseConnections(documentName?: string) {\n\t\t// Iterate through all connections for all documents\n\t\t// and invoke their close method, which is a graceful\n\t\t// disconnect wrapper around the underlying websocket.close\n\t\tthis.documents.forEach((document: Document) => {\n\t\t\t// If a documentName was specified, bail if it doesn't match\n\t\t\tif (documentName && document.name !== documentName) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tdocument.connections.forEach((_clients, connection) => {\n\t\t\t\tconnection.close(ResetConnection);\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * The `handleConnection` method receives incoming WebSocket connections,\n\t * runs all hooks:\n\t *\n\t *  - onConnect for all connections\n\t *  - onAuthenticate only if required\n\t *\n\t * … and if nothing fails it'll fully establish the connection and\n\t * load the Document then.\n\t */\n\thandleConnection(\n\t\tincoming: WebSocket | WebSocketLike,\n\t\trequest: Request,\n\t\tdefaultContext: Context = {} as Context,\n\t): ClientConnection {\n\t\tconst clientConnection = new ClientConnection(\n\t\t\tincoming,\n\t\t\trequest,\n\t\t\tthis,\n\t\t\tthis.hooks.bind(this),\n\t\t\t{\n\t\t\t\ttimeout: this.configuration.timeout,\n\t\t\t},\n\t\t\tdefaultContext,\n\t\t);\n\t\tclientConnection.onClose(\n\t\t\t(document: Document, hookPayload: onDisconnectPayload) => {\n\t\t\t\t// Check if there are still no connections to the document, as these hooks\n\t\t\t\t// may take some time to resolve (e.g. database queries). If a\n\t\t\t\t// new connection were to come in during that time it would rely on the\n\t\t\t\t// document in the map that we remove now.\n\t\t\t\tif (document.getConnectionsCount() > 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// If it’s the last connection, we need to make sure to store the\n\t\t\t\t// document. Use the debouncer executeNow helper, to run scheduled\n\t\t\t\t// onStoreDocument immediately and clear running timers.\n\t\t\t\t// If there is no scheduled run for this document there is no point in\n\t\t\t\t// triggering onStoreDocument hook, as everything seems to be stored already.\n\t\t\t\t// Only run this if the document has finished loading earlier (i.e. not to persist the empty\n\t\t\t\t// ydoc if the onLoadDocument hook returned an error)\n\t\t\t\tif (\n\t\t\t\t\t!document.isLoading &&\n\t\t\t\t\tthis.debouncer.isDebounced(`onStoreDocument-${document.name}`)\n\t\t\t\t) {\n\t\t\t\t\tif (this.configuration.unloadImmediately) {\n\t\t\t\t\t\tthis.debouncer.executeNow(`onStoreDocument-${document.name}`);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Remove document from memory immediately\n\t\t\t\t\tthis.unloadDocument(document);\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\n\t\treturn clientConnection;\n\t}\n\n\t/**\n\t * Handle update of the given document\n\t *\n\t * \"connection\" is not necessarily type \"Connection\", it's the Yjs \"origin\" (which is \"Connection\" if\n\t * the update is incoming from the provider, but can be anything if the updates is originated from an extension.\n\t */\n\tprivate handleDocumentUpdate(\n\t\tdocument: Document,\n\t\torigin: unknown,\n\t\tupdate: Uint8Array,\n\t) {\n\t\tconst connection =\n\t\t\tisTransactionOrigin(origin) && origin.source === \"connection\"\n\t\t\t\t? origin.connection\n\t\t\t\t: undefined;\n\t\tconst request = connection?.request;\n\t\tconst context = isTransactionOrigin(origin)\n\t\t\t? origin.source === \"connection\"\n\t\t\t\t? origin.connection.context\n\t\t\t\t: origin.source === \"local\"\n\t\t\t\t\t? (origin.context ?? {})\n\t\t\t\t\t: {}\n\t\t\t: {};\n\n\t\tconst changePayload: onChangePayload = {\n\t\t\tinstance: this,\n\t\t\tclientsCount: document.getConnectionsCount(),\n\t\t\tdocument,\n\t\t\tdocumentName: document.name,\n\t\t\trequestHeaders: request?.headers ?? new Headers(),\n\t\t\trequestParameters: getParameters(request),\n\t\t\tsocketId: connection?.socketId ?? \"\",\n\t\t\tupdate,\n\t\t\ttransactionOrigin: origin,\n\t\t\tconnection: connection,\n\t\t\tcontext,\n\t\t};\n\n\t\tthis.hooks(\"onChange\", changePayload);\n\n\t\tif (shouldSkipStoreHooks(origin)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst storePayload: onStoreDocumentPayload = {\n\t\t\tinstance: this,\n\t\t\tclientsCount: document.getConnectionsCount(),\n\t\t\tdocument,\n\t\t\tlastContext: context,\n\t\t\tlastTransactionOrigin: origin,\n\t\t\tdocumentName: document.name,\n\t\t};\n\n\t\tthis.storeDocumentHooks(document, storePayload);\n\t}\n\n\t/**\n\t * Create a new document by the given request\n\t */\n\tpublic async createDocument(\n\t\tdocumentName: string,\n\t\trequest: Request,\n\t\tsocketId: string,\n\t\tconnection: ConnectionConfiguration,\n\t\tcontext?: Context,\n\t): Promise<Document> {\n\t\tif (!documentName.trim()) {\n\t\t\tthrow new Error(\"Document name must not be empty\");\n\t\t}\n\n\t\tconst existingLoadingDoc = this.loadingDocuments.get(documentName);\n\n\t\tif (existingLoadingDoc) {\n\t\t\treturn existingLoadingDoc;\n\t\t}\n\n\t\tconst existingDoc = this.documents.get(documentName);\n\t\tif (existingDoc) {\n\t\t\treturn Promise.resolve(existingDoc);\n\t\t}\n\n\t\tconst loadDocPromise = this.loadDocument(\n\t\t\tdocumentName,\n\t\t\trequest,\n\t\t\tsocketId,\n\t\t\tconnection,\n\t\t\tcontext,\n\t\t);\n\n\t\tthis.loadingDocuments.set(documentName, loadDocPromise);\n\n\t\ttry {\n\t\t\tconst doc = await loadDocPromise;\n\t\t\tthis.documents.set(documentName, doc);\n\t\t\tthis.loadingDocuments.delete(documentName);\n\t\t\treturn doc;\n\t\t} catch (e) {\n\t\t\tthis.loadingDocuments.delete(documentName);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\tasync loadDocument(\n\t\tdocumentName: string,\n\t\trequest: Request,\n\t\tsocketId: string,\n\t\tconnectionConfig: ConnectionConfiguration,\n\t\tcontext?: Context,\n\t): Promise<Document> {\n\t\tconst requestHeaders = request.headers;\n\t\tconst requestParameters = getParameters(request);\n\n\t\tconst resolvedContext = (context ?? {}) as Context;\n\n\t\tconst yDocOptions = await this.hooks(\"onCreateDocument\", {\n\t\t\tdocumentName,\n\t\t\trequestHeaders,\n\t\t\trequestParameters,\n\t\t\tconnectionConfig,\n\t\t\tcontext: resolvedContext,\n\t\t\tsocketId,\n\t\t\tinstance: this,\n\t\t});\n\n\t\tconst document = new Document(documentName, {\n\t\t\t...this.configuration.yDocOptions,\n\t\t\t...yDocOptions,\n\t\t});\n\n\t\tconst hookPayload = {\n\t\t\tinstance: this,\n\t\t\tcontext: resolvedContext,\n\t\t\tconnectionConfig,\n\t\t\tdocument,\n\t\t\tdocumentName,\n\t\t\tsocketId,\n\t\t\trequestHeaders,\n\t\t\trequestParameters,\n\t\t};\n\n\t\ttry {\n\t\t\tawait this.hooks(\n\t\t\t\t\"onLoadDocument\",\n\t\t\t\thookPayload,\n\t\t\t\t(loadedDocument: Doc | Uint8ArrayConstructor | undefined) => {\n\t\t\t\t\tif (loadedDocument instanceof Doc) {\n\t\t\t\t\t\tapplyUpdate(document, encodeStateAsUpdate(loadedDocument));\n\t\t\t\t\t} else if (loadedDocument instanceof Uint8Array) {\n\t\t\t\t\t\tapplyUpdate(document, loadedDocument);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.closeConnections(documentName);\n\t\t\tthis.unloadDocument(document);\n\t\t\tthrow e;\n\t\t}\n\n\t\tdocument.isLoading = false;\n\n\t\tdocument.onUpdate(\n\t\t\t(document: Document, origin: unknown, update: Uint8Array) => {\n\t\t\t\tdocument.lastChangeTime = Date.now();\n\n\t\t\t\tthis.handleDocumentUpdate(document, origin, update);\n\t\t\t},\n\t\t);\n\n\t\tawait this.hooks(\"afterLoadDocument\", hookPayload);\n\n\t\tdocument.beforeBroadcastStateless(\n\t\t\t(document: Document, stateless: string) => {\n\t\t\t\tconst hookPayload: beforeBroadcastStatelessPayload = {\n\t\t\t\t\tdocument,\n\t\t\t\t\tdocumentName: document.name,\n\t\t\t\t\tpayload: stateless,\n\t\t\t\t};\n\n\t\t\t\tthis.hooks(\"beforeBroadcastStateless\", hookPayload);\n\t\t\t},\n\t\t);\n\n\t\tdocument.awareness.on(\n\t\t\t\"update\",\n\t\t\t(update: AwarenessUpdate, origin: unknown) => {\n\t\t\t\tthis.hooks(\"onAwarenessUpdate\", {\n\t\t\t\t\tdocument,\n\t\t\t\t\tdocumentName,\n\t\t\t\t\tinstance: this,\n\t\t\t\t\t...update,\n\t\t\t\t\ttransactionOrigin: origin,\n\t\t\t\t\tconnection:\n\t\t\t\t\t\tisTransactionOrigin(origin) && origin.source === \"connection\"\n\t\t\t\t\t\t\t? origin.connection\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\tawareness: document.awareness,\n\t\t\t\t\tstates: awarenessStatesToArray(document.awareness.getStates()),\n\t\t\t\t});\n\t\t\t},\n\t\t);\n\n\t\treturn document;\n\t}\n\n\tstoreDocumentHooks(\n\t\tdocument: Document,\n\t\thookPayload: onStoreDocumentPayload,\n\t\timmediately?: boolean,\n\t) {\n\t\tconst debounceId = `onStoreDocument-${document.name}`;\n\t\treturn this.debouncer.debounce(\n\t\t\tdebounceId,\n\t\t\tasync () => {\n\t\t\t\ttry {\n\t\t\t\t\tawait document.saveMutex.runExclusive(async () => {\n\t\t\t\t\t\tawait this.hooks(\"onStoreDocument\", hookPayload);\n\t\t\t\t\t\tawait this.hooks(\"afterStoreDocument\", hookPayload);\n\t\t\t\t\t});\n\t\t\t\t} catch (error: any) {\n\t\t\t\t\tif (error instanceof SkipFurtherHooksError) {\n\t\t\t\t\t\t// Another extension handled this — proceed to unload\n\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\tif (this.shouldUnloadDocument(document)) {\n\t\t\t\t\t\t\t\tthis.unloadDocument(document);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}, 0);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"Caught error during storeDocumentHooks. Document stays in memory to avoid data loss\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tif (this.shouldUnloadDocument(document)) {\n\t\t\t\t\t\tthis.unloadDocument(document);\n\t\t\t\t\t}\n\t\t\t\t}, 0);\n\t\t\t},\n\t\t\timmediately ? 0 : this.configuration.debounce,\n\t\t\tthis.configuration.maxDebounce,\n\t\t);\n\t}\n\n\t/**\n\t * Run the given hook on all configured extensions.\n\t * Runs the given callback after each hook.\n\t */\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n\thooks<T extends HookName>(\n\t\tname: T,\n\t\tpayload: HookPayloadByName<Context>[T],\n\t\t// biome-ignore lint/complexity/noBannedTypes: <explanation>\n\t\tcallback: Function | null = null,\n\t): Promise<any> {\n\t\tconst { extensions } = this.configuration;\n\n\t\t// create a new `thenable` chain\n\t\t// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve\n\t\tlet chain = Promise.resolve();\n\n\t\textensions\n\t\t\t// get me all extensions which have the given hook\n\t\t\t.filter((extension) => typeof extension[name] === \"function\")\n\t\t\t// run through all the configured hooks\n\t\t\t.forEach((extension) => {\n\t\t\t\tchain = chain\n\t\t\t\t\t.then(() => (extension[name] as any)?.(payload))\n\t\t\t\t\t.catch((error) => {\n\t\t\t\t\t\t// make sure to log error messages\n\t\t\t\t\t\tif (error?.message) {\n\t\t\t\t\t\t\tconsole.error(`[${name}]`, error.message);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t});\n\n\t\t\t\tif (callback) {\n\t\t\t\t\tchain = chain.then((...args: any[]) => callback(...args));\n\t\t\t\t}\n\t\t\t});\n\n\t\treturn chain;\n\t}\n\n\tshouldUnloadDocument(document: Document): boolean {\n\t\tconst hasPendingWork =\n\t\t\tthis.debouncer.isDebounced(`onStoreDocument-${document.name}`) ||\n\t\t\tthis.debouncer.isCurrentlyExecuting(`onStoreDocument-${document.name}`) ||\n\t\t\tdocument.saveMutex.isLocked();\n\n\t\treturn hasPendingWork === false && document.getConnectionsCount() === 0;\n\t}\n\n\tasync unloadDocument(document: Document): Promise<any> {\n\t\tconst documentName = document.name;\n\n\t\tif (!this.shouldUnloadDocument(document)) return;\n\n\t\tif (!this.documents.has(documentName)) return;\n\n\t\tif (this.unloadingDocuments.has(documentName))\n\t\t\treturn this.unloadingDocuments.get(documentName);\n\n\t\t// we need to make sure that the logic runs just once, even if multiple clients disconnect together\n\t\tconst actualUnloadingLogic = async () => {\n\t\t\ttry {\n\t\t\t\tawait this.hooks(\"beforeUnloadDocument\", {\n\t\t\t\t\tinstance: this,\n\t\t\t\t\tdocumentName,\n\t\t\t\t\tdocument,\n\t\t\t\t});\n\t\t\t} catch (e) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// need sync check here as well, to avoid timing problems\n\t\t\tif (!this.shouldUnloadDocument(document)) return;\n\n\t\t\tthis.documents.delete(documentName);\n\t\t\tdocument.destroy();\n\t\t\tawait this.hooks(\"afterUnloadDocument\", { instance: this, documentName });\n\t\t};\n\n\t\tconst unloading = actualUnloadingLogic();\n\n\t\tthis.unloadingDocuments.set(documentName, Promise.resolve(unloading));\n\n\t\tawait unloading;\n\n\t\tthis.unloadingDocuments.delete(documentName);\n\t}\n\n\tasync openDirectConnection(\n\t\tdocumentName: string,\n\t\tcontext?: Context,\n\t): Promise<DirectConnection<Context>> {\n\t\tconst connectionConfig: ConnectionConfiguration = {\n\t\t\tisAuthenticated: true,\n\t\t\treadOnly: false,\n\t\t};\n\n\t\tconst document: Document = await this.createDocument(\n\t\t\tdocumentName,\n\t\t\tnew Request(\"http://localhost\"), // direct connection has no request params\n\t\t\tcrypto.randomUUID(),\n\t\t\tconnectionConfig,\n\t\t\tcontext,\n\t\t);\n\n\t\treturn new DirectConnection<Context>(document, this, context);\n\t}\n}\n","import type {\n\tServer as HTTPServer,\n\tIncomingMessage,\n\tServerResponse,\n} from \"node:http\";\nimport { createServer } from \"node:http\";\nimport type { AddressInfo } from \"node:net\";\nimport type { ListenOptions } from \"node:net\";\nimport crossws from \"crossws/adapters/node\";\nimport kleur from \"kleur\";\nimport meta from \"../package.json\" assert { type: \"json\" };\nimport { Hocuspocus, defaultConfiguration } from \"./Hocuspocus.ts\";\nimport type { Configuration, WebSocketLike, onListenPayload } from \"./types.ts\";\n\nexport interface ServerConfiguration<Context = any>\n\textends Configuration<Context> {\n\tport?: number;\n\taddress?: string;\n\tstopOnSignals?: boolean;\n\t/**\n\t * Options passed to the underlying WebSocket server (ws).\n\t * Supports all ws ServerOptions, e.g. { maxPayload: 1024 * 1024 }\n\t */\n\twebsocketOptions?: Record<string, any>;\n}\n\nexport const defaultServerConfiguration = {\n\tport: 80,\n\taddress: \"0.0.0.0\",\n\tstopOnSignals: true,\n};\n\nexport class Server<Context = any> {\n\thttpServer: HTTPServer;\n\n\tprivate crossws: ReturnType<typeof crossws>;\n\n\thocuspocus: Hocuspocus<Context>;\n\n\tconfiguration: ServerConfiguration<Context> = {\n\t\t...defaultConfiguration,\n\t\t...defaultServerConfiguration,\n\t\textensions: [],\n\t};\n\n\tconstructor(configuration?: Partial<ServerConfiguration<Context>>) {\n\t\tif (configuration) {\n\t\t\tthis.configuration = {\n\t\t\t\t...this.configuration,\n\t\t\t\t...configuration,\n\t\t\t};\n\t\t}\n\n\t\tthis.hocuspocus = new Hocuspocus(this.configuration);\n\t\tthis.hocuspocus.server = this;\n\n\t\tthis.httpServer = createServer(this.requestHandler);\n\t\tthis.crossws = crossws({\n\t\t\tserverOptions: this.configuration.websocketOptions,\n\t\t\thooks: {\n\t\t\t\topen: (peer) => {\n\t\t\t\t\tconst clientConnection = this.hocuspocus.handleConnection(\n\t\t\t\t\t\tpeer.websocket as unknown as WebSocketLike,\n\t\t\t\t\t\tpeer.request as Request,\n\t\t\t\t\t);\n\t\t\t\t\t(peer as any)._hocuspocus = clientConnection;\n\t\t\t\t},\n\t\t\t\tmessage: (peer, message) => {\n\t\t\t\t\t(peer as any)._hocuspocus?.handleMessage(message.uint8Array());\n\t\t\t\t},\n\t\t\t\tclose: (peer, event) => {\n\t\t\t\t\t(peer as any)._hocuspocus?.handleClose({\n\t\t\t\t\t\tcode: event.code,\n\t\t\t\t\t\treason: event.reason,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\terror: (peer, error) => {\n\t\t\t\t\tconsole.error(\"WebSocket error for peer:\", peer.id);\n\t\t\t\t\tconsole.error(error);\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\n\t\tthis.setupHttpUpgrade();\n\t}\n\n\tprivate setupHttpUpgrade = () => {\n\t\tthis.httpServer.on(\"upgrade\", async (request, socket, head) => {\n\t\t\ttry {\n\t\t\t\tawait this.hocuspocus.hooks(\"onUpgrade\", {\n\t\t\t\t\trequest,\n\t\t\t\t\tsocket,\n\t\t\t\t\thead,\n\t\t\t\t\tinstance: this.hocuspocus,\n\t\t\t\t});\n\n\t\t\t\t// Let crossws handle the WebSocket upgrade\n\t\t\t\tthis.crossws.handleUpgrade(request, socket, head);\n\t\t\t} catch (error) {\n\t\t\t\t// if a hook rejects and the error is empty, do nothing\n\t\t\t\t// this is only meant to prevent later hooks and the\n\t\t\t\t// default handler to do something. if a error is present\n\t\t\t\t// just rethrow it\n\t\t\t\tif (error) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t};\n\n\trequestHandler = async (\n\t\trequest: IncomingMessage,\n\t\tresponse: ServerResponse,\n\t) => {\n\t\ttry {\n\t\t\tawait this.hocuspocus.hooks(\"onRequest\", {\n\t\t\t\trequest,\n\t\t\t\tresponse,\n\t\t\t\tinstance: this.hocuspocus,\n\t\t\t});\n\n\t\t\t// default response if all prior hooks don't interfere\n\t\t\tresponse.writeHead(200, { \"Content-Type\": \"text/plain\" });\n\t\t\tresponse.end(\"Welcome to Hocuspocus!\");\n\t\t} catch (error) {\n\t\t\t// if a hook rejects and the error is empty, do nothing\n\t\t\t// this is only meant to prevent later hooks and the\n\t\t\t// default handler to do something. if a error is present\n\t\t\t// just rethrow it\n\t\t\tif (error) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t};\n\n\tasync listen(\n\t\tport?: number,\n\t\tcallback: any = null,\n\t): Promise<Hocuspocus<Context>> {\n\t\tif (port) {\n\t\t\tthis.configuration.port = port;\n\t\t}\n\n\t\tif (typeof callback === \"function\") {\n\t\t\tthis.hocuspocus.configuration.extensions.push({\n\t\t\t\tonListen: callback,\n\t\t\t});\n\t\t}\n\n\t\tif (this.configuration.stopOnSignals) {\n\t\t\tconst signalHandler = async () => {\n\t\t\t\tawait this.destroy();\n\t\t\t\tprocess.exit(0);\n\t\t\t};\n\n\t\t\tprocess.on(\"SIGINT\", signalHandler);\n\t\t\tprocess.on(\"SIGQUIT\", signalHandler);\n\t\t\tprocess.on(\"SIGTERM\", signalHandler);\n\t\t}\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n\t\treturn new Promise((resolve: Function, reject: Function) => {\n\t\t\tthis.httpServer.listen(\n\t\t\t\t{\n\t\t\t\t\tport: this.configuration.port,\n\t\t\t\t\taddress: this.configuration.address,\n\t\t\t\t} as ListenOptions,\n\t\t\t\tasync () => {\n\t\t\t\t\tif (\n\t\t\t\t\t\t!this.configuration.quiet &&\n\t\t\t\t\t\tString(process.env.NODE_ENV) !== \"testing\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.showStartScreen();\n\t\t\t\t\t}\n\n\t\t\t\t\tconst onListenPayload = {\n\t\t\t\t\t\tinstance: this.hocuspocus,\n\t\t\t\t\t\tconfiguration: this.configuration,\n\t\t\t\t\t\tport: this.address.port,\n\t\t\t\t\t} as onListenPayload;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait this.hocuspocus.hooks(\"onListen\", onListenPayload);\n\t\t\t\t\t\tresolve(this.hocuspocus);\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\treject(e);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\t}\n\n\tget address(): AddressInfo {\n\t\treturn (this.httpServer.address() || {\n\t\t\tport: this.configuration.port,\n\t\t\taddress: this.configuration.address,\n\t\t\tfamily: \"IPv4\",\n\t\t}) as AddressInfo;\n\t}\n\n\tasync destroy(): Promise<void> {\n\t\tawait new Promise<void>((resolve) => {\n\t\t\tthis.httpServer.close();\n\n\t\t\ttry {\n\t\t\t\tthis.configuration.extensions.push({\n\t\t\t\t\tasync afterUnloadDocument({ instance }) {\n\t\t\t\t\t\tif (instance.getDocumentsCount() === 0) resolve();\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\t// Close all existing connections - this will trigger the close hook\n\t\t\t\tif (this.hocuspocus.getDocumentsCount() === 0) resolve();\n\n\t\t\t\tthis.hocuspocus.closeConnections();\n\n\t\t\t\t// Flush any remaining debounced stores so documents unload\n\t\t\t\t// promptly, even when unloadImmediately is false.\n\t\t\t\tthis.hocuspocus.flushPendingStores();\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(error);\n\t\t\t}\n\t\t});\n\n\t\tawait this.hocuspocus.hooks(\"onDestroy\", { instance: this.hocuspocus });\n\t}\n\n\tget URL(): string {\n\t\treturn `${this.configuration.address}:${this.address.port}`;\n\t}\n\n\tget webSocketURL(): string {\n\t\treturn `ws://${this.URL}`;\n\t}\n\n\tget httpURL(): string {\n\t\treturn `http://${this.URL}`;\n\t}\n\n\tprivate showStartScreen() {\n\t\tconst name = this.configuration.name ? ` (${this.configuration.name})` : \"\";\n\n\t\tconsole.log();\n\t\tconsole.log(\n\t\t\t`  ${kleur.cyan(`Hocuspocus v${meta.version}${name}`)}${kleur.green(\" running at:\")}`,\n\t\t);\n\t\tconsole.log();\n\n\t\tconsole.log(`  > HTTP: ${kleur.cyan(`${this.httpURL}`)}`);\n\t\tconsole.log(`  > WebSocket: ${this.webSocketURL}`);\n\n\t\tconst extensions = this.configuration?.extensions\n\t\t\t.map((extension) => {\n\t\t\t\treturn extension.extensionName ?? extension.constructor?.name;\n\t\t\t})\n\t\t\t.filter((name) => name)\n\t\t\t.filter((name) => name !== \"Object\");\n\n\t\tif (!extensions.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tconsole.log();\n\t\tconsole.log(\"  Extensions:\");\n\n\t\textensions.forEach((name) => {\n\t\t\tconsole.log(`  - ${name}`);\n\t\t});\n\n\t\tconsole.log();\n\t\tconsole.log(`  ${kleur.green(\"Ready.\")}`);\n\t\tconsole.log();\n\t}\n}\n"],"mappings":"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"}