@liveblocks/server 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/decoders/ClientMsg.ts","../src/decoders/jsonYolo.ts","../src/decoders/Op.ts","../src/decoders/y-types.ts","../src/formats/LossyJson.ts","../src/formats/NodeStream.ts","../src/formats/PlainLson.ts","../src/makeInMemorySnapshot.ts","../src/lib/DefaultMap.ts","../src/lib/NestedMap.ts","../src/MetadataDB.ts","../src/protocol/ProtocolVersion.ts","../src/Room.ts","../src/lib/Logger.ts","../src/plugins/InMemoryDriver.ts","../src/lib/text.ts","../src/Storage.ts","../src/YjsStorage.ts","../src/lib/tryCatch.ts","../src/lib/UniqueMap.ts","../src/utils.ts"],"sourcesContent":["/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type { Json, JsonObject } from \"@liveblocks/core\";\nimport { ClientMsgCode } from \"@liveblocks/core\";\nimport type { Decoder } from \"decoders\";\nimport {\n array,\n boolean,\n constant,\n number,\n object,\n optional,\n string,\n taggedUnion,\n} from \"decoders\";\n\nimport type {\n BroadcastEventClientMsg,\n ClientMsg,\n FetchStorageClientMsg,\n FetchYDocClientMsg,\n UpdatePresenceClientMsg,\n UpdateStorageClientMsg,\n UpdateYDocClientMsg,\n} from \"~/protocol\";\n\nimport { jsonObjectYolo, jsonYolo } from \"./jsonYolo\";\nimport { op } from \"./Op\";\nimport type { YUpdate, YVector } from \"./y-types\";\nimport { guidDecoder } from \"./y-types\";\n\nconst updatePresenceClientMsg: Decoder<UpdatePresenceClientMsg<JsonObject>> =\n object({\n type: constant(ClientMsgCode.UPDATE_PRESENCE),\n data: jsonObjectYolo,\n targetActor: optional(number),\n });\n\nconst broadcastEventClientMsg: Decoder<BroadcastEventClientMsg<Json>> = object({\n type: constant(ClientMsgCode.BROADCAST_EVENT),\n event: jsonYolo,\n});\n\nconst fetchStorageClientMsg: Decoder<FetchStorageClientMsg> = object({\n type: constant(ClientMsgCode.FETCH_STORAGE),\n});\n\nconst updateStorageClientMsg: Decoder<UpdateStorageClientMsg> = object({\n type: constant(ClientMsgCode.UPDATE_STORAGE),\n ops: array(op),\n});\n\nconst fetchYDocClientMsg: Decoder<FetchYDocClientMsg> = object({\n type: constant(ClientMsgCode.FETCH_YDOC),\n vector: string.refineType<YVector>(),\n guid: optional(guidDecoder), // Don't specify to update the root doc\n v2: optional(boolean),\n});\n\nconst updateYDocClientMsg: Decoder<UpdateYDocClientMsg> = object({\n type: constant(ClientMsgCode.UPDATE_YDOC),\n update: string.refineType<YUpdate>(),\n guid: optional(guidDecoder), // Don't specify to update the root doc\n v2: optional(boolean),\n});\n\nexport const clientMsgDecoder: Decoder<ClientMsg<JsonObject, Json>> =\n taggedUnion(\"type\", {\n [ClientMsgCode.UPDATE_PRESENCE]: updatePresenceClientMsg,\n [ClientMsgCode.BROADCAST_EVENT]: broadcastEventClientMsg,\n [ClientMsgCode.FETCH_STORAGE]: fetchStorageClientMsg,\n [ClientMsgCode.UPDATE_STORAGE]: updateStorageClientMsg,\n [ClientMsgCode.FETCH_YDOC]: fetchYDocClientMsg,\n [ClientMsgCode.UPDATE_YDOC]: updateYDocClientMsg,\n }).describe(\"Must be a valid client message\");\n\nexport const transientClientMsgDecoder: Decoder<ClientMsg<JsonObject, Json>> =\n taggedUnion(\"type\", {\n // [ClientMsgCode.UPDATE_PRESENCE]: updatePresenceClientMsg,\n // [ClientMsgCode.BROADCAST_EVENT]: broadcastEventClientMsg,\n // [ClientMsgCode.FETCH_STORAGE]: fetchStorageClientMsg,\n [ClientMsgCode.UPDATE_STORAGE]: updateStorageClientMsg,\n // [ClientMsgCode.FETCH_YDOC]: fetchYDocClientMsg,\n // [ClientMsgCode.UPDATE_YDOC]: updateYDocClientMsg,\n }).describe(\"Must be a valid transient client message\");\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type { Json, JsonObject } from \"@liveblocks/core\";\nimport type { Decoder } from \"decoders\";\nimport { unknown } from \"decoders\";\n\n/**\n * Drop-in replacement for the `json` decoder from the decoders standard\n * library, but implemented as a no-op. This is, of course, only safe to use in\n * contexts where you know that the input already is valid JSON.\n *\n * You know this for sure, for example, if you're decoding the result of\n * a `JSON.parse()` call.\n *\n * Done for performance reasons!\n */\nexport const jsonYolo: Decoder<Json> = unknown as Decoder<Json>;\n\n/**\n * Drop-in replacement for the `jsonObject` decoder from the decoders standard\n * library, but implemented as just a check for plain old JavaScript object.\n * This is, of course, only safe to use in contexts where you know that the\n * input already is valid JSON.\n *\n * You know this for sure, for example, if you're decoding the result of\n * a `JSON.parse()` call.\n *\n * Done for performance reasons!\n */\nexport const jsonObjectYolo: Decoder<JsonObject> = jsonYolo.refine(\n (value): value is JsonObject =>\n value !== null && typeof value === \"object\" && !Array.isArray(value),\n \"Must be JSON object\"\n);\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { OpCode } from \"@liveblocks/core\";\nimport type { Decoder } from \"decoders\";\nimport { constant, object, optional, string, taggedUnion } from \"decoders\";\n\nimport type {\n ClientWireOp,\n CreateListOp,\n CreateMapOp,\n CreateObjectOp,\n CreateRegisterOp,\n DeleteCrdtOp,\n DeleteObjectKeyOp,\n SetParentKeyOp,\n UpdateObjectOp,\n} from \"~/protocol\";\n\nimport { jsonObjectYolo, jsonYolo } from \"./jsonYolo\";\n\ntype HasOpId = { opId: string };\n\nconst updateObjectOp: Decoder<UpdateObjectOp & HasOpId> = object({\n type: constant(OpCode.UPDATE_OBJECT),\n opId: string,\n id: string,\n data: jsonObjectYolo,\n});\n\nconst createObjectOp: Decoder<CreateObjectOp & HasOpId> = object({\n type: constant(OpCode.CREATE_OBJECT),\n opId: string,\n id: string,\n parentId: string,\n parentKey: string,\n data: jsonObjectYolo,\n intent: optional(constant(\"set\")),\n deletedId: optional(string),\n});\n\nconst createListOp: Decoder<CreateListOp & HasOpId> = object({\n type: constant(OpCode.CREATE_LIST),\n opId: string,\n id: string,\n parentId: string,\n parentKey: string,\n intent: optional(constant(\"set\")),\n deletedId: optional(string),\n});\n\nconst createMapOp: Decoder<CreateMapOp & HasOpId> = object({\n type: constant(OpCode.CREATE_MAP),\n opId: string,\n id: string,\n parentId: string,\n parentKey: string,\n intent: optional(constant(\"set\")),\n deletedId: optional(string),\n});\n\nconst createRegisterOp: Decoder<CreateRegisterOp & HasOpId> = object({\n type: constant(OpCode.CREATE_REGISTER),\n opId: string,\n id: string,\n parentId: string,\n parentKey: string,\n data: jsonYolo,\n intent: optional(constant(\"set\")),\n deletedId: optional(string),\n});\n\nconst deleteCrdtOp: Decoder<DeleteCrdtOp & HasOpId> = object({\n type: constant(OpCode.DELETE_CRDT),\n opId: string,\n id: string,\n});\n\nconst setParentKeyOp: Decoder<SetParentKeyOp & HasOpId> = object({\n type: constant(OpCode.SET_PARENT_KEY),\n opId: string,\n id: string,\n parentKey: string,\n});\n\nconst deleteObjectKeyOp: Decoder<DeleteObjectKeyOp & HasOpId> = object({\n type: constant(OpCode.DELETE_OBJECT_KEY),\n opId: string,\n id: string,\n key: string,\n});\n\nexport const op: Decoder<ClientWireOp> = taggedUnion(\"type\", {\n [OpCode.UPDATE_OBJECT]: updateObjectOp,\n [OpCode.CREATE_OBJECT]: createObjectOp,\n [OpCode.CREATE_LIST]: createListOp,\n [OpCode.CREATE_MAP]: createMapOp,\n [OpCode.CREATE_REGISTER]: createRegisterOp,\n [OpCode.DELETE_CRDT]: deleteCrdtOp,\n [OpCode.SET_PARENT_KEY]: setParentKeyOp,\n [OpCode.DELETE_OBJECT_KEY]: deleteObjectKeyOp,\n});\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type { Brand } from \"@liveblocks/core\";\nimport { uuid } from \"decoders\";\n\n/**\n * A guid, a unique identifier for a Yjs sub document.\n */\nexport type Guid = Brand<string, \"Guid\">;\n\nexport const guidDecoder = uuid.refineType<Guid>();\n\nexport const ROOT_YDOC_ID = \"root\";\nexport type YDocId = typeof ROOT_YDOC_ID | Guid /* unique ID for subdoc */;\n\n/**\n * Any string that is a valid base64 encoded YJS update.\n */\nexport type YUpdate = Brand<string, \"YUpdate\">;\n\n/**\n * Any string that is a valid base64 encoded YJS state vector.\n */\nexport type YVector = Brand<string, \"YVector\">;\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type { Json, JsonObject } from \"@liveblocks/core\";\nimport { CrdtType } from \"@liveblocks/core\";\n\nimport type { IReadableSnapshot } from \"~/interfaces\";\n\n// ---------------------------------------------------------------------------\n// Non-streaming version\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a storage snapshot to a simple JSON representation, returning a\n * full in-memory JsonObject. Faster than snapshotToLossyJson_lazy for\n * small/medium documents because the result can be passed straight to\n * JSON.stringify(). This format is lossy — the original storage structure\n * cannot be reconstructed from it, so it's output-only.\n */\nexport function snapshotToLossyJson_eager(\n snapshot: IReadableSnapshot\n): JsonObject {\n try {\n return buildObject(snapshot, \"root\", snapshot.get_root().data);\n } finally {\n snapshot.destroy();\n }\n}\n\nfunction buildNode(snapshot: IReadableSnapshot, id: string): Json {\n const node = snapshot.get_node(id);\n if (node.type === CrdtType.OBJECT) {\n return buildObject(snapshot, id, node.data);\n } else if (node.type === CrdtType.LIST) {\n return buildList(snapshot, id);\n } else if (node.type === CrdtType.MAP) {\n return buildMap(snapshot, id);\n } else {\n return node.data;\n }\n}\n\nfunction buildObject(\n snapshot: IReadableSnapshot,\n id: string,\n staticData: JsonObject\n): JsonObject {\n const data = Object.assign(Object.create(null), staticData) as JsonObject;\n for (const [key, childId] of snapshot.iter_children(id)) {\n data[key] = buildNode(snapshot, childId);\n }\n return data;\n}\n\nfunction buildList(snapshot: IReadableSnapshot, id: string): Json[] {\n const data: Json[] = [];\n for (const [_, childId] of snapshot.iter_children(id)) {\n data.push(buildNode(snapshot, childId));\n }\n return data;\n}\n\nfunction buildMap(snapshot: IReadableSnapshot, id: string): JsonObject {\n const data = Object.create(null) as JsonObject;\n for (const [key, childId] of snapshot.iter_children(id)) {\n data[key] = buildNode(snapshot, childId);\n }\n return data;\n}\n\n// ---------------------------------------------------------------------------\n// Streaming version\n// ---------------------------------------------------------------------------\n\n// Generator-of-strings type alias for brevity of signatures\ntype StringGen = Generator<string, void, never>;\n\n/**\n * Serialize a storage snapshot to a simple JSON representation. This format is\n * easy to consume but lossy — the original storage structure cannot be\n * reconstructed from it, so it's an output-only format. Slower than\n * snapshotToLossyJson_eager but can stream documents that don't fit entirely\n * in memory.\n *\n * This generator yields text chunks that together, when concatenated, form the\n * output JSON document.\n */\nexport function* snapshotToLossyJson_lazy(\n snapshot: IReadableSnapshot\n): StringGen {\n try {\n const staticJson = JSON.stringify(snapshot.get_root().data).slice(1, -1);\n yield* emitObject(snapshot, \"root\", staticJson);\n } finally {\n snapshot.destroy();\n }\n}\n\nfunction* emit(snapshot: IReadableSnapshot, id: string): StringGen {\n const node = snapshot.get_node(id);\n if (node.type === CrdtType.OBJECT) {\n yield* emitObject(snapshot, id, JSON.stringify(node.data).slice(1, -1));\n } else if (node.type === CrdtType.LIST) {\n yield* emitList(snapshot, id);\n } else if (node.type === CrdtType.MAP) {\n yield* emitMap(snapshot, id);\n } else if (node.type === CrdtType.REGISTER) {\n yield JSON.stringify(node.data);\n }\n}\n\n/**\n * @param staticJson - The object's static (non-CRDT) properties as a raw JSON\n * string without the surrounding braces, e.g. `\"foo\":1,\"bar\":\"hi\"`.\n *\n * Children are emitted _after_ the static properties. If a child key\n * collides with a static key (which shouldn't normally happen, but\n * defensively), the child wins because JSON.parse keeps the last value\n * for duplicate keys.\n */\nfunction* emitObject(\n snapshot: IReadableSnapshot,\n id: string,\n staticJson: string\n): StringGen {\n let comma = staticJson.length > 0;\n\n yield \"{\";\n yield staticJson;\n\n for (const [key, childId] of snapshot.iter_children(id)) {\n if (comma) yield \",\";\n else comma = true;\n\n yield `${JSON.stringify(key)}:`;\n yield* emit(snapshot, childId);\n }\n yield \"}\";\n}\n\nfunction* emitList(snapshot: IReadableSnapshot, id: string): StringGen {\n let comma = false;\n\n yield \"[\";\n for (const [_, childId] of snapshot.iter_children(id)) {\n if (comma) yield \",\";\n else comma = true;\n yield* emit(snapshot, childId);\n }\n yield \"]\";\n}\n\nfunction* emitMap(snapshot: IReadableSnapshot, id: string): StringGen {\n let comma = false;\n\n yield \"{\";\n for (const [key, childId] of snapshot.iter_children(id)) {\n if (comma) yield \",\";\n else comma = true;\n\n yield `${JSON.stringify(key)}:`;\n yield* emit(snapshot, childId);\n }\n yield \"}\";\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type { StorageNode } from \"@liveblocks/core\";\n\nimport type { IReadableSnapshot } from \"~/interfaces\";\n\n/**\n * Yield all nodes from a snapshot as [id, crdt] tuples.\n * Destroys the snapshot when done (or aborted).\n */\nexport function* snapshotToNodeStream(\n snapshot: IReadableSnapshot\n): Generator<StorageNode, void, never> {\n try {\n yield* snapshot.iter_all();\n } finally {\n snapshot.destroy();\n }\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type {\n JsonObject,\n ObjectStorageNode,\n PlainLson,\n PlainLsonFields,\n PlainLsonList,\n PlainLsonMap,\n PlainLsonObject,\n RootStorageNode,\n SerializedList,\n StorageNode,\n} from \"@liveblocks/core\";\nimport {\n assertNever,\n CrdtType,\n isJsonObject,\n makePosition,\n} from \"@liveblocks/core\";\n\nimport type { IReadableSnapshot } from \"~/interfaces\";\n\nconst SERVER_INIT_OP_PREFIX = \"si\";\n\nfunction generateId(state: { clock: number }) {\n return `${SERVER_INIT_OP_PREFIX}:${state.clock++}`;\n}\n\nfunction isSpecialPlainLsonValue(\n value: PlainLson\n): value is PlainLsonObject | PlainLsonMap | PlainLsonList {\n return isJsonObject(value) && value.liveblocksType !== undefined;\n}\n\n/**\n * Generator that yields NodeTuples for a JSON value.\n * Always yields parent nodes before their children.\n */\nfunction* iterJson(\n key: string,\n data: PlainLson,\n parent: StorageNode,\n state: { clock: number }\n): Generator<StorageNode, void, undefined> {\n if (isSpecialPlainLsonValue(data)) {\n switch (data.liveblocksType) {\n case \"LiveObject\":\n yield* iterObjectInner(key, data.data, parent, state);\n return;\n\n case \"LiveList\":\n yield* iterList(key, data.data, parent, state);\n return;\n\n case \"LiveMap\":\n yield* iterMap(key, data.data, parent, state);\n return;\n\n // istanbul ignore next\n default:\n assertNever(data, \"Unknown `liveblocksType` field\");\n }\n } else {\n yield [\n generateId(state),\n {\n type: CrdtType.REGISTER,\n data,\n parentId: parent[0],\n parentKey: key,\n },\n ];\n }\n}\n\n/**\n * Generator that yields NodeTuples for a LiveMap.\n * Yields the map node first, then its children.\n */\nfunction* iterMap(\n key: string,\n map: PlainLsonFields,\n parent: StorageNode,\n state: { clock: number }\n): Generator<StorageNode, void, undefined> {\n const mapTuple: StorageNode = [\n generateId(state),\n { type: CrdtType.MAP, parentId: parent[0], parentKey: key },\n ];\n\n // Yield the map node first (parent before children)\n yield mapTuple;\n\n // Then yield all children\n for (const [subKey, subValue] of Object.entries(map)) {\n yield* iterJson(subKey, subValue, mapTuple, state);\n }\n}\n\n/**\n * Generator that yields NodeTuples for a LiveList.\n * Yields the list node first, then its children.\n */\nfunction* iterList(\n key: string,\n list: PlainLson[],\n parent: StorageNode,\n state: { clock: number }\n): Generator<StorageNode, void, undefined> {\n const id = generateId(state);\n const crdt: SerializedList = {\n type: CrdtType.LIST,\n parentId: parent[0],\n parentKey: key,\n };\n const listTuple: StorageNode = [id, crdt];\n\n // Yield the list node first (parent before children)\n yield listTuple;\n\n // Then yield all children\n let position = makePosition();\n for (const subValue of list) {\n yield* iterJson(position, subValue, listTuple, state);\n position = makePosition(position);\n }\n}\n\n/**\n * Generator that yields NodeTuples for a LiveObject.\n * Yields the object node first, then its children.\n *\n * Note: The object's data field is populated with non-special values\n * (primitives, arrays, plain objects), while special values (LiveObject,\n * LiveList, LiveMap) are yielded as separate nodes.\n */\nfunction* iterObjectInner(\n key: string,\n value: PlainLsonFields,\n parent: StorageNode | null,\n state: { clock: number }\n): Generator<StorageNode, void, undefined> {\n // First pass: collect non-special data and identify special children\n const data: JsonObject = {};\n const specialChildren: Array<[string, PlainLson]> = [];\n\n for (const [subKey, subValue] of Object.entries(value)) {\n if (isSpecialPlainLsonValue(subValue)) {\n specialChildren.push([subKey, subValue]);\n } else {\n data[subKey] = subValue;\n }\n }\n\n // Create the object tuple with collected data\n const objectTuple: RootStorageNode | ObjectStorageNode =\n parent !== null\n ? [\n generateId(state),\n {\n type: CrdtType.OBJECT,\n data,\n parentId: parent[0],\n parentKey: key,\n },\n ]\n : [\"root\", { type: CrdtType.OBJECT, data }];\n\n // Yield the object node first (parent before children)\n yield objectTuple;\n\n // Then yield all special children\n for (const [subKey, subValue] of specialChildren) {\n yield* iterJson(subKey, subValue, objectTuple, state);\n }\n}\n\n/**\n * Transform a \"Plain LSON\" document to a lazy NodeStream. Used to initialize\n * the storage with a predefined state.\n * Always emits parent nodes before their children.\n */\nexport function* plainLsonToNodeStream(\n root: PlainLsonObject\n): Generator<StorageNode, void, undefined> {\n const state = { clock: 1 };\n yield* iterObjectInner(\"root\", root.data, null, state);\n}\n\n// ---------------------------------------------------------------------------\n// Non-streaming serialization: builds a full PlainLsonObject in memory.\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a storage snapshot to \"Plain LSON\" format, returning a full\n * in-memory PlainLsonObject. Faster than snapshotToPlainLson_lazy for\n * small/medium documents because the result can be passed straight to\n * JSON.stringify().\n */\nexport function snapshotToPlainLson_eager(\n snapshot: IReadableSnapshot\n): PlainLsonObject {\n try {\n return buildObject(snapshot, \"root\", snapshot.get_root().data);\n } finally {\n snapshot.destroy();\n }\n}\n\nfunction buildNode(snapshot: IReadableSnapshot, id: string): PlainLson {\n const node = snapshot.get_node(id);\n if (node.type === CrdtType.OBJECT) {\n return buildObject(snapshot, id, node.data);\n } else if (node.type === CrdtType.LIST) {\n return buildList(snapshot, id);\n } else if (node.type === CrdtType.MAP) {\n return buildMap(snapshot, id);\n } else {\n return node.data;\n }\n}\n\nfunction buildObject(\n snapshot: IReadableSnapshot,\n id: string,\n staticData: JsonObject\n): PlainLsonObject {\n // Static data values are Json, which is a subset of PlainLson\n const data: PlainLsonFields = Object.assign(\n Object.create(null),\n staticData\n ) as PlainLsonFields;\n for (const [key, childId] of snapshot.iter_children(id)) {\n data[key] = buildNode(snapshot, childId);\n }\n return { liveblocksType: \"LiveObject\", data };\n}\n\nfunction buildList(snapshot: IReadableSnapshot, id: string): PlainLsonList {\n const data: PlainLson[] = [];\n for (const [_, childId] of snapshot.iter_children(id)) {\n data.push(buildNode(snapshot, childId));\n }\n return { liveblocksType: \"LiveList\", data };\n}\n\nfunction buildMap(snapshot: IReadableSnapshot, id: string): PlainLsonMap {\n const data = Object.create(null) as PlainLsonFields;\n for (const [key, childId] of snapshot.iter_children(id)) {\n data[key] = buildNode(snapshot, childId);\n }\n return { liveblocksType: \"LiveMap\", data };\n}\n\n// ---------------------------------------------------------------------------\n// Streaming serialization: yields string chunks that concatenate to JSON.\n// ---------------------------------------------------------------------------\n\n// Generator-of-strings type alias for brevity of signatures\ntype StringGen = Generator<string, void, never>;\n\n/**\n * Serialize a storage snapshot to \"Plain LSON\" format. Yields string chunks\n * that, when concatenated, form a valid JSON string representing the storage\n * document. Slower than snapshotToPlainLson_eager but can stream documents\n * that don't fit entirely in memory.\n */\nexport function* snapshotToPlainLson_lazy(\n snapshot: IReadableSnapshot\n): StringGen {\n try {\n const staticJson = JSON.stringify(snapshot.get_root().data).slice(1, -1);\n yield* emitObject(snapshot, \"root\", staticJson);\n } finally {\n snapshot.destroy();\n }\n}\n\nfunction* emit(snapshot: IReadableSnapshot, id: string): StringGen {\n const node = snapshot.get_node(id);\n if (node.type === CrdtType.OBJECT) {\n yield* emitObject(snapshot, id, JSON.stringify(node.data).slice(1, -1));\n } else if (node.type === CrdtType.LIST) {\n yield* emitList(snapshot, id);\n } else if (node.type === CrdtType.MAP) {\n yield* emitMap(snapshot, id);\n } else if (node.type === CrdtType.REGISTER) {\n yield JSON.stringify(node.data);\n }\n}\n\n/**\n * @param staticJson - The object's static (non-CRDT) properties as a raw JSON\n * string without the surrounding braces, e.g. `\"foo\":1,\"bar\":\"hi\"`.\n *\n * Children are emitted _after_ the static properties. If a child key\n * collides with a static key (which shouldn't normally happen, but\n * defensively), the child wins because JSON.parse keeps the last value\n * for duplicate keys.\n */\nfunction* emitObject(\n snapshot: IReadableSnapshot,\n id: string,\n staticJson: string\n): StringGen {\n let comma = staticJson.length > 0;\n\n yield '{\"liveblocksType\":\"LiveObject\",\"data\":{';\n yield staticJson;\n\n for (const [key, childId] of snapshot.iter_children(id)) {\n if (comma) yield \",\";\n else comma = true;\n\n yield `${JSON.stringify(key)}:`;\n yield* emit(snapshot, childId);\n }\n yield \"}}\";\n}\n\nfunction* emitList(snapshot: IReadableSnapshot, id: string): StringGen {\n let comma = false;\n\n yield '{\"liveblocksType\":\"LiveList\",\"data\":[';\n for (const [_, childId] of snapshot.iter_children(id)) {\n if (comma) yield \",\";\n else comma = true;\n yield* emit(snapshot, childId);\n }\n yield \"]}\";\n}\n\nfunction* emitMap(snapshot: IReadableSnapshot, id: string): StringGen {\n let comma = false;\n\n yield '{\"liveblocksType\":\"LiveMap\",\"data\":{';\n for (const [key, childId] of snapshot.iter_children(id)) {\n if (comma) yield \",\";\n else comma = true;\n\n yield `${JSON.stringify(key)}:`;\n yield* emit(snapshot, childId);\n }\n yield \"}}\";\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type {\n NodeMap,\n NodeStream,\n SerializedChild,\n SerializedCrdt,\n SerializedRootObject,\n StorageNode,\n} from \"@liveblocks/core\";\nimport { CrdtType, isRootStorageNode, nn } from \"@liveblocks/core\";\n\nimport type { IReadableSnapshot } from \"~/interfaces\";\nimport { NestedMap } from \"~/lib/NestedMap\";\n\n/**\n * Create a basic in-memory snapshot from a set of storage nodes.\n *\n * Takes a copy of the provided nodes, so the snapshot is isolated from\n * subsequent mutations to the source.\n */\nexport function makeInMemorySnapshot(\n values: NodeMap | NodeStream\n): IReadableSnapshot {\n const map: NodeMap = new Map<string, SerializedCrdt>(values as NodeStream);\n\n if (!map.has(\"root\")) {\n map.set(\"root\", { type: CrdtType.OBJECT, data: {} });\n }\n\n // Collect child entries, sort by (parentId, parentKey), then insert into\n // the revMap so that entriesAt() returns children in parent_key order\n // without needing to re-sort on every iter_children call.\n const entries: Array<[parentId: string, parentKey: string, id: string]> = [];\n const nodeStream = map as NodeStream;\n for (const node of nodeStream) {\n if (isRootStorageNode(node)) continue;\n const [id, crdt] = node;\n entries.push([crdt.parentId, crdt.parentKey, id]);\n }\n entries.sort((a, b) =>\n a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0\n );\n\n const revMap = new NestedMap<string, string, string>();\n for (const [parentId, parentKey, id] of entries) {\n revMap.set(parentId, parentKey, id);\n }\n\n function get_node(id: string): SerializedChild {\n return nn(map.get(id), `Node not found: ${id}`) as SerializedChild;\n }\n\n return {\n get_root: () =>\n nn(\n map.get(\"root\"),\n \"Root not found\"\n ) as SerializedCrdt as SerializedRootObject,\n get_node,\n iter_children: (nodeId) => revMap.entriesAt(nodeId),\n iter_all: () => map as Iterable<StorageNode>,\n destroy() {\n map.clear();\n revMap.clear();\n },\n };\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { raise } from \"@liveblocks/core\";\n\n/**\n * Like ES6 map, but takes a default (factory) function which will be used\n * to create entries for missing keys on the fly.\n *\n * Useful for code like:\n *\n * const map = new DefaultMap(() => []);\n * map.getOrCreate('foo').push('hello');\n * map.getOrCreate('foo').push('world');\n * map.getOrCreate('foo')\n * // ['hello', 'world']\n *\n */\nexport class DefaultMap<K, V> extends Map<K, V> {\n #defaultFn?: (key: K) => V;\n\n /**\n * If the default function is not provided to the constructor, it has to be\n * provided in each .getOrCreate() call individually.\n */\n constructor(\n defaultFn?: (key: K) => V,\n entries?: readonly (readonly [K, V])[] | null\n ) {\n super(entries);\n this.#defaultFn = defaultFn;\n }\n\n /**\n * Gets the value at the given key, or creates it.\n *\n * Difference from normal Map: if the key does not exist, it will be created\n * on the fly using the factory function, and that value will get returned\n * instead of `undefined`.\n */\n getOrCreate(key: K, defaultFn?: (key: K) => V): V {\n if (super.has(key)) {\n // eslint-disable-next-line no-restricted-syntax\n return super.get(key)!;\n } else {\n const fn =\n defaultFn ??\n this.#defaultFn ??\n raise(\"DefaultMap used without a factory function\");\n\n const value = fn(key);\n this.set(key, value);\n return value;\n }\n }\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { DefaultMap } from \"./DefaultMap\";\n\nfunction emptyIterator(): IterableIterator<never> {\n return [][Symbol.iterator]();\n}\n\n/**\n * Like an ES6 Map, but two levels deep. Useful for building reverse lookup\n * tables. Will automatically delete second-level maps when they are empty.\n */\nexport class NestedMap<K1, K2, V> {\n #map: DefaultMap<K1, Map<K2, V>>;\n\n constructor() {\n this.#map = new DefaultMap(() => new Map<K2, V>());\n }\n\n get size(): number {\n let total = 0;\n for (const value of this.#map.values()) {\n total += value.size;\n }\n return total;\n }\n\n count(key1: K1): number {\n return this.#map.get(key1)?.size ?? 0;\n }\n\n *keys(): IterableIterator<[K1, K2]> {\n for (const [key1, nested] of this.#map) {\n for (const key2 of nested.keys()) {\n yield [key1, key2];\n }\n }\n }\n\n has(key1: K1, key2: K2): boolean {\n return this.#map.get(key1)?.has(key2) ?? false;\n }\n\n get(key1: K1, key2: K2): V | undefined {\n return this.#map.get(key1)?.get(key2);\n }\n\n set(key1: K1, key2: K2, value: V): this {\n this.#map.getOrCreate(key1).set(key2, value);\n return this;\n }\n\n delete(key1: K1, key2: K2): void {\n if (!this.#map.has(key1)) {\n return;\n }\n\n const nested = this.#map.get(key1)!;\n nested.delete(key2);\n if (nested.size === 0) {\n this.#map.delete(key1);\n }\n }\n\n clear(): void {\n this.#map.clear();\n }\n\n *[Symbol.iterator](): IterableIterator<[K1, K2, V]> {\n for (const [key1, nested] of this.#map) {\n for (const [key2, value] of nested) {\n yield [key1, key2, value];\n }\n }\n }\n\n entriesAt(key1: K1): IterableIterator<[K2, V]> {\n return this.#map.get(key1)?.entries() ?? emptyIterator();\n }\n\n *filterAt(key1: K1, keys: Iterable<K2>): Iterable<[K2, V]> {\n const nested = this.#map.get(key1);\n if (nested === undefined) {\n return;\n }\n\n for (const k2 of keys) {\n const value = nested.get(k2);\n if (value !== undefined) {\n yield [k2, value];\n }\n }\n }\n\n keysAt(key1: K1): IterableIterator<K2> {\n return this.#map.get(key1)?.keys() ?? emptyIterator();\n }\n\n valuesAt(key1: K1): IterableIterator<V> {\n return this.#map.get(key1)?.values() ?? emptyIterator();\n }\n\n deleteAll(key1: K1): void {\n this.#map.delete(key1);\n }\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type { Awaitable, Json } from \"@liveblocks/core\";\nimport type { Decoder } from \"decoders\";\n\nimport type { IStorageDriver } from \"~/interfaces\";\n\nexport interface MetadataDB {\n // Getter supports optional decoder\n get(key: string): Promise<Json | undefined>;\n get<T>(decoder: Decoder<T>, key: string): Promise<T | undefined>;\n\n put(key: string, value: Json): Awaitable<void>;\n delete(key: string): Awaitable<void>;\n}\n\n/**\n * Returns a thin wrapper around an IStorageDriver to provide MetadataDB\n * functionality, including type-safe reads.\n */\nexport function makeMetadataDB(driver: IStorageDriver): MetadataDB {\n async function get(key: string): Promise<Json | undefined>;\n async function get<T>(\n decoder: Decoder<T>,\n key: string\n ): Promise<T | undefined>;\n async function get<T>(\n a1: string | Decoder<T>,\n a2?: string\n ): Promise<T | Json | undefined> {\n if (a2 === undefined) {\n return await driver.get_meta(a1 as string);\n } else {\n return (a1 as Decoder<T>).value(await driver.get_meta(a2));\n }\n }\n\n return {\n get,\n put: driver.put_meta.bind(driver),\n delete: driver.delete_meta.bind(driver),\n };\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { enum_ } from \"decoders\";\n\nexport enum ProtocolVersion {\n //\n // NOTE:\n // The following versions were once used, but there is no usage of it anymore\n // in the wild, so we've removed support for them:\n //\n // V1 - Initial version\n // V2 - ?\n // V3 - started to broadcast storage operations to the sender to fix some\n // conflicts\n // V4 - created a virtual root to fix an issue where multiple people\n // initialize the storage at the same time\n // V5 - started to broadcast messages in a batch (arrays) for clients\n // V6 - started to validate inputs with decoders.\n //\n\n /**\n * V7 changes the URL params used to authorize the user.\n *\n * In V6 and lower, the ?token= URL param is used, which will only ever\n * contain a `pub-legacy` or `sec-legacy` token.\n *\n * URL PARAM CHANGES:\n * Starting with V7, the ?token= is no longer a legal URL param. Instead,\n * either of the following params is used:\n *\n * - ?tok=... for ID tokens\n * - ?tok=... for Access tokens\n * - ?tok=... for Secret Legacy tokens\n * - ?pubkey=... for public keys (no token, public key can be directly used here)\n *\n * Note that `pub-legacy` tokens are no longer accepted in V7, and are\n * replaced by the direct use of the public key.\n *\n * BEHAVIORAL CHANGES:\n * Starting with V7, the RoomState server message that gets sent when\n * a client initially connects will now include new fields:\n *\n * - `actor`\n * - `scopes`\n *\n * Since v1.2.0 (Jul 31, 2023)\n */\n V7 = 7,\n\n /**\n * V8 changes storage response format and allows streaming.\n *\n * MESSAGE FORMAT CHANGES:\n * - V8: sends 1+ STORAGE_CHUNK messages, followed by 1 final\n * STORAGE_STREAM_END message (with compact nodes)\n * - V7: sends 1 STORAGE_STATE_V7 message (with full nodes)\n *\n * STREAMING BEHAVIOR in V8:\n * - For SQLite-backed rooms: nodes are split into multiple STORAGE_CHUNK\n * messages, followed by STORAGE_STREAM_END\n * - For KV-backed rooms: all nodes are sent in a single STORAGE_CHUNK\n * message that will contain all nodes, followed by STORAGE_STREAM_END\n *\n * Since 3.14.0\n */\n V8 = 8,\n}\n\nexport const protocolVersionDecoder = enum_(ProtocolVersion).describe(\n \"Unsupported protocol version\"\n);\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type {\n BaseUserMeta,\n Brand,\n IUserInfo,\n Json,\n JsonObject,\n} from \"@liveblocks/core\";\nimport {\n assertNever,\n ClientMsgCode,\n nodeStreamToCompactNodes,\n OpCode,\n raise,\n ServerMsgCode,\n tryParseJson,\n WebsocketCloseCodes as CloseCode,\n} from \"@liveblocks/core\";\nimport { Mutex } from \"async-mutex\";\nimport { array, formatInline } from \"decoders\";\nimport { chunked } from \"itertools\";\nimport { nanoid } from \"nanoid\";\n\nimport type { Guid } from \"~/decoders\";\nimport { clientMsgDecoder } from \"~/decoders\";\nimport type { IServerWebSocket, IStorageDriver } from \"~/interfaces\";\nimport { Logger } from \"~/lib/Logger\";\nimport { makeNewInMemoryDriver } from \"~/plugins/InMemoryDriver\";\nimport type {\n ClientMsg as GenericClientMsg,\n IgnoredOp,\n Op,\n ServerMsg as GenericServerMsg,\n ServerWireOp,\n} from \"~/protocol\";\nimport { ProtocolVersion } from \"~/protocol\";\nimport { Storage } from \"~/Storage\";\nimport { YjsStorage } from \"~/YjsStorage\";\n\nimport { tryCatch } from \"./lib/tryCatch\";\nimport { UniqueMap } from \"./lib/UniqueMap\";\nimport { makeRoomStateMsg } from \"./utils\";\n\nconst messagesDecoder = array(clientMsgDecoder);\n\nconst HIGHEST_PROTOCOL_VERSION = Math.max(\n ...Object.values(ProtocolVersion).filter(\n (v): v is number => typeof v === \"number\"\n )\n) as ProtocolVersion;\n\n// Reverse lookup for ServerMsgCodes\nconst SERVER_MSG_CODE_NAMES = Object.fromEntries(\n Object.entries(ServerMsgCode).map(([k, v]) => [v, k])\n) as Record<(typeof ServerMsgCode)[keyof typeof ServerMsgCode], string>;\n\nconst BLACK_HOLE = new Logger([\n /* No targets, i.e. black hole logger */\n]);\n\nexport type LoadingState = \"initial\" | \"loading\" | \"loaded\";\nexport type ActorID = Brand<number, \"ActorID\">;\n\n/**\n * Session keys are also known as the \"nonce\" in the protocol. It's a random,\n * unique, but PRIVATE, identifier for the session, and it's important that\n * this ID is never shared to anyone except the connected client, which\n * receives it as part of its ROOM_STATE message.\n */\nexport type SessionKey = Brand<string, \"SessionKey\">;\n\nexport type PreSerializedServerMsg = Brand<string, \"PreSerializedServerMsg\">;\ntype ClientMsg = GenericClientMsg<JsonObject, Json>;\ntype ServerMsg = GenericServerMsg<JsonObject, BaseUserMeta, Json>;\n\n/**\n * Creates a collector for deferred promises (side effects that should run\n * outside a mutex). Call `defer` to collect promises, then `waitAll` to\n * await them all.\n */\nfunction collectSideEffects() {\n const deferred: Promise<void>[] = [];\n return {\n defer: (p: Promise<void>) => void deferred.push(p),\n waitAll: () => Promise.allSettled(deferred),\n };\n}\n\nfunction serialize(\n msgs: ServerMsg | readonly ServerMsg[]\n): PreSerializedServerMsg {\n return JSON.stringify(msgs) as PreSerializedServerMsg;\n}\n\nexport function ackIgnoredOp(opId: string): IgnoredOp {\n return { type: OpCode.DELETE_CRDT, id: \"ACK\", opId }; // (H)Ack Op\n}\n\nfunction stripOpId(op: Op): ServerWireOp {\n // TODO: Optimize later! Instead of duplicating every op and\n // stripping the opId explicitly, it would be generally more\n // efficient if we treated the opIds as \"envelopes\" around Ops (or\n // send them in a separate array altogether at the protocol level\n // in V8 soon--even better, as it would not even require any stripping!)\n const { opId: _, ...rest } = op; // Strip opIds from all outgoing messages!\n return rest;\n}\n\n/**\n * A known or anonymous user.\n *\n * BY DEFINITION:\n * A User with an assigned `id` property is a non-anonymous user.\n * A User with an assigned `anonymousId` property is an anonymous user.\n * A User with neither of those properties is also an anonymous user.\n *\n * WHAT'S THE DIFFERENCE?\n * When creating a non-anonymous user, other users in the room will be able to\n * observe the assigned `id` property in Presence (e.g. via the `other.user.id`\n * in the Liveblocks client).\n *\n * When creating an anonymous user, you can _optionally_ provide an anonymous\n * ID to (re)use. While not authorized, this still allows you to correlate\n * unique users.\n */\nexport type IUserData = AuthorizedUser | AnonymousUser; // YYY Remove this export before launch. It's a private API, but only needed temporarily, while refactoring our CF server\n\ntype AuthorizedUser = {\n readonly id: string;\n readonly anonymousId?: never;\n readonly info?: IUserInfo;\n};\n\n// Anonymous users, by definition, have no ID, or have an explicitly-assigned\n// anonymous ID (in case you need to control anonymous ID generation, e.g. by\n// tracking a cookie). The anonymous ID will not show up in other clients. To\n// those clients, it will appear as a user without an ID.\ntype AnonymousUser = {\n readonly anonymousId: string;\n readonly id?: never;\n readonly info?: IUserInfo;\n};\n\n/*\n\nSession Types: \n| | Browser Session | Backend Session | Virtual Session |\n|-----------------------------------|-----------------|-----------------|-----------------|\n| Sends enter/leave/presence events | ✓ | | ✓ |\n| Visible to other users in room | ✓ | | ✓ |\n| Has WebSocket connection | ✓ | | |\n| Updated from | Browser | REST API | REST API |\n\n*Note: VirtualSession is not yet implemented. \n\n*/\n\n/**\n * Each BrowserSession is an abstraction around a socket instance, and maintains\n * metadata about the connection.\n */\nexport class BrowserSession<SM, CM extends JsonObject> {\n // ^^ User-defined Session Metadata\n // ^^ User-defined Client Metadata (sent to client in ROOM_STATE)\n\n public readonly version: ProtocolVersion; // Liveblocks protocol version this client will speak\n public readonly actor: ActorID; // Must be unique within the room\n public readonly createdAt: Date;\n\n // Externally provided (public!) user metadata. This information will get shared with other clients\n public readonly user: IUserData;\n public readonly scopes: string[]; // Permissions for this session, sent to connected clients (so consider public info)\n public readonly meta: SM; // Arbitrary *private* meta data to attach to this session (will NOT be shared)\n public readonly publicMeta?: CM; // Metadata sent to client in ROOM_STATE message's \"meta\" field\n\n readonly #_socket: IServerWebSocket;\n readonly #_debug: boolean;\n #_lastActiveAt: Date;\n\n // We keep a status in-memory in the session of whether we already sent a rejected ops message to the client.\n #_hasNotifiedClientStorageUpdateError: boolean;\n\n /** @internal - Never create a BrowserSession instance manually. Use the room.startBrowserSession() API instead. */\n constructor(\n ticket: Ticket<SM, CM>,\n socket: IServerWebSocket,\n debug: boolean\n ) {\n this.version = ticket.version;\n this.actor = ticket.actor;\n this.user = ticket.user;\n this.scopes = ticket.scopes;\n this.meta = ticket.meta ?? (undefined as unknown as SM);\n this.publicMeta = ticket.publicMeta;\n this.#_socket = socket;\n this.#_debug = debug;\n\n const now = new Date();\n this.createdAt = now;\n this.#_lastActiveAt = now;\n this.#_hasNotifiedClientStorageUpdateError = false;\n }\n\n get lastActiveAt(): Date {\n const lastPing = this.#_socket.getLastPongTimestamp?.();\n if (lastPing && lastPing > this.#_lastActiveAt) {\n return lastPing;\n } else {\n return this.#_lastActiveAt;\n }\n }\n\n get hasNotifiedClientStorageUpdateError(): boolean {\n return this.#_hasNotifiedClientStorageUpdateError;\n }\n\n markActive(now = new Date()): void {\n if (now > this.#_lastActiveAt) {\n this.#_lastActiveAt = now;\n }\n }\n\n setHasNotifiedClientStorageUpdateError(): void {\n this.#_hasNotifiedClientStorageUpdateError = true;\n }\n\n sendPong(): number {\n this.markActive();\n\n const sent = this.#_socket.send(\"pong\");\n if (this.#_debug) {\n if (sent < 0) {\n console.error(\n `failed to send \"pong\" to actor=${this.actor} (back pressure)`\n );\n } else if (sent === 0) {\n console.error(\n `failed to send \"pong\" to actor=${this.actor} (connection issue)`\n );\n } else {\n // Success\n console.log(`sent to actor=${this.actor}: \"pong\"`);\n }\n }\n return sent;\n }\n\n send(serverMsg: ServerMsg | ServerMsg[] | PreSerializedServerMsg): number {\n const data =\n typeof serverMsg === \"string\" ? serverMsg : serialize(serverMsg);\n const sent = this.#_socket.send(data);\n if (this.#_debug) {\n if (sent < 0) {\n console.error(\n `failed to send message to actor=${this.actor} (back pressure)`\n );\n } else if (sent === 0) {\n console.error(\n `failed to send message to actor=${this.actor} (connection issue)`\n );\n }\n\n const msgs = JSON.parse(data) as ServerMsg | ServerMsg[];\n for (const msg of Array.isArray(msgs) ? msgs : [msgs]) {\n console.log(\n `sent to actor=${this.actor}: [${\n SERVER_MSG_CODE_NAMES[msg.type] ?? msg.type\n }] ${JSON.stringify(msg)}`\n );\n }\n }\n return sent;\n }\n\n /**\n * @internal\n * Closes the socket associated to this BrowserSession.\n *\n * NOTE: Never call this API directly! Call .endBrowserSession() instead.\n */\n closeSocket(code: number, reason?: string): void {\n this.#_socket.close(code, reason);\n }\n}\n\nexport class BackendSession extends BrowserSession<never, never> {\n /** @internal Never call this constructor directly */\n constructor(\n ticket: Ticket<never, never>,\n socket: IServerWebSocket,\n debug: boolean\n ) {\n super(ticket, socket, debug);\n }\n}\n\nexport type Ticket<SM, CM extends JsonObject> = {\n readonly sessionKey: SessionKey; // Should stay private\n readonly version: ProtocolVersion;\n readonly actor: ActorID;\n readonly meta?: SM; // Private Session metadata\n readonly publicMeta?: CM; // Client metadata is *public* metadata sent to client in ROOM_STATE message\n readonly user: IUserData; // User-provided, public, metadata\n readonly scopes: string[];\n};\n\nexport type CreateTicketOptions<SM, CM extends JsonObject> = {\n /** The Liveblocks protocol version this client will speak */\n version?: ProtocolVersion;\n meta?: SM;\n publicMeta?: CM;\n /** A user-provided ID to externally recognize the user by */\n id?: string;\n /**\n * A user-provided anonymous ID to use. When `id` is provided, this field is\n * ignored. When both fields are missing, a new anonymous ID will be\n * generated.\n */\n anonymousId?: string;\n /** Static user metadata to assign this session, will get broadcasted to other clients */\n info?: IUserInfo;\n /** Permissions to assign this session */\n scopes?: string[];\n\n /** An explicit actor ID to use. Supported for legacy use cases only. It's best to not set this and let it get assigned dynamically, as it's important for this identifier to be unique. */\n actor?: ActorID;\n};\n\ntype InternalData = {\n readonly storage: Storage;\n readonly yjsStorage: YjsStorage;\n readonly mutex: Mutex;\n};\n\ntype RoomOptions<SM, CM extends JsonObject, C> = {\n /**\n * Bring your own persistence backend\n */\n storage?: IStorageDriver;\n logger?: Logger;\n\n /**\n * Whether to allow streaming storage responses. Only safe with drivers\n * that can guarantee that no Ops from other clients can get interleaved\n * between the chunk generation until the last chunk has been sent.\n * Defaults to true, but is notably NOT safe to use from DOS-KV backends.\n */\n allowStreaming?: boolean;\n\n // YYY Restructure these hooks to all take a single `event` param\n hooks?: {\n /** Customize which incoming messages from a client are allowed or disallowed. */\n isClientMsgAllowed?: (\n msg: ClientMsg,\n session: BrowserSession<SM, CM>\n ) => { allowed: true } | { allowed: false; reason: string };\n\n /** Called whenever the server acknowledged a ping with a pong */\n onDidPong?: (ctx?: C) => void | Promise<void>;\n\n /** Called before the room is attempted to be loaded */\n onRoomWillLoad?: (ctx?: C) => void | Promise<void>;\n /** Called right after the room's contents are loaded, but before any session has been started */\n onRoomDidLoad?: (ctx?: C) => void | Promise<void>;\n\n /** Called right before the room is attempted to be unloaded. Synchronous. May throw to abort the unloading. */\n onRoomWillUnload?: (ctx?: C) => void;\n /** Called right after the room has been unloaded from memory. Synchronous. */\n onRoomDidUnload?: (ctx?: C) => void;\n\n /** Called when a new user entered the room. */\n onSessionDidStart?: (\n session: BrowserSession<SM, CM>,\n ctx?: C\n ) => void | Promise<void>;\n /** Called when a user left the room. */\n onSessionDidEnd?: (\n session: BrowserSession<SM, CM>,\n ctx?: C\n ) => void | Promise<void>;\n\n /**\n * Called when Liveblocks Storage for the room was updated.\n *\n * IMPORTANT! If you implement these as async functions, it's important to\n * note that these run outside of the storage mutex that guarantees\n * a consistent view of storage.\n * Therefore, only ever use this hook to implement a side effect (like\n * trigger a notification), don't read storage in this hook directly.\n */\n postClientMsgStorageDidUpdate?: (ctx?: C) => void | Promise<void>;\n /**\n * Called when Yjs Storage for the room was updated.\n *\n * IMPORTANT! If you implement these as async functions, it's important to\n * note that these run outside of the storage mutex that guarantees\n * a consistent view of storage.\n * Therefore, only ever use this hook to implement a side effect (like\n * trigger a notification), don't read storage in this hook directly.\n */\n postClientMsgYdocDidUpdate?: (\n ctx?: C,\n sess?: BrowserSession<SM, CM>\n ) => void | Promise<void>;\n };\n\n /** Enable debug logging */\n enableDebugLogging?: boolean;\n};\n\n/**\n * A Liveblocks Room server.\n */\nexport class Room<RM, SM, CM extends JsonObject, C = undefined> {\n // ^^^^^^^^^^ User-defined Room Metadata, Session Metadata, and Client Metadata\n\n public meta: RM;\n public readonly driver: IStorageDriver;\n public logger: Logger;\n\n private _loadData$: Promise<void> | null = null;\n private _data: InternalData | null = null;\n private _qsize = 0;\n\n private readonly sessions = new UniqueMap<\n SessionKey,\n BrowserSession<SM, CM>,\n ActorID\n >((s) => s.actor);\n\n private readonly hooks: {\n isClientMsgAllowed: (\n msg: ClientMsg,\n session: BrowserSession<SM, CM>\n ) => { allowed: true } | { allowed: false; reason: string };\n\n onDidPong?: (ctx?: C) => void | Promise<void>;\n\n onRoomWillLoad?: (ctx?: C) => void | Promise<void>;\n onRoomDidLoad?: (ctx?: C) => void | Promise<void>;\n\n onRoomWillUnload?: (ctx?: C) => void;\n onRoomDidUnload?: (ctx?: C) => void;\n\n onSessionDidStart?: (\n session: BrowserSession<SM, CM>,\n ctx: C | undefined\n ) => void | Promise<void>;\n onSessionDidEnd?: (\n session: BrowserSession<SM, CM>,\n ctx: C | undefined\n ) => void | Promise<void>;\n\n // Don't like these callback names yet. Think about how to better abstract it later.\n postClientMsgStorageDidUpdate?: (ctx?: C) => void | Promise<void>;\n postClientMsgYdocDidUpdate?: (\n ctx?: C,\n sess?: BrowserSession<SM, CM>\n ) => void | Promise<void>;\n };\n\n readonly #_debug: boolean;\n readonly #_allowStreaming: boolean;\n\n constructor(meta: RM, options?: RoomOptions<SM, CM, C>) {\n const driver = options?.storage ?? makeNewInMemoryDriver();\n this.meta = meta;\n this.driver = driver;\n this.logger = options?.logger ?? BLACK_HOLE;\n this.#_allowStreaming = options?.allowStreaming ?? true;\n this.hooks = {\n isClientMsgAllowed:\n options?.hooks?.isClientMsgAllowed ??\n (() => {\n return {\n allowed: true,\n };\n }),\n\n // YYY .load() isn't called on the RoomServer yet! As soon as it does, these hooks will get called\n onRoomWillLoad: options?.hooks?.onRoomWillLoad,\n onRoomDidLoad: options?.hooks?.onRoomDidLoad,\n\n onRoomWillUnload: options?.hooks?.onRoomWillUnload,\n onRoomDidUnload: options?.hooks?.onRoomDidUnload,\n\n onSessionDidStart: options?.hooks?.onSessionDidStart,\n onSessionDidEnd: options?.hooks?.onSessionDidEnd,\n\n postClientMsgStorageDidUpdate:\n options?.hooks?.postClientMsgStorageDidUpdate,\n postClientMsgYdocDidUpdate: options?.hooks?.postClientMsgYdocDidUpdate,\n };\n this.#_debug = options?.enableDebugLogging ?? false;\n }\n\n public get loadingState(): LoadingState {\n if (this._loadData$ === null) {\n return \"initial\";\n } else if (this._data === null) {\n return \"loading\";\n } else {\n return \"loaded\";\n }\n }\n\n public get numSessions(): number { return this.sessions.size; } // prettier-ignore\n\n public get storage(): Storage { return this.data.storage; } // prettier-ignore\n public get yjsStorage(): YjsStorage { return this.data.yjsStorage; } // prettier-ignore\n\n public get mutex(): Mutex { return this.data.mutex; } // prettier-ignore\n\n private get data(): InternalData { return this._data ?? raise(\"Cannot use room before it's loaded\"); } // prettier-ignore\n\n // ------------------------------------------------------------------------------------\n // Public API\n // ------------------------------------------------------------------------------------\n\n /**\n * Initializes the Room, so it's ready to start accepting connections. Safe\n * to call multiple times. After awaiting `room.load()` the Room is ready to\n * be used.\n */\n public async load(ctx?: C): Promise<void> {\n if (this._loadData$ === null) {\n this._data = null;\n this._loadData$ = this._load(ctx).catch((e) => {\n this._data = null;\n this._loadData$ = null;\n throw e;\n });\n }\n return this._loadData$;\n }\n\n /**\n * Releases the currently-loaded storage tree from worker memory, freeing it\n * up to be garbage collected. The next time a user will join the room, the\n * room will be reloaded from storage.\n */\n public unload(ctx?: C): void {\n this.hooks.onRoomWillUnload?.(ctx); // May throw to cancel unloading\n if (this._data) {\n this.storage.unload();\n this.yjsStorage.unload();\n }\n // YYY Abort any potentially in-flight _loadData$ calls here\n this._loadData$ = null;\n // this._data = null; // YYY Should we also clear _data? I think so!\n this.hooks.onRoomDidUnload?.(ctx);\n }\n\n /**\n * Issues a Ticket with a new/unique actor ID\n *\n * IMPORTANT! As the caller of this function, you are responsible for\n * ensuring you trust the values passed in here. Never pass unauthorized\n * values in here.\n *\n * The returned Ticket can be turned into a active Session once the socket\n * connection is established. If the socket is never established, this\n * unused Ticket will simply get garbage collected.\n */\n public async createTicket(\n options?: CreateTicketOptions<SM, CM>\n ): Promise<Ticket<SM, CM>> {\n const actor$ = options?.actor ?? this.getNextActor();\n const sessionKey = nanoid() as SessionKey;\n const info = options?.info;\n const ticket: Ticket<SM, CM> = {\n version: options?.version ?? HIGHEST_PROTOCOL_VERSION,\n actor: await actor$,\n sessionKey,\n meta: options?.meta,\n publicMeta: options?.publicMeta,\n user: options?.id\n ? { id: options.id, info }\n : { anonymousId: options?.anonymousId ?? nanoid(), info },\n scopes: options?.scopes ?? [\"room:write\"],\n };\n if (this.#_debug) {\n console.log(`new ticket created: ${JSON.stringify(ticket)}`);\n }\n return ticket;\n }\n\n public async createBackendSession_experimental(): Promise<\n [session: BackendSession, outgoingMessages: PreSerializedServerMsg[]]\n > {\n const ticket = (await this.createTicket()) as Ticket<never, never>;\n const capturedServerMsgs: PreSerializedServerMsg[] = [];\n const stub = {\n send: (data) => {\n if (typeof data === \"string\") {\n capturedServerMsgs.push(data as PreSerializedServerMsg);\n }\n return 0;\n },\n close: () => {}, // noop\n } satisfies IServerWebSocket;\n const session = new BackendSession(ticket, stub, false);\n return [session, capturedServerMsgs];\n }\n\n /**\n * Restores the given sessions as the Room server's session list. Can only be\n * called as long as there are no existing sessions.\n *\n * The key difference with the .startBrowserSession() API is that restoreSessions is\n * used in cases where a session was hibernated and needs to be restored,\n * without _conceptually_ starting a new session.\n *\n * Because there are no side effects to restoreSession, it's synchronous.\n */\n public restoreSessions(\n sessions: {\n ticket: Ticket<SM, CM>;\n socket: IServerWebSocket;\n lastActivity: Date;\n }[]\n ): void {\n if (this.sessions.size > 0) {\n throw new Error(\"This API can only be called before any sessions exist\");\n }\n\n for (const { ticket, socket, lastActivity } of sessions) {\n const newSession = new BrowserSession(ticket, socket, this.#_debug);\n this.sessions.set(ticket.sessionKey, newSession);\n newSession.markActive(lastActivity);\n }\n }\n\n /**\n * Registers a new BrowserSession into the Room server's session list, along with\n * the socket connection to use for that BrowserSession, now that it is known.\n *\n * This kicks off a few side effects:\n * - Sends a ROOM_STATE message to the socket.\n * - Broadcasts a USER_JOINED message to all other sessions in the room.\n */\n public startBrowserSession(\n ticket: Ticket<SM, CM>,\n socket: IServerWebSocket,\n ctx?: C,\n defer: (promise: Promise<void>) => void = () => {\n throw new Error(\n \"One of your hook handlers returned a promise, but no side effect collector was provided. \" +\n \"Pass a `defer` callback to startBrowserSession() to collect async side effects.\"\n );\n }\n ): void {\n let existing: SessionKey | undefined;\n while (\n (existing = this.sessions.lookupPrimaryKey(ticket.actor)) !== undefined\n ) {\n // If this happens, it means a new connection attempt is happening for an\n // existing actor ID. It's most likely from a reconnection attempt using\n // a legacy token (which has the actor ID hardcoded in it), where the old\n // session hasn't been closed explicitly. We'll actively kill it now.\n\n // Terminate old session\n this.endBrowserSession(\n existing,\n CloseCode.KICKED,\n \"Closed stale connection\",\n ctx,\n defer\n );\n\n this.logger.warn(\n `Previous session for actor ${ticket.actor} killed in favor of new session`\n );\n }\n\n const newSession = new BrowserSession(ticket, socket, this.#_debug);\n this.sessions.set(ticket.sessionKey, newSession);\n\n const users: Record<ActorID, BaseUserMeta & { scopes: string[] }> = {};\n for (const session of this.otherSessions(ticket.sessionKey)) {\n users[session.actor] = {\n id: session.user.id,\n info: session.user.info,\n scopes: session.scopes,\n };\n }\n\n newSession.send(\n makeRoomStateMsg(\n newSession.actor,\n ticket.sessionKey, // called \"nonce\" in the protocol\n newSession.scopes,\n users,\n ticket.publicMeta\n )\n );\n\n this.sendToOthers(\n ticket.sessionKey,\n {\n type: ServerMsgCode.USER_JOINED,\n actor: newSession.actor,\n id: newSession.user.id,\n info: newSession.user.info,\n scopes: newSession.scopes,\n },\n ctx,\n defer\n );\n\n // Call the hook, but don't await the results here\n const p$ = this.hooks.onSessionDidStart?.(newSession, ctx);\n if (p$) defer(p$);\n }\n\n /**\n * Unregisters the BrowserSession for the given actor. Call this when the socket has\n * been closed from the client's end.\n *\n * This kicks off a few side effects:\n * - Broadcasts a USER_LEFT message to all other sessions in the room.\n */\n public endBrowserSession(\n key: SessionKey,\n code: number,\n reason: string,\n ctx?: C,\n defer: (promise: Promise<void>) => void = () => {\n throw new Error(\n \"Your onSessionDidEnd handler returned a promise, but no side effect collector was provided. \" +\n \"Pass a `defer` callback to endBrowserSession() to collect async side effects.\"\n );\n }\n ): void {\n const sessions = this.sessions;\n\n const session = sessions.get(key);\n if (session === undefined) return;\n\n session.closeSocket(code, reason);\n\n const deleted = sessions.delete(key);\n if (deleted) {\n for (const other of this.otherSessions(key)) {\n other.send({ type: ServerMsgCode.USER_LEFT, actor: session.actor });\n }\n\n // Call the hook\n const p$ = this.hooks.onSessionDidEnd?.(session, ctx);\n if (p$) defer(p$);\n }\n }\n\n /**\n * Force-closes all sessions matching the given predicate.\n */\n public endSessionBy(\n predicate: (session: BrowserSession<SM, CM>) => boolean,\n code: number,\n reason: string,\n ctx?: C,\n defer: (promise: Promise<void>) => void = () => {\n throw new Error(\n \"Your onSessionDidEnd handler returned a promise, but no side effect collector was provided. \" +\n \"Pass a `defer` callback to endSessionBy() to collect async side effects.\"\n );\n }\n ): number {\n let count = 0;\n for (const [key, session] of this.sessions) {\n if (predicate(session)) {\n count++;\n this.endBrowserSession(key, code, reason, ctx, defer);\n }\n }\n return count;\n }\n\n /**\n * Handles a raw incoming socket message, which can be a ping, or an\n * JSON-encoded message batch.\n */\n public async handleData(\n key: SessionKey,\n data: unknown,\n ctx?: C,\n defer: (promise: Promise<void>) => void = () => {\n throw new Error(\n \"One of your hook handlers returned a promise, but no side effect collector was provided. \" +\n \"Pass a `defer` callback to handleData() to collect async side effects.\"\n );\n }\n ): Promise<void> {\n const text =\n typeof data === \"string\" ? data : raise(\"Unsupported message format\");\n\n if (text === \"ping\") {\n await this.handlePing(key, ctx);\n } else {\n const json = tryParseJson(text);\n const messages = messagesDecoder.decode(json);\n\n if (!messages.ok) {\n const reason =\n process.env.NODE_ENV !== \"production\"\n ? formatInline(messages.error)\n : \"Invalid message format\";\n\n this.endBrowserSession(\n key,\n CloseCode.INVALID_MESSAGE_FORMAT,\n reason,\n ctx,\n defer\n );\n return;\n }\n\n // TODO: Decide on these limits later.\n // If qsize is > 0, then it means there is a traffic jam. This shouldn't\n // be a problem for a while, but it grows beyond a certain (soft or hard)\n // limit, we may want to take measures.\n if (this._qsize > 10_000) {\n // Over hard limit\n // TODO: Maybe disconnect this sockets with a 42xx close code? This\n // will make the client back off more aggressively. See\n // https://github.com/liveblocks/liveblocks/blob/223f7ce0d77380fecd3b08ed9454ca8c330bbe16/packages/liveblocks-core/src/types/IWebSocket.ts#L53\n } else if (this._qsize > 5_000) {\n // Over soft limit\n // TODO: Maybe instruct clients to increase their throttle values?\n }\n\n this._qsize++;\n\n // Run this.handleMsgs(), but guarded by a mutex lock, ensuring that no\n // two messages will get processed simultaneously. This provides similar\n // concurrency protection as Cloudflare's I/O gates\n try {\n await this.processClientMsg(key, messages.value, ctx);\n } finally {\n this._qsize--;\n }\n }\n }\n\n /**\n * Processes an incoming batch of 1 or more ClientMsgs on behalf of\n * a (regular user/browser) session.\n *\n * IMPORTANT: Only use this API on \"trusted\" data!\n * To handle untrusted input data, use `.handleData()` instead.\n *\n * Before calling this API, make sure:\n * 1. The call site is entitled to call this message on behalf of this session; and\n * 2. The ClientMsg payload has been validated to be correct.\n */\n public async processClientMsg(\n key: SessionKey,\n messages: ClientMsg[],\n ctx?: C\n ): Promise<void> {\n await this.load(ctx);\n const { defer, waitAll } = collectSideEffects();\n await this.mutex.runExclusive(() =>\n this._processClientMsg_withExclusiveAccess(key, messages, ctx, defer)\n );\n\n // Run all deferred work (like queueing messages, sending notifications,\n // etc) outside of the mutex\n await waitAll();\n }\n\n /**\n * Processes an incoming batch of 1 or more ClientMsgs on behalf of\n * a BACKEND session.\n *\n * Difference 1: HTTP RESPONSE instead of WEB SOCKET RESPONSE\n * ----------------------------------------------------------\n * For \"normal\" sessions that have a socket attached, any \"responses\" (i.e.\n * server messages like acks or fixops) will be sent back through that\n * existing socket connection.\n *\n * The key difference when using this method is that there is no such socket,\n * so any \"response\" ServerMsgs will get sent back as an HTTP response.\n *\n * Difference 2: No auth check\n * ---------------------------\n * Another key difference is that when processing a backend session, no\n * \"isClientMsgAllowed()\" check is performed, because those checks assume\n * a session.\n */\n public async processClientMsgFromBackendSession(\n session: BackendSession,\n messages: ClientMsg[],\n ctx?: C\n ): Promise<void> {\n await this.load(ctx);\n const { defer, waitAll } = collectSideEffects();\n await this.mutex.runExclusive(() =>\n this._processClientMsgFromBackendSession_withExclusiveAccess(\n session,\n messages,\n ctx,\n defer\n )\n );\n\n // Run all deferred work (like queueing messages, sending notifications,\n // etc) outside of the mutex\n await waitAll();\n }\n\n public getSession(\n sessionKey: SessionKey\n ): BrowserSession<SM, CM> | undefined {\n return this.sessions.get(sessionKey);\n }\n\n public listSessions(): BrowserSession<SM, CM>[] {\n return Array.from(this.sessions.values());\n }\n\n /**\n * Will send the given ServerMsg to all Sessions, except the Session\n * where the message originates from.\n */\n public sendToOthers(\n sender: SessionKey,\n serverMsg: ServerMsg | readonly ServerMsg[],\n ctx?: C,\n defer: (promise: Promise<void>) => void = () => {\n throw new Error(\n \"One of your hook handlers returned a promise, but no side effect collector was provided. \" +\n \"Pass a `defer` callback to sendToOthers() to collect async side effects.\"\n );\n }\n ): void {\n const msg = serialize(serverMsg);\n for (const [key, session] of this.otherSessionEntries(sender)) {\n const success = session.send(msg);\n if (success === 0) {\n // If there is a connection issue, terminate the session at once.\n // Note that in the case of -1 (= back pressure), we don't terminate\n // the connection.\n this.endBrowserSession(\n key,\n CloseCode.KICKED,\n \"Closed broken connection\",\n ctx,\n defer\n );\n }\n }\n }\n\n /**\n * Will broadcast the given ServerMsg to all Sessions in the Room.\n */\n public sendToAll(\n serverMsg: ServerMsg | readonly ServerMsg[],\n ctx?: C,\n defer: (promise: Promise<void>) => void = () => {\n throw new Error(\n \"One of your hook handlers returned a promise, but no side effect collector was provided. \" +\n \"Pass a `defer` callback to sendToAll() to collect async side effects.\"\n );\n }\n ): void {\n const msg = serialize(serverMsg);\n for (const [key, session] of this.sessions) {\n const success = session.send(msg);\n if (success === 0) {\n // If there is a connection issue, terminate the session at once.\n // Note that in the case of -1 (= back pressure), we don't terminate\n // the connection.\n this.endBrowserSession(\n key,\n CloseCode.KICKED,\n \"Closed broken connection\",\n ctx,\n defer\n );\n }\n }\n }\n\n // ------------------------------------------------------------------------------------\n // Private APIs\n // ------------------------------------------------------------------------------------\n\n private async _loadStorage(): Promise<Storage> {\n const storage = new Storage(this.driver);\n await storage.load(this.logger);\n return storage;\n }\n\n private async _loadYjsStorage(): Promise<YjsStorage> {\n const yjsStorage = new YjsStorage(this.driver);\n await yjsStorage.load(this.logger);\n return yjsStorage;\n }\n\n // Don't ever manually call this!\n private async _load(ctx?: C): Promise<void> {\n await this.hooks.onRoomWillLoad?.(ctx);\n\n // YYY Maybe later run these in parallel? See https://github.com/liveblocks/liveblocks-cloudflare/pull/721#discussion_r1489076389\n const storage = await this._loadStorage();\n const yjsStorage = await this._loadYjsStorage();\n\n this._data = {\n mutex: new Mutex(),\n storage,\n yjsStorage,\n };\n\n await this.hooks.onRoomDidLoad?.(ctx);\n }\n\n /**\n * Returns a new, unique, actor ID.\n */\n private async getNextActor(): Promise<ActorID> {\n return (await this.driver.next_actor()) as ActorID;\n }\n\n /**\n * Iterates over all *other* Sessions and their session keys.\n */\n private *otherSessionEntries(\n currentKey: SessionKey\n ): Generator<[SessionKey, BrowserSession<SM, CM>]> {\n for (const [key, session] of this.sessions) {\n if (key !== currentKey) {\n yield [key, session];\n }\n }\n }\n\n /**\n * Iterates over all *other* Sessions.\n */\n private *otherSessions(\n currentKey: SessionKey\n ): Generator<BrowserSession<SM, CM>> {\n for (const [key, session] of this.sessions) {\n if (key !== currentKey) {\n yield session;\n }\n }\n }\n\n /**\n * @internal\n * Handles an incoming ping, by sending a pong back.\n */\n // eslint-disable-next-line @typescript-eslint/require-await\n private async handlePing(sessionKey: SessionKey, ctx?: C): Promise<void> {\n const session = this.sessions.get(sessionKey);\n if (session === undefined) {\n this.logger\n .withContext({ sessionKey })\n .warn(\"[probe] in handlePing, no such session exists\");\n return;\n }\n\n const sent = session.sendPong();\n\n // 0 means there was a connection issue\n // -1 means there was back pressure, which is no issue (we'll just count the ping)\n if (sent !== 0) {\n await this.hooks.onDidPong?.(ctx);\n }\n }\n\n private async _processClientMsg_withExclusiveAccess(\n sessionKey: SessionKey,\n messages: ClientMsg[],\n ctx: C | undefined,\n defer: (p: Promise<void>) => void\n ): Promise<void> {\n const session = this.sessions.get(sessionKey);\n if (!session) {\n this.logger\n .withContext({ sessionKey })\n .warn(\"[probe] in handleClientMsgs, no such session exists\");\n return;\n }\n\n // Keep two ServerMsg buffers to send at the end:\n // - Messages to fan-out to all *others* (current session not included)\n // - Messages to reply back to the current sender (i.e. acks and rejections)\n const toFanOut: ServerMsg[] = [];\n const toReply: ServerMsg[] = [];\n const replyImmediately = (msg: ServerMsg | ServerMsg[]) =>\n void session.send(msg);\n const scheduleFanOut = (msg: ServerMsg) => void toFanOut.push(msg);\n const scheduleReply = (msg: ServerMsg) => void toReply.push(msg);\n\n for (const msg of messages) {\n const isMsgAllowed = this.hooks.isClientMsgAllowed(msg, session);\n if (isMsgAllowed.allowed) {\n await this.handleOne(\n session,\n msg,\n replyImmediately,\n scheduleFanOut,\n scheduleReply,\n ctx,\n defer\n );\n } else {\n if (!session.hasNotifiedClientStorageUpdateError) {\n toReply.push({\n type: ServerMsgCode.REJECT_STORAGE_OP,\n opIds:\n msg.type === ClientMsgCode.UPDATE_STORAGE\n ? msg.ops.map((op) => op.opId)\n : [],\n reason: isMsgAllowed.reason,\n });\n session.setHasNotifiedClientStorageUpdateError();\n }\n }\n }\n\n if (toFanOut.length > 0) {\n this.sendToOthers(sessionKey, toFanOut, ctx, defer);\n }\n\n if (toReply.length > 0) {\n session.send(toReply);\n }\n }\n\n // TODO It's a bit bothering how much duplication there is between this method\n // and the _processClientMsg_withExclusiveAccess version. A better\n // abstraction is needed.\n private async _processClientMsgFromBackendSession_withExclusiveAccess(\n session: BackendSession,\n messages: ClientMsg[],\n ctx: C | undefined,\n defer: (p: Promise<void>) => void\n ): Promise<void> {\n // Keep two ServerMsg buffers to send at the end:\n // - Messages to fan-out to all *others* (current session not included)\n // - Messages to reply back to the current sender (i.e. acks and rejections)\n const toFanOut: ServerMsg[] = [];\n const toReplyImmediately: ServerMsg[] = [];\n const toReplyAfter: ServerMsg[] = [];\n\n const replyImmediately = (msg: ServerMsg | ServerMsg[]) => {\n if (Array.isArray(msg)) {\n for (const m of msg) {\n toReplyImmediately.push(m);\n }\n } else {\n toReplyImmediately.push(msg);\n }\n };\n const scheduleFanOut = (msg: ServerMsg) => void toFanOut.push(msg);\n const scheduleReply = (msg: ServerMsg) => void toReplyAfter.push(msg);\n\n for (const msg of messages) {\n await this.handleOne(\n session,\n msg,\n replyImmediately,\n scheduleFanOut,\n scheduleReply,\n ctx,\n defer\n );\n }\n\n if (toReplyImmediately.length > 0) {\n session.send(toReplyImmediately);\n toReplyImmediately.length = 0;\n }\n\n if (toFanOut.length > 0) {\n this.sendToOthers(\"(transient)\" as SessionKey, toFanOut, ctx, defer);\n toFanOut.length = 0;\n }\n\n if (toReplyAfter.length > 0) {\n session.send(toReplyAfter);\n toReplyAfter.length = 0;\n }\n }\n\n private async handleOne(\n session: BrowserSession<SM, CM>,\n msg: ClientMsg,\n replyImmediately: (msg: ServerMsg | ServerMsg[]) => void,\n scheduleFanOut: (msg: ServerMsg) => void,\n scheduleReply: (msg: ServerMsg) => void,\n ctx: C | undefined,\n defer: (p: Promise<void>) => void\n ): Promise<void> {\n if (!this.mutex.isLocked()) {\n throw new Error(\"Handling messages requires exclusive access\");\n }\n\n switch (msg.type) {\n case ClientMsgCode.UPDATE_PRESENCE: {\n // YYY Maybe consider calling session.sendToOthers() directly here instead of queueing for fan-out?\n scheduleFanOut({\n type: ServerMsgCode.UPDATE_PRESENCE,\n actor: session.actor,\n data: msg.data,\n targetActor: msg.targetActor,\n });\n break;\n }\n\n case ClientMsgCode.BROADCAST_EVENT: {\n // YYY Maybe consider calling session.sendToOthers() directly here instead of queueing for fan-out?\n scheduleFanOut({\n type: ServerMsgCode.BROADCASTED_EVENT,\n actor: session.actor,\n event: msg.event,\n });\n break;\n }\n\n case ClientMsgCode.FETCH_STORAGE: {\n if (session.version >= ProtocolVersion.V8) {\n if (this.#_allowStreaming) {\n const NODES_PER_CHUNK = 250; // = arbitrary! Could be tuned later\n\n for (const chunk of chunked(\n nodeStreamToCompactNodes(this.storage.loadedDriver.iter_nodes()),\n NODES_PER_CHUNK\n )) {\n // NOTE: We don't take a storage snapshot here, because this\n // iteration is happening synchronously, so consistency of the\n // current document automatically guaranteed. If we ever make\n // this streaming asynchronous, however, we need to take\n // a storage snapshot to guarantee document consistency.\n replyImmediately({\n type: ServerMsgCode.STORAGE_CHUNK,\n nodes: chunk,\n });\n }\n } else {\n replyImmediately({\n type: ServerMsgCode.STORAGE_CHUNK,\n nodes: Array.from(\n nodeStreamToCompactNodes(this.storage.loadedDriver.iter_nodes())\n ),\n });\n }\n\n replyImmediately({ type: ServerMsgCode.STORAGE_STREAM_END });\n } else {\n replyImmediately({\n type: ServerMsgCode.STORAGE_STATE_V7,\n items: Array.from(this.storage.loadedDriver.iter_nodes()),\n });\n }\n break;\n }\n\n case ClientMsgCode.UPDATE_STORAGE: {\n // Bump storage version to indicate data will get mutated\n // A driver can use this information to implement copy-on-write\n // semantics to provide snapshot isolation.\n this.driver.bump_storage_version?.();\n\n const result = await this.storage.applyOps(msg.ops);\n\n const opsToForward: ServerWireOp[] = result.flatMap((r) =>\n r.action === \"accepted\" ? [r.op] : []\n );\n\n const opsToSendBack: ServerWireOp[] = result.flatMap((r) => {\n switch (r.action) {\n case \"ignored\":\n // HACK! We send a cleverly composed message, that will act\n // as an acknowledgement to all old clients out there in\n // the wild.\n return r.ignoredOpId !== undefined\n ? [ackIgnoredOp(r.ignoredOpId)]\n : [];\n\n case \"accepted\":\n return r.fix !== undefined ? [r.fix] : [];\n\n // istanbul ignore next\n default:\n return assertNever(r, \"Unhandled case\");\n }\n });\n\n if (opsToForward.length > 0) {\n scheduleFanOut({\n type: ServerMsgCode.UPDATE_STORAGE,\n ops: opsToForward.map(stripOpId),\n });\n scheduleReply({\n type: ServerMsgCode.UPDATE_STORAGE,\n ops: opsToForward,\n });\n }\n\n if (opsToSendBack.length > 0) {\n replyImmediately({\n type: ServerMsgCode.UPDATE_STORAGE,\n ops: opsToSendBack,\n });\n }\n\n if (opsToForward.length > 0) {\n // NOTE! These are being called after *every* handleOne() call\n // currently. Should we not just call these once at the end of\n // handleClientMsgs()?\n const p$ = this.hooks.postClientMsgStorageDidUpdate?.(ctx);\n if (p$) defer(p$);\n }\n break;\n }\n\n case ClientMsgCode.FETCH_YDOC: {\n const vector = msg.vector;\n const guid = msg.guid as Guid | undefined;\n const isV2 = msg.v2;\n const [update, stateVector, snapshotHash] = await Promise.all([\n this.yjsStorage.getYDocUpdate(this.logger, vector, guid, isV2),\n this.yjsStorage.getYStateVector(guid),\n this.yjsStorage.getSnapshotHash({ guid, isV2 }),\n ]);\n\n if (update !== null && snapshotHash !== null) {\n replyImmediately({\n type: ServerMsgCode.UPDATE_YDOC,\n update,\n isSync: true, // this is no longer used by the client, instead we use the presence of stateVector\n stateVector,\n guid,\n v2: isV2,\n remoteSnapshotHash: snapshotHash,\n });\n }\n break;\n }\n\n case ClientMsgCode.UPDATE_YDOC: {\n const update = msg.update;\n const guid = msg.guid as Guid | undefined;\n const isV2 = msg.v2;\n const [result, error] = await tryCatch(\n this.yjsStorage.addYDocUpdate(this.logger, update, guid, isV2)\n );\n\n if (error)\n // Ignore any errors\n break;\n\n this.sendToAll(\n {\n type: ServerMsgCode.UPDATE_YDOC,\n update,\n guid,\n isSync: false,\n stateVector: null,\n v2: isV2,\n remoteSnapshotHash: result.snapshotHash,\n },\n ctx,\n defer\n );\n if (result.isUpdated) {\n const p$ = this.hooks.postClientMsgYdocDidUpdate?.(ctx, session);\n if (p$) defer(p$);\n }\n\n break;\n }\n\n default: {\n try {\n return assertNever(msg, \"Unrecognized client msg\");\n } catch {\n // Ignore\n }\n }\n }\n }\n}\n\nexport { serialize as serializeServerMsg };\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type { JsonObject } from \"@liveblocks/core\";\nimport { raise } from \"@liveblocks/core\";\n\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARNING = 2,\n ERROR = 3,\n}\n\nfunction formatError(err: Error) {\n const prefix = `${err.name}: ${err.message}`;\n return (\n err.stack?.startsWith(prefix) ? err.stack : `${prefix}\\n${err.stack ?? \"\"}`\n ).trimEnd();\n}\n\n/**\n * Inherit from this abstract log target to implement your own custom\n * LogTarget.\n */\nexport abstract class LogTarget {\n public readonly level: LogLevel;\n\n #cache = new WeakMap<JsonObject, string>();\n\n constructor(level: LogLevel | keyof typeof LogLevelNames = LogLevel.INFO) {\n this.level =\n typeof level === \"number\"\n ? level\n : (LogLevelNames[level] ?? LogLevel.INFO);\n }\n\n /** Helper for formatting a log level */\n protected formatLevel(level: LogLevel): string {\n switch (level) {\n case LogLevel.DEBUG:\n return \"debug\";\n case LogLevel.INFO:\n return \"info\";\n case LogLevel.WARNING:\n return \"warn\";\n case LogLevel.ERROR:\n return \"error\";\n default:\n return raise(\"Invalid log level\");\n }\n }\n\n /** Helper for formatting an Arg */\n protected formatArg(arg: string | Error): string {\n return typeof arg === \"object\"\n ? arg instanceof Error\n ? formatError(arg)\n : JSON.stringify(arg)\n : String(arg); // Coerce to string in case TypeScript is bypassed\n }\n\n /**\n * Helper for formatting a Context. Override this in a subclass to change the\n * formatting.\n */\n protected formatContextImpl(context: JsonObject): string {\n const parts = [];\n for (const [k, v] of Object.entries(context ?? {})) {\n if (v !== undefined) {\n // Object, or null, or array\n const sv = typeof v === \"object\" ? JSON.stringify(v) : v;\n parts.push(`${k}=${sv}`);\n }\n }\n return parts.length > 0 ? `[${parts.join(\" \")}]` : \"\";\n }\n\n /**\n * Helper for formatting a Context. Will only compute the string once for\n * every Context instance, and keep its computed string value cached for\n * performance.\n */\n protected formatContext(context: JsonObject): string {\n let formatted = this.#cache.get(context);\n if (formatted === undefined) {\n formatted = this.formatContextImpl(context);\n this.#cache.set(context, formatted);\n }\n return formatted;\n }\n\n /**\n * Implement this in a concrete subclass. The goal is to do whatever to log\n * the given log level, context, and log arg. You'll typically want to\n * utilize the pre-defined helper methods .formatContext() and .formatArg()\n * to implement this.\n */\n abstract log(level: LogLevel, context: JsonObject, arg: string | Error): void;\n}\n\n//\n// Console log target ----------------------------------------------------------\n//\n\nconst CONSOLE_METHOD = {\n [LogLevel.DEBUG]: \"info\",\n [LogLevel.INFO]: \"info\",\n [LogLevel.WARNING]: \"warn\",\n [LogLevel.ERROR]: \"error\",\n} as const;\n\nexport class ConsoleTarget extends LogTarget {\n log(level: LogLevel, context: JsonObject, arg: string | Error): void {\n console[CONSOLE_METHOD[level]](\n this.formatArg(arg),\n this.formatContext(context)\n );\n }\n}\n\n//\n// Logger implementation ------------------------------------------------------\n//\n\n// Friendly names to pass to the constructor\nconst LogLevelNames = {\n debug: LogLevel.DEBUG,\n info: LogLevel.INFO,\n warning: LogLevel.WARNING,\n error: LogLevel.ERROR,\n} as const;\n\ntype LogFn = (arg: string | Error) => void;\n\n/**\n * Structured logger with configurable log targets.\n */\nexport class Logger {\n public readonly debug: LogFn;\n public readonly info: LogFn;\n public readonly warn: LogFn;\n public readonly error: LogFn;\n\n public readonly o: {\n readonly debug?: LogFn;\n readonly info?: LogFn;\n readonly warn?: LogFn;\n readonly error?: LogFn;\n };\n\n private readonly _context: JsonObject;\n private readonly _targets: readonly LogTarget[];\n\n constructor(\n target: LogTarget | readonly LogTarget[] = new ConsoleTarget(),\n context: JsonObject = {}\n ) {\n this._context = context;\n this._targets = Array.isArray(target) ? target : [target];\n\n const minLevel: number = Math.min(...this._targets.map((t) => t.level));\n\n const noop = () => {};\n const makeLogFn = (lvl: LogLevel) => (arg: string | Error) =>\n this._targets.forEach((target) => {\n if (target.level <= lvl) {\n target.log(lvl, this._context, arg);\n }\n });\n\n this.o = {\n /* eslint-disable @typescript-eslint/no-unsafe-enum-comparison */\n debug: minLevel <= LogLevel.DEBUG ? makeLogFn(LogLevel.DEBUG) : undefined,\n info: minLevel <= LogLevel.INFO ? makeLogFn(LogLevel.INFO) : undefined,\n warn:\n minLevel <= LogLevel.WARNING ? makeLogFn(LogLevel.WARNING) : undefined,\n error: minLevel <= LogLevel.ERROR ? makeLogFn(LogLevel.ERROR) : undefined,\n /* eslint-enable @typescript-eslint/no-unsafe-enum-comparison */\n };\n\n this.debug = this.o.debug ?? noop;\n this.info = this.o.info ?? noop;\n this.warn = this.o.warn ?? noop;\n this.error = this.o.error ?? noop;\n }\n\n /**\n * Creates a new Logger instance with the given extra context applied. All\n * log calls made from that new Logger will carry all current _and_ the extra\n * context, with the extra context taking precedence. Assign an explicit\n * `undefined` value to a key to \"remove\" it from the context.\n */\n withContext(extra: JsonObject): Logger {\n const combined: JsonObject = { ...this._context, ...extra };\n return new Logger(this._targets, combined);\n }\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n/* eslint-disable @typescript-eslint/explicit-module-boundary-types */\n/* eslint-disable @typescript-eslint/require-await */\nimport type {\n Json,\n JsonObject,\n NodeMap,\n NodeStream,\n PlainLsonObject,\n SerializedChild,\n SerializedCrdt,\n SerializedObject,\n SerializedRootObject,\n} from \"@liveblocks/core\";\nimport { asPos, CrdtType, isRootStorageNode, nn } from \"@liveblocks/core\";\nimport { ifilter, imap } from \"itertools\";\n\nimport type { YDocId } from \"~/decoders/y-types\";\nimport { plainLsonToNodeStream } from \"~/formats/PlainLson\";\nimport type {\n IReadableSnapshot,\n IStorageDriver,\n IStorageDriverNodeAPI,\n} from \"~/interfaces\";\nimport { NestedMap } from \"~/lib/NestedMap\";\nimport { quote } from \"~/lib/text\";\nimport { makeInMemorySnapshot } from \"~/makeInMemorySnapshot\";\nimport type { Pos } from \"~/types\";\n\nfunction buildRevNodes(nodeStream: NodeStream) {\n const result = new NestedMap<string, string, string>();\n for (const node of nodeStream) {\n if (isRootStorageNode(node)) continue;\n\n // Highest node id wins in case of conflict (deterministic across backends)\n const [id, crdt] = node;\n const existing = result.get(crdt.parentId, crdt.parentKey);\n if (existing === undefined || id > existing) {\n result.set(crdt.parentId, crdt.parentKey, id);\n }\n }\n return result;\n}\n\n/**\n * Builds the reverse node index, and corrects any data corruption found\n * along the way.\n */\nfunction buildReverseLookup(nodes: NodeMap) {\n const revNodes = buildRevNodes(nodes as NodeStream);\n\n const queue: string[] = [\"root\"];\n const reachableNodes: Set<string> = new Set();\n\n while (queue.length > 0) {\n const nodeId = queue.pop()!;\n const node = nn(nodes.get(nodeId));\n\n if (node.type === CrdtType.OBJECT) {\n for (const key of revNodes.keysAt(nodeId)) {\n delete node.data[key]; // Remove static data that conflicts with child nodes\n }\n }\n\n if (node.type !== CrdtType.REGISTER) {\n queue.push(...revNodes.valuesAt(nodeId));\n } else {\n const parent = nodes.get(node.parentId);\n if (parent?.type === CrdtType.OBJECT) {\n continue;\n }\n }\n\n reachableNodes.add(nodeId);\n }\n\n // Delete unreachable nodes (safe to delete from Map during iteration)\n let deletedCount = 0;\n for (const [id] of nodes) {\n if (!reachableNodes.has(id)) {\n nodes.delete(id);\n deletedCount++;\n }\n }\n\n // If no nodes were dropped (99% happy path), revNodes is correct already.\n // Otherwise, recompute it.\n return deletedCount === 0 ? revNodes : buildRevNodes(nodes as NodeStream);\n}\n\nfunction hasStaticDataAt(\n node: SerializedCrdt,\n key: string\n): node is SerializedObject | SerializedRootObject {\n return (\n node.type === CrdtType.OBJECT &&\n Object.prototype.hasOwnProperty.call(node.data, key) &&\n node.data[key] !== undefined\n );\n}\n\n/**\n * Implements the most basic in-memory store. Used if no explicit store is\n * provided.\n */\nexport class InMemoryDriver implements IStorageDriver {\n private _nextActor;\n private _nodes: NodeMap;\n private _metadb: Map<string, Json>;\n private _ydb: Map<string, Uint8Array>;\n\n constructor(options?: {\n initialActor?: number;\n initialNodes?: Iterable<[string, SerializedCrdt]>;\n }) {\n this._nodes = new Map();\n this._metadb = new Map();\n this._ydb = new Map();\n\n this._nextActor = options?.initialActor ?? -1;\n\n for (const [key, value] of options?.initialNodes ?? []) {\n this._nodes.set(key, value);\n }\n }\n\n raw_iter_nodes() {\n return this._nodes[Symbol.iterator]();\n }\n\n /** Deletes all nodes and replaces them with the given document. */\n DANGEROUSLY_reset_nodes(doc: PlainLsonObject) {\n this._nodes.clear();\n for (const [id, node] of plainLsonToNodeStream(doc)) {\n this._nodes.set(id, node);\n }\n }\n\n async get_meta(key: string) {\n return this._metadb.get(key);\n }\n async put_meta(key: string, value: Json) {\n this._metadb.set(key, value);\n }\n async delete_meta(key: string) {\n this._metadb.delete(key);\n }\n\n next_actor() {\n return ++this._nextActor;\n }\n\n async iter_y_updates(docId: YDocId) {\n const prefix = `${docId}@|@`;\n return imap(\n ifilter(this._ydb.entries(), ([k]) => k.startsWith(prefix)),\n ([k, v]) => [k.slice(prefix.length), v] as [string, Uint8Array]\n );\n }\n async write_y_updates(docId: YDocId, key: string, data: Uint8Array) {\n this._ydb.set(`${docId}@|@${key}`, data);\n }\n async delete_y_updates(docId: YDocId, keys: string[]) {\n for (const key of keys) {\n this._ydb.delete(`${docId}@|@${key}`);\n }\n }\n\n /** @private Only use this in unit tests, never in production. */\n async DANGEROUSLY_wipe_all_y_updates() {\n this._ydb.clear();\n }\n\n // Intercept load_nodes_api to add caching layer\n load_nodes_api(): IStorageDriverNodeAPI {\n // For the in-memory backend, this._nodes IS the \"on-disk\" storage,\n // so we operate on it directly (no separate cache needed).\n const nodes = this._nodes;\n if (!nodes.has(\"root\")) {\n nodes.set(\"root\", { type: CrdtType.OBJECT, data: {} });\n }\n\n const revNodes = buildReverseLookup(nodes);\n\n function get_next_sibling(parentId: string, pos: Pos): Pos | undefined {\n let nextPos: Pos | undefined;\n // Find the smallest position greater than current\n for (const siblingKey of revNodes.keysAt(parentId)) {\n const siblingPos = asPos(siblingKey);\n if (\n siblingPos > pos &&\n (nextPos === undefined || siblingPos < nextPos)\n ) {\n nextPos = siblingPos;\n }\n }\n return nextPos;\n }\n\n /**\n * Inserts a node in the storage tree, deleting any nodes that already exist\n * under this key (including all of its children), if any.\n */\n async function set_child(\n id: string,\n node: SerializedChild,\n allowOverwrite = false\n ): Promise<void> {\n const parentNode = nodes.get(node.parentId);\n // Reject orphans - parent must exist\n if (parentNode === undefined) {\n throw new Error(`No such parent ${quote(node.parentId)}`);\n }\n\n if (\n node.type === CrdtType.REGISTER &&\n parentNode.type === CrdtType.OBJECT\n ) {\n throw new Error(\"Cannot add register under object\");\n }\n\n const conflictingSiblingId = revNodes.get(node.parentId, node.parentKey);\n if (conflictingSiblingId !== id) {\n // Conflict!\n const parentNode = nodes.get(node.parentId);\n const hasConflictingData =\n parentNode !== undefined &&\n hasStaticDataAt(parentNode, node.parentKey);\n if (conflictingSiblingId !== undefined || hasConflictingData) {\n if (allowOverwrite) {\n delete_child_key(node.parentId, node.parentKey);\n } else {\n throw new Error(`Key ${quote(node.parentKey)} already exists`); // prettier-ignore\n }\n }\n\n // Finally, modify revNodes\n revNodes.set(node.parentId, node.parentKey, id);\n }\n\n nodes.set(id, node);\n }\n\n /**\n * Conceptually this is like \"detaching\" the node from its parent, and\n * \"reattaching\" it at the new position.\n *\n * However, this is a native operation, because doing a naive\n * delete-then-insert would would immediately destroy all (grand)children\n * when it's deleted.\n */\n async function move_sibling(id: string, newPos: Pos): Promise<void> {\n const node = nodes.get(id);\n if (node?.parentId === undefined) {\n return;\n }\n\n // If there is a conflicting sibling at the new position, disallow the move\n if (revNodes.has(node.parentId, newPos))\n throw new Error(`Pos ${quote(newPos)} already taken`); // prettier-ignore\n\n revNodes.delete(node.parentId, node.parentKey);\n const newNode = { ...node, parentKey: newPos };\n nodes.set(id, newNode);\n revNodes.set(node.parentId, newPos, id);\n }\n\n /**\n * Sets some static data on a node. The node must be an OBJECT node, or this\n * method will be a no-op.\n *\n * If any keys exist that also conflict with a child node, then the conflict\n * mode will determine what will happen. By default, an error will be thrown.\n * But if `allowOverwrite` is set to true, the conflicting child node (and\n * its entire subtree) will be deleted to make room for the new static data.\n */\n async function set_object_data(\n id: string,\n data: JsonObject,\n allowOverwrite = false\n ): Promise<void> {\n const node = nodes.get(id);\n if (node?.type !== CrdtType.OBJECT) {\n // Nothing to do\n return;\n }\n\n for (const key of Object.keys(data)) {\n // Handle if conflict!\n const childId = revNodes.get(id, key);\n if (childId !== undefined) {\n if (allowOverwrite) {\n delete_node(childId);\n } else {\n throw new Error(`Child node already exists under ${quote(key)}`); // prettier-ignore\n }\n }\n }\n\n nodes.set(id, { ...node, data: { ...node.data, ...data } });\n }\n\n /**\n * Delete a node from the tree, including all of its children.\n */\n function delete_node(id: string): void {\n const node = nodes.get(id);\n if (node?.parentId === undefined) {\n return;\n }\n\n // Delete the entry in the parent's children administration for this node\n revNodes.delete(node.parentId, node.parentKey);\n\n // Now proceed to deleting the node tree recursively\n const queue = [id];\n while (queue.length > 0) {\n const currid = queue.pop()!;\n queue.push(...revNodes.valuesAt(currid));\n nodes.delete(currid);\n revNodes.deleteAll(currid);\n }\n }\n\n /**\n * Deletes the child key under a given node, whether it's a static object\n * field, or a child node.\n */\n function delete_child_key(id: string, key: string): void {\n // At most one of these will do something, the other is a no-op\n const node = nodes.get(id);\n if (node !== undefined && hasStaticDataAt(node, key)) {\n const { [key]: _, ...rest } = node.data;\n nodes.set(id, { ...node, data: rest });\n }\n\n const childId = revNodes.get(id, key);\n if (childId !== undefined) {\n delete_node(childId);\n }\n }\n\n const api: IStorageDriverNodeAPI = {\n /**\n * Return the node with the given id, or undefined if no such node exists.\n * Must always return a valid root node for id=\"root\", even if empty.\n */\n get_node: (id) => nodes.get(id),\n\n /**\n * Yield all nodes as [id, node] pairs. Must always include the root node.\n */\n iter_nodes: () => nodes as NodeStream,\n\n /**\n * Return true iff a node with the given id exists. Must return true for \"root\".\n */\n has_node: (id) => nodes.has(id),\n\n /**\n * Return the id of the child node at (parentId, parentKey), or undefined if\n * none. Only checks child nodes registered via set_child, NOT static data\n * keys on OBJECT nodes.\n */\n get_child_at: (id, key) => revNodes.get(id, key),\n\n /**\n * Return true iff a child node exists at (parentId, parentKey). Static data\n * keys on OBJECT nodes do not count—return false for those.\n */\n has_child_at: (id, key) => revNodes.has(id, key),\n\n /**\n * Return the position of the closest sibling \"to the right\" of `pos` under\n * parentId, or undefined if no such sibling exists. The given `pos` may, but\n * does not have to exist already. Positions compare lexicographically.\n */\n get_next_sibling,\n\n /**\n * Insert a child node with the given id.\n *\n * If allowOverwrite=false (default): throw if a node with this id exists.\n * If allowOverwrite=true: replace any existing node at this id, deleting its\n * entire subtree if it has children.\n */\n set_child,\n\n /**\n * Change a node's parentKey, effectively repositioning the node within its\n * parent. The new position must be free.\n * Throw if another node already occupies (parentId, newPos).\n */\n move_sibling,\n\n /**\n * Delete a node and its entire subtree recursively.\n * Ignore if id=\"root\" (root is immortal).\n */\n delete_node,\n\n /**\n * Delete a key from node `id`. Handle two cases:\n *\n * 1. If id is an OBJECT with `key` in its data: remove that data field.\n * 2. If a child exists at (id, key): delete that child and all its\n * descendants recursively.\n *\n * No-op if neither applies or if the node doesn't exist.\n */\n delete_child_key,\n\n /**\n * Replace the data object of an OBJECT node.\n *\n * If allowOverwrite=false (default): throw if any key in `data` conflicts\n * with an existing child's parentKey.\n * If allowOverwrite=true: first delete any conflicting children (and their\n * entire subtrees), then set the data.\n */\n set_object_data,\n\n /**\n * Return a readable snapshot of the storage tree.\n *\n * @param lowMemory When true, the call site hints that the snapshot should\n * be optimized for lower memory consumption, even if that means slower\n * access.\n */\n get_snapshot(_lowMemory?: boolean): IReadableSnapshot {\n return makeInMemorySnapshot(nodes);\n },\n };\n return api;\n }\n}\n\nexport function makeNewInMemoryDriver(options?: {\n initialActor?: number;\n initialNodes?: Iterable<[string, SerializedCrdt]>;\n}): IStorageDriver {\n return new InMemoryDriver(options);\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n/**\n * Wraps single-quotes around any string value. Useful for displaying field\n * names or other identifiers in error messages or logs.\n *\n * Examples:\n * quote(\"hi\") // \"'hi'\"\n * quote(\"i'm\") // \"'i'm'\"\n *\n * Note: no \"escaping\" happens here to the string value. This is because this\n * is intended to be used for human consumption, not machine consumption.\n */\nexport function quote(value: string | undefined): string {\n return value !== undefined ? `'${value}'` : \"???\";\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type {\n Awaitable,\n SerializedChild,\n SerializedCrdt,\n} from \"@liveblocks/core\";\nimport {\n asPos,\n assertNever,\n CrdtType,\n makePosition,\n OpCode,\n} from \"@liveblocks/core\";\n\nimport type { IStorageDriver, IStorageDriverNodeAPI } from \"~/interfaces\";\nimport type { Logger } from \"~/lib/Logger\";\nimport type {\n ClientWireOp,\n CreateOp,\n DeleteCrdtOp,\n DeleteObjectKeyOp,\n FixOp,\n HasOpId,\n SetParentKeyOp,\n UpdateObjectOp,\n} from \"~/protocol\";\nimport type { Pos } from \"~/types\";\n\ntype ApplyOpResult = OpAccepted | OpIgnored;\n\nexport type OpAccepted = {\n action: \"accepted\";\n op: ClientWireOp;\n fix?: FixOp;\n};\n\nexport type OpIgnored = {\n action: \"ignored\";\n ignoredOpId?: string;\n};\n\nfunction accept(op: ClientWireOp, fix?: FixOp): OpAccepted {\n return { action: \"accepted\", op, fix };\n}\n\nfunction ignore(ignoredOp: ClientWireOp): OpIgnored {\n return { action: \"ignored\", ignoredOpId: ignoredOp.opId };\n}\n\nfunction nodeFromCreateChildOp(op: CreateOp): SerializedChild {\n switch (op.type) {\n case OpCode.CREATE_LIST:\n return {\n type: CrdtType.LIST,\n parentId: op.parentId,\n parentKey: op.parentKey,\n };\n\n case OpCode.CREATE_MAP:\n return {\n type: CrdtType.MAP,\n parentId: op.parentId,\n parentKey: op.parentKey,\n };\n\n case OpCode.CREATE_OBJECT:\n return {\n type: CrdtType.OBJECT,\n parentId: op.parentId,\n parentKey: op.parentKey,\n data: op.data,\n };\n\n case OpCode.CREATE_REGISTER:\n return {\n type: CrdtType.REGISTER,\n parentId: op.parentId,\n parentKey: op.parentKey,\n data: op.data,\n };\n\n // istanbul ignore next\n default:\n return assertNever(op, \"Unknown op code\");\n }\n}\n\nexport class Storage {\n // The actual underlying storage API (could be backed by in-memory store,\n // SQLite, Redis, Postgres, Cloudflare Durable Object Storage, etc.)\n private readonly coreDriver: IStorageDriver;\n private _loadedDriver: IStorageDriverNodeAPI | undefined;\n\n constructor(coreDriver: IStorageDriver) {\n this.coreDriver = coreDriver;\n }\n\n // -------------------------------------------------------------------------\n // Public API (for Storage)\n // -------------------------------------------------------------------------\n\n get loadedDriver(): IStorageDriverNodeAPI {\n if (this._loadedDriver === undefined) {\n throw new Error(\"Cannot access tree before it's been loaded\");\n }\n return this._loadedDriver;\n }\n\n // REFACTOR NOTE: Eventually raw_iter_nodes has to be removed here\n raw_iter_nodes(): Awaitable<Iterable<[string, SerializedCrdt]>> {\n return this.coreDriver.raw_iter_nodes();\n }\n\n /**\n * Load the room data from object storage into memory. Persisted room\n * data consists of the main node map, which represents the Liveblocks\n * Storage tree, and special keys where we store usage metrics, or room\n * metadata.\n */\n async load(logger: Logger): Promise<void> {\n this._loadedDriver = await this.coreDriver.load_nodes_api(logger);\n }\n\n unload(): void {\n this._loadedDriver = undefined;\n }\n\n /**\n * Applies a batch of Ops.\n */\n async applyOps(ops: ClientWireOp[]): Promise<ApplyOpResult[]> {\n const results: ApplyOpResult[] = [];\n for (const op of ops) {\n results.push(await this.applyOp(op));\n }\n return results;\n }\n\n // -------------------------------------------------------------------------\n // Private APIs (for Storage)\n // -------------------------------------------------------------------------\n\n /**\n * Applies a single Op.\n */\n private async applyOp(op: ClientWireOp): Promise<ApplyOpResult> {\n switch (op.type) {\n case OpCode.CREATE_LIST:\n case OpCode.CREATE_MAP:\n case OpCode.CREATE_REGISTER:\n case OpCode.CREATE_OBJECT:\n return await this.applyCreateOp(op);\n\n case OpCode.UPDATE_OBJECT:\n return await this.applyUpdateObjectOp(op);\n\n case OpCode.SET_PARENT_KEY:\n return await this.applySetParentKeyOp(op);\n\n case OpCode.DELETE_OBJECT_KEY:\n return await this.applyDeleteObjectKeyOp(op);\n\n case OpCode.DELETE_CRDT:\n return await this.applyDeleteCrdtOp(op);\n\n // istanbul ignore next\n default:\n if (process.env.NODE_ENV === \"production\") {\n return ignore(op);\n } else {\n return assertNever(op, \"Invalid op\");\n }\n }\n }\n\n private async applyCreateOp(op: CreateOp & HasOpId): Promise<ApplyOpResult> {\n if (this.loadedDriver.has_node(op.id)) {\n // Node already exists, the operation is ignored\n return ignore(op);\n }\n\n const node = nodeFromCreateChildOp(op);\n\n const parent = this.loadedDriver.get_node(node.parentId);\n if (parent === undefined) {\n // Parent does not exist because the op is invalid or because it was deleted in race condition.\n return ignore(op);\n }\n\n // How to create this node in the node map depends on the parent node's type\n switch (parent.type) {\n case CrdtType.OBJECT:\n // Register children under object nodes are forbidden. We'll simply\n // ignore these Ops. This matches the eventual storage behavior: if\n // we'd persist them, they would get ignored when re-loading the\n // persisted room data into memory the next time the room loads.\n if (op.type === OpCode.CREATE_REGISTER) {\n return ignore(op);\n }\n // fall through\n\n case CrdtType.MAP:\n // Children of maps and objects require no special needs\n await this.loadedDriver.set_child(op.id, node, true);\n return accept(op);\n\n case CrdtType.LIST:\n // List items need special handling around conflicting resolution,\n // which depends on the users intention\n return this.createChildAsListItem(op, node);\n\n case CrdtType.REGISTER:\n // It's illegal for registers to have children\n return ignore(op);\n\n // istanbul ignore next\n default:\n return assertNever(parent, \"Unhandled CRDT type\");\n }\n }\n\n private async createChildAsListItem(\n op: CreateOp & HasOpId,\n node: SerializedChild\n ): Promise<ApplyOpResult> {\n let fix: FixOp | undefined;\n\n // The default intent, when not explicitly provided, is to insert, not set,\n // into the list.\n const intent: \"insert\" | \"set\" = op.intent ?? \"insert\";\n\n // istanbul ignore else\n if (intent === \"insert\") {\n const insertedParentKey = await this.insertIntoList(op.id, node);\n\n // If the inserted parent key is different from the input, it means there\n // was a conflict and the node has been inserted in an alternative free\n // list position. We should broadcast a modified Op to all clients that\n // has the modified position, and send a \"fix\" op back to the originating\n // client.\n if (insertedParentKey !== node.parentKey) {\n op = { ...op, parentKey: insertedParentKey };\n fix = {\n type: OpCode.SET_PARENT_KEY,\n id: op.id,\n parentKey: insertedParentKey,\n };\n return accept(op, fix);\n }\n\n // No conflict, node got inserted as intended\n return accept(op);\n }\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n else if (intent === \"set\") {\n // The intent here is to \"set\", not insert, into the list, replacing the\n // existing item that\n\n // Special handling required here. They will include a \"deletedId\" that\n // points to the object they expect to be replacing. If in the mean time,\n // that object disappeared there (because it was moved, for example), be\n // sure to delete it anyway.\n // We should not just trust the given value, because we're about to\n // delete a node. It's only safe to delete the node if it indeed is\n // a sibling of the current node.\n const deletedId =\n op.deletedId !== undefined &&\n op.deletedId !== op.id &&\n this.loadedDriver.get_node(op.deletedId)?.parentId === node.parentId\n ? op.deletedId\n : undefined;\n\n if (deletedId !== undefined) {\n await this.loadedDriver.delete_node(deletedId);\n }\n\n const prevItemId = this.loadedDriver.get_child_at(\n node.parentId,\n node.parentKey\n );\n if (prevItemId !== undefined && prevItemId !== deletedId) {\n // If this \"set\" operation indeed removed an item, but it wasn't the\n // expected `deletedId`, let the invoking client know that they'll\n // have to delete this object, too.\n fix = {\n type: OpCode.DELETE_CRDT,\n id: prevItemId,\n };\n }\n\n await this.loadedDriver.set_child(op.id, node, true);\n\n return accept(op, fix);\n } else {\n return assertNever(intent, \"Invalid intent\");\n }\n }\n\n private async applyDeleteObjectKeyOp(\n op: DeleteObjectKeyOp & HasOpId\n ): Promise<ApplyOpResult> {\n await this.loadedDriver.delete_child_key(op.id, op.key);\n return accept(op);\n }\n\n private async applyUpdateObjectOp(\n op: UpdateObjectOp & HasOpId\n ): Promise<ApplyOpResult> {\n await this.loadedDriver.set_object_data(op.id, op.data, true);\n return accept(op);\n }\n\n private async applyDeleteCrdtOp(\n op: DeleteCrdtOp & HasOpId\n ): Promise<ApplyOpResult> {\n await this.loadedDriver.delete_node(op.id);\n return accept(op);\n }\n\n private async applySetParentKeyOp(\n op: SetParentKeyOp & HasOpId\n ): Promise<ApplyOpResult> {\n const newPosition = await this.moveToPosInList(op.id, op.parentKey);\n if (newPosition === undefined) {\n // The operation got rejected because it didn't make sense, ignore it\n return ignore(op);\n }\n\n // If the inserted node is different from the input, it means there was\n // a conflict and the node has been inserted in a new, free, list position.\n // We should broadcast a modified Op to all clients that has the modified\n // position, and send a \"fix\" op back to the originating client.\n if (newPosition !== op.parentKey) {\n const modifiedOp = { ...op, parentKey: newPosition };\n const fix: FixOp = {\n type: OpCode.SET_PARENT_KEY,\n id: op.id,\n parentKey: newPosition,\n };\n return accept(modifiedOp, fix);\n } else {\n return accept(op);\n }\n }\n\n /**\n * Inserts a new node in the storage tree, under a list parent. If an\n * existing sibling node already exist under this key, however, it will look\n * for another free position under that parent and insert it under\n * a different parent key that is guaranteed to be available.\n *\n * Returns the key that was used for the insertion.\n */\n private async insertIntoList(\n id: string,\n node: SerializedChild\n ): Promise<string> {\n // First, compute the key to use to insert this node\n const key = this.findFreeListPosition(node.parentId, asPos(node.parentKey));\n if (key !== node.parentKey) {\n node = { ...node, parentKey: key };\n }\n await this.loadedDriver.set_child(id, node);\n return node.parentKey;\n }\n\n /**\n * Tries to move a node to the given position under the same parent. If\n * a conflicting sibling node already exist at this position, it will use\n * another free position instead, to avoid the conflict.\n *\n * Returns the position (parentKey) that the node was eventually placed at.\n * If the node could be inserted without conflict, it will return the same\n * parentKey position.\n *\n * Will return `undefined` if this action could not be interpreted. Will be\n * a no-op for non-list items.\n */\n private async moveToPosInList(\n id: string,\n targetKey: string\n ): Promise<string | undefined> {\n const node = this.loadedDriver.get_node(id);\n if (node?.parentId === undefined) {\n return; /* reject */\n }\n\n if (this.loadedDriver.get_node(node.parentId)?.type !== CrdtType.LIST) {\n // SetParentKeyOp is a no-op for all nodes, except list items\n return; /* reject */\n }\n\n if (node.parentKey === targetKey) {\n // Already there\n return targetKey; /* no-op */\n }\n\n // First, compute the key to use to insert this node\n const key = this.findFreeListPosition(node.parentId, asPos(targetKey));\n if (key !== node.parentKey) {\n await this.loadedDriver.move_sibling(id, key);\n }\n return key;\n }\n\n /**\n * Checks whether the given parentKey is a \"free position\" under the\n * parentId, i.e. there are no siblings that have the same key. If a sibling\n * exists under that key, it tries to generate new positions until it finds\n * a free slot, and returns that. The returned value is therefore always safe\n * to use as parentKey.\n */\n private findFreeListPosition(parentId: string, parentPos: Pos): Pos {\n if (!this.loadedDriver.has_child_at(parentId, parentPos)) {\n return parentPos;\n }\n\n const currPos = parentPos;\n const nextPos = this.loadedDriver.get_next_sibling(parentId, currPos);\n if (nextPos !== undefined) {\n return makePosition(currPos, nextPos); // Between current and next\n } else {\n return makePosition(currPos); // After current (fallback)\n }\n }\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { DefaultMap } from \"@liveblocks/core\";\nimport { Base64 } from \"js-base64\";\nimport { nanoid } from \"nanoid\";\nimport * as Y from \"yjs\";\n\nimport type { Guid, YDocId } from \"~/decoders\";\nimport { ROOT_YDOC_ID } from \"~/decoders\";\nimport type { IStorageDriver } from \"~/interfaces\";\nimport type { Logger } from \"~/lib/Logger\";\n\n// How big an update can be until we compress all individual updates into\n// a single vector and persist that instead (i.e. when we trigger \"garbage\n// collection\")\nconst MAX_Y_UPDATE_SIZE = 100_000;\n\ntype YUpdateInfo = {\n currentKey: string;\n lastVector: Uint8Array | undefined;\n};\n\nexport class YjsStorage {\n private readonly driver: IStorageDriver;\n\n private readonly doc: Y.Doc = new Y.Doc(); // the root document\n private readonly lastUpdatesById = new Map<YDocId, YUpdateInfo>();\n private readonly lastSnapshotById = new Map<YDocId, Y.Snapshot>();\n // Keeps track of which keys are loaded, so we can clean them up without calling `.list()`\n private readonly keysById = new DefaultMap<YDocId, Set<string>>(\n () => new Set()\n );\n private readonly initPromisesById: Map<YDocId, Promise<Y.Doc>> = new Map();\n\n constructor(driver: IStorageDriver) {\n this.driver = driver;\n this.doc.on(\"subdocs\", ({ removed }) => {\n removed.forEach((subdoc: Y.Doc) => {\n subdoc.destroy(); // will remove listeners\n });\n });\n }\n\n // ------------------------------------------------------------------------------------\n // Public API\n // ------------------------------------------------------------------------------------\n\n public async getYDoc(docId: YDocId): Promise<Y.Doc> {\n const doc = await this.loadDocByIdIfNotAlreadyLoaded(docId);\n return doc;\n }\n\n /**\n * If passed a state vector, an update with diff will be returned, if not the entire doc is returned.\n *\n * @param stateVector a base64 encoded target state vector created by running Y.encodeStateVector(Doc) on the client\n * @returns a base64 encoded array of YJS updates\n */\n public async getYDocUpdate(\n logger: Logger,\n stateVector: string = \"\",\n guid?: Guid,\n isV2: boolean = false\n ): Promise<string | null> {\n const update = await this.getYDocUpdateBinary(\n logger,\n stateVector,\n guid,\n isV2\n );\n if (!update) return null;\n return Base64.fromUint8Array(update);\n }\n\n public async getYDocUpdateBinary(\n logger: Logger,\n stateVector: string = \"\",\n guid?: Guid,\n isV2: boolean = false\n ): Promise<Uint8Array | null> {\n const doc = guid !== undefined ? await this.getYSubdoc(guid) : this.doc;\n if (!doc) {\n return null;\n }\n let encodedTargetVector;\n try {\n // if given a state vector, attempt to decode it a single diffed update\n encodedTargetVector =\n stateVector.length > 0 ? Base64.toUint8Array(stateVector) : undefined;\n } catch (e) {\n logger.warn(\n \"Could not get update from passed vector, returning all updates\"\n );\n }\n if (isV2) {\n return Y.encodeStateAsUpdateV2(doc, encodedTargetVector);\n }\n return Y.encodeStateAsUpdate(doc, encodedTargetVector);\n }\n\n public async getYStateVector(guid?: Guid): Promise<string | null> {\n const doc = guid !== undefined ? await this.getYSubdoc(guid) : this.doc;\n if (!doc) {\n return null;\n }\n return Base64.fromUint8Array(Y.encodeStateVector(doc));\n }\n\n public async getSnapshotHash(options: {\n guid?: Guid;\n isV2?: boolean;\n }): Promise<string | null> {\n const doc =\n options.guid !== undefined\n ? await this.getYSubdoc(options.guid)\n : this.doc;\n if (!doc) {\n return null;\n }\n const snapshot = this._getOrPutLastSnapshot(doc);\n return this.calculateSnapshotHash(snapshot, { isV2: options.isV2 });\n }\n\n /**\n * @param update base64 encoded uint8array\n * @returns\n */\n public async addYDocUpdate(\n logger: Logger,\n update: string | Uint8Array,\n guid?: Guid,\n isV2?: boolean\n ): Promise<{ isUpdated: boolean; snapshotHash: string }> {\n const doc = guid !== undefined ? await this.getYSubdoc(guid) : this.doc;\n if (!doc) {\n throw new Error(`YDoc with guid ${guid} not found`);\n }\n\n try {\n // takes a snapshot if none is stored in memory - NOTE: snapshots are a combination of statevector + deleteset, not a full doc\n const beforeSnapshot = this._getOrPutLastSnapshot(doc);\n const updateAsU8 =\n typeof update === \"string\" ? Base64.toUint8Array(update) : update;\n const applyUpdate = isV2 ? Y.applyUpdateV2 : Y.applyUpdate;\n applyUpdate(doc, updateAsU8, \"client\");\n // put the new \"after update\" snapshot\n const afterSnapshot = this._putLastSnapshot(doc);\n // Check the snapshot before/after to see if the update had an effect\n const updated = !Y.equalSnapshots(beforeSnapshot, afterSnapshot);\n if (updated) {\n await this.handleYDocUpdate(doc);\n }\n\n return {\n isUpdated: updated,\n snapshotHash: await this.calculateSnapshotHash(afterSnapshot, { isV2 }),\n };\n } catch (e) {\n // The only reason this would happen is if a user would send bad data\n logger.warn(`Ignored bad YDoc update: ${String(e)}`);\n throw new Error(\n \"Bad YDoc update. Data is corrupted, or data does not match the encoding.\"\n );\n }\n }\n\n public loadDocByIdIfNotAlreadyLoaded(docId: YDocId): Promise<Y.Doc> {\n let loaded$ = this.initPromisesById.get(docId);\n let doc = docId === ROOT_YDOC_ID ? this.doc : this.findYSubdocByGuid(docId);\n if (!doc) {\n // An API call can load a subdoc without the root doc (this._doc) being loaded, we account for that by just instantiating a doc here.\n doc = new Y.Doc();\n }\n if (loaded$ === undefined) {\n loaded$ = this._loadYDocFromDurableStorage(doc, docId);\n this.initPromisesById.set(docId, loaded$);\n }\n return loaded$;\n }\n\n public async load(_logger: Logger): Promise<void> {\n await this.loadDocByIdIfNotAlreadyLoaded(ROOT_YDOC_ID);\n }\n\n /**\n * Unloads the Yjs documents from memory.\n */\n public unload(): void {\n // YYY Implement this later!\n // YYY We're currently never unloading data read into memory, but let's\n // sync this with the .unload() method from Storage, so there will not be\n // any surprises here later!\n //\n // this.doc = new Y.Doc();\n // this.initPromisesById.clear();\n // this.lastUpdatesById.clear();\n // this.keysById.clear();\n // this.initPromisesById.clear();\n }\n\n // ------------------------------------------------------------------------------------\n // Private APIs\n // ------------------------------------------------------------------------------------\n\n // NOTE: We could instead store the hash of snapshot instead of the whole snapshot to optimize memory usage.\n private _getOrPutLastSnapshot(doc: Y.Doc): Y.Snapshot {\n const docId: YDocId =\n doc.guid === this.doc.guid ? ROOT_YDOC_ID : (doc.guid as Guid);\n const snapshot = this.lastSnapshotById.get(docId);\n if (snapshot) {\n return snapshot;\n }\n return this._putLastSnapshot(doc);\n }\n\n // NOTE: We could instead store the hash of snapshot instead of the whole snapshot to optimize memory usage.\n private _putLastSnapshot(doc: Y.Doc): Y.Snapshot {\n const docId: YDocId =\n doc.guid === this.doc.guid ? ROOT_YDOC_ID : (doc.guid as Guid);\n const snapshot = Y.snapshot(doc);\n this.lastSnapshotById.set(docId, snapshot);\n return snapshot;\n }\n /**\n * Given a record of updates, merge them and compress if savings are significant\n */\n private _loadAndCompressYJSUpdates = async (\n docUpdates: Record<string, Uint8Array>,\n doc: Y.Doc,\n docId: YDocId\n ): Promise<void> => {\n // the percent we need to save to trigger re-writing storage, ie. only rewrite storage if we save more than 20%\n const SAVINGS_THRESHOLD = 0.2;\n // get all updates from disk\n const updates = Object.values(docUpdates);\n // uint8arrays size on disk is equal to their length, combine them to see how much we're using\n const sizeOnDisk = updates.reduce((acc, update) => {\n return acc + update.length;\n }, 0);\n if (updates.length > 0) {\n const docKeys = Object.keys(docUpdates);\n // keep track of keys in use\n this.keysById.set(docId, new Set(docKeys));\n\n const mergedUpdate = Y.mergeUpdates(updates);\n // Garbage collection won't happen unless we actually apply the update\n Y.applyUpdate(doc, mergedUpdate);\n\n // get the update so we can check out how big it is\n const garbageCollectedUpdate = Y.encodeStateAsUpdate(doc);\n\n if (\n garbageCollectedUpdate.length <\n sizeOnDisk * (1 - SAVINGS_THRESHOLD)\n ) {\n const newKey = nanoid();\n await this.driver.write_y_updates(\n docId,\n newKey,\n garbageCollectedUpdate\n );\n // delete all old keys, we're going to write new merged updates\n await this.driver.delete_y_updates(docId, docKeys);\n this.keysById.set(docId, new Set([newKey]));\n }\n }\n };\n\n private _loadYDocFromDurableStorage = async (\n doc: Y.Doc,\n docId: YDocId\n ): Promise<Y.Doc> => {\n const docUpdates = Object.fromEntries(\n await this.driver.iter_y_updates(docId)\n );\n await this._loadAndCompressYJSUpdates(docUpdates, doc, docId);\n // store the vector of the last update\n this.lastUpdatesById.set(docId, {\n currentKey: nanoid(),\n lastVector: Y.encodeStateVector(doc),\n });\n doc.emit(\"load\", [doc]); // sets the \"isLoaded\" to true on the doc\n\n return doc;\n };\n\n private findYSubdocByGuid(guid: Guid): Y.Doc | null {\n for (const subdoc of this.doc.getSubdocs()) {\n if (subdoc.guid === guid) {\n return subdoc;\n }\n }\n return null;\n }\n\n private async calculateSnapshotHash(\n snapshot: Y.Snapshot,\n { isV2 }: { isV2?: boolean }\n ): Promise<string> {\n const encodedSnapshot = isV2\n ? Y.encodeSnapshotV2(snapshot)\n : Y.encodeSnapshot(snapshot);\n return Base64.fromUint8Array(\n new Uint8Array(\n await crypto.subtle.digest(\"SHA-256\", new Uint8Array(encodedSnapshot))\n )\n );\n }\n\n // gets a subdoc, it will be loaded if not already loaded\n private async getYSubdoc(guid: Guid): Promise<Y.Doc | null> {\n const subdoc = this.findYSubdocByGuid(guid);\n if (!subdoc) {\n return null;\n }\n await this.loadDocByIdIfNotAlreadyLoaded(guid);\n return subdoc;\n }\n\n // When the YJS doc changes, update it in durable storage\n private async handleYDocUpdate(doc: Y.Doc): Promise<void> {\n const docId: YDocId =\n doc.guid === this.doc.guid ? ROOT_YDOC_ID : (doc.guid as Guid);\n const docUpdateInfo = this.lastUpdatesById.get(docId);\n // get the update since last vector\n const updateSinceLastVector = Y.encodeStateAsUpdate(\n doc,\n docUpdateInfo?.lastVector\n );\n // this should happen before the await on putYDoc to avoid race conditions\n // but we need the current key before, so store it here\n const storageKey = docUpdateInfo?.currentKey ?? nanoid();\n if (updateSinceLastVector.length > MAX_Y_UPDATE_SIZE) {\n // compress update, not using the vector, we want to write the whole doc\n const newKey = nanoid();\n await this.driver.write_y_updates(\n docId,\n newKey,\n Y.encodeStateAsUpdate(doc)\n );\n // delete all old keys on disk\n await this.driver.delete_y_updates(\n docId,\n Array.from(this.keysById.getOrCreate(docId))\n );\n // update the keys we have stored\n this.keysById.set(docId, new Set([newKey]));\n // future updates will write from this vector and to this key\n this.lastUpdatesById.set(docId, {\n currentKey: nanoid(), // start writing to a new key\n lastVector: Y.encodeStateVector(doc),\n });\n } else {\n // in this case, the update is small enough, just overwrite it\n await this.driver.write_y_updates(\n docId,\n storageKey,\n updateSinceLastVector\n );\n const keys = [storageKey];\n // keep track of keys used\n const currentKeys = this.keysById.getOrCreate(docId);\n for (const key of keys) {\n currentKeys.add(key);\n }\n }\n }\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n/**\n * Given a promise or promise factory, returns a 2-tuple of success or failure.\n * This pattern avoids having to build deeply nested try / catch clauses, where\n * success variables need to be defined as a `let` outside of the `try` block.\n *\n * Turns:\n *\n * let result;\n * try {\n * result = await doSomething();\n * } catch (error) {\n * // do something with error\n * }\n *\n * doAnotherThing(result);\n *\n * Into:\n *\n * const [result, error] = await tryCatch(doSomething());\n * if (error) {\n * // do something with error\n * }\n * doAnotherThing(result);\n *\n */\nexport async function tryCatch<T, E = Error>(\n promise: Promise<T> | (() => Promise<T>) | (() => T)\n): Promise<[T, undefined] | [undefined, E]> {\n try {\n const data = await (typeof promise === \"function\" ? promise() : promise);\n return [data, undefined];\n } catch (error) {\n return [undefined, error as E];\n }\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n/**\n * Like ES6 map, but also provides a unique reverse lookup index for values\n * stored in the map.\n *\n * Useful for code like:\n *\n * // Store a list of persons by their IDs, but each person's email must also\n * // be unique\n * const map = new UniqueMap((person) => person.email);\n * map.set(1, { name: 'John Doe', email: 'john@example.org' });\n * map.set(2, { name: 'John Foo', email: 'john@example.org' }); // Will error!\n * map.delete(1);\n * map.set(3, { name: 'Johnny', email: 'john@example.org' }); // Now it's allowed\n *\n * map.getReverseKey('john@example.org') // 3\n * map.getReverse('john@example.org') // { name: 'Johnny', email: 'john@example.org' }\n *\n */\nexport class UniqueMap<K, V, UK> extends Map<K, V> {\n // / \\\n // Primary key Unique key\n #_revMap: Map<UK, K>;\n #_keyFn: (value: V) => UK;\n\n constructor(\n keyFn: (value: V) => UK\n // entries?: readonly (readonly [K, V])[] | null\n ) {\n super(); // super(entries)\n this.#_keyFn = keyFn;\n this.#_revMap = new Map();\n }\n\n lookupPrimaryKey(uniqKey: UK): K | undefined {\n return this.#_revMap.get(uniqKey);\n }\n\n lookup(uniqKey: UK): V | undefined {\n const key = this.#_revMap.get(uniqKey);\n return key !== undefined ? this.get(key) : undefined;\n }\n\n set(key: K, value: V): this {\n const uniqKey = this.#_keyFn(value);\n const primaryKey = this.#_revMap.get(uniqKey);\n if (primaryKey !== undefined && primaryKey !== key) {\n throw new Error(`Unique key ${String(uniqKey)} already exists`);\n }\n this.#_revMap.set(uniqKey, key);\n return super.set(key, value);\n }\n\n delete(primaryKey: K): boolean {\n const value = this.get(primaryKey);\n if (value !== undefined) {\n const indexedKey = this.#_keyFn(value);\n this.#_revMap.delete(indexedKey);\n }\n return super.delete(primaryKey);\n }\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type { BaseUserMeta, JsonObject } from \"@liveblocks/core\";\nimport { ServerMsgCode } from \"@liveblocks/core\";\n\nimport type { RoomStateServerMsg } from \"~/protocol\";\n\n/**\n * Concatenates multiple Uint8Arrays into a single Uint8Array.\n */\nexport function concatUint8Arrays(arrays: Uint8Array[]): Uint8Array {\n const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const arr of arrays) {\n result.set(arr, offset);\n offset += arr.length;\n }\n return result;\n}\n\nexport function makeRoomStateMsg(\n actor: number,\n nonce: string,\n scopes: string[],\n users: Record<number, BaseUserMeta & { scopes: string[] }>,\n publicMeta?: JsonObject\n): RoomStateServerMsg<BaseUserMeta> {\n return {\n type: ServerMsgCode.ROOM_STATE,\n actor,\n nonce,\n scopes,\n users,\n meta: publicMeta ?? {},\n };\n}\n"],"mappings":"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","names":["constant","object","optional","string","taggedUnion","object","constant","optional","string","taggedUnion","snapshot","snapshot","CrdtType","snapshot","buildObject","buildNode","buildList","buildMap","emitObject","emit","emitList","emitMap","CrdtType","CrdtType","ProtocolVersion","assertNever","ClientMsgCode","OpCode","raise","ServerMsgCode","array","nanoid","raise","LogLevel","raise","target","CrdtType","isRootStorageNode","nn","isRootStorageNode","nn","CrdtType","parentNode","asPos","assertNever","CrdtType","makePosition","OpCode","op","OpCode","CrdtType","assertNever","asPos","makePosition","DefaultMap","DefaultMap","snapshot","applyUpdate","array","ServerMsgCode","OpCode","op","__debug","raise","nanoid","ClientMsgCode","assertNever"]}
1
+ {"version":3,"sources":["../src/decoders/ClientMsg.ts","../src/decoders/jsonYolo.ts","../src/decoders/Op.ts","../src/decoders/y-types.ts","../src/formats/LossyJson.ts","../src/formats/NodeStream.ts","../src/formats/PlainLson.ts","../src/makeInMemorySnapshot.ts","../src/lib/DefaultMap.ts","../src/lib/NestedMap.ts","../src/MetadataDB.ts","../src/protocol/ProtocolVersion.ts","../src/Room.ts","../src/lib/Logger.ts","../src/plugins/InMemoryDriver.ts","../src/lib/text.ts","../src/Storage.ts","../src/YjsStorage.ts","../src/lib/tryCatch.ts","../src/lib/UniqueMap.ts","../src/utils.ts"],"sourcesContent":["/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type { Json, JsonObject } from \"@liveblocks/core\";\nimport { ClientMsgCode } from \"@liveblocks/core\";\nimport type { Decoder } from \"decoders\";\nimport {\n array,\n boolean,\n constant,\n number,\n object,\n optional,\n string,\n taggedUnion,\n} from \"decoders\";\n\nimport type {\n BroadcastEventClientMsg,\n ClientMsg,\n FetchStorageClientMsg,\n FetchYDocClientMsg,\n UpdatePresenceClientMsg,\n UpdateStorageClientMsg,\n UpdateYDocClientMsg,\n} from \"~/protocol\";\n\nimport { jsonObjectYolo, jsonYolo } from \"./jsonYolo\";\nimport { op } from \"./Op\";\nimport type { YUpdate, YVector } from \"./y-types\";\nimport { guidDecoder } from \"./y-types\";\n\nconst updatePresenceClientMsg: Decoder<UpdatePresenceClientMsg<JsonObject>> =\n object({\n type: constant(ClientMsgCode.UPDATE_PRESENCE),\n data: jsonObjectYolo,\n targetActor: optional(number),\n });\n\nconst broadcastEventClientMsg: Decoder<BroadcastEventClientMsg<Json>> = object({\n type: constant(ClientMsgCode.BROADCAST_EVENT),\n event: jsonYolo,\n});\n\nconst fetchStorageClientMsg: Decoder<FetchStorageClientMsg> = object({\n type: constant(ClientMsgCode.FETCH_STORAGE),\n});\n\nconst updateStorageClientMsg: Decoder<UpdateStorageClientMsg> = object({\n type: constant(ClientMsgCode.UPDATE_STORAGE),\n ops: array(op),\n});\n\nconst fetchYDocClientMsg: Decoder<FetchYDocClientMsg> = object({\n type: constant(ClientMsgCode.FETCH_YDOC),\n vector: string.refineType<YVector>(),\n guid: optional(guidDecoder), // Don't specify to update the root doc\n v2: optional(boolean),\n});\n\nconst updateYDocClientMsg: Decoder<UpdateYDocClientMsg> = object({\n type: constant(ClientMsgCode.UPDATE_YDOC),\n update: string.refineType<YUpdate>(),\n guid: optional(guidDecoder), // Don't specify to update the root doc\n v2: optional(boolean),\n});\n\nexport const clientMsgDecoder: Decoder<ClientMsg<JsonObject, Json>> =\n taggedUnion(\"type\", {\n [ClientMsgCode.UPDATE_PRESENCE]: updatePresenceClientMsg,\n [ClientMsgCode.BROADCAST_EVENT]: broadcastEventClientMsg,\n [ClientMsgCode.FETCH_STORAGE]: fetchStorageClientMsg,\n [ClientMsgCode.UPDATE_STORAGE]: updateStorageClientMsg,\n [ClientMsgCode.FETCH_YDOC]: fetchYDocClientMsg,\n [ClientMsgCode.UPDATE_YDOC]: updateYDocClientMsg,\n }).describe(\"Must be a valid client message\");\n\nexport const transientClientMsgDecoder: Decoder<ClientMsg<JsonObject, Json>> =\n taggedUnion(\"type\", {\n // [ClientMsgCode.UPDATE_PRESENCE]: updatePresenceClientMsg,\n // [ClientMsgCode.BROADCAST_EVENT]: broadcastEventClientMsg,\n // [ClientMsgCode.FETCH_STORAGE]: fetchStorageClientMsg,\n [ClientMsgCode.UPDATE_STORAGE]: updateStorageClientMsg,\n // [ClientMsgCode.FETCH_YDOC]: fetchYDocClientMsg,\n // [ClientMsgCode.UPDATE_YDOC]: updateYDocClientMsg,\n }).describe(\"Must be a valid transient client message\");\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type { Json, JsonObject } from \"@liveblocks/core\";\nimport type { Decoder } from \"decoders\";\nimport { unknown } from \"decoders\";\n\n/**\n * Drop-in replacement for the `json` decoder from the decoders standard\n * library, but implemented as a no-op. This is, of course, only safe to use in\n * contexts where you know that the input already is valid JSON.\n *\n * You know this for sure, for example, if you're decoding the result of\n * a `JSON.parse()` call.\n *\n * Done for performance reasons!\n */\nexport const jsonYolo: Decoder<Json> = unknown as Decoder<Json>;\n\n/**\n * Drop-in replacement for the `jsonObject` decoder from the decoders standard\n * library, but implemented as just a check for plain old JavaScript object.\n * This is, of course, only safe to use in contexts where you know that the\n * input already is valid JSON.\n *\n * You know this for sure, for example, if you're decoding the result of\n * a `JSON.parse()` call.\n *\n * Done for performance reasons!\n */\nexport const jsonObjectYolo: Decoder<JsonObject> = jsonYolo.refine(\n (value): value is JsonObject =>\n value !== null && typeof value === \"object\" && !Array.isArray(value),\n \"Must be JSON object\"\n);\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { OpCode } from \"@liveblocks/core\";\nimport type { Decoder } from \"decoders\";\nimport { constant, object, optional, string, taggedUnion } from \"decoders\";\n\nimport type {\n ClientWireOp,\n CreateListOp,\n CreateMapOp,\n CreateObjectOp,\n CreateRegisterOp,\n DeleteCrdtOp,\n DeleteObjectKeyOp,\n SetParentKeyOp,\n UpdateObjectOp,\n} from \"~/protocol\";\n\nimport { jsonObjectYolo, jsonYolo } from \"./jsonYolo\";\n\ntype HasOpId = { opId: string };\n\nconst updateObjectOp: Decoder<UpdateObjectOp & HasOpId> = object({\n type: constant(OpCode.UPDATE_OBJECT),\n opId: string,\n id: string,\n data: jsonObjectYolo,\n});\n\nconst createObjectOp: Decoder<CreateObjectOp & HasOpId> = object({\n type: constant(OpCode.CREATE_OBJECT),\n opId: string,\n id: string,\n parentId: string,\n parentKey: string,\n data: jsonObjectYolo,\n intent: optional(constant(\"set\")),\n deletedId: optional(string),\n});\n\nconst createListOp: Decoder<CreateListOp & HasOpId> = object({\n type: constant(OpCode.CREATE_LIST),\n opId: string,\n id: string,\n parentId: string,\n parentKey: string,\n intent: optional(constant(\"set\")),\n deletedId: optional(string),\n});\n\nconst createMapOp: Decoder<CreateMapOp & HasOpId> = object({\n type: constant(OpCode.CREATE_MAP),\n opId: string,\n id: string,\n parentId: string,\n parentKey: string,\n intent: optional(constant(\"set\")),\n deletedId: optional(string),\n});\n\nconst createRegisterOp: Decoder<CreateRegisterOp & HasOpId> = object({\n type: constant(OpCode.CREATE_REGISTER),\n opId: string,\n id: string,\n parentId: string,\n parentKey: string,\n data: jsonYolo,\n intent: optional(constant(\"set\")),\n deletedId: optional(string),\n});\n\nconst deleteCrdtOp: Decoder<DeleteCrdtOp & HasOpId> = object({\n type: constant(OpCode.DELETE_CRDT),\n opId: string,\n id: string,\n});\n\nconst setParentKeyOp: Decoder<SetParentKeyOp & HasOpId> = object({\n type: constant(OpCode.SET_PARENT_KEY),\n opId: string,\n id: string,\n parentKey: string,\n});\n\nconst deleteObjectKeyOp: Decoder<DeleteObjectKeyOp & HasOpId> = object({\n type: constant(OpCode.DELETE_OBJECT_KEY),\n opId: string,\n id: string,\n key: string,\n});\n\nexport const op: Decoder<ClientWireOp> = taggedUnion(\"type\", {\n [OpCode.UPDATE_OBJECT]: updateObjectOp,\n [OpCode.CREATE_OBJECT]: createObjectOp,\n [OpCode.CREATE_LIST]: createListOp,\n [OpCode.CREATE_MAP]: createMapOp,\n [OpCode.CREATE_REGISTER]: createRegisterOp,\n [OpCode.DELETE_CRDT]: deleteCrdtOp,\n [OpCode.SET_PARENT_KEY]: setParentKeyOp,\n [OpCode.DELETE_OBJECT_KEY]: deleteObjectKeyOp,\n});\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type { Brand } from \"@liveblocks/core\";\nimport { uuid } from \"decoders\";\n\n/**\n * A guid, a unique identifier for a Yjs sub document.\n */\nexport type Guid = Brand<string, \"Guid\">;\n\nexport const guidDecoder = uuid.refineType<Guid>();\n\nexport const ROOT_YDOC_ID = \"root\";\nexport type YDocId = typeof ROOT_YDOC_ID | Guid /* unique ID for subdoc */;\n\n/**\n * Any string that is a valid base64 encoded YJS update.\n */\nexport type YUpdate = Brand<string, \"YUpdate\">;\n\n/**\n * Any string that is a valid base64 encoded YJS state vector.\n */\nexport type YVector = Brand<string, \"YVector\">;\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type { Json, JsonObject } from \"@liveblocks/core\";\nimport { CrdtType } from \"@liveblocks/core\";\n\nimport type { IReadableSnapshot } from \"~/interfaces\";\n\n// ---------------------------------------------------------------------------\n// Non-streaming version\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a storage snapshot to a simple JSON representation, returning a\n * full in-memory JsonObject. Faster than snapshotToLossyJson_lazy for\n * small/medium documents because the result can be passed straight to\n * JSON.stringify(). This format is lossy — the original storage structure\n * cannot be reconstructed from it, so it's output-only.\n */\nexport function snapshotToLossyJson_eager(\n snapshot: IReadableSnapshot\n): JsonObject {\n try {\n return buildObject(snapshot, \"root\", snapshot.get_root().data);\n } finally {\n snapshot.destroy();\n }\n}\n\nfunction buildNode(snapshot: IReadableSnapshot, id: string): Json {\n const node = snapshot.get_node(id);\n if (node.type === CrdtType.OBJECT) {\n return buildObject(snapshot, id, node.data);\n } else if (node.type === CrdtType.LIST) {\n return buildList(snapshot, id);\n } else if (node.type === CrdtType.MAP) {\n return buildMap(snapshot, id);\n } else {\n return node.data;\n }\n}\n\nfunction buildObject(\n snapshot: IReadableSnapshot,\n id: string,\n staticData: JsonObject\n): JsonObject {\n const data = Object.assign(Object.create(null), staticData) as JsonObject;\n for (const [key, childId] of snapshot.iter_children(id)) {\n data[key] = buildNode(snapshot, childId);\n }\n return data;\n}\n\nfunction buildList(snapshot: IReadableSnapshot, id: string): Json[] {\n const data: Json[] = [];\n for (const [_, childId] of snapshot.iter_children(id)) {\n data.push(buildNode(snapshot, childId));\n }\n return data;\n}\n\nfunction buildMap(snapshot: IReadableSnapshot, id: string): JsonObject {\n const data = Object.create(null) as JsonObject;\n for (const [key, childId] of snapshot.iter_children(id)) {\n data[key] = buildNode(snapshot, childId);\n }\n return data;\n}\n\n// ---------------------------------------------------------------------------\n// Streaming version\n// ---------------------------------------------------------------------------\n\n// Generator-of-strings type alias for brevity of signatures\ntype StringGen = Generator<string, void, never>;\n\n/**\n * Serialize a storage snapshot to a simple JSON representation. This format is\n * easy to consume but lossy — the original storage structure cannot be\n * reconstructed from it, so it's an output-only format. Slower than\n * snapshotToLossyJson_eager but can stream documents that don't fit entirely\n * in memory.\n *\n * This generator yields text chunks that together, when concatenated, form the\n * output JSON document.\n */\nexport function* snapshotToLossyJson_lazy(\n snapshot: IReadableSnapshot\n): StringGen {\n try {\n const staticJson = JSON.stringify(snapshot.get_root().data).slice(1, -1);\n yield* emitObject(snapshot, \"root\", staticJson);\n } finally {\n snapshot.destroy();\n }\n}\n\nfunction* emit(snapshot: IReadableSnapshot, id: string): StringGen {\n const node = snapshot.get_node(id);\n if (node.type === CrdtType.OBJECT) {\n yield* emitObject(snapshot, id, JSON.stringify(node.data).slice(1, -1));\n } else if (node.type === CrdtType.LIST) {\n yield* emitList(snapshot, id);\n } else if (node.type === CrdtType.MAP) {\n yield* emitMap(snapshot, id);\n } else if (node.type === CrdtType.REGISTER) {\n yield JSON.stringify(node.data);\n }\n}\n\n/**\n * @param staticJson - The object's static (non-CRDT) properties as a raw JSON\n * string without the surrounding braces, e.g. `\"foo\":1,\"bar\":\"hi\"`.\n *\n * Children are emitted _after_ the static properties. If a child key\n * collides with a static key (which shouldn't normally happen, but\n * defensively), the child wins because JSON.parse keeps the last value\n * for duplicate keys.\n */\nfunction* emitObject(\n snapshot: IReadableSnapshot,\n id: string,\n staticJson: string\n): StringGen {\n let comma = staticJson.length > 0;\n\n yield \"{\";\n yield staticJson;\n\n for (const [key, childId] of snapshot.iter_children(id)) {\n if (comma) yield \",\";\n else comma = true;\n\n yield `${JSON.stringify(key)}:`;\n yield* emit(snapshot, childId);\n }\n yield \"}\";\n}\n\nfunction* emitList(snapshot: IReadableSnapshot, id: string): StringGen {\n let comma = false;\n\n yield \"[\";\n for (const [_, childId] of snapshot.iter_children(id)) {\n if (comma) yield \",\";\n else comma = true;\n yield* emit(snapshot, childId);\n }\n yield \"]\";\n}\n\nfunction* emitMap(snapshot: IReadableSnapshot, id: string): StringGen {\n let comma = false;\n\n yield \"{\";\n for (const [key, childId] of snapshot.iter_children(id)) {\n if (comma) yield \",\";\n else comma = true;\n\n yield `${JSON.stringify(key)}:`;\n yield* emit(snapshot, childId);\n }\n yield \"}\";\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type { StorageNode } from \"@liveblocks/core\";\n\nimport type { IReadableSnapshot } from \"~/interfaces\";\n\n/**\n * Yield all nodes from a snapshot as [id, crdt] tuples.\n * Destroys the snapshot when done (or aborted).\n */\nexport function* snapshotToNodeStream(\n snapshot: IReadableSnapshot\n): Generator<StorageNode, void, never> {\n try {\n yield* snapshot.iter_all();\n } finally {\n snapshot.destroy();\n }\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type {\n JsonObject,\n ObjectStorageNode,\n PlainLson,\n PlainLsonFields,\n PlainLsonList,\n PlainLsonMap,\n PlainLsonObject,\n RootStorageNode,\n SerializedList,\n StorageNode,\n} from \"@liveblocks/core\";\nimport {\n assertNever,\n CrdtType,\n isJsonObject,\n makePosition,\n} from \"@liveblocks/core\";\n\nimport type { IReadableSnapshot } from \"~/interfaces\";\n\nconst SERVER_INIT_OP_PREFIX = \"si\";\n\nfunction generateId(state: { clock: number }) {\n return `${SERVER_INIT_OP_PREFIX}:${state.clock++}`;\n}\n\nfunction isSpecialPlainLsonValue(\n value: PlainLson\n): value is PlainLsonObject | PlainLsonMap | PlainLsonList {\n return isJsonObject(value) && value.liveblocksType !== undefined;\n}\n\n/**\n * Generator that yields NodeTuples for a JSON value.\n * Always yields parent nodes before their children.\n */\nfunction* iterJson(\n key: string,\n data: PlainLson,\n parent: StorageNode,\n state: { clock: number }\n): Generator<StorageNode, void, undefined> {\n if (isSpecialPlainLsonValue(data)) {\n switch (data.liveblocksType) {\n case \"LiveObject\":\n yield* iterObjectInner(key, data.data, parent, state);\n return;\n\n case \"LiveList\":\n yield* iterList(key, data.data, parent, state);\n return;\n\n case \"LiveMap\":\n yield* iterMap(key, data.data, parent, state);\n return;\n\n // istanbul ignore next\n default:\n assertNever(data, \"Unknown `liveblocksType` field\");\n }\n } else {\n yield [\n generateId(state),\n {\n type: CrdtType.REGISTER,\n data,\n parentId: parent[0],\n parentKey: key,\n },\n ];\n }\n}\n\n/**\n * Generator that yields NodeTuples for a LiveMap.\n * Yields the map node first, then its children.\n */\nfunction* iterMap(\n key: string,\n map: PlainLsonFields,\n parent: StorageNode,\n state: { clock: number }\n): Generator<StorageNode, void, undefined> {\n const mapTuple: StorageNode = [\n generateId(state),\n { type: CrdtType.MAP, parentId: parent[0], parentKey: key },\n ];\n\n // Yield the map node first (parent before children)\n yield mapTuple;\n\n // Then yield all children\n for (const [subKey, subValue] of Object.entries(map)) {\n yield* iterJson(subKey, subValue, mapTuple, state);\n }\n}\n\n/**\n * Generator that yields NodeTuples for a LiveList.\n * Yields the list node first, then its children.\n */\nfunction* iterList(\n key: string,\n list: PlainLson[],\n parent: StorageNode,\n state: { clock: number }\n): Generator<StorageNode, void, undefined> {\n const id = generateId(state);\n const crdt: SerializedList = {\n type: CrdtType.LIST,\n parentId: parent[0],\n parentKey: key,\n };\n const listTuple: StorageNode = [id, crdt];\n\n // Yield the list node first (parent before children)\n yield listTuple;\n\n // Then yield all children\n let position = makePosition();\n for (const subValue of list) {\n yield* iterJson(position, subValue, listTuple, state);\n position = makePosition(position);\n }\n}\n\n/**\n * Generator that yields NodeTuples for a LiveObject.\n * Yields the object node first, then its children.\n *\n * Note: The object's data field is populated with non-special values\n * (primitives, arrays, plain objects), while special values (LiveObject,\n * LiveList, LiveMap) are yielded as separate nodes.\n */\nfunction* iterObjectInner(\n key: string,\n value: PlainLsonFields,\n parent: StorageNode | null,\n state: { clock: number }\n): Generator<StorageNode, void, undefined> {\n // First pass: collect non-special data and identify special children\n const data: JsonObject = {};\n const specialChildren: Array<[string, PlainLson]> = [];\n\n for (const [subKey, subValue] of Object.entries(value)) {\n if (isSpecialPlainLsonValue(subValue)) {\n specialChildren.push([subKey, subValue]);\n } else {\n data[subKey] = subValue;\n }\n }\n\n // Create the object tuple with collected data\n const objectTuple: RootStorageNode | ObjectStorageNode =\n parent !== null\n ? [\n generateId(state),\n {\n type: CrdtType.OBJECT,\n data,\n parentId: parent[0],\n parentKey: key,\n },\n ]\n : [\"root\", { type: CrdtType.OBJECT, data }];\n\n // Yield the object node first (parent before children)\n yield objectTuple;\n\n // Then yield all special children\n for (const [subKey, subValue] of specialChildren) {\n yield* iterJson(subKey, subValue, objectTuple, state);\n }\n}\n\n/**\n * Transform a \"Plain LSON\" document to a lazy NodeStream. Used to initialize\n * the storage with a predefined state.\n * Always emits parent nodes before their children.\n */\nexport function* plainLsonToNodeStream(\n root: PlainLsonObject\n): Generator<StorageNode, void, undefined> {\n const state = { clock: 1 };\n yield* iterObjectInner(\"root\", root.data, null, state);\n}\n\n// ---------------------------------------------------------------------------\n// Non-streaming serialization: builds a full PlainLsonObject in memory.\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a storage snapshot to \"Plain LSON\" format, returning a full\n * in-memory PlainLsonObject. Faster than snapshotToPlainLson_lazy for\n * small/medium documents because the result can be passed straight to\n * JSON.stringify().\n */\nexport function snapshotToPlainLson_eager(\n snapshot: IReadableSnapshot\n): PlainLsonObject {\n try {\n return buildObject(snapshot, \"root\", snapshot.get_root().data);\n } finally {\n snapshot.destroy();\n }\n}\n\nfunction buildNode(snapshot: IReadableSnapshot, id: string): PlainLson {\n const node = snapshot.get_node(id);\n if (node.type === CrdtType.OBJECT) {\n return buildObject(snapshot, id, node.data);\n } else if (node.type === CrdtType.LIST) {\n return buildList(snapshot, id);\n } else if (node.type === CrdtType.MAP) {\n return buildMap(snapshot, id);\n } else {\n return node.data;\n }\n}\n\nfunction buildObject(\n snapshot: IReadableSnapshot,\n id: string,\n staticData: JsonObject\n): PlainLsonObject {\n // Static data values are Json, which is a subset of PlainLson\n const data: PlainLsonFields = Object.assign(\n Object.create(null),\n staticData\n ) as PlainLsonFields;\n for (const [key, childId] of snapshot.iter_children(id)) {\n data[key] = buildNode(snapshot, childId);\n }\n return { liveblocksType: \"LiveObject\", data };\n}\n\nfunction buildList(snapshot: IReadableSnapshot, id: string): PlainLsonList {\n const data: PlainLson[] = [];\n for (const [_, childId] of snapshot.iter_children(id)) {\n data.push(buildNode(snapshot, childId));\n }\n return { liveblocksType: \"LiveList\", data };\n}\n\nfunction buildMap(snapshot: IReadableSnapshot, id: string): PlainLsonMap {\n const data = Object.create(null) as PlainLsonFields;\n for (const [key, childId] of snapshot.iter_children(id)) {\n data[key] = buildNode(snapshot, childId);\n }\n return { liveblocksType: \"LiveMap\", data };\n}\n\n// ---------------------------------------------------------------------------\n// Streaming serialization: yields string chunks that concatenate to JSON.\n// ---------------------------------------------------------------------------\n\n// Generator-of-strings type alias for brevity of signatures\ntype StringGen = Generator<string, void, never>;\n\n/**\n * Serialize a storage snapshot to \"Plain LSON\" format. Yields string chunks\n * that, when concatenated, form a valid JSON string representing the storage\n * document. Slower than snapshotToPlainLson_eager but can stream documents\n * that don't fit entirely in memory.\n */\nexport function* snapshotToPlainLson_lazy(\n snapshot: IReadableSnapshot\n): StringGen {\n try {\n const staticJson = JSON.stringify(snapshot.get_root().data).slice(1, -1);\n yield* emitObject(snapshot, \"root\", staticJson);\n } finally {\n snapshot.destroy();\n }\n}\n\nfunction* emit(snapshot: IReadableSnapshot, id: string): StringGen {\n const node = snapshot.get_node(id);\n if (node.type === CrdtType.OBJECT) {\n yield* emitObject(snapshot, id, JSON.stringify(node.data).slice(1, -1));\n } else if (node.type === CrdtType.LIST) {\n yield* emitList(snapshot, id);\n } else if (node.type === CrdtType.MAP) {\n yield* emitMap(snapshot, id);\n } else if (node.type === CrdtType.REGISTER) {\n yield JSON.stringify(node.data);\n }\n}\n\n/**\n * @param staticJson - The object's static (non-CRDT) properties as a raw JSON\n * string without the surrounding braces, e.g. `\"foo\":1,\"bar\":\"hi\"`.\n *\n * Children are emitted _after_ the static properties. If a child key\n * collides with a static key (which shouldn't normally happen, but\n * defensively), the child wins because JSON.parse keeps the last value\n * for duplicate keys.\n */\nfunction* emitObject(\n snapshot: IReadableSnapshot,\n id: string,\n staticJson: string\n): StringGen {\n let comma = staticJson.length > 0;\n\n yield '{\"liveblocksType\":\"LiveObject\",\"data\":{';\n yield staticJson;\n\n for (const [key, childId] of snapshot.iter_children(id)) {\n if (comma) yield \",\";\n else comma = true;\n\n yield `${JSON.stringify(key)}:`;\n yield* emit(snapshot, childId);\n }\n yield \"}}\";\n}\n\nfunction* emitList(snapshot: IReadableSnapshot, id: string): StringGen {\n let comma = false;\n\n yield '{\"liveblocksType\":\"LiveList\",\"data\":[';\n for (const [_, childId] of snapshot.iter_children(id)) {\n if (comma) yield \",\";\n else comma = true;\n yield* emit(snapshot, childId);\n }\n yield \"]}\";\n}\n\nfunction* emitMap(snapshot: IReadableSnapshot, id: string): StringGen {\n let comma = false;\n\n yield '{\"liveblocksType\":\"LiveMap\",\"data\":{';\n for (const [key, childId] of snapshot.iter_children(id)) {\n if (comma) yield \",\";\n else comma = true;\n\n yield `${JSON.stringify(key)}:`;\n yield* emit(snapshot, childId);\n }\n yield \"}}\";\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type {\n NodeMap,\n NodeStream,\n SerializedChild,\n SerializedCrdt,\n SerializedRootObject,\n StorageNode,\n} from \"@liveblocks/core\";\nimport { CrdtType, isRootStorageNode, nn } from \"@liveblocks/core\";\n\nimport type { IReadableSnapshot } from \"~/interfaces\";\nimport { NestedMap } from \"~/lib/NestedMap\";\n\n/**\n * Create a basic in-memory snapshot from a set of storage nodes.\n *\n * Takes a copy of the provided nodes, so the snapshot is isolated from\n * subsequent mutations to the source.\n */\nexport function makeInMemorySnapshot(\n values: NodeMap | NodeStream\n): IReadableSnapshot {\n const map: NodeMap = new Map<string, SerializedCrdt>(values as NodeStream);\n\n if (!map.has(\"root\")) {\n map.set(\"root\", { type: CrdtType.OBJECT, data: {} });\n }\n\n // Collect child entries, sort by (parentId, parentKey), then insert into\n // the revMap so that entriesAt() returns children in parent_key order\n // without needing to re-sort on every iter_children call.\n const entries: Array<[parentId: string, parentKey: string, id: string]> = [];\n const nodeStream = map as NodeStream;\n for (const node of nodeStream) {\n if (isRootStorageNode(node)) continue;\n const [id, crdt] = node;\n entries.push([crdt.parentId, crdt.parentKey, id]);\n }\n entries.sort((a, b) =>\n a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0\n );\n\n const revMap = new NestedMap<string, string, string>();\n for (const [parentId, parentKey, id] of entries) {\n revMap.set(parentId, parentKey, id);\n }\n\n function get_node(id: string): SerializedChild {\n return nn(map.get(id), `Node not found: ${id}`) as SerializedChild;\n }\n\n return {\n get_root: () =>\n nn(\n map.get(\"root\"),\n \"Root not found\"\n ) as SerializedCrdt as SerializedRootObject,\n get_node,\n iter_children: (nodeId) => revMap.entriesAt(nodeId),\n iter_all: () => map as Iterable<StorageNode>,\n destroy() {\n map.clear();\n revMap.clear();\n },\n };\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { raise } from \"@liveblocks/core\";\n\n/**\n * Like ES6 map, but takes a default (factory) function which will be used\n * to create entries for missing keys on the fly.\n *\n * Useful for code like:\n *\n * const map = new DefaultMap(() => []);\n * map.getOrCreate('foo').push('hello');\n * map.getOrCreate('foo').push('world');\n * map.getOrCreate('foo')\n * // ['hello', 'world']\n *\n */\nexport class DefaultMap<K, V> extends Map<K, V> {\n #defaultFn?: (key: K) => V;\n\n /**\n * If the default function is not provided to the constructor, it has to be\n * provided in each .getOrCreate() call individually.\n */\n constructor(\n defaultFn?: (key: K) => V,\n entries?: readonly (readonly [K, V])[] | null\n ) {\n super(entries);\n this.#defaultFn = defaultFn;\n }\n\n /**\n * Gets the value at the given key, or creates it.\n *\n * Difference from normal Map: if the key does not exist, it will be created\n * on the fly using the factory function, and that value will get returned\n * instead of `undefined`.\n */\n getOrCreate(key: K, defaultFn?: (key: K) => V): V {\n if (super.has(key)) {\n // eslint-disable-next-line no-restricted-syntax\n return super.get(key)!;\n } else {\n const fn =\n defaultFn ??\n this.#defaultFn ??\n raise(\"DefaultMap used without a factory function\");\n\n const value = fn(key);\n this.set(key, value);\n return value;\n }\n }\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { DefaultMap } from \"./DefaultMap\";\n\nfunction emptyIterator(): IterableIterator<never> {\n return [][Symbol.iterator]();\n}\n\n/**\n * Like an ES6 Map, but two levels deep. Useful for building reverse lookup\n * tables. Will automatically delete second-level maps when they are empty.\n */\nexport class NestedMap<K1, K2, V> {\n #map: DefaultMap<K1, Map<K2, V>>;\n\n constructor() {\n this.#map = new DefaultMap(() => new Map<K2, V>());\n }\n\n get size(): number {\n let total = 0;\n for (const value of this.#map.values()) {\n total += value.size;\n }\n return total;\n }\n\n count(key1: K1): number {\n return this.#map.get(key1)?.size ?? 0;\n }\n\n *keys(): IterableIterator<[K1, K2]> {\n for (const [key1, nested] of this.#map) {\n for (const key2 of nested.keys()) {\n yield [key1, key2];\n }\n }\n }\n\n has(key1: K1, key2: K2): boolean {\n return this.#map.get(key1)?.has(key2) ?? false;\n }\n\n get(key1: K1, key2: K2): V | undefined {\n return this.#map.get(key1)?.get(key2);\n }\n\n set(key1: K1, key2: K2, value: V): this {\n this.#map.getOrCreate(key1).set(key2, value);\n return this;\n }\n\n delete(key1: K1, key2: K2): void {\n if (!this.#map.has(key1)) {\n return;\n }\n\n const nested = this.#map.get(key1)!;\n nested.delete(key2);\n if (nested.size === 0) {\n this.#map.delete(key1);\n }\n }\n\n clear(): void {\n this.#map.clear();\n }\n\n *[Symbol.iterator](): IterableIterator<[K1, K2, V]> {\n for (const [key1, nested] of this.#map) {\n for (const [key2, value] of nested) {\n yield [key1, key2, value];\n }\n }\n }\n\n entriesAt(key1: K1): IterableIterator<[K2, V]> {\n return this.#map.get(key1)?.entries() ?? emptyIterator();\n }\n\n *filterAt(key1: K1, keys: Iterable<K2>): Iterable<[K2, V]> {\n const nested = this.#map.get(key1);\n if (nested === undefined) {\n return;\n }\n\n for (const k2 of keys) {\n const value = nested.get(k2);\n if (value !== undefined) {\n yield [k2, value];\n }\n }\n }\n\n keysAt(key1: K1): IterableIterator<K2> {\n return this.#map.get(key1)?.keys() ?? emptyIterator();\n }\n\n valuesAt(key1: K1): IterableIterator<V> {\n return this.#map.get(key1)?.values() ?? emptyIterator();\n }\n\n deleteAll(key1: K1): void {\n this.#map.delete(key1);\n }\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type { Awaitable, Json } from \"@liveblocks/core\";\nimport type { Decoder } from \"decoders\";\n\nimport type { IStorageDriver } from \"~/interfaces\";\n\nexport interface MetadataDB {\n // Getter supports optional decoder\n get(key: string): Promise<Json | undefined>;\n get<T>(decoder: Decoder<T>, key: string): Promise<T | undefined>;\n\n put(key: string, value: Json): Awaitable<void>;\n delete(key: string): Awaitable<void>;\n}\n\n/**\n * Returns a thin wrapper around an IStorageDriver to provide MetadataDB\n * functionality, including type-safe reads.\n */\nexport function makeMetadataDB(driver: IStorageDriver): MetadataDB {\n async function get(key: string): Promise<Json | undefined>;\n async function get<T>(\n decoder: Decoder<T>,\n key: string\n ): Promise<T | undefined>;\n async function get<T>(\n a1: string | Decoder<T>,\n a2?: string\n ): Promise<T | Json | undefined> {\n if (a2 === undefined) {\n return await driver.get_meta(a1 as string);\n } else {\n return (a1 as Decoder<T>).value(await driver.get_meta(a2));\n }\n }\n\n return {\n get,\n put: driver.put_meta.bind(driver),\n delete: driver.delete_meta.bind(driver),\n };\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { enum_ } from \"decoders\";\n\nexport enum ProtocolVersion {\n //\n // NOTE:\n // The following versions were once used, but there is no usage of it anymore\n // in the wild, so we've removed support for them:\n //\n // V1 - Initial version\n // V2 - ?\n // V3 - started to broadcast storage operations to the sender to fix some\n // conflicts\n // V4 - created a virtual root to fix an issue where multiple people\n // initialize the storage at the same time\n // V5 - started to broadcast messages in a batch (arrays) for clients\n // V6 - started to validate inputs with decoders.\n //\n\n /**\n * V7 changes the URL params used to authorize the user.\n *\n * In V6 and lower, the ?token= URL param is used, which will only ever\n * contain a `pub-legacy` or `sec-legacy` token.\n *\n * URL PARAM CHANGES:\n * Starting with V7, the ?token= is no longer a legal URL param. Instead,\n * either of the following params is used:\n *\n * - ?tok=... for ID tokens\n * - ?tok=... for Access tokens\n * - ?tok=... for Secret Legacy tokens\n * - ?pubkey=... for public keys (no token, public key can be directly used here)\n *\n * Note that `pub-legacy` tokens are no longer accepted in V7, and are\n * replaced by the direct use of the public key.\n *\n * BEHAVIORAL CHANGES:\n * Starting with V7, the RoomState server message that gets sent when\n * a client initially connects will now include new fields:\n *\n * - `actor`\n * - `scopes`\n *\n * Since v1.2.0 (Jul 31, 2023)\n */\n V7 = 7,\n\n /**\n * V8 changes storage response format and allows streaming.\n *\n * MESSAGE FORMAT CHANGES:\n * - V8: sends 1+ STORAGE_CHUNK messages, followed by 1 final\n * STORAGE_STREAM_END message (with compact nodes)\n * - V7: sends 1 STORAGE_STATE_V7 message (with full nodes)\n *\n * STREAMING BEHAVIOR in V8:\n * - For SQLite-backed rooms: nodes are split into multiple STORAGE_CHUNK\n * messages, followed by STORAGE_STREAM_END\n * - For KV-backed rooms: all nodes are sent in a single STORAGE_CHUNK\n * message that will contain all nodes, followed by STORAGE_STREAM_END\n *\n * Since 3.14.0\n */\n V8 = 8,\n}\n\nexport const protocolVersionDecoder = enum_(ProtocolVersion).describe(\n \"Unsupported protocol version\"\n);\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type {\n BaseUserMeta,\n Brand,\n IUserInfo,\n Json,\n JsonObject,\n} from \"@liveblocks/core\";\nimport {\n assertNever,\n ClientMsgCode,\n nodeStreamToCompactNodes,\n OpCode,\n raise,\n ServerMsgCode,\n tryParseJson,\n WebsocketCloseCodes as CloseCode,\n} from \"@liveblocks/core\";\nimport { Mutex } from \"async-mutex\";\nimport { array, formatInline } from \"decoders\";\nimport { chunked } from \"itertools\";\nimport { nanoid } from \"nanoid\";\n\nimport type { Guid } from \"~/decoders\";\nimport { clientMsgDecoder } from \"~/decoders\";\nimport type { IServerWebSocket, IStorageDriver } from \"~/interfaces\";\nimport { Logger } from \"~/lib/Logger\";\nimport { makeNewInMemoryDriver } from \"~/plugins/InMemoryDriver\";\nimport type {\n ClientMsg as GenericClientMsg,\n IgnoredOp,\n Op,\n ServerMsg as GenericServerMsg,\n ServerWireOp,\n} from \"~/protocol\";\nimport { ProtocolVersion } from \"~/protocol\";\nimport { Storage } from \"~/Storage\";\nimport { YjsStorage } from \"~/YjsStorage\";\n\nimport { tryCatch } from \"./lib/tryCatch\";\nimport { UniqueMap } from \"./lib/UniqueMap\";\nimport type { LeasedSession } from \"./types\";\nimport { isLeasedSessionExpired, makeRoomStateMsg } from \"./utils\";\n\nconst messagesDecoder = array(clientMsgDecoder);\n\nconst HIGHEST_PROTOCOL_VERSION = Math.max(\n ...Object.values(ProtocolVersion).filter(\n (v): v is number => typeof v === \"number\"\n )\n) as ProtocolVersion;\n\n// Reverse lookup for ServerMsgCodes\nconst SERVER_MSG_CODE_NAMES = Object.fromEntries(\n Object.entries(ServerMsgCode).map(([k, v]) => [v, k])\n) as Record<(typeof ServerMsgCode)[keyof typeof ServerMsgCode], string>;\n\nconst BLACK_HOLE = new Logger([\n /* No targets, i.e. black hole logger */\n]);\n\nexport type LoadingState = \"initial\" | \"loading\" | \"loaded\";\nexport type ActorID = Brand<number, \"ActorID\">;\n\n/**\n * Session keys are also known as the \"nonce\" in the protocol. It's a random,\n * unique, but PRIVATE, identifier for the session, and it's important that\n * this ID is never shared to anyone except the connected client, which\n * receives it as part of its ROOM_STATE message.\n */\nexport type SessionKey = Brand<string, \"SessionKey\">;\n\nexport type PreSerializedServerMsg = Brand<string, \"PreSerializedServerMsg\">;\ntype ClientMsg = GenericClientMsg<JsonObject, Json>;\ntype ServerMsg = GenericServerMsg<JsonObject, BaseUserMeta, Json>;\n\n/**\n * Creates a collector for deferred promises (side effects that should run\n * outside a mutex). Call `defer` to collect promises, then `waitAll` to\n * await them all.\n */\nfunction collectSideEffects() {\n const deferred: Promise<void>[] = [];\n return {\n defer: (p: Promise<void>) => void deferred.push(p),\n waitAll: () => Promise.allSettled(deferred),\n };\n}\n\nfunction serialize(\n msgs: ServerMsg | readonly ServerMsg[]\n): PreSerializedServerMsg {\n return JSON.stringify(msgs) as PreSerializedServerMsg;\n}\n\nexport function ackIgnoredOp(opId: string): IgnoredOp {\n return { type: OpCode.DELETE_CRDT, id: \"ACK\", opId }; // (H)Ack Op\n}\n\nfunction stripOpId(op: Op): ServerWireOp {\n // TODO: Optimize later! Instead of duplicating every op and\n // stripping the opId explicitly, it would be generally more\n // efficient if we treated the opIds as \"envelopes\" around Ops (or\n // send them in a separate array altogether at the protocol level\n // in V8 soon--even better, as it would not even require any stripping!)\n const { opId: _, ...rest } = op; // Strip opIds from all outgoing messages!\n return rest;\n}\n\n/**\n * A known or anonymous user.\n *\n * BY DEFINITION:\n * A User with an assigned `id` property is a non-anonymous user.\n * A User with an assigned `anonymousId` property is an anonymous user.\n * A User with neither of those properties is also an anonymous user.\n *\n * WHAT'S THE DIFFERENCE?\n * When creating a non-anonymous user, other users in the room will be able to\n * observe the assigned `id` property in Presence (e.g. via the `other.user.id`\n * in the Liveblocks client).\n *\n * When creating an anonymous user, you can _optionally_ provide an anonymous\n * ID to (re)use. While not authorized, this still allows you to correlate\n * unique users.\n */\nexport type IUserData = AuthorizedUser | AnonymousUser; // YYY Remove this export before launch. It's a private API, but only needed temporarily, while refactoring our CF server\n\ntype AuthorizedUser = {\n readonly id: string;\n readonly anonymousId?: never;\n readonly info?: IUserInfo;\n};\n\n// Anonymous users, by definition, have no ID, or have an explicitly-assigned\n// anonymous ID (in case you need to control anonymous ID generation, e.g. by\n// tracking a cookie). The anonymous ID will not show up in other clients. To\n// those clients, it will appear as a user without an ID.\ntype AnonymousUser = {\n readonly anonymousId: string;\n readonly id?: never;\n readonly info?: IUserInfo;\n};\n\n/*\n\nSession Types: \n| | Browser Session | Backend Session | Leased Session |\n|-----------------------------------|-----------------|-----------------|-----------------|\n| Sends enter/leave/presence events | ✓ | | ✓ |\n| Visible to other users in room | ✓ | | ✓ |\n| Has WebSocket connection | ✓ | | |\n| Updated from | Browser | REST API | REST API |\n\n*/\n\n/**\n * Each BrowserSession is an abstraction around a socket instance, and maintains\n * metadata about the connection.\n */\nexport class BrowserSession<SM, CM extends JsonObject> {\n // ^^ User-defined Session Metadata\n // ^^ User-defined Client Metadata (sent to client in ROOM_STATE)\n\n public readonly version: ProtocolVersion; // Liveblocks protocol version this client will speak\n public readonly actor: ActorID; // Must be unique within the room\n public readonly createdAt: Date;\n\n // Externally provided (public!) user metadata. This information will get shared with other clients\n public readonly user: IUserData;\n public readonly scopes: string[]; // Permissions for this session, sent to connected clients (so consider public info)\n public readonly meta: SM; // Arbitrary *private* meta data to attach to this session (will NOT be shared)\n public readonly publicMeta?: CM; // Metadata sent to client in ROOM_STATE message's \"meta\" field\n\n readonly #_socket: IServerWebSocket;\n readonly #_debug: boolean;\n #_lastActiveAt: Date;\n\n // We keep a status in-memory in the session of whether we already sent a rejected ops message to the client.\n #_hasNotifiedClientStorageUpdateError: boolean;\n\n /** @internal - Never create a BrowserSession instance manually. Use the room.startBrowserSession() API instead. */\n constructor(\n ticket: Ticket<SM, CM>,\n socket: IServerWebSocket,\n debug: boolean\n ) {\n this.version = ticket.version;\n this.actor = ticket.actor;\n this.user = ticket.user;\n this.scopes = ticket.scopes;\n this.meta = ticket.meta ?? (undefined as unknown as SM);\n this.publicMeta = ticket.publicMeta;\n this.#_socket = socket;\n this.#_debug = debug;\n\n const now = new Date();\n this.createdAt = now;\n this.#_lastActiveAt = now;\n this.#_hasNotifiedClientStorageUpdateError = false;\n }\n\n get lastActiveAt(): Date {\n const lastPing = this.#_socket.getLastPongTimestamp?.();\n if (lastPing && lastPing > this.#_lastActiveAt) {\n return lastPing;\n } else {\n return this.#_lastActiveAt;\n }\n }\n\n get hasNotifiedClientStorageUpdateError(): boolean {\n return this.#_hasNotifiedClientStorageUpdateError;\n }\n\n markActive(now = new Date()): void {\n if (now > this.#_lastActiveAt) {\n this.#_lastActiveAt = now;\n }\n }\n\n setHasNotifiedClientStorageUpdateError(): void {\n this.#_hasNotifiedClientStorageUpdateError = true;\n }\n\n sendPong(): number {\n this.markActive();\n\n const sent = this.#_socket.send(\"pong\");\n if (this.#_debug) {\n if (sent < 0) {\n console.error(\n `failed to send \"pong\" to actor=${this.actor} (back pressure)`\n );\n } else if (sent === 0) {\n console.error(\n `failed to send \"pong\" to actor=${this.actor} (connection issue)`\n );\n } else {\n // Success\n console.log(`sent to actor=${this.actor}: \"pong\"`);\n }\n }\n return sent;\n }\n\n send(serverMsg: ServerMsg | ServerMsg[] | PreSerializedServerMsg): number {\n const data =\n typeof serverMsg === \"string\" ? serverMsg : serialize(serverMsg);\n const sent = this.#_socket.send(data);\n if (this.#_debug) {\n if (sent < 0) {\n console.error(\n `failed to send message to actor=${this.actor} (back pressure)`\n );\n } else if (sent === 0) {\n console.error(\n `failed to send message to actor=${this.actor} (connection issue)`\n );\n }\n\n const msgs = JSON.parse(data) as ServerMsg | ServerMsg[];\n for (const msg of Array.isArray(msgs) ? msgs : [msgs]) {\n console.log(\n `sent to actor=${this.actor}: [${\n SERVER_MSG_CODE_NAMES[msg.type] ?? msg.type\n }] ${JSON.stringify(msg)}`\n );\n }\n }\n return sent;\n }\n\n /**\n * @internal\n * Closes the socket associated to this BrowserSession.\n *\n * NOTE: Never call this API directly! Call .endBrowserSession() instead.\n */\n closeSocket(code: number, reason?: string): void {\n this.#_socket.close(code, reason);\n }\n}\n\nexport class BackendSession extends BrowserSession<never, never> {\n /** @internal Never call this constructor directly */\n constructor(\n ticket: Ticket<never, never>,\n socket: IServerWebSocket,\n debug: boolean\n ) {\n super(ticket, socket, debug);\n }\n}\n\nexport type Ticket<SM, CM extends JsonObject> = {\n readonly sessionKey: SessionKey; // Should stay private\n readonly version: ProtocolVersion;\n readonly actor: ActorID;\n readonly meta?: SM; // Private Session metadata\n readonly publicMeta?: CM; // Client metadata is *public* metadata sent to client in ROOM_STATE message\n readonly user: IUserData; // User-provided, public, metadata\n readonly scopes: string[];\n};\n\nexport type CreateTicketOptions<SM, CM extends JsonObject> = {\n /** The Liveblocks protocol version this client will speak */\n version?: ProtocolVersion;\n meta?: SM;\n publicMeta?: CM;\n /** A user-provided ID to externally recognize the user by */\n id?: string;\n /**\n * A user-provided anonymous ID to use. When `id` is provided, this field is\n * ignored. When both fields are missing, a new anonymous ID will be\n * generated.\n */\n anonymousId?: string;\n /** Static user metadata to assign this session, will get broadcasted to other clients */\n info?: IUserInfo;\n /** Permissions to assign this session */\n scopes?: string[];\n\n /** An explicit actor ID to use. Supported for legacy use cases only. It's best to not set this and let it get assigned dynamically, as it's important for this identifier to be unique. */\n actor?: ActorID;\n};\n\ntype InternalData = {\n readonly storage: Storage;\n readonly yjsStorage: YjsStorage;\n readonly mutex: Mutex;\n};\n\ntype RoomOptions<SM, CM extends JsonObject, C> = {\n /**\n * Bring your own persistence backend\n */\n storage?: IStorageDriver;\n logger?: Logger;\n\n /**\n * Whether to allow streaming storage responses. Only safe with drivers\n * that can guarantee that no Ops from other clients can get interleaved\n * between the chunk generation until the last chunk has been sent.\n * Defaults to true, but is notably NOT safe to use from DOS-KV backends.\n */\n allowStreaming?: boolean;\n\n // YYY Restructure these hooks to all take a single `event` param\n hooks?: {\n /** Customize which incoming messages from a client are allowed or disallowed. */\n isClientMsgAllowed?: (\n msg: ClientMsg,\n session: BrowserSession<SM, CM>\n ) => { allowed: true } | { allowed: false; reason: string };\n\n /** Called whenever the server acknowledged a ping with a pong */\n onDidPong?: (ctx?: C) => void | Promise<void>;\n\n /** Called before the room is attempted to be loaded */\n onRoomWillLoad?: (ctx?: C) => void | Promise<void>;\n /** Called right after the room's contents are loaded, but before any session has been started */\n onRoomDidLoad?: (ctx?: C) => void | Promise<void>;\n\n /** Called right before the room is attempted to be unloaded. Synchronous. May throw to abort the unloading. */\n onRoomWillUnload?: (ctx?: C) => void;\n /** Called right after the room has been unloaded from memory. Synchronous. */\n onRoomDidUnload?: (ctx?: C) => void;\n\n /** Called when a new user entered the room. */\n onSessionDidStart?: (\n session: BrowserSession<SM, CM>,\n ctx?: C\n ) => void | Promise<void>;\n /** Called when a user left the room. */\n onSessionDidEnd?: (\n session: BrowserSession<SM, CM>,\n ctx?: C\n ) => void | Promise<void>;\n\n /**\n * Called when Liveblocks Storage for the room was updated.\n *\n * IMPORTANT! If you implement these as async functions, it's important to\n * note that these run outside of the storage mutex that guarantees\n * a consistent view of storage.\n * Therefore, only ever use this hook to implement a side effect (like\n * trigger a notification), don't read storage in this hook directly.\n */\n postClientMsgStorageDidUpdate?: (ctx?: C) => void | Promise<void>;\n /**\n * Called when Yjs Storage for the room was updated.\n *\n * IMPORTANT! If you implement these as async functions, it's important to\n * note that these run outside of the storage mutex that guarantees\n * a consistent view of storage.\n * Therefore, only ever use this hook to implement a side effect (like\n * trigger a notification), don't read storage in this hook directly.\n */\n postClientMsgYdocDidUpdate?: (\n ctx?: C,\n sess?: BrowserSession<SM, CM>\n ) => void | Promise<void>;\n };\n\n /** Enable debug logging */\n enableDebugLogging?: boolean;\n};\n\n/**\n * A Liveblocks Room server.\n */\nexport class Room<RM, SM, CM extends JsonObject, C = undefined> {\n // ^^^^^^^^^^ User-defined Room Metadata, Session Metadata, and Client Metadata\n\n public meta: RM;\n public readonly driver: IStorageDriver;\n public logger: Logger;\n\n private _loadData$: Promise<void> | null = null;\n private _data: InternalData | null = null;\n private _qsize = 0;\n\n private readonly sessions = new UniqueMap<\n SessionKey,\n BrowserSession<SM, CM>,\n ActorID\n >((s) => s.actor);\n\n private readonly hooks: {\n isClientMsgAllowed: (\n msg: ClientMsg,\n session: BrowserSession<SM, CM>\n ) => { allowed: true } | { allowed: false; reason: string };\n\n onDidPong?: (ctx?: C) => void | Promise<void>;\n\n onRoomWillLoad?: (ctx?: C) => void | Promise<void>;\n onRoomDidLoad?: (ctx?: C) => void | Promise<void>;\n\n onRoomWillUnload?: (ctx?: C) => void;\n onRoomDidUnload?: (ctx?: C) => void;\n\n onSessionDidStart?: (\n session: BrowserSession<SM, CM>,\n ctx: C | undefined\n ) => void | Promise<void>;\n onSessionDidEnd?: (\n session: BrowserSession<SM, CM>,\n ctx: C | undefined\n ) => void | Promise<void>;\n\n // Don't like these callback names yet. Think about how to better abstract it later.\n postClientMsgStorageDidUpdate?: (ctx?: C) => void | Promise<void>;\n postClientMsgYdocDidUpdate?: (\n ctx?: C,\n sess?: BrowserSession<SM, CM>\n ) => void | Promise<void>;\n };\n\n readonly #_debug: boolean;\n readonly #_allowStreaming: boolean;\n\n constructor(meta: RM, options?: RoomOptions<SM, CM, C>) {\n const driver = options?.storage ?? makeNewInMemoryDriver();\n this.meta = meta;\n this.driver = driver;\n this.logger = options?.logger ?? BLACK_HOLE;\n this.#_allowStreaming = options?.allowStreaming ?? true;\n this.hooks = {\n isClientMsgAllowed:\n options?.hooks?.isClientMsgAllowed ??\n (() => {\n return {\n allowed: true,\n };\n }),\n\n // YYY .load() isn't called on the RoomServer yet! As soon as it does, these hooks will get called\n onRoomWillLoad: options?.hooks?.onRoomWillLoad,\n onRoomDidLoad: options?.hooks?.onRoomDidLoad,\n\n onRoomWillUnload: options?.hooks?.onRoomWillUnload,\n onRoomDidUnload: options?.hooks?.onRoomDidUnload,\n\n onSessionDidStart: options?.hooks?.onSessionDidStart,\n onSessionDidEnd: options?.hooks?.onSessionDidEnd,\n\n postClientMsgStorageDidUpdate:\n options?.hooks?.postClientMsgStorageDidUpdate,\n postClientMsgYdocDidUpdate: options?.hooks?.postClientMsgYdocDidUpdate,\n };\n this.#_debug = options?.enableDebugLogging ?? false;\n }\n\n public get loadingState(): LoadingState {\n if (this._loadData$ === null) {\n return \"initial\";\n } else if (this._data === null) {\n return \"loading\";\n } else {\n return \"loaded\";\n }\n }\n\n public get numSessions(): number { return this.sessions.size; } // prettier-ignore\n\n public get storage(): Storage { return this.data.storage; } // prettier-ignore\n public get yjsStorage(): YjsStorage { return this.data.yjsStorage; } // prettier-ignore\n\n public get mutex(): Mutex { return this.data.mutex; } // prettier-ignore\n\n private get data(): InternalData { return this._data ?? raise(\"Cannot use room before it's loaded\"); } // prettier-ignore\n\n // ------------------------------------------------------------------------------------\n // Public API\n // ------------------------------------------------------------------------------------\n\n /**\n * Initializes the Room, so it's ready to start accepting connections. Safe\n * to call multiple times. After awaiting `room.load()` the Room is ready to\n * be used.\n */\n public async load(ctx?: C): Promise<void> {\n if (this._loadData$ === null) {\n this._data = null;\n this._loadData$ = this._load(ctx).catch((e) => {\n this._data = null;\n this._loadData$ = null;\n throw e;\n });\n }\n return this._loadData$;\n }\n\n /**\n * Releases the currently-loaded storage tree from worker memory, freeing it\n * up to be garbage collected. The next time a user will join the room, the\n * room will be reloaded from storage.\n */\n public unload(ctx?: C): void {\n this.hooks.onRoomWillUnload?.(ctx); // May throw to cancel unloading\n if (this._data) {\n this.storage.unload();\n this.yjsStorage.unload();\n }\n // YYY Abort any potentially in-flight _loadData$ calls here\n this._loadData$ = null;\n // this._data = null; // YYY Should we also clear _data? I think so!\n this.hooks.onRoomDidUnload?.(ctx);\n }\n\n /**\n * Issues a Ticket with a new/unique actor ID\n *\n * IMPORTANT! As the caller of this function, you are responsible for\n * ensuring you trust the values passed in here. Never pass unauthorized\n * values in here.\n *\n * The returned Ticket can be turned into a active Session once the socket\n * connection is established. If the socket is never established, this\n * unused Ticket will simply get garbage collected.\n */\n public async createTicket(\n options?: CreateTicketOptions<SM, CM>\n ): Promise<Ticket<SM, CM>> {\n const actor$ = options?.actor ?? this.getNextActor();\n const sessionKey = nanoid() as SessionKey;\n const info = options?.info;\n const ticket: Ticket<SM, CM> = {\n version: options?.version ?? HIGHEST_PROTOCOL_VERSION,\n actor: await actor$,\n sessionKey,\n meta: options?.meta,\n publicMeta: options?.publicMeta,\n user: options?.id\n ? { id: options.id, info }\n : { anonymousId: options?.anonymousId ?? nanoid(), info },\n scopes: options?.scopes ?? [\"room:write\"],\n };\n if (this.#_debug) {\n console.log(`new ticket created: ${JSON.stringify(ticket)}`);\n }\n return ticket;\n }\n\n public async createBackendSession_experimental(): Promise<\n [session: BackendSession, outgoingMessages: PreSerializedServerMsg[]]\n > {\n const ticket = (await this.createTicket()) as Ticket<never, never>;\n const capturedServerMsgs: PreSerializedServerMsg[] = [];\n const stub = {\n send: (data) => {\n if (typeof data === \"string\") {\n capturedServerMsgs.push(data as PreSerializedServerMsg);\n }\n return 0;\n },\n close: () => {}, // noop\n } satisfies IServerWebSocket;\n const session = new BackendSession(ticket, stub, false);\n return [session, capturedServerMsgs];\n }\n\n /**\n * Restores the given sessions as the Room server's session list. Can only be\n * called as long as there are no existing sessions.\n *\n * The key difference with the .startBrowserSession() API is that restoreSessions is\n * used in cases where a session was hibernated and needs to be restored,\n * without _conceptually_ starting a new session.\n *\n * Because there are no side effects to restoreSession, it's synchronous.\n */\n public restoreSessions(\n sessions: {\n ticket: Ticket<SM, CM>;\n socket: IServerWebSocket;\n lastActivity: Date;\n }[]\n ): void {\n if (this.sessions.size > 0) {\n throw new Error(\"This API can only be called before any sessions exist\");\n }\n\n for (const { ticket, socket, lastActivity } of sessions) {\n const newSession = new BrowserSession(ticket, socket, this.#_debug);\n this.sessions.set(ticket.sessionKey, newSession);\n newSession.markActive(lastActivity);\n }\n }\n\n private async sendSessionStartMessages(\n newSession: BrowserSession<SM, CM>,\n ticket: Ticket<SM, CM>,\n ctx?: C,\n defer: (promise: Promise<void>) => void = () => {\n throw new Error(\n \"One of your hook handlers returned a promise, but no side effect collector was provided. \" +\n \"Pass a `defer` callback to sendSessionStartMessages() to collect async side effects.\"\n );\n }\n ): Promise<void> {\n const users: Record<ActorID, BaseUserMeta & { scopes: string[] }> = {};\n // Add regular sessions\n for (const session of this.otherSessions(ticket.sessionKey)) {\n users[session.actor] = {\n id: session.user.id,\n info: session.user.info,\n scopes: session.scopes,\n };\n }\n\n // List all active server sessions\n const leasedSessions: LeasedSession[] = await this.listLeasedSessions(\n ctx,\n defer\n );\n // Add server sessions\n for (const leasedSession of leasedSessions) {\n users[leasedSession.actorId as ActorID] = {\n id: leasedSession.sessionId,\n info: leasedSession.info,\n scopes: [],\n };\n }\n\n // this must happen before presence messages are sent\n newSession.send(\n makeRoomStateMsg(\n newSession.actor,\n ticket.sessionKey, // called \"nonce\" in the protocol\n newSession.scopes,\n users,\n ticket.publicMeta\n )\n );\n\n // Send each server session's full presence to the new user\n // NOTE: this doesn't exist for other browser sessions because those other sessions send from their frontend clients in response to room state messages.\n for (const leasedSession of leasedSessions) {\n newSession.send({\n type: ServerMsgCode.UPDATE_PRESENCE,\n actor: leasedSession.actorId as ActorID,\n targetActor: newSession.actor, // full presence to new user\n data: leasedSession.presence as JsonObject,\n });\n }\n }\n\n /**\n * Registers a new BrowserSession into the Room server's session list, along with\n * the socket connection to use for that BrowserSession, now that it is known.\n *\n * This kicks off a few side effects:\n * - Sends a ROOM_STATE message to the socket.\n * - Broadcasts a USER_JOINED message to all other sessions in the room.\n */\n public async startBrowserSession(\n ticket: Ticket<SM, CM>,\n socket: IServerWebSocket,\n ctx?: C,\n defer: (promise: Promise<void>) => void = () => {\n throw new Error(\n \"One of your hook handlers returned a promise, but no side effect collector was provided. \" +\n \"Pass a `defer` callback to startBrowserSession() to collect async side effects.\"\n );\n }\n ): Promise<void> {\n let existing: SessionKey | undefined;\n while (\n (existing = this.sessions.lookupPrimaryKey(ticket.actor)) !== undefined\n ) {\n // If this happens, it means a new connection attempt is happening for an\n // existing actor ID. It's most likely from a reconnection attempt using\n // a legacy token (which has the actor ID hardcoded in it), where the old\n // session hasn't been closed explicitly. We'll actively kill it now.\n\n // Terminate old session\n this.endBrowserSession(\n existing,\n CloseCode.KICKED,\n \"Closed stale connection\",\n ctx,\n defer\n );\n\n this.logger.warn(\n `Previous session for actor ${ticket.actor} killed in favor of new session`\n );\n }\n\n const newSession = new BrowserSession(ticket, socket, this.#_debug);\n this.sessions.set(ticket.sessionKey, newSession);\n\n // send sessions start messages\n await this.sendSessionStartMessages(newSession, ticket, ctx, defer);\n\n this.sendToOthers(\n ticket.sessionKey,\n {\n type: ServerMsgCode.USER_JOINED,\n actor: newSession.actor,\n id: newSession.user.id,\n info: newSession.user.info,\n scopes: newSession.scopes,\n },\n ctx,\n defer\n );\n\n // Call the hook, but don't await the results here\n const p$ = this.hooks.onSessionDidStart?.(newSession, ctx);\n if (p$) defer(p$);\n }\n\n /**\n * Unregisters the BrowserSession for the given actor. Call this when the socket has\n * been closed from the client's end.\n *\n * This kicks off a few side effects:\n * - Broadcasts a USER_LEFT message to all other sessions in the room.\n */\n public endBrowserSession(\n key: SessionKey,\n code: number,\n reason: string,\n ctx?: C,\n defer: (promise: Promise<void>) => void = () => {\n throw new Error(\n \"Your onSessionDidEnd handler returned a promise, but no side effect collector was provided. \" +\n \"Pass a `defer` callback to endBrowserSession() to collect async side effects.\"\n );\n }\n ): void {\n const sessions = this.sessions;\n\n const session = sessions.get(key);\n if (session === undefined) return;\n\n session.closeSocket(code, reason);\n\n const deleted = sessions.delete(key);\n if (deleted) {\n for (const other of this.otherSessions(key)) {\n other.send({ type: ServerMsgCode.USER_LEFT, actor: session.actor });\n }\n\n // Call the hook\n const p$ = this.hooks.onSessionDidEnd?.(session, ctx);\n if (p$) defer(p$);\n }\n }\n\n /**\n * Force-closes all sessions matching the given predicate.\n */\n public endSessionBy(\n predicate: (session: BrowserSession<SM, CM>) => boolean,\n code: number,\n reason: string,\n ctx?: C,\n defer: (promise: Promise<void>) => void = () => {\n throw new Error(\n \"Your onSessionDidEnd handler returned a promise, but no side effect collector was provided. \" +\n \"Pass a `defer` callback to endSessionBy() to collect async side effects.\"\n );\n }\n ): number {\n let count = 0;\n for (const [key, session] of this.sessions) {\n if (predicate(session)) {\n count++;\n this.endBrowserSession(key, code, reason, ctx, defer);\n }\n }\n return count;\n }\n\n /**\n * Handles a raw incoming socket message, which can be a ping, or an\n * JSON-encoded message batch.\n */\n public async handleData(\n key: SessionKey,\n data: unknown,\n ctx?: C,\n defer: (promise: Promise<void>) => void = () => {\n throw new Error(\n \"One of your hook handlers returned a promise, but no side effect collector was provided. \" +\n \"Pass a `defer` callback to handleData() to collect async side effects.\"\n );\n }\n ): Promise<void> {\n const text =\n typeof data === \"string\" ? data : raise(\"Unsupported message format\");\n\n if (text === \"ping\") {\n await this.handlePing(key, ctx);\n } else {\n const json = tryParseJson(text);\n const messages = messagesDecoder.decode(json);\n\n if (!messages.ok) {\n const reason =\n process.env.NODE_ENV !== \"production\"\n ? formatInline(messages.error)\n : \"Invalid message format\";\n\n this.endBrowserSession(\n key,\n CloseCode.INVALID_MESSAGE_FORMAT,\n reason,\n ctx,\n defer\n );\n return;\n }\n\n // TODO: Decide on these limits later.\n // If qsize is > 0, then it means there is a traffic jam. This shouldn't\n // be a problem for a while, but it grows beyond a certain (soft or hard)\n // limit, we may want to take measures.\n if (this._qsize > 10_000) {\n // Over hard limit\n // TODO: Maybe disconnect this sockets with a 42xx close code? This\n // will make the client back off more aggressively. See\n // https://github.com/liveblocks/liveblocks/blob/223f7ce0d77380fecd3b08ed9454ca8c330bbe16/packages/liveblocks-core/src/types/IWebSocket.ts#L53\n } else if (this._qsize > 5_000) {\n // Over soft limit\n // TODO: Maybe instruct clients to increase their throttle values?\n }\n\n this._qsize++;\n\n // Run this.handleMsgs(), but guarded by a mutex lock, ensuring that no\n // two messages will get processed simultaneously. This provides similar\n // concurrency protection as Cloudflare's I/O gates\n try {\n await this.processClientMsg(key, messages.value, ctx);\n } finally {\n this._qsize--;\n }\n }\n }\n\n /**\n * Processes an incoming batch of 1 or more ClientMsgs on behalf of\n * a (regular user/browser) session.\n *\n * IMPORTANT: Only use this API on \"trusted\" data!\n * To handle untrusted input data, use `.handleData()` instead.\n *\n * Before calling this API, make sure:\n * 1. The call site is entitled to call this message on behalf of this session; and\n * 2. The ClientMsg payload has been validated to be correct.\n */\n public async processClientMsg(\n key: SessionKey,\n messages: ClientMsg[],\n ctx?: C\n ): Promise<void> {\n await this.load(ctx);\n const { defer, waitAll } = collectSideEffects();\n await this.mutex.runExclusive(() =>\n this._processClientMsg_withExclusiveAccess(key, messages, ctx, defer)\n );\n\n // Run all deferred work (like queueing messages, sending notifications,\n // etc) outside of the mutex\n await waitAll();\n }\n\n /**\n * Processes an incoming batch of 1 or more ClientMsgs on behalf of\n * a BACKEND session.\n *\n * Difference 1: HTTP RESPONSE instead of WEB SOCKET RESPONSE\n * ----------------------------------------------------------\n * For \"normal\" sessions that have a socket attached, any \"responses\" (i.e.\n * server messages like acks or fixops) will be sent back through that\n * existing socket connection.\n *\n * The key difference when using this method is that there is no such socket,\n * so any \"response\" ServerMsgs will get sent back as an HTTP response.\n *\n * Difference 2: No auth check\n * ---------------------------\n * Another key difference is that when processing a backend session, no\n * \"isClientMsgAllowed()\" check is performed, because those checks assume\n * a session.\n */\n public async processClientMsgFromBackendSession(\n session: BackendSession,\n messages: ClientMsg[],\n ctx?: C\n ): Promise<void> {\n await this.load(ctx);\n const { defer, waitAll } = collectSideEffects();\n await this.mutex.runExclusive(() =>\n this._processClientMsgFromBackendSession_withExclusiveAccess(\n session,\n messages,\n ctx,\n defer\n )\n );\n\n // Run all deferred work (like queueing messages, sending notifications,\n // etc) outside of the mutex\n await waitAll();\n }\n\n public getSession(\n sessionKey: SessionKey\n ): BrowserSession<SM, CM> | undefined {\n return this.sessions.get(sessionKey);\n }\n\n public listSessions(): BrowserSession<SM, CM>[] {\n return Array.from(this.sessions.values());\n }\n\n /**\n * Upsert a leased session. Creates a new session if it doesn't exist (or is expired),\n * or updates an existing session with merged presence.\n */\n public async upsertLeasedSession(\n sessionId: string,\n presence: JsonObject,\n ttl: number,\n info: IUserInfo,\n ctx?: C,\n defer: (promise: Promise<void>) => void = () => {\n throw new Error(\n \"One of your hook handlers returned a promise, but no side effect collector was provided. \" +\n \"Pass a `defer` callback to upsertLeasedSession() to collect async side effects.\"\n );\n }\n ): Promise<void> {\n const existingSession = await this.driver.get_leased_session(sessionId);\n const isExpired =\n existingSession !== undefined && isLeasedSessionExpired(existingSession);\n\n if (isExpired) {\n await this.deleteLeasedSession(existingSession, ctx, defer);\n }\n\n if (existingSession === undefined || isExpired) {\n // Creating new session (or was expired)\n const actorId = await this.getNextActor();\n const now = Date.now();\n const session: LeasedSession = {\n sessionId,\n presence,\n updatedAt: now,\n info,\n ttl,\n actorId,\n };\n\n await this.driver.put_leased_session(session);\n\n // Broadcast USER_JOINED to all existing sessions\n this.sendToAll(\n {\n type: ServerMsgCode.USER_JOINED,\n actor: actorId,\n id: sessionId,\n info,\n scopes: [],\n },\n ctx,\n defer\n );\n // now send the presence to all sessions\n this.sendToAll(\n {\n type: ServerMsgCode.UPDATE_PRESENCE,\n actor: actorId,\n data: presence,\n targetActor: 1,\n },\n ctx,\n defer\n );\n } else {\n // Updating existing session (and not expired)\n // Merge/patch the presence\n const mergedPresence = {\n ...(existingSession.presence as JsonObject),\n ...presence,\n };\n const updatedSession: LeasedSession = {\n ...existingSession,\n //info, UserInfo is immutable after creation\n presence: mergedPresence,\n updatedAt: Date.now(),\n ttl,\n };\n\n await this.driver.put_leased_session(updatedSession);\n\n // Broadcast UPDATE_PRESENCE WITHOUT targetActor to all sessions (patch)\n this.sendToAll(\n {\n type: ServerMsgCode.UPDATE_PRESENCE,\n actor: existingSession.actorId,\n data: presence, // Send only the patch, not the full merged presence\n // NO targetActor - this makes it a partial presence patch\n },\n ctx,\n defer\n );\n }\n }\n\n /**\n * List all server sessions. As a side effect, it will delete expired sessions.\n */\n public async listLeasedSessions(\n ctx?: C,\n defer: (promise: Promise<void>) => void = () => {\n throw new Error(\n \"One of your hook handlers returned a promise, but no side effect collector was provided. \" +\n \"Pass a `defer` callback to listLeasedSessions() to collect async side effects.\"\n );\n }\n ): Promise<LeasedSession[]> {\n await this.load(ctx);\n const sessions = await this.driver.list_leased_sessions();\n const validSessions: LeasedSession[] = [];\n const toDelete: LeasedSession[] = [];\n for (const [_, session] of sessions) {\n if (isLeasedSessionExpired(session)) {\n toDelete.push(session);\n } else {\n validSessions.push(session);\n }\n }\n\n for (const session of toDelete) {\n await this.deleteLeasedSession(session, ctx, defer);\n }\n\n return validSessions;\n }\n\n /**\n * Delete a server session and broadcast USER_LEFT to all sessions.\n */\n public async deleteLeasedSession(\n session: LeasedSession,\n ctx?: C,\n defer: (promise: Promise<void>) => void = () => {\n throw new Error(\n \"One of your hook handlers returned a promise, but no side effect collector was provided. \" +\n \"Pass a `defer` callback to deleteLeasedSession() to collect async side effects.\"\n );\n }\n ): Promise<void> {\n // Broadcast USER_LEFT to all sessions\n this.sendToAll(\n {\n type: ServerMsgCode.USER_LEFT,\n actor: session.actorId,\n },\n ctx,\n defer\n );\n await this.driver.delete_leased_session(session.sessionId);\n }\n\n /**\n * Delete all server sessions and broadcast USER_LEFT to all sessions.\n */\n public async deleteAllLeasedSessions(\n ctx?: C,\n defer: (promise: Promise<void>) => void = () => {\n throw new Error(\n \"One of your hook handlers returned a promise, but no side effect collector was provided. \" +\n \"Pass a `defer` callback to deleteAllLeasedSessions() to collect async side effects.\"\n );\n }\n ): Promise<void> {\n await this.load(ctx);\n const sessions = await this.driver.list_leased_sessions();\n for (const [_, session] of sessions) {\n await this.deleteLeasedSession(session, ctx, defer);\n }\n }\n\n /**\n * Will send the given ServerMsg through all Session, except the Session\n * where the message originates from.\n */\n public sendToOthers(\n sender: SessionKey,\n serverMsg: ServerMsg | readonly ServerMsg[],\n ctx?: C,\n defer: (promise: Promise<void>) => void = () => {\n throw new Error(\n \"One of your hook handlers returned a promise, but no side effect collector was provided. \" +\n \"Pass a `defer` callback to sendToOthers() to collect async side effects.\"\n );\n }\n ): void {\n const msg = serialize(serverMsg);\n for (const [key, session] of this.otherSessionEntries(sender)) {\n const success = session.send(msg);\n if (success === 0) {\n // If there is a connection issue, terminate the session at once.\n // Note that in the case of -1 (= back pressure), we don't terminate\n // the connection.\n this.endBrowserSession(\n key,\n CloseCode.KICKED,\n \"Closed broken connection\",\n ctx,\n defer\n );\n }\n }\n }\n\n /**\n * Will broadcast the given ServerMsg to all Sessions in the Room.\n */\n public sendToAll(\n serverMsg: ServerMsg | readonly ServerMsg[],\n ctx?: C,\n defer: (promise: Promise<void>) => void = () => {\n throw new Error(\n \"One of your hook handlers returned a promise, but no side effect collector was provided. \" +\n \"Pass a `defer` callback to sendToAll() to collect async side effects.\"\n );\n }\n ): void {\n const msg = serialize(serverMsg);\n for (const [key, session] of this.sessions) {\n const success = session.send(msg);\n if (success === 0) {\n // If there is a connection issue, terminate the session at once.\n // Note that in the case of -1 (= back pressure), we don't terminate\n // the connection.\n this.endBrowserSession(\n key,\n CloseCode.KICKED,\n \"Closed broken connection\",\n ctx,\n defer\n );\n }\n }\n }\n\n // ------------------------------------------------------------------------------------\n // Private APIs\n // ------------------------------------------------------------------------------------\n\n private async _loadStorage(): Promise<Storage> {\n const storage = new Storage(this.driver);\n await storage.load(this.logger);\n return storage;\n }\n\n private async _loadYjsStorage(): Promise<YjsStorage> {\n const yjsStorage = new YjsStorage(this.driver);\n await yjsStorage.load(this.logger);\n return yjsStorage;\n }\n\n // Don't ever manually call this!\n private async _load(ctx?: C): Promise<void> {\n await this.hooks.onRoomWillLoad?.(ctx);\n\n // YYY Maybe later run these in parallel? See https://github.com/liveblocks/liveblocks-cloudflare/pull/721#discussion_r1489076389\n const storage = await this._loadStorage();\n const yjsStorage = await this._loadYjsStorage();\n\n this._data = {\n mutex: new Mutex(),\n storage,\n yjsStorage,\n };\n\n await this.hooks.onRoomDidLoad?.(ctx);\n }\n\n /**\n * Returns a new, unique, actor ID.\n */\n private async getNextActor(): Promise<ActorID> {\n return (await this.driver.next_actor()) as ActorID;\n }\n\n /**\n * Iterates over all *other* Sessions and their session keys.\n */\n private *otherSessionEntries(\n currentKey: SessionKey\n ): Generator<[SessionKey, BrowserSession<SM, CM>]> {\n for (const [key, session] of this.sessions) {\n if (key !== currentKey) {\n yield [key, session];\n }\n }\n }\n\n /**\n * Iterates over all *other* Sessions.\n */\n private *otherSessions(\n currentKey: SessionKey\n ): Generator<BrowserSession<SM, CM>> {\n for (const [key, session] of this.sessions) {\n if (key !== currentKey) {\n yield session;\n }\n }\n }\n\n /**\n * @internal\n * Handles an incoming ping, by sending a pong back.\n */\n // eslint-disable-next-line @typescript-eslint/require-await\n private async handlePing(sessionKey: SessionKey, ctx?: C): Promise<void> {\n const session = this.sessions.get(sessionKey);\n if (session === undefined) {\n this.logger\n .withContext({ sessionKey })\n .warn(\"[probe] in handlePing, no such session exists\");\n return;\n }\n\n const sent = session.sendPong();\n\n // 0 means there was a connection issue\n // -1 means there was back pressure, which is no issue (we'll just count the ping)\n if (sent !== 0) {\n await this.hooks.onDidPong?.(ctx);\n }\n }\n\n private async _processClientMsg_withExclusiveAccess(\n sessionKey: SessionKey,\n messages: ClientMsg[],\n ctx: C | undefined,\n defer: (p: Promise<void>) => void\n ): Promise<void> {\n const session = this.sessions.get(sessionKey);\n if (!session) {\n this.logger\n .withContext({ sessionKey })\n .warn(\"[probe] in handleClientMsgs, no such session exists\");\n return;\n }\n\n // Keep two ServerMsg buffers to send at the end:\n // - Messages to fan-out to all *others* (current session not included)\n // - Messages to reply back to the current sender (i.e. acks and rejections)\n const toFanOut: ServerMsg[] = [];\n const toReply: ServerMsg[] = [];\n const replyImmediately = (msg: ServerMsg | ServerMsg[]) =>\n void session.send(msg);\n const scheduleFanOut = (msg: ServerMsg) => void toFanOut.push(msg);\n const scheduleReply = (msg: ServerMsg) => void toReply.push(msg);\n\n for (const msg of messages) {\n const isMsgAllowed = this.hooks.isClientMsgAllowed(msg, session);\n if (isMsgAllowed.allowed) {\n await this.handleOne(\n session,\n msg,\n replyImmediately,\n scheduleFanOut,\n scheduleReply,\n ctx,\n defer\n );\n } else {\n if (!session.hasNotifiedClientStorageUpdateError) {\n toReply.push({\n type: ServerMsgCode.REJECT_STORAGE_OP,\n opIds:\n msg.type === ClientMsgCode.UPDATE_STORAGE\n ? msg.ops.map((op) => op.opId)\n : [],\n reason: isMsgAllowed.reason,\n });\n session.setHasNotifiedClientStorageUpdateError();\n }\n }\n }\n\n if (toFanOut.length > 0) {\n this.sendToOthers(sessionKey, toFanOut, ctx, defer);\n }\n\n if (toReply.length > 0) {\n session.send(toReply);\n }\n }\n\n // TODO It's a bit bothering how much duplication there is between this method\n // and the _processClientMsg_withExclusiveAccess version. A better\n // abstraction is needed.\n private async _processClientMsgFromBackendSession_withExclusiveAccess(\n session: BackendSession,\n messages: ClientMsg[],\n ctx: C | undefined,\n defer: (p: Promise<void>) => void\n ): Promise<void> {\n // Keep two ServerMsg buffers to send at the end:\n // - Messages to fan-out to all *others* (current session not included)\n // - Messages to reply back to the current sender (i.e. acks and rejections)\n const toFanOut: ServerMsg[] = [];\n const toReplyImmediately: ServerMsg[] = [];\n const toReplyAfter: ServerMsg[] = [];\n\n const replyImmediately = (msg: ServerMsg | ServerMsg[]) => {\n if (Array.isArray(msg)) {\n for (const m of msg) {\n toReplyImmediately.push(m);\n }\n } else {\n toReplyImmediately.push(msg);\n }\n };\n const scheduleFanOut = (msg: ServerMsg) => void toFanOut.push(msg);\n const scheduleReply = (msg: ServerMsg) => void toReplyAfter.push(msg);\n\n for (const msg of messages) {\n await this.handleOne(\n session,\n msg,\n replyImmediately,\n scheduleFanOut,\n scheduleReply,\n ctx,\n defer\n );\n }\n\n if (toReplyImmediately.length > 0) {\n session.send(toReplyImmediately);\n toReplyImmediately.length = 0;\n }\n\n if (toFanOut.length > 0) {\n this.sendToOthers(\"(transient)\" as SessionKey, toFanOut, ctx, defer);\n toFanOut.length = 0;\n }\n\n if (toReplyAfter.length > 0) {\n session.send(toReplyAfter);\n toReplyAfter.length = 0;\n }\n }\n\n private async handleOne(\n session: BrowserSession<SM, CM>,\n msg: ClientMsg,\n replyImmediately: (msg: ServerMsg | ServerMsg[]) => void,\n scheduleFanOut: (msg: ServerMsg) => void,\n scheduleReply: (msg: ServerMsg) => void,\n ctx: C | undefined,\n defer: (p: Promise<void>) => void\n ): Promise<void> {\n if (!this.mutex.isLocked()) {\n throw new Error(\"Handling messages requires exclusive access\");\n }\n\n switch (msg.type) {\n case ClientMsgCode.UPDATE_PRESENCE: {\n // YYY Maybe consider calling session.sendToOthers() directly here instead of queueing for fan-out?\n scheduleFanOut({\n type: ServerMsgCode.UPDATE_PRESENCE,\n actor: session.actor,\n data: msg.data,\n targetActor: msg.targetActor,\n });\n break;\n }\n\n case ClientMsgCode.BROADCAST_EVENT: {\n // YYY Maybe consider calling session.sendToOthers() directly here instead of queueing for fan-out?\n scheduleFanOut({\n type: ServerMsgCode.BROADCASTED_EVENT,\n actor: session.actor,\n event: msg.event,\n });\n break;\n }\n\n case ClientMsgCode.FETCH_STORAGE: {\n if (session.version >= ProtocolVersion.V8) {\n if (this.#_allowStreaming) {\n const NODES_PER_CHUNK = 250; // = arbitrary! Could be tuned later\n\n for (const chunk of chunked(\n nodeStreamToCompactNodes(this.storage.loadedDriver.iter_nodes()),\n NODES_PER_CHUNK\n )) {\n // NOTE: We don't take a storage snapshot here, because this\n // iteration is happening synchronously, so consistency of the\n // current document automatically guaranteed. If we ever make\n // this streaming asynchronous, however, we need to take\n // a storage snapshot to guarantee document consistency.\n replyImmediately({\n type: ServerMsgCode.STORAGE_CHUNK,\n nodes: chunk,\n });\n }\n } else {\n replyImmediately({\n type: ServerMsgCode.STORAGE_CHUNK,\n nodes: Array.from(\n nodeStreamToCompactNodes(this.storage.loadedDriver.iter_nodes())\n ),\n });\n }\n\n replyImmediately({ type: ServerMsgCode.STORAGE_STREAM_END });\n } else {\n replyImmediately({\n type: ServerMsgCode.STORAGE_STATE_V7,\n items: Array.from(this.storage.loadedDriver.iter_nodes()),\n });\n }\n break;\n }\n\n case ClientMsgCode.UPDATE_STORAGE: {\n // Bump storage version to indicate data will get mutated\n // A driver can use this information to implement copy-on-write\n // semantics to provide snapshot isolation.\n this.driver.bump_storage_version?.();\n\n const result = await this.storage.applyOps(msg.ops);\n\n const opsToForward: ServerWireOp[] = result.flatMap((r) =>\n r.action === \"accepted\" ? [r.op] : []\n );\n\n const opsToSendBack: ServerWireOp[] = result.flatMap((r) => {\n switch (r.action) {\n case \"ignored\":\n // HACK! We send a cleverly composed message, that will act\n // as an acknowledgement to all old clients out there in\n // the wild.\n return r.ignoredOpId !== undefined\n ? [ackIgnoredOp(r.ignoredOpId)]\n : [];\n\n case \"accepted\":\n return r.fix !== undefined ? [r.fix] : [];\n\n // istanbul ignore next\n default:\n return assertNever(r, \"Unhandled case\");\n }\n });\n\n if (opsToForward.length > 0) {\n scheduleFanOut({\n type: ServerMsgCode.UPDATE_STORAGE,\n ops: opsToForward.map(stripOpId),\n });\n scheduleReply({\n type: ServerMsgCode.UPDATE_STORAGE,\n ops: opsToForward,\n });\n }\n\n if (opsToSendBack.length > 0) {\n replyImmediately({\n type: ServerMsgCode.UPDATE_STORAGE,\n ops: opsToSendBack,\n });\n }\n\n if (opsToForward.length > 0) {\n // NOTE! These are being called after *every* handleOne() call\n // currently. Should we not just call these once at the end of\n // handleClientMsgs()?\n const p$ = this.hooks.postClientMsgStorageDidUpdate?.(ctx);\n if (p$) defer(p$);\n }\n break;\n }\n\n case ClientMsgCode.FETCH_YDOC: {\n const vector = msg.vector;\n const guid = msg.guid as Guid | undefined;\n const isV2 = msg.v2;\n const [update, stateVector, snapshotHash] = await Promise.all([\n this.yjsStorage.getYDocUpdate(this.logger, vector, guid, isV2),\n this.yjsStorage.getYStateVector(guid),\n this.yjsStorage.getSnapshotHash({ guid, isV2 }),\n ]);\n\n if (update !== null && snapshotHash !== null) {\n replyImmediately({\n type: ServerMsgCode.UPDATE_YDOC,\n update,\n isSync: true, // this is no longer used by the client, instead we use the presence of stateVector\n stateVector,\n guid,\n v2: isV2,\n remoteSnapshotHash: snapshotHash,\n });\n }\n break;\n }\n\n case ClientMsgCode.UPDATE_YDOC: {\n const update = msg.update;\n const guid = msg.guid as Guid | undefined;\n const isV2 = msg.v2;\n const [result, error] = await tryCatch(\n this.yjsStorage.addYDocUpdate(this.logger, update, guid, isV2)\n );\n\n if (error)\n // Ignore any errors\n break;\n\n this.sendToAll(\n {\n type: ServerMsgCode.UPDATE_YDOC,\n update,\n guid,\n isSync: false,\n stateVector: null,\n v2: isV2,\n remoteSnapshotHash: result.snapshotHash,\n },\n ctx,\n defer\n );\n if (result.isUpdated) {\n const p$ = this.hooks.postClientMsgYdocDidUpdate?.(ctx, session);\n if (p$) defer(p$);\n }\n\n break;\n }\n\n default: {\n try {\n return assertNever(msg, \"Unrecognized client msg\");\n } catch {\n // Ignore\n }\n }\n }\n }\n}\n\nexport { serialize as serializeServerMsg };\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type { JsonObject } from \"@liveblocks/core\";\nimport { raise } from \"@liveblocks/core\";\n\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARNING = 2,\n ERROR = 3,\n}\n\nfunction formatError(err: Error) {\n const prefix = `${err.name}: ${err.message}`;\n return (\n err.stack?.startsWith(prefix) ? err.stack : `${prefix}\\n${err.stack ?? \"\"}`\n ).trimEnd();\n}\n\n/**\n * Inherit from this abstract log target to implement your own custom\n * LogTarget.\n */\nexport abstract class LogTarget {\n public readonly level: LogLevel;\n\n #cache = new WeakMap<JsonObject, string>();\n\n constructor(level: LogLevel | keyof typeof LogLevelNames = LogLevel.INFO) {\n this.level =\n typeof level === \"number\"\n ? level\n : (LogLevelNames[level] ?? LogLevel.INFO);\n }\n\n /** Helper for formatting a log level */\n protected formatLevel(level: LogLevel): string {\n switch (level) {\n case LogLevel.DEBUG:\n return \"debug\";\n case LogLevel.INFO:\n return \"info\";\n case LogLevel.WARNING:\n return \"warn\";\n case LogLevel.ERROR:\n return \"error\";\n default:\n return raise(\"Invalid log level\");\n }\n }\n\n /** Helper for formatting an Arg */\n protected formatArg(arg: string | Error): string {\n return typeof arg === \"object\"\n ? arg instanceof Error\n ? formatError(arg)\n : JSON.stringify(arg)\n : String(arg); // Coerce to string in case TypeScript is bypassed\n }\n\n /**\n * Helper for formatting a Context. Override this in a subclass to change the\n * formatting.\n */\n protected formatContextImpl(context: JsonObject): string {\n const parts = [];\n for (const [k, v] of Object.entries(context ?? {})) {\n if (v !== undefined) {\n // Object, or null, or array\n const sv = typeof v === \"object\" ? JSON.stringify(v) : v;\n parts.push(`${k}=${sv}`);\n }\n }\n return parts.length > 0 ? `[${parts.join(\" \")}]` : \"\";\n }\n\n /**\n * Helper for formatting a Context. Will only compute the string once for\n * every Context instance, and keep its computed string value cached for\n * performance.\n */\n protected formatContext(context: JsonObject): string {\n let formatted = this.#cache.get(context);\n if (formatted === undefined) {\n formatted = this.formatContextImpl(context);\n this.#cache.set(context, formatted);\n }\n return formatted;\n }\n\n /**\n * Implement this in a concrete subclass. The goal is to do whatever to log\n * the given log level, context, and log arg. You'll typically want to\n * utilize the pre-defined helper methods .formatContext() and .formatArg()\n * to implement this.\n */\n abstract log(level: LogLevel, context: JsonObject, arg: string | Error): void;\n}\n\n//\n// Console log target ----------------------------------------------------------\n//\n\nconst CONSOLE_METHOD = {\n [LogLevel.DEBUG]: \"info\",\n [LogLevel.INFO]: \"info\",\n [LogLevel.WARNING]: \"warn\",\n [LogLevel.ERROR]: \"error\",\n} as const;\n\nexport class ConsoleTarget extends LogTarget {\n log(level: LogLevel, context: JsonObject, arg: string | Error): void {\n console[CONSOLE_METHOD[level]](\n this.formatArg(arg),\n this.formatContext(context)\n );\n }\n}\n\n//\n// Logger implementation ------------------------------------------------------\n//\n\n// Friendly names to pass to the constructor\nconst LogLevelNames = {\n debug: LogLevel.DEBUG,\n info: LogLevel.INFO,\n warning: LogLevel.WARNING,\n error: LogLevel.ERROR,\n} as const;\n\ntype LogFn = (arg: string | Error) => void;\n\n/**\n * Structured logger with configurable log targets.\n */\nexport class Logger {\n public readonly debug: LogFn;\n public readonly info: LogFn;\n public readonly warn: LogFn;\n public readonly error: LogFn;\n\n public readonly o: {\n readonly debug?: LogFn;\n readonly info?: LogFn;\n readonly warn?: LogFn;\n readonly error?: LogFn;\n };\n\n private readonly _context: JsonObject;\n private readonly _targets: readonly LogTarget[];\n\n constructor(\n target: LogTarget | readonly LogTarget[] = new ConsoleTarget(),\n context: JsonObject = {}\n ) {\n this._context = context;\n this._targets = Array.isArray(target) ? target : [target];\n\n const minLevel: number = Math.min(...this._targets.map((t) => t.level));\n\n const noop = () => {};\n const makeLogFn = (lvl: LogLevel) => (arg: string | Error) =>\n this._targets.forEach((target) => {\n if (target.level <= lvl) {\n target.log(lvl, this._context, arg);\n }\n });\n\n this.o = {\n /* eslint-disable @typescript-eslint/no-unsafe-enum-comparison */\n debug: minLevel <= LogLevel.DEBUG ? makeLogFn(LogLevel.DEBUG) : undefined,\n info: minLevel <= LogLevel.INFO ? makeLogFn(LogLevel.INFO) : undefined,\n warn:\n minLevel <= LogLevel.WARNING ? makeLogFn(LogLevel.WARNING) : undefined,\n error: minLevel <= LogLevel.ERROR ? makeLogFn(LogLevel.ERROR) : undefined,\n /* eslint-enable @typescript-eslint/no-unsafe-enum-comparison */\n };\n\n this.debug = this.o.debug ?? noop;\n this.info = this.o.info ?? noop;\n this.warn = this.o.warn ?? noop;\n this.error = this.o.error ?? noop;\n }\n\n /**\n * Creates a new Logger instance with the given extra context applied. All\n * log calls made from that new Logger will carry all current _and_ the extra\n * context, with the extra context taking precedence. Assign an explicit\n * `undefined` value to a key to \"remove\" it from the context.\n */\n withContext(extra: JsonObject): Logger {\n const combined: JsonObject = { ...this._context, ...extra };\n return new Logger(this._targets, combined);\n }\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n/* eslint-disable @typescript-eslint/explicit-module-boundary-types */\n/* eslint-disable @typescript-eslint/require-await */\nimport type {\n Json,\n JsonObject,\n NodeMap,\n NodeStream,\n PlainLsonObject,\n SerializedChild,\n SerializedCrdt,\n SerializedObject,\n SerializedRootObject,\n} from \"@liveblocks/core\";\nimport { asPos, CrdtType, isRootStorageNode, nn } from \"@liveblocks/core\";\nimport { ifilter, imap } from \"itertools\";\n\nimport type { YDocId } from \"~/decoders/y-types\";\nimport { plainLsonToNodeStream } from \"~/formats/PlainLson\";\nimport type {\n IReadableSnapshot,\n IStorageDriver,\n IStorageDriverNodeAPI,\n LeasedSession,\n} from \"~/interfaces\";\nimport { NestedMap } from \"~/lib/NestedMap\";\nimport { quote } from \"~/lib/text\";\nimport { makeInMemorySnapshot } from \"~/makeInMemorySnapshot\";\nimport type { Pos } from \"~/types\";\n\nfunction buildRevNodes(nodeStream: NodeStream) {\n const result = new NestedMap<string, string, string>();\n for (const node of nodeStream) {\n if (isRootStorageNode(node)) continue;\n\n // Highest node id wins in case of conflict (deterministic across backends)\n const [id, crdt] = node;\n const existing = result.get(crdt.parentId, crdt.parentKey);\n if (existing === undefined || id > existing) {\n result.set(crdt.parentId, crdt.parentKey, id);\n }\n }\n return result;\n}\n\n/**\n * Builds the reverse node index, and corrects any data corruption found\n * along the way.\n */\nfunction buildReverseLookup(nodes: NodeMap) {\n const revNodes = buildRevNodes(nodes as NodeStream);\n\n const queue: string[] = [\"root\"];\n const reachableNodes: Set<string> = new Set();\n\n while (queue.length > 0) {\n const nodeId = queue.pop()!;\n const node = nn(nodes.get(nodeId));\n\n if (node.type === CrdtType.OBJECT) {\n for (const key of revNodes.keysAt(nodeId)) {\n delete node.data[key]; // Remove static data that conflicts with child nodes\n }\n }\n\n if (node.type !== CrdtType.REGISTER) {\n queue.push(...revNodes.valuesAt(nodeId));\n } else {\n const parent = nodes.get(node.parentId);\n if (parent?.type === CrdtType.OBJECT) {\n continue;\n }\n }\n\n reachableNodes.add(nodeId);\n }\n\n // Delete unreachable nodes (safe to delete from Map during iteration)\n let deletedCount = 0;\n for (const [id] of nodes) {\n if (!reachableNodes.has(id)) {\n nodes.delete(id);\n deletedCount++;\n }\n }\n\n // If no nodes were dropped (99% happy path), revNodes is correct already.\n // Otherwise, recompute it.\n return deletedCount === 0 ? revNodes : buildRevNodes(nodes as NodeStream);\n}\n\nfunction hasStaticDataAt(\n node: SerializedCrdt,\n key: string\n): node is SerializedObject | SerializedRootObject {\n return (\n node.type === CrdtType.OBJECT &&\n Object.prototype.hasOwnProperty.call(node.data, key) &&\n node.data[key] !== undefined\n );\n}\n\n/**\n * Implements the most basic in-memory store. Used if no explicit store is\n * provided.\n */\nexport class InMemoryDriver implements IStorageDriver {\n private _nextActor;\n private _nodes: NodeMap;\n private _metadb: Map<string, Json>;\n private _ydb: Map<string, Uint8Array>;\n private _leasedSessions: Map<string, LeasedSession>;\n\n constructor(options?: {\n initialActor?: number;\n initialNodes?: Iterable<[string, SerializedCrdt]>;\n }) {\n this._nodes = new Map();\n this._metadb = new Map();\n this._ydb = new Map();\n this._leasedSessions = new Map();\n\n this._nextActor = options?.initialActor ?? -1;\n\n for (const [key, value] of options?.initialNodes ?? []) {\n this._nodes.set(key, value);\n }\n }\n\n raw_iter_nodes() {\n return this._nodes[Symbol.iterator]();\n }\n\n /** Deletes all nodes and replaces them with the given document. */\n DANGEROUSLY_reset_nodes(doc: PlainLsonObject) {\n this._nodes.clear();\n for (const [id, node] of plainLsonToNodeStream(doc)) {\n this._nodes.set(id, node);\n }\n }\n\n async get_meta(key: string) {\n return this._metadb.get(key);\n }\n async put_meta(key: string, value: Json) {\n this._metadb.set(key, value);\n }\n async delete_meta(key: string) {\n this._metadb.delete(key);\n }\n\n async list_leased_sessions() {\n return this._leasedSessions.entries();\n }\n\n async get_leased_session(sessionId: string) {\n return this._leasedSessions.get(sessionId);\n }\n\n async put_leased_session(session: LeasedSession) {\n this._leasedSessions.set(session.sessionId, session);\n }\n\n async delete_leased_session(sessionId: string) {\n this._leasedSessions.delete(sessionId);\n }\n\n next_actor() {\n return ++this._nextActor;\n }\n\n async iter_y_updates(docId: YDocId) {\n const prefix = `${docId}@|@`;\n return imap(\n ifilter(this._ydb.entries(), ([k]) => k.startsWith(prefix)),\n ([k, v]) => [k.slice(prefix.length), v] as [string, Uint8Array]\n );\n }\n async write_y_updates(docId: YDocId, key: string, data: Uint8Array) {\n this._ydb.set(`${docId}@|@${key}`, data);\n }\n async delete_y_updates(docId: YDocId, keys: string[]) {\n for (const key of keys) {\n this._ydb.delete(`${docId}@|@${key}`);\n }\n }\n\n /** @private Only use this in unit tests, never in production. */\n async DANGEROUSLY_wipe_all_y_updates() {\n this._ydb.clear();\n }\n\n // Intercept load_nodes_api to add caching layer\n load_nodes_api(): IStorageDriverNodeAPI {\n // For the in-memory backend, this._nodes IS the \"on-disk\" storage,\n // so we operate on it directly (no separate cache needed).\n const nodes = this._nodes;\n if (!nodes.has(\"root\")) {\n nodes.set(\"root\", { type: CrdtType.OBJECT, data: {} });\n }\n\n const revNodes = buildReverseLookup(nodes);\n\n function get_next_sibling(parentId: string, pos: Pos): Pos | undefined {\n let nextPos: Pos | undefined;\n // Find the smallest position greater than current\n for (const siblingKey of revNodes.keysAt(parentId)) {\n const siblingPos = asPos(siblingKey);\n if (\n siblingPos > pos &&\n (nextPos === undefined || siblingPos < nextPos)\n ) {\n nextPos = siblingPos;\n }\n }\n return nextPos;\n }\n\n /**\n * Inserts a node in the storage tree, deleting any nodes that already exist\n * under this key (including all of its children), if any.\n */\n async function set_child(\n id: string,\n node: SerializedChild,\n allowOverwrite = false\n ): Promise<void> {\n const parentNode = nodes.get(node.parentId);\n // Reject orphans - parent must exist\n if (parentNode === undefined) {\n throw new Error(`No such parent ${quote(node.parentId)}`);\n }\n\n if (\n node.type === CrdtType.REGISTER &&\n parentNode.type === CrdtType.OBJECT\n ) {\n throw new Error(\"Cannot add register under object\");\n }\n\n const conflictingSiblingId = revNodes.get(node.parentId, node.parentKey);\n if (conflictingSiblingId !== id) {\n // Conflict!\n const parentNode = nodes.get(node.parentId);\n const hasConflictingData =\n parentNode !== undefined &&\n hasStaticDataAt(parentNode, node.parentKey);\n if (conflictingSiblingId !== undefined || hasConflictingData) {\n if (allowOverwrite) {\n delete_child_key(node.parentId, node.parentKey);\n } else {\n throw new Error(`Key ${quote(node.parentKey)} already exists`); // prettier-ignore\n }\n }\n\n // Finally, modify revNodes\n revNodes.set(node.parentId, node.parentKey, id);\n }\n\n nodes.set(id, node);\n }\n\n /**\n * Conceptually this is like \"detaching\" the node from its parent, and\n * \"reattaching\" it at the new position.\n *\n * However, this is a native operation, because doing a naive\n * delete-then-insert would would immediately destroy all (grand)children\n * when it's deleted.\n */\n async function move_sibling(id: string, newPos: Pos): Promise<void> {\n const node = nodes.get(id);\n if (node?.parentId === undefined) {\n return;\n }\n\n // If there is a conflicting sibling at the new position, disallow the move\n if (revNodes.has(node.parentId, newPos))\n throw new Error(`Pos ${quote(newPos)} already taken`); // prettier-ignore\n\n revNodes.delete(node.parentId, node.parentKey);\n const newNode = { ...node, parentKey: newPos };\n nodes.set(id, newNode);\n revNodes.set(node.parentId, newPos, id);\n }\n\n /**\n * Sets some static data on a node. The node must be an OBJECT node, or this\n * method will be a no-op.\n *\n * If any keys exist that also conflict with a child node, then the conflict\n * mode will determine what will happen. By default, an error will be thrown.\n * But if `allowOverwrite` is set to true, the conflicting child node (and\n * its entire subtree) will be deleted to make room for the new static data.\n */\n async function set_object_data(\n id: string,\n data: JsonObject,\n allowOverwrite = false\n ): Promise<void> {\n const node = nodes.get(id);\n if (node?.type !== CrdtType.OBJECT) {\n // Nothing to do\n return;\n }\n\n for (const key of Object.keys(data)) {\n // Handle if conflict!\n const childId = revNodes.get(id, key);\n if (childId !== undefined) {\n if (allowOverwrite) {\n delete_node(childId);\n } else {\n throw new Error(`Child node already exists under ${quote(key)}`); // prettier-ignore\n }\n }\n }\n\n nodes.set(id, { ...node, data: { ...node.data, ...data } });\n }\n\n /**\n * Delete a node from the tree, including all of its children.\n */\n function delete_node(id: string): void {\n const node = nodes.get(id);\n if (node?.parentId === undefined) {\n return;\n }\n\n // Delete the entry in the parent's children administration for this node\n revNodes.delete(node.parentId, node.parentKey);\n\n // Now proceed to deleting the node tree recursively\n const queue = [id];\n while (queue.length > 0) {\n const currid = queue.pop()!;\n queue.push(...revNodes.valuesAt(currid));\n nodes.delete(currid);\n revNodes.deleteAll(currid);\n }\n }\n\n /**\n * Deletes the child key under a given node, whether it's a static object\n * field, or a child node.\n */\n function delete_child_key(id: string, key: string): void {\n // At most one of these will do something, the other is a no-op\n const node = nodes.get(id);\n if (node !== undefined && hasStaticDataAt(node, key)) {\n const { [key]: _, ...rest } = node.data;\n nodes.set(id, { ...node, data: rest });\n }\n\n const childId = revNodes.get(id, key);\n if (childId !== undefined) {\n delete_node(childId);\n }\n }\n\n const api: IStorageDriverNodeAPI = {\n /**\n * Return the node with the given id, or undefined if no such node exists.\n * Must always return a valid root node for id=\"root\", even if empty.\n */\n get_node: (id) => nodes.get(id),\n\n /**\n * Yield all nodes as [id, node] pairs. Must always include the root node.\n */\n iter_nodes: () => nodes as NodeStream,\n\n /**\n * Return true iff a node with the given id exists. Must return true for \"root\".\n */\n has_node: (id) => nodes.has(id),\n\n /**\n * Return the id of the child node at (parentId, parentKey), or undefined if\n * none. Only checks child nodes registered via set_child, NOT static data\n * keys on OBJECT nodes.\n */\n get_child_at: (id, key) => revNodes.get(id, key),\n\n /**\n * Return true iff a child node exists at (parentId, parentKey). Static data\n * keys on OBJECT nodes do not count—return false for those.\n */\n has_child_at: (id, key) => revNodes.has(id, key),\n\n /**\n * Return the position of the closest sibling \"to the right\" of `pos` under\n * parentId, or undefined if no such sibling exists. The given `pos` may, but\n * does not have to exist already. Positions compare lexicographically.\n */\n get_next_sibling,\n\n /**\n * Insert a child node with the given id.\n *\n * If allowOverwrite=false (default): throw if a node with this id exists.\n * If allowOverwrite=true: replace any existing node at this id, deleting its\n * entire subtree if it has children.\n */\n set_child,\n\n /**\n * Change a node's parentKey, effectively repositioning the node within its\n * parent. The new position must be free.\n * Throw if another node already occupies (parentId, newPos).\n */\n move_sibling,\n\n /**\n * Delete a node and its entire subtree recursively.\n * Ignore if id=\"root\" (root is immortal).\n */\n delete_node,\n\n /**\n * Delete a key from node `id`. Handle two cases:\n *\n * 1. If id is an OBJECT with `key` in its data: remove that data field.\n * 2. If a child exists at (id, key): delete that child and all its\n * descendants recursively.\n *\n * No-op if neither applies or if the node doesn't exist.\n */\n delete_child_key,\n\n /**\n * Replace the data object of an OBJECT node.\n *\n * If allowOverwrite=false (default): throw if any key in `data` conflicts\n * with an existing child's parentKey.\n * If allowOverwrite=true: first delete any conflicting children (and their\n * entire subtrees), then set the data.\n */\n set_object_data,\n\n /**\n * Return a readable snapshot of the storage tree.\n *\n * @param lowMemory When true, the call site hints that the snapshot should\n * be optimized for lower memory consumption, even if that means slower\n * access.\n */\n get_snapshot(_lowMemory?: boolean): IReadableSnapshot {\n return makeInMemorySnapshot(nodes);\n },\n };\n return api;\n }\n}\n\nexport function makeNewInMemoryDriver(options?: {\n initialActor?: number;\n initialNodes?: Iterable<[string, SerializedCrdt]>;\n}): IStorageDriver {\n return new InMemoryDriver(options);\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n/**\n * Wraps single-quotes around any string value. Useful for displaying field\n * names or other identifiers in error messages or logs.\n *\n * Examples:\n * quote(\"hi\") // \"'hi'\"\n * quote(\"i'm\") // \"'i'm'\"\n *\n * Note: no \"escaping\" happens here to the string value. This is because this\n * is intended to be used for human consumption, not machine consumption.\n */\nexport function quote(value: string | undefined): string {\n return value !== undefined ? `'${value}'` : \"???\";\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type {\n Awaitable,\n SerializedChild,\n SerializedCrdt,\n} from \"@liveblocks/core\";\nimport {\n asPos,\n assertNever,\n CrdtType,\n makePosition,\n OpCode,\n} from \"@liveblocks/core\";\n\nimport type { IStorageDriver, IStorageDriverNodeAPI } from \"~/interfaces\";\nimport type { Logger } from \"~/lib/Logger\";\nimport type {\n ClientWireOp,\n CreateOp,\n DeleteCrdtOp,\n DeleteObjectKeyOp,\n FixOp,\n HasOpId,\n SetParentKeyOp,\n UpdateObjectOp,\n} from \"~/protocol\";\nimport type { Pos } from \"~/types\";\n\ntype ApplyOpResult = OpAccepted | OpIgnored;\n\nexport type OpAccepted = {\n action: \"accepted\";\n op: ClientWireOp;\n fix?: FixOp;\n};\n\nexport type OpIgnored = {\n action: \"ignored\";\n ignoredOpId?: string;\n};\n\nfunction accept(op: ClientWireOp, fix?: FixOp): OpAccepted {\n return { action: \"accepted\", op, fix };\n}\n\nfunction ignore(ignoredOp: ClientWireOp): OpIgnored {\n return { action: \"ignored\", ignoredOpId: ignoredOp.opId };\n}\n\nfunction nodeFromCreateChildOp(op: CreateOp): SerializedChild {\n switch (op.type) {\n case OpCode.CREATE_LIST:\n return {\n type: CrdtType.LIST,\n parentId: op.parentId,\n parentKey: op.parentKey,\n };\n\n case OpCode.CREATE_MAP:\n return {\n type: CrdtType.MAP,\n parentId: op.parentId,\n parentKey: op.parentKey,\n };\n\n case OpCode.CREATE_OBJECT:\n return {\n type: CrdtType.OBJECT,\n parentId: op.parentId,\n parentKey: op.parentKey,\n data: op.data,\n };\n\n case OpCode.CREATE_REGISTER:\n return {\n type: CrdtType.REGISTER,\n parentId: op.parentId,\n parentKey: op.parentKey,\n data: op.data,\n };\n\n // istanbul ignore next\n default:\n return assertNever(op, \"Unknown op code\");\n }\n}\n\nexport class Storage {\n // The actual underlying storage API (could be backed by in-memory store,\n // SQLite, Redis, Postgres, Cloudflare Durable Object Storage, etc.)\n private readonly coreDriver: IStorageDriver;\n private _loadedDriver: IStorageDriverNodeAPI | undefined;\n\n constructor(coreDriver: IStorageDriver) {\n this.coreDriver = coreDriver;\n }\n\n // -------------------------------------------------------------------------\n // Public API (for Storage)\n // -------------------------------------------------------------------------\n\n get loadedDriver(): IStorageDriverNodeAPI {\n if (this._loadedDriver === undefined) {\n throw new Error(\"Cannot access tree before it's been loaded\");\n }\n return this._loadedDriver;\n }\n\n // REFACTOR NOTE: Eventually raw_iter_nodes has to be removed here\n raw_iter_nodes(): Awaitable<Iterable<[string, SerializedCrdt]>> {\n return this.coreDriver.raw_iter_nodes();\n }\n\n /**\n * Load the room data from object storage into memory. Persisted room\n * data consists of the main node map, which represents the Liveblocks\n * Storage tree, and special keys where we store usage metrics, or room\n * metadata.\n */\n async load(logger: Logger): Promise<void> {\n this._loadedDriver = await this.coreDriver.load_nodes_api(logger);\n }\n\n unload(): void {\n this._loadedDriver = undefined;\n }\n\n /**\n * Applies a batch of Ops.\n */\n async applyOps(ops: ClientWireOp[]): Promise<ApplyOpResult[]> {\n const results: ApplyOpResult[] = [];\n for (const op of ops) {\n results.push(await this.applyOp(op));\n }\n return results;\n }\n\n // -------------------------------------------------------------------------\n // Private APIs (for Storage)\n // -------------------------------------------------------------------------\n\n /**\n * Applies a single Op.\n */\n private async applyOp(op: ClientWireOp): Promise<ApplyOpResult> {\n switch (op.type) {\n case OpCode.CREATE_LIST:\n case OpCode.CREATE_MAP:\n case OpCode.CREATE_REGISTER:\n case OpCode.CREATE_OBJECT:\n return await this.applyCreateOp(op);\n\n case OpCode.UPDATE_OBJECT:\n return await this.applyUpdateObjectOp(op);\n\n case OpCode.SET_PARENT_KEY:\n return await this.applySetParentKeyOp(op);\n\n case OpCode.DELETE_OBJECT_KEY:\n return await this.applyDeleteObjectKeyOp(op);\n\n case OpCode.DELETE_CRDT:\n return await this.applyDeleteCrdtOp(op);\n\n // istanbul ignore next\n default:\n if (process.env.NODE_ENV === \"production\") {\n return ignore(op);\n } else {\n return assertNever(op, \"Invalid op\");\n }\n }\n }\n\n private async applyCreateOp(op: CreateOp & HasOpId): Promise<ApplyOpResult> {\n if (this.loadedDriver.has_node(op.id)) {\n // Node already exists, the operation is ignored\n return ignore(op);\n }\n\n const node = nodeFromCreateChildOp(op);\n\n const parent = this.loadedDriver.get_node(node.parentId);\n if (parent === undefined) {\n // Parent does not exist because the op is invalid or because it was deleted in race condition.\n return ignore(op);\n }\n\n // How to create this node in the node map depends on the parent node's type\n switch (parent.type) {\n case CrdtType.OBJECT:\n // Register children under object nodes are forbidden. We'll simply\n // ignore these Ops. This matches the eventual storage behavior: if\n // we'd persist them, they would get ignored when re-loading the\n // persisted room data into memory the next time the room loads.\n if (op.type === OpCode.CREATE_REGISTER) {\n return ignore(op);\n }\n // fall through\n\n case CrdtType.MAP:\n // Children of maps and objects require no special needs\n await this.loadedDriver.set_child(op.id, node, true);\n return accept(op);\n\n case CrdtType.LIST:\n // List items need special handling around conflicting resolution,\n // which depends on the users intention\n return this.createChildAsListItem(op, node);\n\n case CrdtType.REGISTER:\n // It's illegal for registers to have children\n return ignore(op);\n\n // istanbul ignore next\n default:\n return assertNever(parent, \"Unhandled CRDT type\");\n }\n }\n\n private async createChildAsListItem(\n op: CreateOp & HasOpId,\n node: SerializedChild\n ): Promise<ApplyOpResult> {\n let fix: FixOp | undefined;\n\n // The default intent, when not explicitly provided, is to insert, not set,\n // into the list.\n const intent: \"insert\" | \"set\" = op.intent ?? \"insert\";\n\n // istanbul ignore else\n if (intent === \"insert\") {\n const insertedParentKey = await this.insertIntoList(op.id, node);\n\n // If the inserted parent key is different from the input, it means there\n // was a conflict and the node has been inserted in an alternative free\n // list position. We should broadcast a modified Op to all clients that\n // has the modified position, and send a \"fix\" op back to the originating\n // client.\n if (insertedParentKey !== node.parentKey) {\n op = { ...op, parentKey: insertedParentKey };\n fix = {\n type: OpCode.SET_PARENT_KEY,\n id: op.id,\n parentKey: insertedParentKey,\n };\n return accept(op, fix);\n }\n\n // No conflict, node got inserted as intended\n return accept(op);\n }\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n else if (intent === \"set\") {\n // The intent here is to \"set\", not insert, into the list, replacing the\n // existing item that\n\n // Special handling required here. They will include a \"deletedId\" that\n // points to the object they expect to be replacing. If in the mean time,\n // that object disappeared there (because it was moved, for example), be\n // sure to delete it anyway.\n // We should not just trust the given value, because we're about to\n // delete a node. It's only safe to delete the node if it indeed is\n // a sibling of the current node.\n const deletedId =\n op.deletedId !== undefined &&\n op.deletedId !== op.id &&\n this.loadedDriver.get_node(op.deletedId)?.parentId === node.parentId\n ? op.deletedId\n : undefined;\n\n if (deletedId !== undefined) {\n await this.loadedDriver.delete_node(deletedId);\n }\n\n const prevItemId = this.loadedDriver.get_child_at(\n node.parentId,\n node.parentKey\n );\n if (prevItemId !== undefined && prevItemId !== deletedId) {\n // If this \"set\" operation indeed removed an item, but it wasn't the\n // expected `deletedId`, let the invoking client know that they'll\n // have to delete this object, too.\n fix = {\n type: OpCode.DELETE_CRDT,\n id: prevItemId,\n };\n }\n\n await this.loadedDriver.set_child(op.id, node, true);\n\n return accept(op, fix);\n } else {\n return assertNever(intent, \"Invalid intent\");\n }\n }\n\n private async applyDeleteObjectKeyOp(\n op: DeleteObjectKeyOp & HasOpId\n ): Promise<ApplyOpResult> {\n await this.loadedDriver.delete_child_key(op.id, op.key);\n return accept(op);\n }\n\n private async applyUpdateObjectOp(\n op: UpdateObjectOp & HasOpId\n ): Promise<ApplyOpResult> {\n await this.loadedDriver.set_object_data(op.id, op.data, true);\n return accept(op);\n }\n\n private async applyDeleteCrdtOp(\n op: DeleteCrdtOp & HasOpId\n ): Promise<ApplyOpResult> {\n await this.loadedDriver.delete_node(op.id);\n return accept(op);\n }\n\n private async applySetParentKeyOp(\n op: SetParentKeyOp & HasOpId\n ): Promise<ApplyOpResult> {\n const newPosition = await this.moveToPosInList(op.id, op.parentKey);\n if (newPosition === undefined) {\n // The operation got rejected because it didn't make sense, ignore it\n return ignore(op);\n }\n\n // If the inserted node is different from the input, it means there was\n // a conflict and the node has been inserted in a new, free, list position.\n // We should broadcast a modified Op to all clients that has the modified\n // position, and send a \"fix\" op back to the originating client.\n if (newPosition !== op.parentKey) {\n const modifiedOp = { ...op, parentKey: newPosition };\n const fix: FixOp = {\n type: OpCode.SET_PARENT_KEY,\n id: op.id,\n parentKey: newPosition,\n };\n return accept(modifiedOp, fix);\n } else {\n return accept(op);\n }\n }\n\n /**\n * Inserts a new node in the storage tree, under a list parent. If an\n * existing sibling node already exist under this key, however, it will look\n * for another free position under that parent and insert it under\n * a different parent key that is guaranteed to be available.\n *\n * Returns the key that was used for the insertion.\n */\n private async insertIntoList(\n id: string,\n node: SerializedChild\n ): Promise<string> {\n // First, compute the key to use to insert this node\n const key = this.findFreeListPosition(node.parentId, asPos(node.parentKey));\n if (key !== node.parentKey) {\n node = { ...node, parentKey: key };\n }\n await this.loadedDriver.set_child(id, node);\n return node.parentKey;\n }\n\n /**\n * Tries to move a node to the given position under the same parent. If\n * a conflicting sibling node already exist at this position, it will use\n * another free position instead, to avoid the conflict.\n *\n * Returns the position (parentKey) that the node was eventually placed at.\n * If the node could be inserted without conflict, it will return the same\n * parentKey position.\n *\n * Will return `undefined` if this action could not be interpreted. Will be\n * a no-op for non-list items.\n */\n private async moveToPosInList(\n id: string,\n targetKey: string\n ): Promise<string | undefined> {\n const node = this.loadedDriver.get_node(id);\n if (node?.parentId === undefined) {\n return; /* reject */\n }\n\n if (this.loadedDriver.get_node(node.parentId)?.type !== CrdtType.LIST) {\n // SetParentKeyOp is a no-op for all nodes, except list items\n return; /* reject */\n }\n\n if (node.parentKey === targetKey) {\n // Already there\n return targetKey; /* no-op */\n }\n\n // First, compute the key to use to insert this node\n const key = this.findFreeListPosition(node.parentId, asPos(targetKey));\n if (key !== node.parentKey) {\n await this.loadedDriver.move_sibling(id, key);\n }\n return key;\n }\n\n /**\n * Checks whether the given parentKey is a \"free position\" under the\n * parentId, i.e. there are no siblings that have the same key. If a sibling\n * exists under that key, it tries to generate new positions until it finds\n * a free slot, and returns that. The returned value is therefore always safe\n * to use as parentKey.\n */\n private findFreeListPosition(parentId: string, parentPos: Pos): Pos {\n if (!this.loadedDriver.has_child_at(parentId, parentPos)) {\n return parentPos;\n }\n\n const currPos = parentPos;\n const nextPos = this.loadedDriver.get_next_sibling(parentId, currPos);\n if (nextPos !== undefined) {\n return makePosition(currPos, nextPos); // Between current and next\n } else {\n return makePosition(currPos); // After current (fallback)\n }\n }\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { DefaultMap } from \"@liveblocks/core\";\nimport { Base64 } from \"js-base64\";\nimport { nanoid } from \"nanoid\";\nimport * as Y from \"yjs\";\n\nimport type { Guid, YDocId } from \"~/decoders\";\nimport { ROOT_YDOC_ID } from \"~/decoders\";\nimport type { IStorageDriver } from \"~/interfaces\";\nimport type { Logger } from \"~/lib/Logger\";\n\n// How big an update can be until we compress all individual updates into\n// a single vector and persist that instead (i.e. when we trigger \"garbage\n// collection\")\nconst MAX_Y_UPDATE_SIZE = 100_000;\n\ntype YUpdateInfo = {\n currentKey: string;\n lastVector: Uint8Array | undefined;\n};\n\nexport class YjsStorage {\n private readonly driver: IStorageDriver;\n\n private readonly doc: Y.Doc = new Y.Doc(); // the root document\n private readonly lastUpdatesById = new Map<YDocId, YUpdateInfo>();\n private readonly lastSnapshotById = new Map<YDocId, Y.Snapshot>();\n // Keeps track of which keys are loaded, so we can clean them up without calling `.list()`\n private readonly keysById = new DefaultMap<YDocId, Set<string>>(\n () => new Set()\n );\n private readonly initPromisesById: Map<YDocId, Promise<Y.Doc>> = new Map();\n\n constructor(driver: IStorageDriver) {\n this.driver = driver;\n this.doc.on(\"subdocs\", ({ removed }) => {\n removed.forEach((subdoc: Y.Doc) => {\n subdoc.destroy(); // will remove listeners\n });\n });\n }\n\n // ------------------------------------------------------------------------------------\n // Public API\n // ------------------------------------------------------------------------------------\n\n public async getYDoc(docId: YDocId): Promise<Y.Doc> {\n const doc = await this.loadDocByIdIfNotAlreadyLoaded(docId);\n return doc;\n }\n\n /**\n * If passed a state vector, an update with diff will be returned, if not the entire doc is returned.\n *\n * @param stateVector a base64 encoded target state vector created by running Y.encodeStateVector(Doc) on the client\n * @returns a base64 encoded array of YJS updates\n */\n public async getYDocUpdate(\n logger: Logger,\n stateVector: string = \"\",\n guid?: Guid,\n isV2: boolean = false\n ): Promise<string | null> {\n const update = await this.getYDocUpdateBinary(\n logger,\n stateVector,\n guid,\n isV2\n );\n if (!update) return null;\n return Base64.fromUint8Array(update);\n }\n\n public async getYDocUpdateBinary(\n logger: Logger,\n stateVector: string = \"\",\n guid?: Guid,\n isV2: boolean = false\n ): Promise<Uint8Array | null> {\n const doc = guid !== undefined ? await this.getYSubdoc(guid) : this.doc;\n if (!doc) {\n return null;\n }\n let encodedTargetVector;\n try {\n // if given a state vector, attempt to decode it a single diffed update\n encodedTargetVector =\n stateVector.length > 0 ? Base64.toUint8Array(stateVector) : undefined;\n } catch (e) {\n logger.warn(\n \"Could not get update from passed vector, returning all updates\"\n );\n }\n if (isV2) {\n return Y.encodeStateAsUpdateV2(doc, encodedTargetVector);\n }\n return Y.encodeStateAsUpdate(doc, encodedTargetVector);\n }\n\n public async getYStateVector(guid?: Guid): Promise<string | null> {\n const doc = guid !== undefined ? await this.getYSubdoc(guid) : this.doc;\n if (!doc) {\n return null;\n }\n return Base64.fromUint8Array(Y.encodeStateVector(doc));\n }\n\n public async getSnapshotHash(options: {\n guid?: Guid;\n isV2?: boolean;\n }): Promise<string | null> {\n const doc =\n options.guid !== undefined\n ? await this.getYSubdoc(options.guid)\n : this.doc;\n if (!doc) {\n return null;\n }\n const snapshot = this._getOrPutLastSnapshot(doc);\n return this.calculateSnapshotHash(snapshot, { isV2: options.isV2 });\n }\n\n /**\n * @param update base64 encoded uint8array\n * @returns\n */\n public async addYDocUpdate(\n logger: Logger,\n update: string | Uint8Array,\n guid?: Guid,\n isV2?: boolean\n ): Promise<{ isUpdated: boolean; snapshotHash: string }> {\n const doc = guid !== undefined ? await this.getYSubdoc(guid) : this.doc;\n if (!doc) {\n throw new Error(`YDoc with guid ${guid} not found`);\n }\n\n try {\n // takes a snapshot if none is stored in memory - NOTE: snapshots are a combination of statevector + deleteset, not a full doc\n const beforeSnapshot = this._getOrPutLastSnapshot(doc);\n const updateAsU8 =\n typeof update === \"string\" ? Base64.toUint8Array(update) : update;\n const applyUpdate = isV2 ? Y.applyUpdateV2 : Y.applyUpdate;\n applyUpdate(doc, updateAsU8, \"client\");\n // put the new \"after update\" snapshot\n const afterSnapshot = this._putLastSnapshot(doc);\n // Check the snapshot before/after to see if the update had an effect\n const updated = !Y.equalSnapshots(beforeSnapshot, afterSnapshot);\n if (updated) {\n await this.handleYDocUpdate(doc);\n }\n\n return {\n isUpdated: updated,\n snapshotHash: await this.calculateSnapshotHash(afterSnapshot, { isV2 }),\n };\n } catch (e) {\n // The only reason this would happen is if a user would send bad data\n logger.warn(`Ignored bad YDoc update: ${String(e)}`);\n throw new Error(\n \"Bad YDoc update. Data is corrupted, or data does not match the encoding.\"\n );\n }\n }\n\n public loadDocByIdIfNotAlreadyLoaded(docId: YDocId): Promise<Y.Doc> {\n let loaded$ = this.initPromisesById.get(docId);\n let doc = docId === ROOT_YDOC_ID ? this.doc : this.findYSubdocByGuid(docId);\n if (!doc) {\n // An API call can load a subdoc without the root doc (this._doc) being loaded, we account for that by just instantiating a doc here.\n doc = new Y.Doc();\n }\n if (loaded$ === undefined) {\n loaded$ = this._loadYDocFromDurableStorage(doc, docId);\n this.initPromisesById.set(docId, loaded$);\n }\n return loaded$;\n }\n\n public async load(_logger: Logger): Promise<void> {\n await this.loadDocByIdIfNotAlreadyLoaded(ROOT_YDOC_ID);\n }\n\n /**\n * Unloads the Yjs documents from memory.\n */\n public unload(): void {\n // YYY Implement this later!\n // YYY We're currently never unloading data read into memory, but let's\n // sync this with the .unload() method from Storage, so there will not be\n // any surprises here later!\n //\n // this.doc = new Y.Doc();\n // this.initPromisesById.clear();\n // this.lastUpdatesById.clear();\n // this.keysById.clear();\n // this.initPromisesById.clear();\n }\n\n // ------------------------------------------------------------------------------------\n // Private APIs\n // ------------------------------------------------------------------------------------\n\n // NOTE: We could instead store the hash of snapshot instead of the whole snapshot to optimize memory usage.\n private _getOrPutLastSnapshot(doc: Y.Doc): Y.Snapshot {\n const docId: YDocId =\n doc.guid === this.doc.guid ? ROOT_YDOC_ID : (doc.guid as Guid);\n const snapshot = this.lastSnapshotById.get(docId);\n if (snapshot) {\n return snapshot;\n }\n return this._putLastSnapshot(doc);\n }\n\n // NOTE: We could instead store the hash of snapshot instead of the whole snapshot to optimize memory usage.\n private _putLastSnapshot(doc: Y.Doc): Y.Snapshot {\n const docId: YDocId =\n doc.guid === this.doc.guid ? ROOT_YDOC_ID : (doc.guid as Guid);\n const snapshot = Y.snapshot(doc);\n this.lastSnapshotById.set(docId, snapshot);\n return snapshot;\n }\n /**\n * Given a record of updates, merge them and compress if savings are significant\n */\n private _loadAndCompressYJSUpdates = async (\n docUpdates: Record<string, Uint8Array>,\n doc: Y.Doc,\n docId: YDocId\n ): Promise<void> => {\n // the percent we need to save to trigger re-writing storage, ie. only rewrite storage if we save more than 20%\n const SAVINGS_THRESHOLD = 0.2;\n // get all updates from disk\n const updates = Object.values(docUpdates);\n // uint8arrays size on disk is equal to their length, combine them to see how much we're using\n const sizeOnDisk = updates.reduce((acc, update) => {\n return acc + update.length;\n }, 0);\n if (updates.length > 0) {\n const docKeys = Object.keys(docUpdates);\n // keep track of keys in use\n this.keysById.set(docId, new Set(docKeys));\n\n const mergedUpdate = Y.mergeUpdates(updates);\n // Garbage collection won't happen unless we actually apply the update\n Y.applyUpdate(doc, mergedUpdate);\n\n // get the update so we can check out how big it is\n const garbageCollectedUpdate = Y.encodeStateAsUpdate(doc);\n\n if (\n garbageCollectedUpdate.length <\n sizeOnDisk * (1 - SAVINGS_THRESHOLD)\n ) {\n const newKey = nanoid();\n await this.driver.write_y_updates(\n docId,\n newKey,\n garbageCollectedUpdate\n );\n // delete all old keys, we're going to write new merged updates\n await this.driver.delete_y_updates(docId, docKeys);\n this.keysById.set(docId, new Set([newKey]));\n }\n }\n };\n\n private _loadYDocFromDurableStorage = async (\n doc: Y.Doc,\n docId: YDocId\n ): Promise<Y.Doc> => {\n const docUpdates = Object.fromEntries(\n await this.driver.iter_y_updates(docId)\n );\n await this._loadAndCompressYJSUpdates(docUpdates, doc, docId);\n // store the vector of the last update\n this.lastUpdatesById.set(docId, {\n currentKey: nanoid(),\n lastVector: Y.encodeStateVector(doc),\n });\n doc.emit(\"load\", [doc]); // sets the \"isLoaded\" to true on the doc\n\n return doc;\n };\n\n private findYSubdocByGuid(guid: Guid): Y.Doc | null {\n for (const subdoc of this.doc.getSubdocs()) {\n if (subdoc.guid === guid) {\n return subdoc;\n }\n }\n return null;\n }\n\n private async calculateSnapshotHash(\n snapshot: Y.Snapshot,\n { isV2 }: { isV2?: boolean }\n ): Promise<string> {\n const encodedSnapshot = isV2\n ? Y.encodeSnapshotV2(snapshot)\n : Y.encodeSnapshot(snapshot);\n return Base64.fromUint8Array(\n new Uint8Array(\n await crypto.subtle.digest(\"SHA-256\", new Uint8Array(encodedSnapshot))\n )\n );\n }\n\n // gets a subdoc, it will be loaded if not already loaded\n private async getYSubdoc(guid: Guid): Promise<Y.Doc | null> {\n const subdoc = this.findYSubdocByGuid(guid);\n if (!subdoc) {\n return null;\n }\n await this.loadDocByIdIfNotAlreadyLoaded(guid);\n return subdoc;\n }\n\n // When the YJS doc changes, update it in durable storage\n private async handleYDocUpdate(doc: Y.Doc): Promise<void> {\n const docId: YDocId =\n doc.guid === this.doc.guid ? ROOT_YDOC_ID : (doc.guid as Guid);\n const docUpdateInfo = this.lastUpdatesById.get(docId);\n // get the update since last vector\n const updateSinceLastVector = Y.encodeStateAsUpdate(\n doc,\n docUpdateInfo?.lastVector\n );\n // this should happen before the await on putYDoc to avoid race conditions\n // but we need the current key before, so store it here\n const storageKey = docUpdateInfo?.currentKey ?? nanoid();\n if (updateSinceLastVector.length > MAX_Y_UPDATE_SIZE) {\n // compress update, not using the vector, we want to write the whole doc\n const newKey = nanoid();\n await this.driver.write_y_updates(\n docId,\n newKey,\n Y.encodeStateAsUpdate(doc)\n );\n // delete all old keys on disk\n await this.driver.delete_y_updates(\n docId,\n Array.from(this.keysById.getOrCreate(docId))\n );\n // update the keys we have stored\n this.keysById.set(docId, new Set([newKey]));\n // future updates will write from this vector and to this key\n this.lastUpdatesById.set(docId, {\n currentKey: nanoid(), // start writing to a new key\n lastVector: Y.encodeStateVector(doc),\n });\n } else {\n // in this case, the update is small enough, just overwrite it\n await this.driver.write_y_updates(\n docId,\n storageKey,\n updateSinceLastVector\n );\n const keys = [storageKey];\n // keep track of keys used\n const currentKeys = this.keysById.getOrCreate(docId);\n for (const key of keys) {\n currentKeys.add(key);\n }\n }\n }\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n/**\n * Given a promise or promise factory, returns a 2-tuple of success or failure.\n * This pattern avoids having to build deeply nested try / catch clauses, where\n * success variables need to be defined as a `let` outside of the `try` block.\n *\n * Turns:\n *\n * let result;\n * try {\n * result = await doSomething();\n * } catch (error) {\n * // do something with error\n * }\n *\n * doAnotherThing(result);\n *\n * Into:\n *\n * const [result, error] = await tryCatch(doSomething());\n * if (error) {\n * // do something with error\n * }\n * doAnotherThing(result);\n *\n */\nexport async function tryCatch<T, E = Error>(\n promise: Promise<T> | (() => Promise<T>) | (() => T)\n): Promise<[T, undefined] | [undefined, E]> {\n try {\n const data = await (typeof promise === \"function\" ? promise() : promise);\n return [data, undefined];\n } catch (error) {\n return [undefined, error as E];\n }\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n/**\n * Like ES6 map, but also provides a unique reverse lookup index for values\n * stored in the map.\n *\n * Useful for code like:\n *\n * // Store a list of persons by their IDs, but each person's email must also\n * // be unique\n * const map = new UniqueMap((person) => person.email);\n * map.set(1, { name: 'John Doe', email: 'john@example.org' });\n * map.set(2, { name: 'John Foo', email: 'john@example.org' }); // Will error!\n * map.delete(1);\n * map.set(3, { name: 'Johnny', email: 'john@example.org' }); // Now it's allowed\n *\n * map.getReverseKey('john@example.org') // 3\n * map.getReverse('john@example.org') // { name: 'Johnny', email: 'john@example.org' }\n *\n */\nexport class UniqueMap<K, V, UK> extends Map<K, V> {\n // / \\\n // Primary key Unique key\n #_revMap: Map<UK, K>;\n #_keyFn: (value: V) => UK;\n\n constructor(\n keyFn: (value: V) => UK\n // entries?: readonly (readonly [K, V])[] | null\n ) {\n super(); // super(entries)\n this.#_keyFn = keyFn;\n this.#_revMap = new Map();\n }\n\n lookupPrimaryKey(uniqKey: UK): K | undefined {\n return this.#_revMap.get(uniqKey);\n }\n\n lookup(uniqKey: UK): V | undefined {\n const key = this.#_revMap.get(uniqKey);\n return key !== undefined ? this.get(key) : undefined;\n }\n\n set(key: K, value: V): this {\n const uniqKey = this.#_keyFn(value);\n const primaryKey = this.#_revMap.get(uniqKey);\n if (primaryKey !== undefined && primaryKey !== key) {\n throw new Error(`Unique key ${String(uniqKey)} already exists`);\n }\n this.#_revMap.set(uniqKey, key);\n return super.set(key, value);\n }\n\n delete(primaryKey: K): boolean {\n const value = this.get(primaryKey);\n if (value !== undefined) {\n const indexedKey = this.#_keyFn(value);\n this.#_revMap.delete(indexedKey);\n }\n return super.delete(primaryKey);\n }\n}\n","/**\n * Copyright (c) Liveblocks Inc.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published\n * by the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type { BaseUserMeta, JsonObject } from \"@liveblocks/core\";\nimport { ServerMsgCode } from \"@liveblocks/core\";\n\nimport type { RoomStateServerMsg } from \"~/protocol\";\nimport type { LeasedSession } from \"~/types\";\n\n/**\n * Concatenates multiple Uint8Arrays into a single Uint8Array.\n */\nexport function concatUint8Arrays(arrays: Uint8Array[]): Uint8Array {\n const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const arr of arrays) {\n result.set(arr, offset);\n offset += arr.length;\n }\n return result;\n}\n\nexport function makeRoomStateMsg(\n actor: number,\n nonce: string,\n scopes: string[],\n users: Record<number, BaseUserMeta & { scopes: string[] }>,\n publicMeta?: JsonObject\n): RoomStateServerMsg<BaseUserMeta> {\n return {\n type: ServerMsgCode.ROOM_STATE,\n actor,\n nonce,\n scopes,\n users,\n meta: publicMeta ?? {},\n };\n}\n\n/**\n * Check if a leased session is expired.\n * Returns true if the current time is greater than or equal to updatedAt + ttl.\n */\nexport function isLeasedSessionExpired(leasedSession: LeasedSession): boolean {\n const now = Date.now();\n return now >= leasedSession.updatedAt + leasedSession.ttl;\n}\n"],"mappings":";;;;;;;;;;;;AAkBA,SAAS,qBAAqB;AAE9B;AAAA,EACE;AAAA,EACA;AAAA,EACA,YAAAA;AAAA,EACA;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,EACA,UAAAC;AAAA,EACA,eAAAC;AAAA,OACK;;;ACVP,SAAS,eAAe;AAYjB,IAAM,WAA0B;AAahC,IAAM,iBAAsC,SAAS;AAAA,EAC1D,CAAC,UACC,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAAA,EACrE;AACF;;;AC/BA,SAAS,cAAc;AAEvB,SAAS,UAAU,QAAQ,UAAU,QAAQ,mBAAmB;AAkBhE,IAAM,iBAAoD,OAAO;AAAA,EAC/D,MAAM,SAAS,OAAO,aAAa;AAAA,EACnC,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,MAAM;AACR,CAAC;AAED,IAAM,iBAAoD,OAAO;AAAA,EAC/D,MAAM,SAAS,OAAO,aAAa;AAAA,EACnC,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,UAAU;AAAA,EACV,WAAW;AAAA,EACX,MAAM;AAAA,EACN,QAAQ,SAAS,SAAS,KAAK,CAAC;AAAA,EAChC,WAAW,SAAS,MAAM;AAC5B,CAAC;AAED,IAAM,eAAgD,OAAO;AAAA,EAC3D,MAAM,SAAS,OAAO,WAAW;AAAA,EACjC,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,UAAU;AAAA,EACV,WAAW;AAAA,EACX,QAAQ,SAAS,SAAS,KAAK,CAAC;AAAA,EAChC,WAAW,SAAS,MAAM;AAC5B,CAAC;AAED,IAAM,cAA8C,OAAO;AAAA,EACzD,MAAM,SAAS,OAAO,UAAU;AAAA,EAChC,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,UAAU;AAAA,EACV,WAAW;AAAA,EACX,QAAQ,SAAS,SAAS,KAAK,CAAC;AAAA,EAChC,WAAW,SAAS,MAAM;AAC5B,CAAC;AAED,IAAM,mBAAwD,OAAO;AAAA,EACnE,MAAM,SAAS,OAAO,eAAe;AAAA,EACrC,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,UAAU;AAAA,EACV,WAAW;AAAA,EACX,MAAM;AAAA,EACN,QAAQ,SAAS,SAAS,KAAK,CAAC;AAAA,EAChC,WAAW,SAAS,MAAM;AAC5B,CAAC;AAED,IAAM,eAAgD,OAAO;AAAA,EAC3D,MAAM,SAAS,OAAO,WAAW;AAAA,EACjC,MAAM;AAAA,EACN,IAAI;AACN,CAAC;AAED,IAAM,iBAAoD,OAAO;AAAA,EAC/D,MAAM,SAAS,OAAO,cAAc;AAAA,EACpC,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,WAAW;AACb,CAAC;AAED,IAAM,oBAA0D,OAAO;AAAA,EACrE,MAAM,SAAS,OAAO,iBAAiB;AAAA,EACvC,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,KAAK;AACP,CAAC;AAEM,IAAM,KAA4B,YAAY,QAAQ;AAAA,EAC3D,CAAC,OAAO,aAAa,GAAG;AAAA,EACxB,CAAC,OAAO,aAAa,GAAG;AAAA,EACxB,CAAC,OAAO,WAAW,GAAG;AAAA,EACtB,CAAC,OAAO,UAAU,GAAG;AAAA,EACrB,CAAC,OAAO,eAAe,GAAG;AAAA,EAC1B,CAAC,OAAO,WAAW,GAAG;AAAA,EACtB,CAAC,OAAO,cAAc,GAAG;AAAA,EACzB,CAAC,OAAO,iBAAiB,GAAG;AAC9B,CAAC;;;ACjGD,SAAS,YAAY;AAOd,IAAM,cAAc,KAAK,WAAiB;AAE1C,IAAM,eAAe;;;AHmB5B,IAAM,0BACJC,QAAO;AAAA,EACL,MAAMC,UAAS,cAAc,eAAe;AAAA,EAC5C,MAAM;AAAA,EACN,aAAaC,UAAS,MAAM;AAC9B,CAAC;AAEH,IAAM,0BAAkEF,QAAO;AAAA,EAC7E,MAAMC,UAAS,cAAc,eAAe;AAAA,EAC5C,OAAO;AACT,CAAC;AAED,IAAM,wBAAwDD,QAAO;AAAA,EACnE,MAAMC,UAAS,cAAc,aAAa;AAC5C,CAAC;AAED,IAAM,yBAA0DD,QAAO;AAAA,EACrE,MAAMC,UAAS,cAAc,cAAc;AAAA,EAC3C,KAAK,MAAM,EAAE;AACf,CAAC;AAED,IAAM,qBAAkDD,QAAO;AAAA,EAC7D,MAAMC,UAAS,cAAc,UAAU;AAAA,EACvC,QAAQE,QAAO,WAAoB;AAAA,EACnC,MAAMD,UAAS,WAAW;AAAA;AAAA,EAC1B,IAAIA,UAAS,OAAO;AACtB,CAAC;AAED,IAAM,sBAAoDF,QAAO;AAAA,EAC/D,MAAMC,UAAS,cAAc,WAAW;AAAA,EACxC,QAAQE,QAAO,WAAoB;AAAA,EACnC,MAAMD,UAAS,WAAW;AAAA;AAAA,EAC1B,IAAIA,UAAS,OAAO;AACtB,CAAC;AAEM,IAAM,mBACXE,aAAY,QAAQ;AAAA,EAClB,CAAC,cAAc,eAAe,GAAG;AAAA,EACjC,CAAC,cAAc,eAAe,GAAG;AAAA,EACjC,CAAC,cAAc,aAAa,GAAG;AAAA,EAC/B,CAAC,cAAc,cAAc,GAAG;AAAA,EAChC,CAAC,cAAc,UAAU,GAAG;AAAA,EAC5B,CAAC,cAAc,WAAW,GAAG;AAC/B,CAAC,EAAE,SAAS,gCAAgC;AAEvC,IAAM,4BACXA,aAAY,QAAQ;AAAA;AAAA;AAAA;AAAA,EAIlB,CAAC,cAAc,cAAc,GAAG;AAAA;AAAA;AAGlC,CAAC,EAAE,SAAS,0CAA0C;;;AIjFxD,SAAS,gBAAgB;AAelB,SAAS,0BACdC,WACY;AACZ,MAAI;AACF,WAAO,YAAYA,WAAU,QAAQA,UAAS,SAAS,EAAE,IAAI;AAAA,EAC/D,UAAE;AACA,IAAAA,UAAS,QAAQ;AAAA,EACnB;AACF;AAEA,SAAS,UAAUA,WAA6B,IAAkB;AAChE,QAAM,OAAOA,UAAS,SAAS,EAAE;AACjC,MAAI,KAAK,SAAS,SAAS,QAAQ;AACjC,WAAO,YAAYA,WAAU,IAAI,KAAK,IAAI;AAAA,EAC5C,WAAW,KAAK,SAAS,SAAS,MAAM;AACtC,WAAO,UAAUA,WAAU,EAAE;AAAA,EAC/B,WAAW,KAAK,SAAS,SAAS,KAAK;AACrC,WAAO,SAASA,WAAU,EAAE;AAAA,EAC9B,OAAO;AACL,WAAO,KAAK;AAAA,EACd;AACF;AAEA,SAAS,YACPA,WACA,IACA,YACY;AACZ,QAAM,OAAO,OAAO,OAAO,uBAAO,OAAO,IAAI,GAAG,UAAU;AAC1D,aAAW,CAAC,KAAK,OAAO,KAAKA,UAAS,cAAc,EAAE,GAAG;AACvD,SAAK,GAAG,IAAI,UAAUA,WAAU,OAAO;AAAA,EACzC;AACA,SAAO;AACT;AAEA,SAAS,UAAUA,WAA6B,IAAoB;AAClE,QAAM,OAAe,CAAC;AACtB,aAAW,CAAC,GAAG,OAAO,KAAKA,UAAS,cAAc,EAAE,GAAG;AACrD,SAAK,KAAK,UAAUA,WAAU,OAAO,CAAC;AAAA,EACxC;AACA,SAAO;AACT;AAEA,SAAS,SAASA,WAA6B,IAAwB;AACrE,QAAM,OAAO,uBAAO,OAAO,IAAI;AAC/B,aAAW,CAAC,KAAK,OAAO,KAAKA,UAAS,cAAc,EAAE,GAAG;AACvD,SAAK,GAAG,IAAI,UAAUA,WAAU,OAAO;AAAA,EACzC;AACA,SAAO;AACT;AAmBO,UAAU,yBACfA,WACW;AACX,MAAI;AACF,UAAM,aAAa,KAAK,UAAUA,UAAS,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,EAAE;AACvE,WAAO,WAAWA,WAAU,QAAQ,UAAU;AAAA,EAChD,UAAE;AACA,IAAAA,UAAS,QAAQ;AAAA,EACnB;AACF;AAEA,UAAU,KAAKA,WAA6B,IAAuB;AACjE,QAAM,OAAOA,UAAS,SAAS,EAAE;AACjC,MAAI,KAAK,SAAS,SAAS,QAAQ;AACjC,WAAO,WAAWA,WAAU,IAAI,KAAK,UAAU,KAAK,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC;AAAA,EACxE,WAAW,KAAK,SAAS,SAAS,MAAM;AACtC,WAAO,SAASA,WAAU,EAAE;AAAA,EAC9B,WAAW,KAAK,SAAS,SAAS,KAAK;AACrC,WAAO,QAAQA,WAAU,EAAE;AAAA,EAC7B,WAAW,KAAK,SAAS,SAAS,UAAU;AAC1C,UAAM,KAAK,UAAU,KAAK,IAAI;AAAA,EAChC;AACF;AAWA,UAAU,WACRA,WACA,IACA,YACW;AACX,MAAI,QAAQ,WAAW,SAAS;AAEhC,QAAM;AACN,QAAM;AAEN,aAAW,CAAC,KAAK,OAAO,KAAKA,UAAS,cAAc,EAAE,GAAG;AACvD,QAAI,MAAO,OAAM;AAAA,QACZ,SAAQ;AAEb,UAAM,GAAG,KAAK,UAAU,GAAG,CAAC;AAC5B,WAAO,KAAKA,WAAU,OAAO;AAAA,EAC/B;AACA,QAAM;AACR;AAEA,UAAU,SAASA,WAA6B,IAAuB;AACrE,MAAI,QAAQ;AAEZ,QAAM;AACN,aAAW,CAAC,GAAG,OAAO,KAAKA,UAAS,cAAc,EAAE,GAAG;AACrD,QAAI,MAAO,OAAM;AAAA,QACZ,SAAQ;AACb,WAAO,KAAKA,WAAU,OAAO;AAAA,EAC/B;AACA,QAAM;AACR;AAEA,UAAU,QAAQA,WAA6B,IAAuB;AACpE,MAAI,QAAQ;AAEZ,QAAM;AACN,aAAW,CAAC,KAAK,OAAO,KAAKA,UAAS,cAAc,EAAE,GAAG;AACvD,QAAI,MAAO,OAAM;AAAA,QACZ,SAAQ;AAEb,UAAM,GAAG,KAAK,UAAU,GAAG,CAAC;AAC5B,WAAO,KAAKA,WAAU,OAAO;AAAA,EAC/B;AACA,QAAM;AACR;;;ACzJO,UAAU,qBACfC,WACqC;AACrC,MAAI;AACF,WAAOA,UAAS,SAAS;AAAA,EAC3B,UAAE;AACA,IAAAA,UAAS,QAAQ;AAAA,EACnB;AACF;;;ACJA;AAAA,EACE;AAAA,EACA,YAAAC;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAIP,IAAM,wBAAwB;AAE9B,SAAS,WAAW,OAA0B;AAC5C,SAAO,GAAG,qBAAqB,IAAI,MAAM,OAAO;AAClD;AAEA,SAAS,wBACP,OACyD;AACzD,SAAO,aAAa,KAAK,KAAK,MAAM,mBAAmB;AACzD;AAMA,UAAU,SACR,KACA,MACA,QACA,OACyC;AACzC,MAAI,wBAAwB,IAAI,GAAG;AACjC,YAAQ,KAAK,gBAAgB;AAAA,MAC3B,KAAK;AACH,eAAO,gBAAgB,KAAK,KAAK,MAAM,QAAQ,KAAK;AACpD;AAAA,MAEF,KAAK;AACH,eAAO,SAAS,KAAK,KAAK,MAAM,QAAQ,KAAK;AAC7C;AAAA,MAEF,KAAK;AACH,eAAO,QAAQ,KAAK,KAAK,MAAM,QAAQ,KAAK;AAC5C;AAAA,MAGF;AACE,oBAAY,MAAM,gCAAgC;AAAA,IACtD;AAAA,EACF,OAAO;AACL,UAAM;AAAA,MACJ,WAAW,KAAK;AAAA,MAChB;AAAA,QACE,MAAMA,UAAS;AAAA,QACf;AAAA,QACA,UAAU,OAAO,CAAC;AAAA,QAClB,WAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF;AACF;AAMA,UAAU,QACR,KACA,KACA,QACA,OACyC;AACzC,QAAM,WAAwB;AAAA,IAC5B,WAAW,KAAK;AAAA,IAChB,EAAE,MAAMA,UAAS,KAAK,UAAU,OAAO,CAAC,GAAG,WAAW,IAAI;AAAA,EAC5D;AAGA,QAAM;AAGN,aAAW,CAAC,QAAQ,QAAQ,KAAK,OAAO,QAAQ,GAAG,GAAG;AACpD,WAAO,SAAS,QAAQ,UAAU,UAAU,KAAK;AAAA,EACnD;AACF;AAMA,UAAU,SACR,KACA,MACA,QACA,OACyC;AACzC,QAAM,KAAK,WAAW,KAAK;AAC3B,QAAM,OAAuB;AAAA,IAC3B,MAAMA,UAAS;AAAA,IACf,UAAU,OAAO,CAAC;AAAA,IAClB,WAAW;AAAA,EACb;AACA,QAAM,YAAyB,CAAC,IAAI,IAAI;AAGxC,QAAM;AAGN,MAAI,WAAW,aAAa;AAC5B,aAAW,YAAY,MAAM;AAC3B,WAAO,SAAS,UAAU,UAAU,WAAW,KAAK;AACpD,eAAW,aAAa,QAAQ;AAAA,EAClC;AACF;AAUA,UAAU,gBACR,KACA,OACA,QACA,OACyC;AAEzC,QAAM,OAAmB,CAAC;AAC1B,QAAM,kBAA8C,CAAC;AAErD,aAAW,CAAC,QAAQ,QAAQ,KAAK,OAAO,QAAQ,KAAK,GAAG;AACtD,QAAI,wBAAwB,QAAQ,GAAG;AACrC,sBAAgB,KAAK,CAAC,QAAQ,QAAQ,CAAC;AAAA,IACzC,OAAO;AACL,WAAK,MAAM,IAAI;AAAA,IACjB;AAAA,EACF;AAGA,QAAM,cACJ,WAAW,OACP;AAAA,IACE,WAAW,KAAK;AAAA,IAChB;AAAA,MACE,MAAMA,UAAS;AAAA,MACf;AAAA,MACA,UAAU,OAAO,CAAC;AAAA,MAClB,WAAW;AAAA,IACb;AAAA,EACF,IACA,CAAC,QAAQ,EAAE,MAAMA,UAAS,QAAQ,KAAK,CAAC;AAG9C,QAAM;AAGN,aAAW,CAAC,QAAQ,QAAQ,KAAK,iBAAiB;AAChD,WAAO,SAAS,QAAQ,UAAU,aAAa,KAAK;AAAA,EACtD;AACF;AAOO,UAAU,sBACf,MACyC;AACzC,QAAM,QAAQ,EAAE,OAAO,EAAE;AACzB,SAAO,gBAAgB,QAAQ,KAAK,MAAM,MAAM,KAAK;AACvD;AAYO,SAAS,0BACdC,WACiB;AACjB,MAAI;AACF,WAAOC,aAAYD,WAAU,QAAQA,UAAS,SAAS,EAAE,IAAI;AAAA,EAC/D,UAAE;AACA,IAAAA,UAAS,QAAQ;AAAA,EACnB;AACF;AAEA,SAASE,WAAUF,WAA6B,IAAuB;AACrE,QAAM,OAAOA,UAAS,SAAS,EAAE;AACjC,MAAI,KAAK,SAASD,UAAS,QAAQ;AACjC,WAAOE,aAAYD,WAAU,IAAI,KAAK,IAAI;AAAA,EAC5C,WAAW,KAAK,SAASD,UAAS,MAAM;AACtC,WAAOI,WAAUH,WAAU,EAAE;AAAA,EAC/B,WAAW,KAAK,SAASD,UAAS,KAAK;AACrC,WAAOK,UAASJ,WAAU,EAAE;AAAA,EAC9B,OAAO;AACL,WAAO,KAAK;AAAA,EACd;AACF;AAEA,SAASC,aACPD,WACA,IACA,YACiB;AAEjB,QAAM,OAAwB,OAAO;AAAA,IACnC,uBAAO,OAAO,IAAI;AAAA,IAClB;AAAA,EACF;AACA,aAAW,CAAC,KAAK,OAAO,KAAKA,UAAS,cAAc,EAAE,GAAG;AACvD,SAAK,GAAG,IAAIE,WAAUF,WAAU,OAAO;AAAA,EACzC;AACA,SAAO,EAAE,gBAAgB,cAAc,KAAK;AAC9C;AAEA,SAASG,WAAUH,WAA6B,IAA2B;AACzE,QAAM,OAAoB,CAAC;AAC3B,aAAW,CAAC,GAAG,OAAO,KAAKA,UAAS,cAAc,EAAE,GAAG;AACrD,SAAK,KAAKE,WAAUF,WAAU,OAAO,CAAC;AAAA,EACxC;AACA,SAAO,EAAE,gBAAgB,YAAY,KAAK;AAC5C;AAEA,SAASI,UAASJ,WAA6B,IAA0B;AACvE,QAAM,OAAO,uBAAO,OAAO,IAAI;AAC/B,aAAW,CAAC,KAAK,OAAO,KAAKA,UAAS,cAAc,EAAE,GAAG;AACvD,SAAK,GAAG,IAAIE,WAAUF,WAAU,OAAO;AAAA,EACzC;AACA,SAAO,EAAE,gBAAgB,WAAW,KAAK;AAC3C;AAeO,UAAU,yBACfA,WACW;AACX,MAAI;AACF,UAAM,aAAa,KAAK,UAAUA,UAAS,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,EAAE;AACvE,WAAOK,YAAWL,WAAU,QAAQ,UAAU;AAAA,EAChD,UAAE;AACA,IAAAA,UAAS,QAAQ;AAAA,EACnB;AACF;AAEA,UAAUM,MAAKN,WAA6B,IAAuB;AACjE,QAAM,OAAOA,UAAS,SAAS,EAAE;AACjC,MAAI,KAAK,SAASD,UAAS,QAAQ;AACjC,WAAOM,YAAWL,WAAU,IAAI,KAAK,UAAU,KAAK,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC;AAAA,EACxE,WAAW,KAAK,SAASD,UAAS,MAAM;AACtC,WAAOQ,UAASP,WAAU,EAAE;AAAA,EAC9B,WAAW,KAAK,SAASD,UAAS,KAAK;AACrC,WAAOS,SAAQR,WAAU,EAAE;AAAA,EAC7B,WAAW,KAAK,SAASD,UAAS,UAAU;AAC1C,UAAM,KAAK,UAAU,KAAK,IAAI;AAAA,EAChC;AACF;AAWA,UAAUM,YACRL,WACA,IACA,YACW;AACX,MAAI,QAAQ,WAAW,SAAS;AAEhC,QAAM;AACN,QAAM;AAEN,aAAW,CAAC,KAAK,OAAO,KAAKA,UAAS,cAAc,EAAE,GAAG;AACvD,QAAI,MAAO,OAAM;AAAA,QACZ,SAAQ;AAEb,UAAM,GAAG,KAAK,UAAU,GAAG,CAAC;AAC5B,WAAOM,MAAKN,WAAU,OAAO;AAAA,EAC/B;AACA,QAAM;AACR;AAEA,UAAUO,UAASP,WAA6B,IAAuB;AACrE,MAAI,QAAQ;AAEZ,QAAM;AACN,aAAW,CAAC,GAAG,OAAO,KAAKA,UAAS,cAAc,EAAE,GAAG;AACrD,QAAI,MAAO,OAAM;AAAA,QACZ,SAAQ;AACb,WAAOM,MAAKN,WAAU,OAAO;AAAA,EAC/B;AACA,QAAM;AACR;AAEA,UAAUQ,SAAQR,WAA6B,IAAuB;AACpE,MAAI,QAAQ;AAEZ,QAAM;AACN,aAAW,CAAC,KAAK,OAAO,KAAKA,UAAS,cAAc,EAAE,GAAG;AACvD,QAAI,MAAO,OAAM;AAAA,QACZ,SAAQ;AAEb,UAAM,GAAG,KAAK,UAAU,GAAG,CAAC;AAC5B,WAAOM,MAAKN,WAAU,OAAO;AAAA,EAC/B;AACA,QAAM;AACR;;;AC/UA,SAAS,YAAAS,WAAU,mBAAmB,UAAU;;;ACRhD,SAAS,aAAa;AAjBtB;AAgCO,IAAM,aAAN,cAA+B,IAAU;AAAA;AAAA;AAAA;AAAA;AAAA,EAO9C,YACE,WACA,SACA;AACA,UAAM,OAAO;AAVf;AAWE,uBAAK,YAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,YAAY,KAAQ,WAA8B;AAChD,QAAI,MAAM,IAAI,GAAG,GAAG;AAElB,aAAO,MAAM,IAAI,GAAG;AAAA,IACtB,OAAO;AACL,YAAM,KACJ,aACA,mBAAK,eACL,MAAM,4CAA4C;AAEpD,YAAM,QAAQ,GAAG,GAAG;AACpB,WAAK,IAAI,KAAK,KAAK;AACnB,aAAO;AAAA,IACT;AAAA,EACF;AACF;AApCE;;;ACdF,SAAS,gBAAyC;AAChD,SAAO,CAAC,EAAE,OAAO,QAAQ,EAAE;AAC7B;AArBA;AA2BO,IAAM,YAAN,MAA2B;AAAA,EAGhC,cAAc;AAFd;AAGE,uBAAK,MAAO,IAAI,WAAW,MAAM,oBAAI,IAAW,CAAC;AAAA,EACnD;AAAA,EAEA,IAAI,OAAe;AACjB,QAAI,QAAQ;AACZ,eAAW,SAAS,mBAAK,MAAK,OAAO,GAAG;AACtC,eAAS,MAAM;AAAA,IACjB;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,MAAkB;AACtB,WAAO,mBAAK,MAAK,IAAI,IAAI,GAAG,QAAQ;AAAA,EACtC;AAAA,EAEA,CAAC,OAAmC;AAClC,eAAW,CAAC,MAAM,MAAM,KAAK,mBAAK,OAAM;AACtC,iBAAW,QAAQ,OAAO,KAAK,GAAG;AAChC,cAAM,CAAC,MAAM,IAAI;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,IAAI,MAAU,MAAmB;AAC/B,WAAO,mBAAK,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;AAAA,EAC3C;AAAA,EAEA,IAAI,MAAU,MAAyB;AACrC,WAAO,mBAAK,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI;AAAA,EACtC;AAAA,EAEA,IAAI,MAAU,MAAU,OAAgB;AACtC,uBAAK,MAAK,YAAY,IAAI,EAAE,IAAI,MAAM,KAAK;AAC3C,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,MAAU,MAAgB;AAC/B,QAAI,CAAC,mBAAK,MAAK,IAAI,IAAI,GAAG;AACxB;AAAA,IACF;AAEA,UAAM,SAAS,mBAAK,MAAK,IAAI,IAAI;AACjC,WAAO,OAAO,IAAI;AAClB,QAAI,OAAO,SAAS,GAAG;AACrB,yBAAK,MAAK,OAAO,IAAI;AAAA,IACvB;AAAA,EACF;AAAA,EAEA,QAAc;AACZ,uBAAK,MAAK,MAAM;AAAA,EAClB;AAAA,EAEA,EAAE,OAAO,QAAQ,IAAmC;AAClD,eAAW,CAAC,MAAM,MAAM,KAAK,mBAAK,OAAM;AACtC,iBAAW,CAAC,MAAM,KAAK,KAAK,QAAQ;AAClC,cAAM,CAAC,MAAM,MAAM,KAAK;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AAAA,EAEA,UAAU,MAAqC;AAC7C,WAAO,mBAAK,MAAK,IAAI,IAAI,GAAG,QAAQ,KAAK,cAAc;AAAA,EACzD;AAAA,EAEA,CAAC,SAAS,MAAU,MAAuC;AACzD,UAAM,SAAS,mBAAK,MAAK,IAAI,IAAI;AACjC,QAAI,WAAW,QAAW;AACxB;AAAA,IACF;AAEA,eAAW,MAAM,MAAM;AACrB,YAAM,QAAQ,OAAO,IAAI,EAAE;AAC3B,UAAI,UAAU,QAAW;AACvB,cAAM,CAAC,IAAI,KAAK;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,OAAO,MAAgC;AACrC,WAAO,mBAAK,MAAK,IAAI,IAAI,GAAG,KAAK,KAAK,cAAc;AAAA,EACtD;AAAA,EAEA,SAAS,MAA+B;AACtC,WAAO,mBAAK,MAAK,IAAI,IAAI,GAAG,OAAO,KAAK,cAAc;AAAA,EACxD;AAAA,EAEA,UAAU,MAAgB;AACxB,uBAAK,MAAK,OAAO,IAAI;AAAA,EACvB;AACF;AA5FE;;;AFQK,SAAS,qBACd,QACmB;AACnB,QAAM,MAAe,IAAI,IAA4B,MAAoB;AAEzE,MAAI,CAAC,IAAI,IAAI,MAAM,GAAG;AACpB,QAAI,IAAI,QAAQ,EAAE,MAAMC,UAAS,QAAQ,MAAM,CAAC,EAAE,CAAC;AAAA,EACrD;AAKA,QAAM,UAAoE,CAAC;AAC3E,QAAM,aAAa;AACnB,aAAW,QAAQ,YAAY;AAC7B,QAAI,kBAAkB,IAAI,EAAG;AAC7B,UAAM,CAAC,IAAI,IAAI,IAAI;AACnB,YAAQ,KAAK,CAAC,KAAK,UAAU,KAAK,WAAW,EAAE,CAAC;AAAA,EAClD;AACA,UAAQ;AAAA,IAAK,CAAC,GAAG,MACf,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI;AAAA,EAC5E;AAEA,QAAM,SAAS,IAAI,UAAkC;AACrD,aAAW,CAAC,UAAU,WAAW,EAAE,KAAK,SAAS;AAC/C,WAAO,IAAI,UAAU,WAAW,EAAE;AAAA,EACpC;AAEA,WAAS,SAAS,IAA6B;AAC7C,WAAO,GAAG,IAAI,IAAI,EAAE,GAAG,mBAAmB,EAAE,EAAE;AAAA,EAChD;AAEA,SAAO;AAAA,IACL,UAAU,MACR;AAAA,MACE,IAAI,IAAI,MAAM;AAAA,MACd;AAAA,IACF;AAAA,IACF;AAAA,IACA,eAAe,CAAC,WAAW,OAAO,UAAU,MAAM;AAAA,IAClD,UAAU,MAAM;AAAA,IAChB,UAAU;AACR,UAAI,MAAM;AACV,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AACF;;;AG/CO,SAAS,eAAe,QAAoC;AAMjE,iBAAe,IACb,IACA,IAC+B;AAC/B,QAAI,OAAO,QAAW;AACpB,aAAO,MAAM,OAAO,SAAS,EAAY;AAAA,IAC3C,OAAO;AACL,aAAQ,GAAkB,MAAM,MAAM,OAAO,SAAS,EAAE,CAAC;AAAA,IAC3D;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,KAAK,OAAO,SAAS,KAAK,MAAM;AAAA,IAChC,QAAQ,OAAO,YAAY,KAAK,MAAM;AAAA,EACxC;AACF;;;ACxCA,SAAS,aAAa;AAEf,IAAK,kBAAL,kBAAKC,qBAAL;AA2CL,EAAAA,kCAAA,QAAK,KAAL;AAkBA,EAAAA,kCAAA,QAAK,KAAL;AA7DU,SAAAA;AAAA,GAAA;AAgEL,IAAM,yBAAyB,MAAM,eAAe,EAAE;AAAA,EAC3D;AACF;;;AC7DA;AAAA,EACE,eAAAC;AAAA,EACA,iBAAAC;AAAA,EACA;AAAA,EACA,UAAAC;AAAA,EACA,SAAAC;AAAA,EACA,iBAAAC;AAAA,EACA;AAAA,EACA,uBAAuB;AAAA,OAClB;AACP,SAAS,aAAa;AACtB,SAAS,SAAAC,QAAO,oBAAoB;AACpC,SAAS,eAAe;AACxB,SAAS,UAAAC,eAAc;;;ACnBvB,SAAS,SAAAC,cAAa;AAEf,IAAK,WAAL,kBAAKC,cAAL;AACL,EAAAA,oBAAA,WAAQ,KAAR;AACA,EAAAA,oBAAA,UAAO,KAAP;AACA,EAAAA,oBAAA,aAAU,KAAV;AACA,EAAAA,oBAAA,WAAQ,KAAR;AAJU,SAAAA;AAAA,GAAA;AAOZ,SAAS,YAAY,KAAY;AAC/B,QAAM,SAAS,GAAG,IAAI,IAAI,KAAK,IAAI,OAAO;AAC1C,UACE,IAAI,OAAO,WAAW,MAAM,IAAI,IAAI,QAAQ,GAAG,MAAM;AAAA,EAAK,IAAI,SAAS,EAAE,IACzE,QAAQ;AACZ;AAhCA;AAsCO,IAAe,YAAf,MAAyB;AAAA,EAK9B,YAAY,QAA+C,cAAe;AAJ1E,wBAAgB;AAEhB,+BAAS,oBAAI,QAA4B;AAGvC,SAAK,QACH,OAAO,UAAU,WACb,QACC,cAAc,KAAK,KAAK;AAAA,EACjC;AAAA;AAAA,EAGU,YAAY,OAAyB;AAC7C,YAAQ,OAAO;AAAA,MACb,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAOC,OAAM,mBAAmB;AAAA,IACpC;AAAA,EACF;AAAA;AAAA,EAGU,UAAU,KAA6B;AAC/C,WAAO,OAAO,QAAQ,WAClB,eAAe,QACb,YAAY,GAAG,IACf,KAAK,UAAU,GAAG,IACpB,OAAO,GAAG;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMU,kBAAkB,SAA6B;AACvD,UAAM,QAAQ,CAAC;AACf,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,WAAW,CAAC,CAAC,GAAG;AAClD,UAAI,MAAM,QAAW;AAEnB,cAAM,KAAK,OAAO,MAAM,WAAW,KAAK,UAAU,CAAC,IAAI;AACvD,cAAM,KAAK,GAAG,CAAC,IAAI,EAAE,EAAE;AAAA,MACzB;AAAA,IACF;AACA,WAAO,MAAM,SAAS,IAAI,IAAI,MAAM,KAAK,GAAG,CAAC,MAAM;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOU,cAAc,SAA6B;AACnD,QAAI,YAAY,mBAAK,QAAO,IAAI,OAAO;AACvC,QAAI,cAAc,QAAW;AAC3B,kBAAY,KAAK,kBAAkB,OAAO;AAC1C,yBAAK,QAAO,IAAI,SAAS,SAAS;AAAA,IACpC;AACA,WAAO;AAAA,EACT;AASF;AAvEE;AA6EF,IAAM,iBAAiB;AAAA,EACrB,CAAC,aAAc,GAAG;AAAA,EAClB,CAAC,YAAa,GAAG;AAAA,EACjB,CAAC,eAAgB,GAAG;AAAA,EACpB,CAAC,aAAc,GAAG;AACpB;AAEO,IAAM,gBAAN,cAA4B,UAAU;AAAA,EAC3C,IAAI,OAAiB,SAAqB,KAA2B;AACnE,YAAQ,eAAe,KAAK,CAAC;AAAA,MAC3B,KAAK,UAAU,GAAG;AAAA,MAClB,KAAK,cAAc,OAAO;AAAA,IAC5B;AAAA,EACF;AACF;AAOA,IAAM,gBAAgB;AAAA,EACpB,OAAO;AAAA,EACP,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AACT;AAOO,IAAM,SAAN,MAAM,QAAO;AAAA,EAgBlB,YACE,SAA2C,IAAI,cAAc,GAC7D,UAAsB,CAAC,GACvB;AAlBF,wBAAgB;AAChB,wBAAgB;AAChB,wBAAgB;AAChB,wBAAgB;AAEhB,wBAAgB;AAOhB,wBAAiB;AACjB,wBAAiB;AAMf,SAAK,WAAW;AAChB,SAAK,WAAW,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AAExD,UAAM,WAAmB,KAAK,IAAI,GAAG,KAAK,SAAS,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAEtE,UAAM,OAAO,MAAM;AAAA,IAAC;AACpB,UAAM,YAAY,CAAC,QAAkB,CAAC,QACpC,KAAK,SAAS,QAAQ,CAACC,YAAW;AAChC,UAAIA,QAAO,SAAS,KAAK;AACvB,QAAAA,QAAO,IAAI,KAAK,KAAK,UAAU,GAAG;AAAA,MACpC;AAAA,IACF,CAAC;AAEH,SAAK,IAAI;AAAA;AAAA,MAEP,OAAO,YAAY,gBAAiB,UAAU,aAAc,IAAI;AAAA,MAChE,MAAM,YAAY,eAAgB,UAAU,YAAa,IAAI;AAAA,MAC7D,MACE,YAAY,kBAAmB,UAAU,eAAgB,IAAI;AAAA,MAC/D,OAAO,YAAY,gBAAiB,UAAU,aAAc,IAAI;AAAA;AAAA,IAElE;AAEA,SAAK,QAAQ,KAAK,EAAE,SAAS;AAC7B,SAAK,OAAO,KAAK,EAAE,QAAQ;AAC3B,SAAK,OAAO,KAAK,EAAE,QAAQ;AAC3B,SAAK,QAAQ,KAAK,EAAE,SAAS;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,YAAY,OAA2B;AACrC,UAAM,WAAuB,EAAE,GAAG,KAAK,UAAU,GAAG,MAAM;AAC1D,WAAO,IAAI,QAAO,KAAK,UAAU,QAAQ;AAAA,EAC3C;AACF;;;ACpLA,SAAS,OAAO,YAAAC,WAAU,qBAAAC,oBAAmB,MAAAC,WAAU;AACvD,SAAS,SAAS,YAAY;;;ACHvB,SAAS,MAAM,OAAmC;AACvD,SAAO,UAAU,SAAY,IAAI,KAAK,MAAM;AAC9C;;;ADgBA,SAAS,cAAc,YAAwB;AAC7C,QAAM,SAAS,IAAI,UAAkC;AACrD,aAAW,QAAQ,YAAY;AAC7B,QAAIC,mBAAkB,IAAI,EAAG;AAG7B,UAAM,CAAC,IAAI,IAAI,IAAI;AACnB,UAAM,WAAW,OAAO,IAAI,KAAK,UAAU,KAAK,SAAS;AACzD,QAAI,aAAa,UAAa,KAAK,UAAU;AAC3C,aAAO,IAAI,KAAK,UAAU,KAAK,WAAW,EAAE;AAAA,IAC9C;AAAA,EACF;AACA,SAAO;AACT;AAMA,SAAS,mBAAmB,OAAgB;AAC1C,QAAM,WAAW,cAAc,KAAmB;AAElD,QAAM,QAAkB,CAAC,MAAM;AAC/B,QAAM,iBAA8B,oBAAI,IAAI;AAE5C,SAAO,MAAM,SAAS,GAAG;AACvB,UAAM,SAAS,MAAM,IAAI;AACzB,UAAM,OAAOC,IAAG,MAAM,IAAI,MAAM,CAAC;AAEjC,QAAI,KAAK,SAASC,UAAS,QAAQ;AACjC,iBAAW,OAAO,SAAS,OAAO,MAAM,GAAG;AACzC,eAAO,KAAK,KAAK,GAAG;AAAA,MACtB;AAAA,IACF;AAEA,QAAI,KAAK,SAASA,UAAS,UAAU;AACnC,YAAM,KAAK,GAAG,SAAS,SAAS,MAAM,CAAC;AAAA,IACzC,OAAO;AACL,YAAM,SAAS,MAAM,IAAI,KAAK,QAAQ;AACtC,UAAI,QAAQ,SAASA,UAAS,QAAQ;AACpC;AAAA,MACF;AAAA,IACF;AAEA,mBAAe,IAAI,MAAM;AAAA,EAC3B;AAGA,MAAI,eAAe;AACnB,aAAW,CAAC,EAAE,KAAK,OAAO;AACxB,QAAI,CAAC,eAAe,IAAI,EAAE,GAAG;AAC3B,YAAM,OAAO,EAAE;AACf;AAAA,IACF;AAAA,EACF;AAIA,SAAO,iBAAiB,IAAI,WAAW,cAAc,KAAmB;AAC1E;AAEA,SAAS,gBACP,MACA,KACiD;AACjD,SACE,KAAK,SAASA,UAAS,UACvB,OAAO,UAAU,eAAe,KAAK,KAAK,MAAM,GAAG,KACnD,KAAK,KAAK,GAAG,MAAM;AAEvB;AAMO,IAAM,iBAAN,MAA+C;AAAA,EAOpD,YAAY,SAGT;AATH,wBAAQ;AACR,wBAAQ;AACR,wBAAQ;AACR,wBAAQ;AACR,wBAAQ;AAMN,SAAK,SAAS,oBAAI,IAAI;AACtB,SAAK,UAAU,oBAAI,IAAI;AACvB,SAAK,OAAO,oBAAI,IAAI;AACpB,SAAK,kBAAkB,oBAAI,IAAI;AAE/B,SAAK,aAAa,SAAS,gBAAgB;AAE3C,eAAW,CAAC,KAAK,KAAK,KAAK,SAAS,gBAAgB,CAAC,GAAG;AACtD,WAAK,OAAO,IAAI,KAAK,KAAK;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,iBAAiB;AACf,WAAO,KAAK,OAAO,OAAO,QAAQ,EAAE;AAAA,EACtC;AAAA;AAAA,EAGA,wBAAwB,KAAsB;AAC5C,SAAK,OAAO,MAAM;AAClB,eAAW,CAAC,IAAI,IAAI,KAAK,sBAAsB,GAAG,GAAG;AACnD,WAAK,OAAO,IAAI,IAAI,IAAI;AAAA,IAC1B;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,KAAa;AAC1B,WAAO,KAAK,QAAQ,IAAI,GAAG;AAAA,EAC7B;AAAA,EACA,MAAM,SAAS,KAAa,OAAa;AACvC,SAAK,QAAQ,IAAI,KAAK,KAAK;AAAA,EAC7B;AAAA,EACA,MAAM,YAAY,KAAa;AAC7B,SAAK,QAAQ,OAAO,GAAG;AAAA,EACzB;AAAA,EAEA,MAAM,uBAAuB;AAC3B,WAAO,KAAK,gBAAgB,QAAQ;AAAA,EACtC;AAAA,EAEA,MAAM,mBAAmB,WAAmB;AAC1C,WAAO,KAAK,gBAAgB,IAAI,SAAS;AAAA,EAC3C;AAAA,EAEA,MAAM,mBAAmB,SAAwB;AAC/C,SAAK,gBAAgB,IAAI,QAAQ,WAAW,OAAO;AAAA,EACrD;AAAA,EAEA,MAAM,sBAAsB,WAAmB;AAC7C,SAAK,gBAAgB,OAAO,SAAS;AAAA,EACvC;AAAA,EAEA,aAAa;AACX,WAAO,EAAE,KAAK;AAAA,EAChB;AAAA,EAEA,MAAM,eAAe,OAAe;AAClC,UAAM,SAAS,GAAG,KAAK;AACvB,WAAO;AAAA,MACL,QAAQ,KAAK,KAAK,QAAQ,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,MAAM,CAAC;AAAA,MAC1D,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,OAAO,MAAM,GAAG,CAAC;AAAA,IACxC;AAAA,EACF;AAAA,EACA,MAAM,gBAAgB,OAAe,KAAa,MAAkB;AAClE,SAAK,KAAK,IAAI,GAAG,KAAK,MAAM,GAAG,IAAI,IAAI;AAAA,EACzC;AAAA,EACA,MAAM,iBAAiB,OAAe,MAAgB;AACpD,eAAW,OAAO,MAAM;AACtB,WAAK,KAAK,OAAO,GAAG,KAAK,MAAM,GAAG,EAAE;AAAA,IACtC;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,iCAAiC;AACrC,SAAK,KAAK,MAAM;AAAA,EAClB;AAAA;AAAA,EAGA,iBAAwC;AAGtC,UAAM,QAAQ,KAAK;AACnB,QAAI,CAAC,MAAM,IAAI,MAAM,GAAG;AACtB,YAAM,IAAI,QAAQ,EAAE,MAAMA,UAAS,QAAQ,MAAM,CAAC,EAAE,CAAC;AAAA,IACvD;AAEA,UAAM,WAAW,mBAAmB,KAAK;AAEzC,aAAS,iBAAiB,UAAkB,KAA2B;AACrE,UAAI;AAEJ,iBAAW,cAAc,SAAS,OAAO,QAAQ,GAAG;AAClD,cAAM,aAAa,MAAM,UAAU;AACnC,YACE,aAAa,QACZ,YAAY,UAAa,aAAa,UACvC;AACA,oBAAU;AAAA,QACZ;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAMA,mBAAe,UACb,IACA,MACA,iBAAiB,OACF;AACf,YAAM,aAAa,MAAM,IAAI,KAAK,QAAQ;AAE1C,UAAI,eAAe,QAAW;AAC5B,cAAM,IAAI,MAAM,kBAAkB,MAAM,KAAK,QAAQ,CAAC,EAAE;AAAA,MAC1D;AAEA,UACE,KAAK,SAASA,UAAS,YACvB,WAAW,SAASA,UAAS,QAC7B;AACA,cAAM,IAAI,MAAM,kCAAkC;AAAA,MACpD;AAEA,YAAM,uBAAuB,SAAS,IAAI,KAAK,UAAU,KAAK,SAAS;AACvE,UAAI,yBAAyB,IAAI;AAE/B,cAAMC,cAAa,MAAM,IAAI,KAAK,QAAQ;AAC1C,cAAM,qBACJA,gBAAe,UACf,gBAAgBA,aAAY,KAAK,SAAS;AAC5C,YAAI,yBAAyB,UAAa,oBAAoB;AAC5D,cAAI,gBAAgB;AAClB,6BAAiB,KAAK,UAAU,KAAK,SAAS;AAAA,UAChD,OAAO;AACL,kBAAM,IAAI,MAAM,OAAO,MAAM,KAAK,SAAS,CAAC,iBAAiB;AAAA,UAC/D;AAAA,QACF;AAGA,iBAAS,IAAI,KAAK,UAAU,KAAK,WAAW,EAAE;AAAA,MAChD;AAEA,YAAM,IAAI,IAAI,IAAI;AAAA,IACpB;AAUA,mBAAe,aAAa,IAAY,QAA4B;AAClE,YAAM,OAAO,MAAM,IAAI,EAAE;AACzB,UAAI,MAAM,aAAa,QAAW;AAChC;AAAA,MACF;AAGA,UAAI,SAAS,IAAI,KAAK,UAAU,MAAM;AACpC,cAAM,IAAI,MAAM,OAAO,MAAM,MAAM,CAAC,gBAAgB;AAEtD,eAAS,OAAO,KAAK,UAAU,KAAK,SAAS;AAC7C,YAAM,UAAU,EAAE,GAAG,MAAM,WAAW,OAAO;AAC7C,YAAM,IAAI,IAAI,OAAO;AACrB,eAAS,IAAI,KAAK,UAAU,QAAQ,EAAE;AAAA,IACxC;AAWA,mBAAe,gBACb,IACA,MACA,iBAAiB,OACF;AACf,YAAM,OAAO,MAAM,IAAI,EAAE;AACzB,UAAI,MAAM,SAASD,UAAS,QAAQ;AAElC;AAAA,MACF;AAEA,iBAAW,OAAO,OAAO,KAAK,IAAI,GAAG;AAEnC,cAAM,UAAU,SAAS,IAAI,IAAI,GAAG;AACpC,YAAI,YAAY,QAAW;AACzB,cAAI,gBAAgB;AAClB,wBAAY,OAAO;AAAA,UACrB,OAAO;AACL,kBAAM,IAAI,MAAM,mCAAmC,MAAM,GAAG,CAAC,EAAE;AAAA,UACjE;AAAA,QACF;AAAA,MACF;AAEA,YAAM,IAAI,IAAI,EAAE,GAAG,MAAM,MAAM,EAAE,GAAG,KAAK,MAAM,GAAG,KAAK,EAAE,CAAC;AAAA,IAC5D;AAKA,aAAS,YAAY,IAAkB;AACrC,YAAM,OAAO,MAAM,IAAI,EAAE;AACzB,UAAI,MAAM,aAAa,QAAW;AAChC;AAAA,MACF;AAGA,eAAS,OAAO,KAAK,UAAU,KAAK,SAAS;AAG7C,YAAM,QAAQ,CAAC,EAAE;AACjB,aAAO,MAAM,SAAS,GAAG;AACvB,cAAM,SAAS,MAAM,IAAI;AACzB,cAAM,KAAK,GAAG,SAAS,SAAS,MAAM,CAAC;AACvC,cAAM,OAAO,MAAM;AACnB,iBAAS,UAAU,MAAM;AAAA,MAC3B;AAAA,IACF;AAMA,aAAS,iBAAiB,IAAY,KAAmB;AAEvD,YAAM,OAAO,MAAM,IAAI,EAAE;AACzB,UAAI,SAAS,UAAa,gBAAgB,MAAM,GAAG,GAAG;AACpD,cAAM,EAAE,CAAC,GAAG,GAAG,GAAG,GAAG,KAAK,IAAI,KAAK;AACnC,cAAM,IAAI,IAAI,EAAE,GAAG,MAAM,MAAM,KAAK,CAAC;AAAA,MACvC;AAEA,YAAM,UAAU,SAAS,IAAI,IAAI,GAAG;AACpC,UAAI,YAAY,QAAW;AACzB,oBAAY,OAAO;AAAA,MACrB;AAAA,IACF;AAEA,UAAM,MAA6B;AAAA;AAAA;AAAA;AAAA;AAAA,MAKjC,UAAU,CAAC,OAAO,MAAM,IAAI,EAAE;AAAA;AAAA;AAAA;AAAA,MAK9B,YAAY,MAAM;AAAA;AAAA;AAAA;AAAA,MAKlB,UAAU,CAAC,OAAO,MAAM,IAAI,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAO9B,cAAc,CAAC,IAAI,QAAQ,SAAS,IAAI,IAAI,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA,MAM/C,cAAc,CAAC,IAAI,QAAQ,SAAS,IAAI,IAAI,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAO/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MASA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAWA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAUA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MASA,aAAa,YAAyC;AACpD,eAAO,qBAAqB,KAAK;AAAA,MACnC;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;AAEO,SAAS,sBAAsB,SAGnB;AACjB,SAAO,IAAI,eAAe,OAAO;AACnC;;;AEvcA;AAAA,EACE,SAAAE;AAAA,EACA,eAAAC;AAAA,EACA,YAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,UAAAC;AAAA,OACK;AA6BP,SAAS,OAAOC,KAAkB,KAAyB;AACzD,SAAO,EAAE,QAAQ,YAAY,IAAAA,KAAI,IAAI;AACvC;AAEA,SAAS,OAAO,WAAoC;AAClD,SAAO,EAAE,QAAQ,WAAW,aAAa,UAAU,KAAK;AAC1D;AAEA,SAAS,sBAAsBA,KAA+B;AAC5D,UAAQA,IAAG,MAAM;AAAA,IACf,KAAKC,QAAO;AACV,aAAO;AAAA,QACL,MAAMC,UAAS;AAAA,QACf,UAAUF,IAAG;AAAA,QACb,WAAWA,IAAG;AAAA,MAChB;AAAA,IAEF,KAAKC,QAAO;AACV,aAAO;AAAA,QACL,MAAMC,UAAS;AAAA,QACf,UAAUF,IAAG;AAAA,QACb,WAAWA,IAAG;AAAA,MAChB;AAAA,IAEF,KAAKC,QAAO;AACV,aAAO;AAAA,QACL,MAAMC,UAAS;AAAA,QACf,UAAUF,IAAG;AAAA,QACb,WAAWA,IAAG;AAAA,QACd,MAAMA,IAAG;AAAA,MACX;AAAA,IAEF,KAAKC,QAAO;AACV,aAAO;AAAA,QACL,MAAMC,UAAS;AAAA,QACf,UAAUF,IAAG;AAAA,QACb,WAAWA,IAAG;AAAA,QACd,MAAMA,IAAG;AAAA,MACX;AAAA,IAGF;AACE,aAAOG,aAAYH,KAAI,iBAAiB;AAAA,EAC5C;AACF;AAEO,IAAM,UAAN,MAAc;AAAA,EAMnB,YAAY,YAA4B;AAHxC;AAAA;AAAA,wBAAiB;AACjB,wBAAQ;AAGN,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,eAAsC;AACxC,QAAI,KAAK,kBAAkB,QAAW;AACpC,YAAM,IAAI,MAAM,4CAA4C;AAAA,IAC9D;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,iBAAgE;AAC9D,WAAO,KAAK,WAAW,eAAe;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,KAAK,QAA+B;AACxC,SAAK,gBAAgB,MAAM,KAAK,WAAW,eAAe,MAAM;AAAA,EAClE;AAAA,EAEA,SAAe;AACb,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAS,KAA+C;AAC5D,UAAM,UAA2B,CAAC;AAClC,eAAWA,OAAM,KAAK;AACpB,cAAQ,KAAK,MAAM,KAAK,QAAQA,GAAE,CAAC;AAAA,IACrC;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,QAAQA,KAA0C;AAC9D,YAAQA,IAAG,MAAM;AAAA,MACf,KAAKC,QAAO;AAAA,MACZ,KAAKA,QAAO;AAAA,MACZ,KAAKA,QAAO;AAAA,MACZ,KAAKA,QAAO;AACV,eAAO,MAAM,KAAK,cAAcD,GAAE;AAAA,MAEpC,KAAKC,QAAO;AACV,eAAO,MAAM,KAAK,oBAAoBD,GAAE;AAAA,MAE1C,KAAKC,QAAO;AACV,eAAO,MAAM,KAAK,oBAAoBD,GAAE;AAAA,MAE1C,KAAKC,QAAO;AACV,eAAO,MAAM,KAAK,uBAAuBD,GAAE;AAAA,MAE7C,KAAKC,QAAO;AACV,eAAO,MAAM,KAAK,kBAAkBD,GAAE;AAAA,MAGxC;AACE,YAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,iBAAO,OAAOA,GAAE;AAAA,QAClB,OAAO;AACL,iBAAOG,aAAYH,KAAI,YAAY;AAAA,QACrC;AAAA,IACJ;AAAA,EACF;AAAA,EAEA,MAAc,cAAcA,KAAgD;AAC1E,QAAI,KAAK,aAAa,SAASA,IAAG,EAAE,GAAG;AAErC,aAAO,OAAOA,GAAE;AAAA,IAClB;AAEA,UAAM,OAAO,sBAAsBA,GAAE;AAErC,UAAM,SAAS,KAAK,aAAa,SAAS,KAAK,QAAQ;AACvD,QAAI,WAAW,QAAW;AAExB,aAAO,OAAOA,GAAE;AAAA,IAClB;AAGA,YAAQ,OAAO,MAAM;AAAA,MACnB,KAAKE,UAAS;AAKZ,YAAIF,IAAG,SAASC,QAAO,iBAAiB;AACtC,iBAAO,OAAOD,GAAE;AAAA,QAClB;AAAA,MAGF,KAAKE,UAAS;AAEZ,cAAM,KAAK,aAAa,UAAUF,IAAG,IAAI,MAAM,IAAI;AACnD,eAAO,OAAOA,GAAE;AAAA,MAElB,KAAKE,UAAS;AAGZ,eAAO,KAAK,sBAAsBF,KAAI,IAAI;AAAA,MAE5C,KAAKE,UAAS;AAEZ,eAAO,OAAOF,GAAE;AAAA,MAGlB;AACE,eAAOG,aAAY,QAAQ,qBAAqB;AAAA,IACpD;AAAA,EACF;AAAA,EAEA,MAAc,sBACZH,KACA,MACwB;AACxB,QAAI;AAIJ,UAAM,SAA2BA,IAAG,UAAU;AAG9C,QAAI,WAAW,UAAU;AACvB,YAAM,oBAAoB,MAAM,KAAK,eAAeA,IAAG,IAAI,IAAI;AAO/D,UAAI,sBAAsB,KAAK,WAAW;AACxC,QAAAA,MAAK,EAAE,GAAGA,KAAI,WAAW,kBAAkB;AAC3C,cAAM;AAAA,UACJ,MAAMC,QAAO;AAAA,UACb,IAAID,IAAG;AAAA,UACP,WAAW;AAAA,QACb;AACA,eAAO,OAAOA,KAAI,GAAG;AAAA,MACvB;AAGA,aAAO,OAAOA,GAAE;AAAA,IAClB,WAES,WAAW,OAAO;AAWzB,YAAM,YACJA,IAAG,cAAc,UACjBA,IAAG,cAAcA,IAAG,MACpB,KAAK,aAAa,SAASA,IAAG,SAAS,GAAG,aAAa,KAAK,WACxDA,IAAG,YACH;AAEN,UAAI,cAAc,QAAW;AAC3B,cAAM,KAAK,aAAa,YAAY,SAAS;AAAA,MAC/C;AAEA,YAAM,aAAa,KAAK,aAAa;AAAA,QACnC,KAAK;AAAA,QACL,KAAK;AAAA,MACP;AACA,UAAI,eAAe,UAAa,eAAe,WAAW;AAIxD,cAAM;AAAA,UACJ,MAAMC,QAAO;AAAA,UACb,IAAI;AAAA,QACN;AAAA,MACF;AAEA,YAAM,KAAK,aAAa,UAAUD,IAAG,IAAI,MAAM,IAAI;AAEnD,aAAO,OAAOA,KAAI,GAAG;AAAA,IACvB,OAAO;AACL,aAAOG,aAAY,QAAQ,gBAAgB;AAAA,IAC7C;AAAA,EACF;AAAA,EAEA,MAAc,uBACZH,KACwB;AACxB,UAAM,KAAK,aAAa,iBAAiBA,IAAG,IAAIA,IAAG,GAAG;AACtD,WAAO,OAAOA,GAAE;AAAA,EAClB;AAAA,EAEA,MAAc,oBACZA,KACwB;AACxB,UAAM,KAAK,aAAa,gBAAgBA,IAAG,IAAIA,IAAG,MAAM,IAAI;AAC5D,WAAO,OAAOA,GAAE;AAAA,EAClB;AAAA,EAEA,MAAc,kBACZA,KACwB;AACxB,UAAM,KAAK,aAAa,YAAYA,IAAG,EAAE;AACzC,WAAO,OAAOA,GAAE;AAAA,EAClB;AAAA,EAEA,MAAc,oBACZA,KACwB;AACxB,UAAM,cAAc,MAAM,KAAK,gBAAgBA,IAAG,IAAIA,IAAG,SAAS;AAClE,QAAI,gBAAgB,QAAW;AAE7B,aAAO,OAAOA,GAAE;AAAA,IAClB;AAMA,QAAI,gBAAgBA,IAAG,WAAW;AAChC,YAAM,aAAa,EAAE,GAAGA,KAAI,WAAW,YAAY;AACnD,YAAM,MAAa;AAAA,QACjB,MAAMC,QAAO;AAAA,QACb,IAAID,IAAG;AAAA,QACP,WAAW;AAAA,MACb;AACA,aAAO,OAAO,YAAY,GAAG;AAAA,IAC/B,OAAO;AACL,aAAO,OAAOA,GAAE;AAAA,IAClB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAc,eACZ,IACA,MACiB;AAEjB,UAAM,MAAM,KAAK,qBAAqB,KAAK,UAAUI,OAAM,KAAK,SAAS,CAAC;AAC1E,QAAI,QAAQ,KAAK,WAAW;AAC1B,aAAO,EAAE,GAAG,MAAM,WAAW,IAAI;AAAA,IACnC;AACA,UAAM,KAAK,aAAa,UAAU,IAAI,IAAI;AAC1C,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAc,gBACZ,IACA,WAC6B;AAC7B,UAAM,OAAO,KAAK,aAAa,SAAS,EAAE;AAC1C,QAAI,MAAM,aAAa,QAAW;AAChC;AAAA,IACF;AAEA,QAAI,KAAK,aAAa,SAAS,KAAK,QAAQ,GAAG,SAASF,UAAS,MAAM;AAErE;AAAA,IACF;AAEA,QAAI,KAAK,cAAc,WAAW;AAEhC,aAAO;AAAA,IACT;AAGA,UAAM,MAAM,KAAK,qBAAqB,KAAK,UAAUE,OAAM,SAAS,CAAC;AACrE,QAAI,QAAQ,KAAK,WAAW;AAC1B,YAAM,KAAK,aAAa,aAAa,IAAI,GAAG;AAAA,IAC9C;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,qBAAqB,UAAkB,WAAqB;AAClE,QAAI,CAAC,KAAK,aAAa,aAAa,UAAU,SAAS,GAAG;AACxD,aAAO;AAAA,IACT;AAEA,UAAM,UAAU;AAChB,UAAM,UAAU,KAAK,aAAa,iBAAiB,UAAU,OAAO;AACpE,QAAI,YAAY,QAAW;AACzB,aAAOC,cAAa,SAAS,OAAO;AAAA,IACtC,OAAO;AACL,aAAOA,cAAa,OAAO;AAAA,IAC7B;AAAA,EACF;AACF;;;ACxaA,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,cAAc;AACvB,SAAS,cAAc;AACvB,YAAY,OAAO;AAUnB,IAAM,oBAAoB;AAOnB,IAAM,aAAN,MAAiB;AAAA,EAYtB,YAAY,QAAwB;AAXpC,wBAAiB;AAEjB,wBAAiB,OAAa,IAAM,MAAI;AACxC;AAAA,wBAAiB,mBAAkB,oBAAI,IAAyB;AAChE,wBAAiB,oBAAmB,oBAAI,IAAwB;AAEhE;AAAA,wBAAiB,YAAW,IAAIC;AAAA,MAC9B,MAAM,oBAAI,IAAI;AAAA,IAChB;AACA,wBAAiB,oBAAgD,oBAAI,IAAI;AAkMzE;AAAA;AAAA;AAAA,wBAAQ,8BAA6B,OACnC,YACA,KACA,UACkB;AAElB,YAAM,oBAAoB;AAE1B,YAAM,UAAU,OAAO,OAAO,UAAU;AAExC,YAAM,aAAa,QAAQ,OAAO,CAAC,KAAK,WAAW;AACjD,eAAO,MAAM,OAAO;AAAA,MACtB,GAAG,CAAC;AACJ,UAAI,QAAQ,SAAS,GAAG;AACtB,cAAM,UAAU,OAAO,KAAK,UAAU;AAEtC,aAAK,SAAS,IAAI,OAAO,IAAI,IAAI,OAAO,CAAC;AAEzC,cAAM,eAAiB,eAAa,OAAO;AAE3C,QAAE,cAAY,KAAK,YAAY;AAG/B,cAAM,yBAA2B,sBAAoB,GAAG;AAExD,YACE,uBAAuB,SACvB,cAAc,IAAI,oBAClB;AACA,gBAAM,SAAS,OAAO;AACtB,gBAAM,KAAK,OAAO;AAAA,YAChB;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAEA,gBAAM,KAAK,OAAO,iBAAiB,OAAO,OAAO;AACjD,eAAK,SAAS,IAAI,OAAO,oBAAI,IAAI,CAAC,MAAM,CAAC,CAAC;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AAEA,wBAAQ,+BAA8B,OACpC,KACA,UACmB;AACnB,YAAM,aAAa,OAAO;AAAA,QACxB,MAAM,KAAK,OAAO,eAAe,KAAK;AAAA,MACxC;AACA,YAAM,KAAK,2BAA2B,YAAY,KAAK,KAAK;AAE5D,WAAK,gBAAgB,IAAI,OAAO;AAAA,QAC9B,YAAY,OAAO;AAAA,QACnB,YAAc,oBAAkB,GAAG;AAAA,MACrC,CAAC;AACD,UAAI,KAAK,QAAQ,CAAC,GAAG,CAAC;AAEtB,aAAO;AAAA,IACT;AAzPE,SAAK,SAAS;AACd,SAAK,IAAI,GAAG,WAAW,CAAC,EAAE,QAAQ,MAAM;AACtC,cAAQ,QAAQ,CAAC,WAAkB;AACjC,eAAO,QAAQ;AAAA,MACjB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,QAAQ,OAA+B;AAClD,UAAM,MAAM,MAAM,KAAK,8BAA8B,KAAK;AAC1D,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,cACX,QACA,cAAsB,IACtB,MACA,OAAgB,OACQ;AACxB,UAAM,SAAS,MAAM,KAAK;AAAA,MACxB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,QAAI,CAAC,OAAQ,QAAO;AACpB,WAAO,OAAO,eAAe,MAAM;AAAA,EACrC;AAAA,EAEA,MAAa,oBACX,QACA,cAAsB,IACtB,MACA,OAAgB,OACY;AAC5B,UAAM,MAAM,SAAS,SAAY,MAAM,KAAK,WAAW,IAAI,IAAI,KAAK;AACpE,QAAI,CAAC,KAAK;AACR,aAAO;AAAA,IACT;AACA,QAAI;AACJ,QAAI;AAEF,4BACE,YAAY,SAAS,IAAI,OAAO,aAAa,WAAW,IAAI;AAAA,IAChE,SAAS,GAAG;AACV,aAAO;AAAA,QACL;AAAA,MACF;AAAA,IACF;AACA,QAAI,MAAM;AACR,aAAS,wBAAsB,KAAK,mBAAmB;AAAA,IACzD;AACA,WAAS,sBAAoB,KAAK,mBAAmB;AAAA,EACvD;AAAA,EAEA,MAAa,gBAAgB,MAAqC;AAChE,UAAM,MAAM,SAAS,SAAY,MAAM,KAAK,WAAW,IAAI,IAAI,KAAK;AACpE,QAAI,CAAC,KAAK;AACR,aAAO;AAAA,IACT;AACA,WAAO,OAAO,eAAiB,oBAAkB,GAAG,CAAC;AAAA,EACvD;AAAA,EAEA,MAAa,gBAAgB,SAGF;AACzB,UAAM,MACJ,QAAQ,SAAS,SACb,MAAM,KAAK,WAAW,QAAQ,IAAI,IAClC,KAAK;AACX,QAAI,CAAC,KAAK;AACR,aAAO;AAAA,IACT;AACA,UAAMC,YAAW,KAAK,sBAAsB,GAAG;AAC/C,WAAO,KAAK,sBAAsBA,WAAU,EAAE,MAAM,QAAQ,KAAK,CAAC;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,cACX,QACA,QACA,MACA,MACuD;AACvD,UAAM,MAAM,SAAS,SAAY,MAAM,KAAK,WAAW,IAAI,IAAI,KAAK;AACpE,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,kBAAkB,IAAI,YAAY;AAAA,IACpD;AAEA,QAAI;AAEF,YAAM,iBAAiB,KAAK,sBAAsB,GAAG;AACrD,YAAM,aACJ,OAAO,WAAW,WAAW,OAAO,aAAa,MAAM,IAAI;AAC7D,YAAMC,eAAc,OAAS,kBAAkB;AAC/C,MAAAA,aAAY,KAAK,YAAY,QAAQ;AAErC,YAAM,gBAAgB,KAAK,iBAAiB,GAAG;AAE/C,YAAM,UAAU,CAAG,iBAAe,gBAAgB,aAAa;AAC/D,UAAI,SAAS;AACX,cAAM,KAAK,iBAAiB,GAAG;AAAA,MACjC;AAEA,aAAO;AAAA,QACL,WAAW;AAAA,QACX,cAAc,MAAM,KAAK,sBAAsB,eAAe,EAAE,KAAK,CAAC;AAAA,MACxE;AAAA,IACF,SAAS,GAAG;AAEV,aAAO,KAAK,4BAA4B,OAAO,CAAC,CAAC,EAAE;AACnD,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEO,8BAA8B,OAA+B;AAClE,QAAI,UAAU,KAAK,iBAAiB,IAAI,KAAK;AAC7C,QAAI,MAAM,UAAU,eAAe,KAAK,MAAM,KAAK,kBAAkB,KAAK;AAC1E,QAAI,CAAC,KAAK;AAER,YAAM,IAAM,MAAI;AAAA,IAClB;AACA,QAAI,YAAY,QAAW;AACzB,gBAAU,KAAK,4BAA4B,KAAK,KAAK;AACrD,WAAK,iBAAiB,IAAI,OAAO,OAAO;AAAA,IAC1C;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAa,KAAK,SAAgC;AAChD,UAAM,KAAK,8BAA8B,YAAY;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA,EAKO,SAAe;AAAA,EAWtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,sBAAsB,KAAwB;AACpD,UAAM,QACJ,IAAI,SAAS,KAAK,IAAI,OAAO,eAAgB,IAAI;AACnD,UAAMD,YAAW,KAAK,iBAAiB,IAAI,KAAK;AAChD,QAAIA,WAAU;AACZ,aAAOA;AAAA,IACT;AACA,WAAO,KAAK,iBAAiB,GAAG;AAAA,EAClC;AAAA;AAAA,EAGQ,iBAAiB,KAAwB;AAC/C,UAAM,QACJ,IAAI,SAAS,KAAK,IAAI,OAAO,eAAgB,IAAI;AACnD,UAAMA,YAAa,WAAS,GAAG;AAC/B,SAAK,iBAAiB,IAAI,OAAOA,SAAQ;AACzC,WAAOA;AAAA,EACT;AAAA,EAgEQ,kBAAkB,MAA0B;AAClD,eAAW,UAAU,KAAK,IAAI,WAAW,GAAG;AAC1C,UAAI,OAAO,SAAS,MAAM;AACxB,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,sBACZA,WACA,EAAE,KAAK,GACU;AACjB,UAAM,kBAAkB,OAClB,mBAAiBA,SAAQ,IACzB,iBAAeA,SAAQ;AAC7B,WAAO,OAAO;AAAA,MACZ,IAAI;AAAA,QACF,MAAM,OAAO,OAAO,OAAO,WAAW,IAAI,WAAW,eAAe,CAAC;AAAA,MACvE;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAc,WAAW,MAAmC;AAC1D,UAAM,SAAS,KAAK,kBAAkB,IAAI;AAC1C,QAAI,CAAC,QAAQ;AACX,aAAO;AAAA,IACT;AACA,UAAM,KAAK,8BAA8B,IAAI;AAC7C,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAc,iBAAiB,KAA2B;AACxD,UAAM,QACJ,IAAI,SAAS,KAAK,IAAI,OAAO,eAAgB,IAAI;AACnD,UAAM,gBAAgB,KAAK,gBAAgB,IAAI,KAAK;AAEpD,UAAM,wBAA0B;AAAA,MAC9B;AAAA,MACA,eAAe;AAAA,IACjB;AAGA,UAAM,aAAa,eAAe,cAAc,OAAO;AACvD,QAAI,sBAAsB,SAAS,mBAAmB;AAEpD,YAAM,SAAS,OAAO;AACtB,YAAM,KAAK,OAAO;AAAA,QAChB;AAAA,QACA;AAAA,QACE,sBAAoB,GAAG;AAAA,MAC3B;AAEA,YAAM,KAAK,OAAO;AAAA,QAChB;AAAA,QACA,MAAM,KAAK,KAAK,SAAS,YAAY,KAAK,CAAC;AAAA,MAC7C;AAEA,WAAK,SAAS,IAAI,OAAO,oBAAI,IAAI,CAAC,MAAM,CAAC,CAAC;AAE1C,WAAK,gBAAgB,IAAI,OAAO;AAAA,QAC9B,YAAY,OAAO;AAAA;AAAA,QACnB,YAAc,oBAAkB,GAAG;AAAA,MACrC,CAAC;AAAA,IACH,OAAO;AAEL,YAAM,KAAK,OAAO;AAAA,QAChB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,YAAM,OAAO,CAAC,UAAU;AAExB,YAAM,cAAc,KAAK,SAAS,YAAY,KAAK;AACnD,iBAAW,OAAO,MAAM;AACtB,oBAAY,IAAI,GAAG;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AACF;;;ACpVA,eAAsB,SACpB,SAC0C;AAC1C,MAAI;AACF,UAAM,OAAO,OAAO,OAAO,YAAY,aAAa,QAAQ,IAAI;AAChE,WAAO,CAAC,MAAM,MAAS;AAAA,EACzB,SAAS,OAAO;AACd,WAAO,CAAC,QAAW,KAAU;AAAA,EAC/B;AACF;;;ACnDA;AAmCO,IAAM,YAAN,cAAkC,IAAU;AAAA,EAMjD,YACE,OAEA;AACA,UAAM;AAPR;AAAA;AAAA;AACA;AAOE,uBAAK,SAAU;AACf,uBAAK,UAAW,oBAAI,IAAI;AAAA,EAC1B;AAAA,EAEA,iBAAiB,SAA4B;AAC3C,WAAO,mBAAK,UAAS,IAAI,OAAO;AAAA,EAClC;AAAA,EAEA,OAAO,SAA4B;AACjC,UAAM,MAAM,mBAAK,UAAS,IAAI,OAAO;AACrC,WAAO,QAAQ,SAAY,KAAK,IAAI,GAAG,IAAI;AAAA,EAC7C;AAAA,EAEA,IAAI,KAAQ,OAAgB;AAC1B,UAAM,UAAU,mBAAK,SAAL,WAAa;AAC7B,UAAM,aAAa,mBAAK,UAAS,IAAI,OAAO;AAC5C,QAAI,eAAe,UAAa,eAAe,KAAK;AAClD,YAAM,IAAI,MAAM,cAAc,OAAO,OAAO,CAAC,iBAAiB;AAAA,IAChE;AACA,uBAAK,UAAS,IAAI,SAAS,GAAG;AAC9B,WAAO,MAAM,IAAI,KAAK,KAAK;AAAA,EAC7B;AAAA,EAEA,OAAO,YAAwB;AAC7B,UAAM,QAAQ,KAAK,IAAI,UAAU;AACjC,QAAI,UAAU,QAAW;AACvB,YAAM,aAAa,mBAAK,SAAL,WAAa;AAChC,yBAAK,UAAS,OAAO,UAAU;AAAA,IACjC;AACA,WAAO,MAAM,OAAO,UAAU;AAAA,EAChC;AACF;AAvCE;AACA;;;ACrBF,SAAS,qBAAqB;AAQvB,SAAS,kBAAkB,QAAkC;AAClE,QAAM,cAAc,OAAO,OAAO,CAAC,KAAK,QAAQ,MAAM,IAAI,QAAQ,CAAC;AACnE,QAAM,SAAS,IAAI,WAAW,WAAW;AACzC,MAAI,SAAS;AACb,aAAW,OAAO,QAAQ;AACxB,WAAO,IAAI,KAAK,MAAM;AACtB,cAAU,IAAI;AAAA,EAChB;AACA,SAAO;AACT;AAEO,SAAS,iBACd,OACA,OACA,QACA,OACA,YACkC;AAClC,SAAO;AAAA,IACL,MAAM,cAAc;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,cAAc,CAAC;AAAA,EACvB;AACF;AAMO,SAAS,uBAAuB,eAAuC;AAC5E,QAAM,MAAM,KAAK,IAAI;AACrB,SAAO,OAAO,cAAc,YAAY,cAAc;AACxD;;;ARDA,IAAM,kBAAkBE,OAAM,gBAAgB;AAE9C,IAAM,2BAA2B,KAAK;AAAA,EACpC,GAAG,OAAO,OAAO,eAAe,EAAE;AAAA,IAChC,CAAC,MAAmB,OAAO,MAAM;AAAA,EACnC;AACF;AAGA,IAAM,wBAAwB,OAAO;AAAA,EACnC,OAAO,QAAQC,cAAa,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AACtD;AAEA,IAAM,aAAa,IAAI,OAAO;AAAA;AAE9B,CAAC;AAsBD,SAAS,qBAAqB;AAC5B,QAAM,WAA4B,CAAC;AACnC,SAAO;AAAA,IACL,OAAO,CAAC,MAAqB,KAAK,SAAS,KAAK,CAAC;AAAA,IACjD,SAAS,MAAM,QAAQ,WAAW,QAAQ;AAAA,EAC5C;AACF;AAEA,SAAS,UACP,MACwB;AACxB,SAAO,KAAK,UAAU,IAAI;AAC5B;AAEO,SAAS,aAAa,MAAyB;AACpD,SAAO,EAAE,MAAMC,QAAO,aAAa,IAAI,OAAO,KAAK;AACrD;AAEA,SAAS,UAAUC,KAAsB;AAMvC,QAAM,EAAE,MAAM,GAAG,GAAG,KAAK,IAAIA;AAC7B,SAAO;AACT;AA3HA;AAgLO,IAAM,iBAAN,MAAgD;AAAA;AAAA,EAsBrD,YACE,QACA,QACA,OACA;AAtBF;AAAA;AAAA,wBAAgB;AAChB;AAAA,wBAAgB;AAChB;AAAA,wBAAgB;AAGhB;AAAA,wBAAgB;AAChB,wBAAgB;AAChB;AAAA,wBAAgB;AAChB;AAAA,wBAAgB;AAEhB;AAAA,uBAAS;AACT,uBAAS;AACT;AAGA;AAAA;AAQE,SAAK,UAAU,OAAO;AACtB,SAAK,QAAQ,OAAO;AACpB,SAAK,OAAO,OAAO;AACnB,SAAK,SAAS,OAAO;AACrB,SAAK,OAAO,OAAO,QAAS;AAC5B,SAAK,aAAa,OAAO;AACzB,uBAAK,UAAW;AAChB,uBAAK,SAAU;AAEf,UAAM,MAAM,oBAAI,KAAK;AACrB,SAAK,YAAY;AACjB,uBAAK,gBAAiB;AACtB,uBAAK,uCAAwC;AAAA,EAC/C;AAAA,EAEA,IAAI,eAAqB;AACvB,UAAM,WAAW,mBAAK,UAAS,uBAAuB;AACtD,QAAI,YAAY,WAAW,mBAAK,iBAAgB;AAC9C,aAAO;AAAA,IACT,OAAO;AACL,aAAO,mBAAK;AAAA,IACd;AAAA,EACF;AAAA,EAEA,IAAI,sCAA+C;AACjD,WAAO,mBAAK;AAAA,EACd;AAAA,EAEA,WAAW,MAAM,oBAAI,KAAK,GAAS;AACjC,QAAI,MAAM,mBAAK,iBAAgB;AAC7B,yBAAK,gBAAiB;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,yCAA+C;AAC7C,uBAAK,uCAAwC;AAAA,EAC/C;AAAA,EAEA,WAAmB;AACjB,SAAK,WAAW;AAEhB,UAAM,OAAO,mBAAK,UAAS,KAAK,MAAM;AACtC,QAAI,mBAAK,UAAS;AAChB,UAAI,OAAO,GAAG;AACZ,gBAAQ;AAAA,UACN,kCAAkC,KAAK,KAAK;AAAA,QAC9C;AAAA,MACF,WAAW,SAAS,GAAG;AACrB,gBAAQ;AAAA,UACN,kCAAkC,KAAK,KAAK;AAAA,QAC9C;AAAA,MACF,OAAO;AAEL,gBAAQ,IAAI,iBAAiB,KAAK,KAAK,UAAU;AAAA,MACnD;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,KAAK,WAAqE;AACxE,UAAM,OACJ,OAAO,cAAc,WAAW,YAAY,UAAU,SAAS;AACjE,UAAM,OAAO,mBAAK,UAAS,KAAK,IAAI;AACpC,QAAI,mBAAK,UAAS;AAChB,UAAI,OAAO,GAAG;AACZ,gBAAQ;AAAA,UACN,mCAAmC,KAAK,KAAK;AAAA,QAC/C;AAAA,MACF,WAAW,SAAS,GAAG;AACrB,gBAAQ;AAAA,UACN,mCAAmC,KAAK,KAAK;AAAA,QAC/C;AAAA,MACF;AAEA,YAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,iBAAW,OAAO,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI,GAAG;AACrD,gBAAQ;AAAA,UACN,iBAAiB,KAAK,KAAK,MACzB,sBAAsB,IAAI,IAAI,KAAK,IAAI,IACzC,KAAK,KAAK,UAAU,GAAG,CAAC;AAAA,QAC1B;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,YAAY,MAAc,QAAuB;AAC/C,uBAAK,UAAS,MAAM,MAAM,MAAM;AAAA,EAClC;AACF;AA5GW;AACA;AACT;AAGA;AAyGK,IAAM,iBAAN,cAA6B,eAA6B;AAAA;AAAA,EAE/D,YACE,QACA,QACA,OACA;AACA,UAAM,QAAQ,QAAQ,KAAK;AAAA,EAC7B;AACF;AArTA,IAAAC,UAAA;AA4aO,IAAM,OAAN,MAAyD;AAAA,EAmD9D,YAAY,MAAU,SAAkC;AAhDxD;AAAA,wBAAO;AACP,wBAAgB;AAChB,wBAAO;AAEP,wBAAQ,cAAmC;AAC3C,wBAAQ,SAA6B;AACrC,wBAAQ,UAAS;AAEjB,wBAAiB,YAAW,IAAI,UAI9B,CAAC,MAAM,EAAE,KAAK;AAEhB,wBAAiB;AA+BjB,uBAASA;AACT,uBAAS;AAGP,UAAM,SAAS,SAAS,WAAW,sBAAsB;AACzD,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,SAAS,SAAS,UAAU;AACjC,uBAAK,kBAAmB,SAAS,kBAAkB;AACnD,SAAK,QAAQ;AAAA,MACX,oBACE,SAAS,OAAO,uBACf,MAAM;AACL,eAAO;AAAA,UACL,SAAS;AAAA,QACX;AAAA,MACF;AAAA;AAAA,MAGF,gBAAgB,SAAS,OAAO;AAAA,MAChC,eAAe,SAAS,OAAO;AAAA,MAE/B,kBAAkB,SAAS,OAAO;AAAA,MAClC,iBAAiB,SAAS,OAAO;AAAA,MAEjC,mBAAmB,SAAS,OAAO;AAAA,MACnC,iBAAiB,SAAS,OAAO;AAAA,MAEjC,+BACE,SAAS,OAAO;AAAA,MAClB,4BAA4B,SAAS,OAAO;AAAA,IAC9C;AACA,uBAAKA,UAAU,SAAS,sBAAsB;AAAA,EAChD;AAAA,EAEA,IAAW,eAA6B;AACtC,QAAI,KAAK,eAAe,MAAM;AAC5B,aAAO;AAAA,IACT,WAAW,KAAK,UAAU,MAAM;AAC9B,aAAO;AAAA,IACT,OAAO;AACL,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,IAAY,cAA0B;AAAE,WAAO,KAAK,SAAS;AAAA,EAAM;AAAA;AAAA,EAEnE,IAAY,UAA0B;AAAE,WAAO,KAAK,KAAK;AAAA,EAAS;AAAA;AAAA,EAClE,IAAY,aAA0B;AAAE,WAAO,KAAK,KAAK;AAAA,EAAY;AAAA;AAAA,EAErE,IAAW,QAA0B;AAAE,WAAO,KAAK,KAAK;AAAA,EAAO;AAAA;AAAA,EAE/D,IAAY,OAA0B;AAAE,WAAO,KAAK,SAASC,OAAM,oCAAoC;AAAA,EAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAW1G,MAAa,KAAK,KAAwB;AACxC,QAAI,KAAK,eAAe,MAAM;AAC5B,WAAK,QAAQ;AACb,WAAK,aAAa,KAAK,MAAM,GAAG,EAAE,MAAM,CAAC,MAAM;AAC7C,aAAK,QAAQ;AACb,aAAK,aAAa;AAClB,cAAM;AAAA,MACR,CAAC;AAAA,IACH;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,OAAO,KAAe;AAC3B,SAAK,MAAM,mBAAmB,GAAG;AACjC,QAAI,KAAK,OAAO;AACd,WAAK,QAAQ,OAAO;AACpB,WAAK,WAAW,OAAO;AAAA,IACzB;AAEA,SAAK,aAAa;AAElB,SAAK,MAAM,kBAAkB,GAAG;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAa,aACX,SACyB;AACzB,UAAM,SAAS,SAAS,SAAS,KAAK,aAAa;AACnD,UAAM,aAAaC,QAAO;AAC1B,UAAM,OAAO,SAAS;AACtB,UAAM,SAAyB;AAAA,MAC7B,SAAS,SAAS,WAAW;AAAA,MAC7B,OAAO,MAAM;AAAA,MACb;AAAA,MACA,MAAM,SAAS;AAAA,MACf,YAAY,SAAS;AAAA,MACrB,MAAM,SAAS,KACX,EAAE,IAAI,QAAQ,IAAI,KAAK,IACvB,EAAE,aAAa,SAAS,eAAeA,QAAO,GAAG,KAAK;AAAA,MAC1D,QAAQ,SAAS,UAAU,CAAC,YAAY;AAAA,IAC1C;AACA,QAAI,mBAAKF,WAAS;AAChB,cAAQ,IAAI,uBAAuB,KAAK,UAAU,MAAM,CAAC,EAAE;AAAA,IAC7D;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAa,oCAEX;AACA,UAAM,SAAU,MAAM,KAAK,aAAa;AACxC,UAAM,qBAA+C,CAAC;AACtD,UAAM,OAAO;AAAA,MACX,MAAM,CAAC,SAAS;AACd,YAAI,OAAO,SAAS,UAAU;AAC5B,6BAAmB,KAAK,IAA8B;AAAA,QACxD;AACA,eAAO;AAAA,MACT;AAAA,MACA,OAAO,MAAM;AAAA,MAAC;AAAA;AAAA,IAChB;AACA,UAAM,UAAU,IAAI,eAAe,QAAQ,MAAM,KAAK;AACtD,WAAO,CAAC,SAAS,kBAAkB;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYO,gBACL,UAKM;AACN,QAAI,KAAK,SAAS,OAAO,GAAG;AAC1B,YAAM,IAAI,MAAM,uDAAuD;AAAA,IACzE;AAEA,eAAW,EAAE,QAAQ,QAAQ,aAAa,KAAK,UAAU;AACvD,YAAM,aAAa,IAAI,eAAe,QAAQ,QAAQ,mBAAKA,SAAO;AAClE,WAAK,SAAS,IAAI,OAAO,YAAY,UAAU;AAC/C,iBAAW,WAAW,YAAY;AAAA,IACpC;AAAA,EACF;AAAA,EAEA,MAAc,yBACZ,YACA,QACA,KACA,QAA0C,MAAM;AAC9C,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF,GACe;AACf,UAAM,QAA8D,CAAC;AAErE,eAAW,WAAW,KAAK,cAAc,OAAO,UAAU,GAAG;AAC3D,YAAM,QAAQ,KAAK,IAAI;AAAA,QACrB,IAAI,QAAQ,KAAK;AAAA,QACjB,MAAM,QAAQ,KAAK;AAAA,QACnB,QAAQ,QAAQ;AAAA,MAClB;AAAA,IACF;AAGA,UAAM,iBAAkC,MAAM,KAAK;AAAA,MACjD;AAAA,MACA;AAAA,IACF;AAEA,eAAW,iBAAiB,gBAAgB;AAC1C,YAAM,cAAc,OAAkB,IAAI;AAAA,QACxC,IAAI,cAAc;AAAA,QAClB,MAAM,cAAc;AAAA,QACpB,QAAQ,CAAC;AAAA,MACX;AAAA,IACF;AAGA,eAAW;AAAA,MACT;AAAA,QACE,WAAW;AAAA,QACX,OAAO;AAAA;AAAA,QACP,WAAW;AAAA,QACX;AAAA,QACA,OAAO;AAAA,MACT;AAAA,IACF;AAIA,eAAW,iBAAiB,gBAAgB;AAC1C,iBAAW,KAAK;AAAA,QACd,MAAMH,eAAc;AAAA,QACpB,OAAO,cAAc;AAAA,QACrB,aAAa,WAAW;AAAA;AAAA,QACxB,MAAM,cAAc;AAAA,MACtB,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAa,oBACX,QACA,QACA,KACA,QAA0C,MAAM;AAC9C,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF,GACe;AACf,QAAI;AACJ,YACG,WAAW,KAAK,SAAS,iBAAiB,OAAO,KAAK,OAAO,QAC9D;AAOA,WAAK;AAAA,QACH;AAAA,QACA,UAAU;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,WAAK,OAAO;AAAA,QACV,8BAA8B,OAAO,KAAK;AAAA,MAC5C;AAAA,IACF;AAEA,UAAM,aAAa,IAAI,eAAe,QAAQ,QAAQ,mBAAKG,SAAO;AAClE,SAAK,SAAS,IAAI,OAAO,YAAY,UAAU;AAG/C,UAAM,KAAK,yBAAyB,YAAY,QAAQ,KAAK,KAAK;AAElE,SAAK;AAAA,MACH,OAAO;AAAA,MACP;AAAA,QACE,MAAMH,eAAc;AAAA,QACpB,OAAO,WAAW;AAAA,QAClB,IAAI,WAAW,KAAK;AAAA,QACpB,MAAM,WAAW,KAAK;AAAA,QACtB,QAAQ,WAAW;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAGA,UAAM,KAAK,KAAK,MAAM,oBAAoB,YAAY,GAAG;AACzD,QAAI,GAAI,OAAM,EAAE;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASO,kBACL,KACA,MACA,QACA,KACA,QAA0C,MAAM;AAC9C,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF,GACM;AACN,UAAM,WAAW,KAAK;AAEtB,UAAM,UAAU,SAAS,IAAI,GAAG;AAChC,QAAI,YAAY,OAAW;AAE3B,YAAQ,YAAY,MAAM,MAAM;AAEhC,UAAM,UAAU,SAAS,OAAO,GAAG;AACnC,QAAI,SAAS;AACX,iBAAW,SAAS,KAAK,cAAc,GAAG,GAAG;AAC3C,cAAM,KAAK,EAAE,MAAMA,eAAc,WAAW,OAAO,QAAQ,MAAM,CAAC;AAAA,MACpE;AAGA,YAAM,KAAK,KAAK,MAAM,kBAAkB,SAAS,GAAG;AACpD,UAAI,GAAI,OAAM,EAAE;AAAA,IAClB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,aACL,WACA,MACA,QACA,KACA,QAA0C,MAAM;AAC9C,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF,GACQ;AACR,QAAI,QAAQ;AACZ,eAAW,CAAC,KAAK,OAAO,KAAK,KAAK,UAAU;AAC1C,UAAI,UAAU,OAAO,GAAG;AACtB;AACA,aAAK,kBAAkB,KAAK,MAAM,QAAQ,KAAK,KAAK;AAAA,MACtD;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,WACX,KACA,MACA,KACA,QAA0C,MAAM;AAC9C,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF,GACe;AACf,UAAM,OACJ,OAAO,SAAS,WAAW,OAAOI,OAAM,4BAA4B;AAEtE,QAAI,SAAS,QAAQ;AACnB,YAAM,KAAK,WAAW,KAAK,GAAG;AAAA,IAChC,OAAO;AACL,YAAM,OAAO,aAAa,IAAI;AAC9B,YAAM,WAAW,gBAAgB,OAAO,IAAI;AAE5C,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,SACJ,QAAQ,IAAI,aAAa,eACrB,aAAa,SAAS,KAAK,IAC3B;AAEN,aAAK;AAAA,UACH;AAAA,UACA,UAAU;AAAA,UACV;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA;AAAA,MACF;AAMA,UAAI,KAAK,SAAS,KAAQ;AAAA,MAK1B,WAAW,KAAK,SAAS,KAAO;AAAA,MAGhC;AAEA,WAAK;AAKL,UAAI;AACF,cAAM,KAAK,iBAAiB,KAAK,SAAS,OAAO,GAAG;AAAA,MACtD,UAAE;AACA,aAAK;AAAA,MACP;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAa,iBACX,KACA,UACA,KACe;AACf,UAAM,KAAK,KAAK,GAAG;AACnB,UAAM,EAAE,OAAO,QAAQ,IAAI,mBAAmB;AAC9C,UAAM,KAAK,MAAM;AAAA,MAAa,MAC5B,KAAK,sCAAsC,KAAK,UAAU,KAAK,KAAK;AAAA,IACtE;AAIA,UAAM,QAAQ;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,MAAa,mCACX,SACA,UACA,KACe;AACf,UAAM,KAAK,KAAK,GAAG;AACnB,UAAM,EAAE,OAAO,QAAQ,IAAI,mBAAmB;AAC9C,UAAM,KAAK,MAAM;AAAA,MAAa,MAC5B,KAAK;AAAA,QACH;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAIA,UAAM,QAAQ;AAAA,EAChB;AAAA,EAEO,WACL,YACoC;AACpC,WAAO,KAAK,SAAS,IAAI,UAAU;AAAA,EACrC;AAAA,EAEO,eAAyC;AAC9C,WAAO,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,oBACX,WACA,UACA,KACA,MACA,KACA,QAA0C,MAAM;AAC9C,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF,GACe;AACf,UAAM,kBAAkB,MAAM,KAAK,OAAO,mBAAmB,SAAS;AACtE,UAAM,YACJ,oBAAoB,UAAa,uBAAuB,eAAe;AAEzE,QAAI,WAAW;AACb,YAAM,KAAK,oBAAoB,iBAAiB,KAAK,KAAK;AAAA,IAC5D;AAEA,QAAI,oBAAoB,UAAa,WAAW;AAE9C,YAAM,UAAU,MAAM,KAAK,aAAa;AACxC,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,UAAyB;AAAA,QAC7B;AAAA,QACA;AAAA,QACA,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,YAAM,KAAK,OAAO,mBAAmB,OAAO;AAG5C,WAAK;AAAA,QACH;AAAA,UACE,MAAMJ,eAAc;AAAA,UACpB,OAAO;AAAA,UACP,IAAI;AAAA,UACJ;AAAA,UACA,QAAQ,CAAC;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,WAAK;AAAA,QACH;AAAA,UACE,MAAMA,eAAc;AAAA,UACpB,OAAO;AAAA,UACP,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,OAAO;AAGL,YAAM,iBAAiB;AAAA,QACrB,GAAI,gBAAgB;AAAA,QACpB,GAAG;AAAA,MACL;AACA,YAAM,iBAAgC;AAAA,QACpC,GAAG;AAAA;AAAA,QAEH,UAAU;AAAA,QACV,WAAW,KAAK,IAAI;AAAA,QACpB;AAAA,MACF;AAEA,YAAM,KAAK,OAAO,mBAAmB,cAAc;AAGnD,WAAK;AAAA,QACH;AAAA,UACE,MAAMA,eAAc;AAAA,UACpB,OAAO,gBAAgB;AAAA,UACvB,MAAM;AAAA;AAAA;AAAA,QAER;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,mBACX,KACA,QAA0C,MAAM;AAC9C,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF,GAC0B;AAC1B,UAAM,KAAK,KAAK,GAAG;AACnB,UAAM,WAAW,MAAM,KAAK,OAAO,qBAAqB;AACxD,UAAM,gBAAiC,CAAC;AACxC,UAAM,WAA4B,CAAC;AACnC,eAAW,CAAC,GAAG,OAAO,KAAK,UAAU;AACnC,UAAI,uBAAuB,OAAO,GAAG;AACnC,iBAAS,KAAK,OAAO;AAAA,MACvB,OAAO;AACL,sBAAc,KAAK,OAAO;AAAA,MAC5B;AAAA,IACF;AAEA,eAAW,WAAW,UAAU;AAC9B,YAAM,KAAK,oBAAoB,SAAS,KAAK,KAAK;AAAA,IACpD;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,oBACX,SACA,KACA,QAA0C,MAAM;AAC9C,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF,GACe;AAEf,SAAK;AAAA,MACH;AAAA,QACE,MAAMA,eAAc;AAAA,QACpB,OAAO,QAAQ;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,UAAM,KAAK,OAAO,sBAAsB,QAAQ,SAAS;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,wBACX,KACA,QAA0C,MAAM;AAC9C,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF,GACe;AACf,UAAM,KAAK,KAAK,GAAG;AACnB,UAAM,WAAW,MAAM,KAAK,OAAO,qBAAqB;AACxD,eAAW,CAAC,GAAG,OAAO,KAAK,UAAU;AACnC,YAAM,KAAK,oBAAoB,SAAS,KAAK,KAAK;AAAA,IACpD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,aACL,QACA,WACA,KACA,QAA0C,MAAM;AAC9C,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF,GACM;AACN,UAAM,MAAM,UAAU,SAAS;AAC/B,eAAW,CAAC,KAAK,OAAO,KAAK,KAAK,oBAAoB,MAAM,GAAG;AAC7D,YAAM,UAAU,QAAQ,KAAK,GAAG;AAChC,UAAI,YAAY,GAAG;AAIjB,aAAK;AAAA,UACH;AAAA,UACA,UAAU;AAAA,UACV;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,UACL,WACA,KACA,QAA0C,MAAM;AAC9C,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF,GACM;AACN,UAAM,MAAM,UAAU,SAAS;AAC/B,eAAW,CAAC,KAAK,OAAO,KAAK,KAAK,UAAU;AAC1C,YAAM,UAAU,QAAQ,KAAK,GAAG;AAChC,UAAI,YAAY,GAAG;AAIjB,aAAK;AAAA,UACH;AAAA,UACA,UAAU;AAAA,UACV;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,eAAiC;AAC7C,UAAM,UAAU,IAAI,QAAQ,KAAK,MAAM;AACvC,UAAM,QAAQ,KAAK,KAAK,MAAM;AAC9B,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,kBAAuC;AACnD,UAAM,aAAa,IAAI,WAAW,KAAK,MAAM;AAC7C,UAAM,WAAW,KAAK,KAAK,MAAM;AACjC,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAc,MAAM,KAAwB;AAC1C,UAAM,KAAK,MAAM,iBAAiB,GAAG;AAGrC,UAAM,UAAU,MAAM,KAAK,aAAa;AACxC,UAAM,aAAa,MAAM,KAAK,gBAAgB;AAE9C,SAAK,QAAQ;AAAA,MACX,OAAO,IAAI,MAAM;AAAA,MACjB;AAAA,MACA;AAAA,IACF;AAEA,UAAM,KAAK,MAAM,gBAAgB,GAAG;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,eAAiC;AAC7C,WAAQ,MAAM,KAAK,OAAO,WAAW;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKA,CAAS,oBACP,YACiD;AACjD,eAAW,CAAC,KAAK,OAAO,KAAK,KAAK,UAAU;AAC1C,UAAI,QAAQ,YAAY;AACtB,cAAM,CAAC,KAAK,OAAO;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,CAAS,cACP,YACmC;AACnC,eAAW,CAAC,KAAK,OAAO,KAAK,KAAK,UAAU;AAC1C,UAAI,QAAQ,YAAY;AACtB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,WAAW,YAAwB,KAAwB;AACvE,UAAM,UAAU,KAAK,SAAS,IAAI,UAAU;AAC5C,QAAI,YAAY,QAAW;AACzB,WAAK,OACF,YAAY,EAAE,WAAW,CAAC,EAC1B,KAAK,+CAA+C;AACvD;AAAA,IACF;AAEA,UAAM,OAAO,QAAQ,SAAS;AAI9B,QAAI,SAAS,GAAG;AACd,YAAM,KAAK,MAAM,YAAY,GAAG;AAAA,IAClC;AAAA,EACF;AAAA,EAEA,MAAc,sCACZ,YACA,UACA,KACA,OACe;AACf,UAAM,UAAU,KAAK,SAAS,IAAI,UAAU;AAC5C,QAAI,CAAC,SAAS;AACZ,WAAK,OACF,YAAY,EAAE,WAAW,CAAC,EAC1B,KAAK,qDAAqD;AAC7D;AAAA,IACF;AAKA,UAAM,WAAwB,CAAC;AAC/B,UAAM,UAAuB,CAAC;AAC9B,UAAM,mBAAmB,CAAC,QACxB,KAAK,QAAQ,KAAK,GAAG;AACvB,UAAM,iBAAiB,CAAC,QAAmB,KAAK,SAAS,KAAK,GAAG;AACjE,UAAM,gBAAgB,CAAC,QAAmB,KAAK,QAAQ,KAAK,GAAG;AAE/D,eAAW,OAAO,UAAU;AAC1B,YAAM,eAAe,KAAK,MAAM,mBAAmB,KAAK,OAAO;AAC/D,UAAI,aAAa,SAAS;AACxB,cAAM,KAAK;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF,OAAO;AACL,YAAI,CAAC,QAAQ,qCAAqC;AAChD,kBAAQ,KAAK;AAAA,YACX,MAAMA,eAAc;AAAA,YACpB,OACE,IAAI,SAASM,eAAc,iBACvB,IAAI,IAAI,IAAI,CAACJ,QAAOA,IAAG,IAAI,IAC3B,CAAC;AAAA,YACP,QAAQ,aAAa;AAAA,UACvB,CAAC;AACD,kBAAQ,uCAAuC;AAAA,QACjD;AAAA,MACF;AAAA,IACF;AAEA,QAAI,SAAS,SAAS,GAAG;AACvB,WAAK,aAAa,YAAY,UAAU,KAAK,KAAK;AAAA,IACpD;AAEA,QAAI,QAAQ,SAAS,GAAG;AACtB,cAAQ,KAAK,OAAO;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,wDACZ,SACA,UACA,KACA,OACe;AAIf,UAAM,WAAwB,CAAC;AAC/B,UAAM,qBAAkC,CAAC;AACzC,UAAM,eAA4B,CAAC;AAEnC,UAAM,mBAAmB,CAAC,QAAiC;AACzD,UAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,mBAAW,KAAK,KAAK;AACnB,6BAAmB,KAAK,CAAC;AAAA,QAC3B;AAAA,MACF,OAAO;AACL,2BAAmB,KAAK,GAAG;AAAA,MAC7B;AAAA,IACF;AACA,UAAM,iBAAiB,CAAC,QAAmB,KAAK,SAAS,KAAK,GAAG;AACjE,UAAM,gBAAgB,CAAC,QAAmB,KAAK,aAAa,KAAK,GAAG;AAEpE,eAAW,OAAO,UAAU;AAC1B,YAAM,KAAK;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,QAAI,mBAAmB,SAAS,GAAG;AACjC,cAAQ,KAAK,kBAAkB;AAC/B,yBAAmB,SAAS;AAAA,IAC9B;AAEA,QAAI,SAAS,SAAS,GAAG;AACvB,WAAK,aAAa,eAA6B,UAAU,KAAK,KAAK;AACnE,eAAS,SAAS;AAAA,IACpB;AAEA,QAAI,aAAa,SAAS,GAAG;AAC3B,cAAQ,KAAK,YAAY;AACzB,mBAAa,SAAS;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,MAAc,UACZ,SACA,KACA,kBACA,gBACA,eACA,KACA,OACe;AACf,QAAI,CAAC,KAAK,MAAM,SAAS,GAAG;AAC1B,YAAM,IAAI,MAAM,6CAA6C;AAAA,IAC/D;AAEA,YAAQ,IAAI,MAAM;AAAA,MAChB,KAAKI,eAAc,iBAAiB;AAElC,uBAAe;AAAA,UACb,MAAMN,eAAc;AAAA,UACpB,OAAO,QAAQ;AAAA,UACf,MAAM,IAAI;AAAA,UACV,aAAa,IAAI;AAAA,QACnB,CAAC;AACD;AAAA,MACF;AAAA,MAEA,KAAKM,eAAc,iBAAiB;AAElC,uBAAe;AAAA,UACb,MAAMN,eAAc;AAAA,UACpB,OAAO,QAAQ;AAAA,UACf,OAAO,IAAI;AAAA,QACb,CAAC;AACD;AAAA,MACF;AAAA,MAEA,KAAKM,eAAc,eAAe;AAChC,YAAI,QAAQ,uBAA+B;AACzC,cAAI,mBAAK,mBAAkB;AACzB,kBAAM,kBAAkB;AAExB,uBAAW,SAAS;AAAA,cAClB,yBAAyB,KAAK,QAAQ,aAAa,WAAW,CAAC;AAAA,cAC/D;AAAA,YACF,GAAG;AAMD,+BAAiB;AAAA,gBACf,MAAMN,eAAc;AAAA,gBACpB,OAAO;AAAA,cACT,CAAC;AAAA,YACH;AAAA,UACF,OAAO;AACL,6BAAiB;AAAA,cACf,MAAMA,eAAc;AAAA,cACpB,OAAO,MAAM;AAAA,gBACX,yBAAyB,KAAK,QAAQ,aAAa,WAAW,CAAC;AAAA,cACjE;AAAA,YACF,CAAC;AAAA,UACH;AAEA,2BAAiB,EAAE,MAAMA,eAAc,mBAAmB,CAAC;AAAA,QAC7D,OAAO;AACL,2BAAiB;AAAA,YACf,MAAMA,eAAc;AAAA,YACpB,OAAO,MAAM,KAAK,KAAK,QAAQ,aAAa,WAAW,CAAC;AAAA,UAC1D,CAAC;AAAA,QACH;AACA;AAAA,MACF;AAAA,MAEA,KAAKM,eAAc,gBAAgB;AAIjC,aAAK,OAAO,uBAAuB;AAEnC,cAAM,SAAS,MAAM,KAAK,QAAQ,SAAS,IAAI,GAAG;AAElD,cAAM,eAA+B,OAAO;AAAA,UAAQ,CAAC,MACnD,EAAE,WAAW,aAAa,CAAC,EAAE,EAAE,IAAI,CAAC;AAAA,QACtC;AAEA,cAAM,gBAAgC,OAAO,QAAQ,CAAC,MAAM;AAC1D,kBAAQ,EAAE,QAAQ;AAAA,YAChB,KAAK;AAIH,qBAAO,EAAE,gBAAgB,SACrB,CAAC,aAAa,EAAE,WAAW,CAAC,IAC5B,CAAC;AAAA,YAEP,KAAK;AACH,qBAAO,EAAE,QAAQ,SAAY,CAAC,EAAE,GAAG,IAAI,CAAC;AAAA,YAG1C;AACE,qBAAOC,aAAY,GAAG,gBAAgB;AAAA,UAC1C;AAAA,QACF,CAAC;AAED,YAAI,aAAa,SAAS,GAAG;AAC3B,yBAAe;AAAA,YACb,MAAMP,eAAc;AAAA,YACpB,KAAK,aAAa,IAAI,SAAS;AAAA,UACjC,CAAC;AACD,wBAAc;AAAA,YACZ,MAAMA,eAAc;AAAA,YACpB,KAAK;AAAA,UACP,CAAC;AAAA,QACH;AAEA,YAAI,cAAc,SAAS,GAAG;AAC5B,2BAAiB;AAAA,YACf,MAAMA,eAAc;AAAA,YACpB,KAAK;AAAA,UACP,CAAC;AAAA,QACH;AAEA,YAAI,aAAa,SAAS,GAAG;AAI3B,gBAAM,KAAK,KAAK,MAAM,gCAAgC,GAAG;AACzD,cAAI,GAAI,OAAM,EAAE;AAAA,QAClB;AACA;AAAA,MACF;AAAA,MAEA,KAAKM,eAAc,YAAY;AAC7B,cAAM,SAAS,IAAI;AACnB,cAAM,OAAO,IAAI;AACjB,cAAM,OAAO,IAAI;AACjB,cAAM,CAAC,QAAQ,aAAa,YAAY,IAAI,MAAM,QAAQ,IAAI;AAAA,UAC5D,KAAK,WAAW,cAAc,KAAK,QAAQ,QAAQ,MAAM,IAAI;AAAA,UAC7D,KAAK,WAAW,gBAAgB,IAAI;AAAA,UACpC,KAAK,WAAW,gBAAgB,EAAE,MAAM,KAAK,CAAC;AAAA,QAChD,CAAC;AAED,YAAI,WAAW,QAAQ,iBAAiB,MAAM;AAC5C,2BAAiB;AAAA,YACf,MAAMN,eAAc;AAAA,YACpB;AAAA,YACA,QAAQ;AAAA;AAAA,YACR;AAAA,YACA;AAAA,YACA,IAAI;AAAA,YACJ,oBAAoB;AAAA,UACtB,CAAC;AAAA,QACH;AACA;AAAA,MACF;AAAA,MAEA,KAAKM,eAAc,aAAa;AAC9B,cAAM,SAAS,IAAI;AACnB,cAAM,OAAO,IAAI;AACjB,cAAM,OAAO,IAAI;AACjB,cAAM,CAAC,QAAQ,KAAK,IAAI,MAAM;AAAA,UAC5B,KAAK,WAAW,cAAc,KAAK,QAAQ,QAAQ,MAAM,IAAI;AAAA,QAC/D;AAEA,YAAI;AAEF;AAEF,aAAK;AAAA,UACH;AAAA,YACE,MAAMN,eAAc;AAAA,YACpB;AAAA,YACA;AAAA,YACA,QAAQ;AAAA,YACR,aAAa;AAAA,YACb,IAAI;AAAA,YACJ,oBAAoB,OAAO;AAAA,UAC7B;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,YAAI,OAAO,WAAW;AACpB,gBAAM,KAAK,KAAK,MAAM,6BAA6B,KAAK,OAAO;AAC/D,cAAI,GAAI,OAAM,EAAE;AAAA,QAClB;AAEA;AAAA,MACF;AAAA,MAEA,SAAS;AACP,YAAI;AACF,iBAAOO,aAAY,KAAK,yBAAyB;AAAA,QACnD,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAnnCWJ,WAAA;AACA;","names":["constant","object","optional","string","taggedUnion","object","constant","optional","string","taggedUnion","snapshot","snapshot","CrdtType","snapshot","buildObject","buildNode","buildList","buildMap","emitObject","emit","emitList","emitMap","CrdtType","CrdtType","ProtocolVersion","assertNever","ClientMsgCode","OpCode","raise","ServerMsgCode","array","nanoid","raise","LogLevel","raise","target","CrdtType","isRootStorageNode","nn","isRootStorageNode","nn","CrdtType","parentNode","asPos","assertNever","CrdtType","makePosition","OpCode","op","OpCode","CrdtType","assertNever","asPos","makePosition","DefaultMap","DefaultMap","snapshot","applyUpdate","array","ServerMsgCode","OpCode","op","__debug","raise","nanoid","ClientMsgCode","assertNever"]}