{
  "version": 3,
  "sources": ["../../src/document/models/CanvasTree/treeSyncUtils.ts", "../../src/document/crdt/sync/CrdtTreeSync.ts", "../../src/document/crdt/sync/findStoreTreeDifferences.ts", "../../src/document/crdt/sync/CrdtRemoteDocumentDataHandler.ts", "../../src/socket/useSocket.ts", "../../src/socket/SocketStats.ts", "../../src/web/lib/showTreeModeMismatchToast.ts", "../../src/document/RemoteDocumentHandler.ts", "../../src/document/models/CanvasTree/TreeSync.ts", "../../src/document/models/CanvasTree/JsonTreeSync.ts", "../../src/document/models/CanvasTree/utils/treeAsserts.ts", "../../src/document/JsonRemoteDocumentDataHandler.ts", "../../src/document/components/chrome/analytics/utils/dateRangePreset.ts", "../../src/document/base-engine/BaseUndoRedoManager.ts"],
  "sourcesContent": ["// Returns a copy of a TreeUpdate with the extra local fields removed.\nimport type { BranchedSerializableRow } from \"@framerjs/crdtree2\"\nimport type { AppEnvironment, ClientTreeUpdate, ServerTreeUpdateOther } from \"document/RemoteDocumentHandler.ts\"\n\nexport type LocalTreeUpdate = ClientTreeUpdate & { count: number; confirmed: boolean }\n\nexport function asClientTreeUpdate(local: LocalTreeUpdate): ClientTreeUpdate {\n\tconst { appEnvironment, session, seq, count, reasons, changes } = local\n\treturn { appEnvironment, session, seq, changes, count, reasons }\n}\n\n// For testing purposes.\nexport function makeServerTreeUpdate(update: ClientTreeUpdate, next: number): ClientTreeUpdate & ServerTreeUpdateOther {\n\treturn { ...update, next }\n}\n\nexport interface CrdtUpdate {\n\tseq: number\n\trows: BranchedSerializableRow[]\n\treasons?: string // only in outgoing updates\n\tnext?: number // only in incoming updates\n\tts: number\n\tappEnvironment?: AppEnvironment\n}\n\nexport function isCrdtUpdate(update: CrdtUpdate | CrdtConfirm): update is CrdtUpdate {\n\treturn \"rows\" in update\n}\n\nexport interface CrdtConfirm {\n\tseq: number\n\tnext: number\n}\n", "import type { CrdtTreePatchMap } from \"@framerjs/crdtree2\"\nimport { assert, getLogger } from \"@framerjs/shared\"\nimport type { AppEnvironment } from \"document/RemoteDocumentHandler.ts\"\nimport type { CanvasTree } from \"document/models/CanvasTree/CanvasTree.ts\"\nimport type { NodeChange } from \"document/models/CanvasTree/TreeDiff.ts\"\nimport type { CanvasNode } from \"document/models/CanvasTree/nodes/CanvasNode.ts\"\nimport type { CrdtConfirm, CrdtUpdate } from \"document/models/CanvasTree/treeSyncUtils.ts\"\nimport { markLoadingPerf, setWsInitialUpdates } from \"utils/performanceTracker.ts\"\nimport type { CrdtTreeCommitter } from \"./CrdtTreeCommitter.ts\"\n\nconst log = getLogger(\"remote:sync\")\n\n/**\n * CRDT protocol state machine.\n *\n * - `CrdtTreeCommitter` is responsible for tree/history state (commit, cursors, readers, store).\n * - `CrdtTreeSync` is responsible for protocol state (init/ready, version ordering, confirm semantics).\n */\nexport class CrdtTreeSync {\n\t// Protocol parity / test hooks: some debug/test harnesses key off `session`.\n\t// CRDT doesn't need JSON's session/seq semantics, but we keep a stable identifier.\n\tsession = 0\n\n\t// tree information\n\ttreeVersion = 0\n\t// counts message durint init\n\tupdatesSeen = 0\n\n\t// init information\n\tinit = 0\n\texpectingInitialUpdates = 0 // counts rows expected during init\n\n\t// sync information\n\thasError = false\n\twaitingForTree = false\n\n\t/** Every websocket message has a sequence number. */\n\tprivate messageSeq = 0\n\n\t/** Outgoing CRDT row messages that haven't been acknowledged by the server yet. */\n\treadonly unconfirmedCrdtUpdates = new Map<number, CrdtUpdate>()\n\n\tappEnvironment: AppEnvironment | undefined\n\n\tconstructor(\n\t\tpublic readonly committer: CrdtTreeCommitter,\n\t\tprivate readonly isMPSSocket = false,\n\t) {}\n\n\tget waitingForInitialUpdates(): boolean {\n\t\treturn this.expectingInitialUpdates > this.updatesSeen\n\t}\n\n\tget isLoading(): boolean {\n\t\treturn this.waitingForTree || this.waitingForInitialUpdates\n\t}\n\n\tget isReady(): boolean {\n\t\treturn !(this.hasError || this.waitingForTree || this.waitingForInitialUpdates)\n\t}\n\n\terror(message: string, cause?: unknown) {\n\t\tthis.hasError = true\n\t\treturn Error(message, { cause })\n\t}\n\n\tprivate recordHistoricTree(version: number, hasHierarchyChanges: boolean) {\n\t\tthis.committer.recordHistoricTree(version, {\n\t\t\tcontainsLocalEdits: this.committer.hasLocalEdits() || this.unconfirmedCrdtUpdates.size > 0,\n\t\t\thasHierarchyChanges,\n\t\t})\n\t}\n\n\thasHierarchyChangesAfterVersion(version: number): boolean {\n\t\tif (this.committer.hasHierarchyChangesAfterVersion(version)) return true\n\n\t\tfor (const update of this.unconfirmedCrdtUpdates.values()) {\n\t\t\tif (update.rows.some(row => row.key === \"parentid\")) return true\n\t\t}\n\n\t\treturn false\n\t}\n\n\t/**\n\t * Decide what to do with a remote tree version (from a confirm or update\n\t * message). Returns \"apply\" if the version moves us forward, \"ignore\" if\n\t * it's an old/duplicate version that should be skipped.\n\t *\n\t * On the new Multiplayer Service tree versions are timestamp-based, so\n\t * forward jumps are not guaranteed to be consecutive. The legacy server\n\t * always advances by exactly one, so anything else is treated as a\n\t * missing-update protocol error.\n\t */\n\tcheckRemoteVersion(nextVersion: number, source: \"confirm\" | \"update\"): \"apply\" | \"ignore\" {\n\t\tassert(Number.isFinite(nextVersion), `${source === \"confirm\" ? \"Confirm\" : \"Update\"} must have tree version`)\n\n\t\tif (nextVersion <= this.treeVersion) {\n\t\t\tlog.debug(`ignoring old ${source}:`, nextVersion, \" <= \", this.treeVersion)\n\t\t\treturn \"ignore\"\n\t\t}\n\n\t\tif (this.isMPSSocket) {\n\t\t\treturn \"apply\"\n\t\t}\n\n\t\tif (nextVersion !== this.treeVersion + 1) {\n\t\t\tthrow this.error(\"missing update: \" + this.treeVersion + \" + 1 != \" + nextVersion)\n\t\t}\n\t\treturn \"apply\"\n\t}\n\n\tverify(treeVersion: number, serverHash: number): boolean {\n\t\tconst entry = this.committer.getTreeForVersion(treeVersion)\n\t\tif (!entry) {\n\t\t\tlog.debug(\"verify: unable to find tree with version\", treeVersion)\n\t\t\treturn true\n\t\t}\n\t\tif (entry.containsLocalEdits) {\n\t\t\tlog.debug(\"verify: unable verify an entry with local edits\")\n\t\t\treturn true\n\t\t}\n\n\t\tconst tree = entry.tree\n\t\tconst localHash = tree.computeTreeHash()\n\t\tif (localHash !== serverHash) {\n\t\t\tlog.warn(\"verify: failed\", localHash, \"!==\", serverHash)\n\t\t\tif (serverHash === 0) return true\n\t\t\tlog.reportError(\"Tree verification failed\", { localHash, serverHash, treeVersion, treeSize: tree.size() })\n\t\t} else {\n\t\t\tlog.debug(\"verify: passed; hash:\", serverHash)\n\t\t}\n\t\treturn localHash === serverHash\n\t}\n\n\tsetTree(tree: CanvasTree, treeVersion: number, options?: { initialChanges?: NodeChange[]; isLoading?: boolean }) {\n\t\tlog.info(\"setTree\", treeVersion)\n\t\tthis.committer.reset(tree, options)\n\t\tthis.treeVersion = treeVersion\n\t\tthis.waitingForTree = false\n\t\tthis.hasError = false\n\t\tthis.unconfirmedCrdtUpdates.clear()\n\t\tthis.recordHistoricTree(treeVersion, false)\n\t}\n\n\t/** Used to not resume an old connection, but to restart after a disconnect. */\n\tresetSession() {\n\t\tthis.messageSeq = 0\n\t\tthis.unconfirmedCrdtUpdates.clear()\n\t\tthis.treeVersion = 0\n\t\tthis.waitingForTree = false\n\t}\n\n\tdebugResetSessionAndTree(tree: CanvasTree) {\n\t\tthis.resetSession()\n\t\tthis.setTree(tree, 0)\n\t}\n\n\t// When we are sent an init, we've already indicated our treeVersion, and will be sent only updates that match it.\n\thandleInit(treeVersion: number, initialUpdates: number) {\n\t\tthis.init += 1\n\t\tif (this.init === 1) {\n\t\t\tmarkLoadingPerf(\"wsTreeInitMessages\")\n\t\t\tsetWsInitialUpdates(initialUpdates)\n\t\t}\n\t\tlog.info(\"init\", this.init, { treeVersion, initialUpdates, localTreeVersion: this.treeVersion })\n\n\t\tthis.hasError = false\n\t\tthis.expectingInitialUpdates = initialUpdates\n\t\tthis.updatesSeen = 0\n\n\t\t// this.unconfirmedCrdtUpdatesAtInit = new Map(this.unconfirmedCrdtUpdates)\n\n\t\tif (this.treeVersion !== treeVersion || this.waitingForTree) {\n\t\t\tthis.waitingForTree = true\n\t\t\treturn true\n\t\t}\n\n\t\tthis.committer.remoteTreeVersion = treeVersion\n\t\tthis.recordHistoricTree(treeVersion, false)\n\n\t\treturn false\n\t}\n\n\t/** Used by the \"not ready\" guard to allow harmless commits (fonts/modules/etc). */\n\thasMessageForRemote(): boolean {\n\t\treturn this.committer.hasLocalEdits()\n\t}\n\n\thasOnlyEmptyChangesForRemote(): boolean {\n\t\tif (!this.committer.hasLocalEdits()) return true\n\t\treturn this.committer.hasOnlyEmptyChangesForRemote()\n\t}\n\n\tonLoadedFirstData() {\n\t\tthis.committer.resetLastSeqTaken()\n\t}\n\n\tcreateMessage(userId: string): CrdtUpdate | null {\n\t\tconst rows = this.committer.takePendingRows(userId)\n\t\tif (rows.length <= 0) return null\n\n\t\tconst reasons = this.committer.getEditReasons()\n\t\tconst message = { seq: ++this.messageSeq, rows, reasons, ts: Date.now(), appEnvironment: this.appEnvironment }\n\n\t\tlog.debug(\"create CRDT rows message:\", rows.length, \"inflight:\", this.unconfirmedCrdtUpdates.size, message.seq)\n\n\t\tthis.unconfirmedCrdtUpdates.set(message.seq, message)\n\t\treturn message\n\t}\n\n\thasUnconfirmedChanges(): boolean {\n\t\treturn this.unconfirmedCrdtUpdates.size > 0\n\t}\n\n\t/** Resends previous local updates when the network becomes ready */\n\tcreateUnconfirmedMessages(): CrdtUpdate[] {\n\t\tassert(this.isReady)\n\n\t\tconst updatesToSend = Array.from(this.unconfirmedCrdtUpdates.values())\n\t\tthis.unconfirmedCrdtUpdates.clear()\n\n\t\treturn updatesToSend.map(update => {\n\t\t\tconst newUpdate = { ...update, seq: ++this.messageSeq }\n\t\t\tthis.unconfirmedCrdtUpdates.set(newUpdate.seq, newUpdate)\n\t\t\treturn newUpdate\n\t\t})\n\t}\n\n\t/** Handles a confirm message, consuming protocol state and advancing committer state. */\n\thandleConfirmMessage(confirm: CrdtConfirm) {\n\t\tif (this.hasError || this.waitingForTree) return\n\n\t\tconst seq = confirm.seq\n\t\tconst unconfirmed = this.unconfirmedCrdtUpdates.get(seq)\n\t\tassert(unconfirmed, \"update not found for seq\", seq)\n\t\tconst hasHierarchyChanges = unconfirmed.rows.some(row => row.key === \"parentid\")\n\t\tthis.unconfirmedCrdtUpdates.delete(seq)\n\n\t\tif (this.checkRemoteVersion(confirm.next, \"confirm\") !== \"apply\") return\n\n\t\tthis.treeVersion = confirm.next\n\t\tthis.committer.handleRemoteConfirm(confirm.next, {\n\t\t\tcontainsLocalEdits: this.committer.hasLocalEdits() || this.unconfirmedCrdtUpdates.size > 0,\n\t\t\thasHierarchyChanges,\n\t\t})\n\t}\n\n\t/** Apply a server CRDT patch update (rows) and advance protocol version. */\n\thandleRemotePatches(\n\t\tpatches: CrdtTreePatchMap,\n\t\tnextVersion: number,\n\t\thasHierarchyChanges: boolean,\n\t): NodeChange[] | undefined {\n\t\tif (this.hasError || this.waitingForTree) return\n\n\t\tif (this.checkRemoteVersion(nextVersion, \"update\") !== \"apply\") return\n\n\t\tthis.treeVersion = nextVersion\n\n\t\treturn this.committer.handleRemotePatches(patches, nextVersion, {\n\t\t\tcontainsLocalEdits: this.committer.hasLocalEdits() || this.unconfirmedCrdtUpdates.size > 0,\n\t\t\thasHierarchyChanges,\n\t\t})\n\t}\n\n\t// --- Partial loading hooks (mirrors TreeSync logic, but backed by `CrdtTreeState`) ---\n\tloadedAllScopes() {\n\t\tthis.committer.loadedAllScopes({\n\t\t\tcontainsLocalEdits: this.committer.hasLocalEdits() || this.unconfirmedCrdtUpdates.size > 0,\n\t\t\thasHierarchyChanges: false,\n\t\t})\n\t}\n\n\t/** Insert one scope on top of the remote tree, before any local tree, because it just got loaded. */\n\tloadOneScope(scope: CanvasNode, keepChanges: boolean): CanvasTree {\n\t\treturn this.committer.loadOneScope(scope, keepChanges)\n\t}\n}\n", "import type { Store } from \"@framerjs/crdtree2\"\nimport { isEqual } from \"library/index.ts\"\n\nconst ignoredStoreNodeProperties = {\n\tchildren: true,\n\tcontentHash: true,\n\tparentid: true,\n\t$keep: true,\n}\n\nfunction filterCache(key: string, value: unknown) {\n\tif (key === \"cached\") return undefined\n\treturn value\n}\n\nfunction normalize(value: unknown): unknown {\n\tif (value === undefined) return undefined\n\tconst normalized = JSON.stringify(value, filterCache)\n\tif (normalized === undefined) return undefined\n\treturn JSON.parse(normalized)\n}\n\nfunction hasDifferentValue(left: unknown, right: unknown): boolean {\n\tif (isEqual(left, right)) return false\n\tif (left === undefined || right === undefined) return true\n\treturn !isEqual(normalize(left), normalize(right))\n}\n\nexport function findStoreTreeDifferences(localStore: Store, remoteStore: Store, errors: string[]): boolean {\n\tconst localHierarchy = localStore.getHierarchy()\n\tconst remoteHierarchy = remoteStore.getHierarchy()\n\tconst seen = new Set<string>()\n\tlet different = false\n\n\tfunction compareNodes(nodeId: string, path: string) {\n\t\tseen.add(nodeId)\n\t\tpath += \".\" + nodeId\n\n\t\tif (!remoteHierarchy.has(nodeId)) {\n\t\t\tdifferent = true\n\t\t\terrors.push(\"-node: \" + path)\n\t\t} else {\n\t\t\tconst problems: string[] = []\n\n\t\t\tconst localChildren = localHierarchy.getChildrenIds(nodeId)\n\t\t\tconst remoteChildren = remoteHierarchy.getChildrenIds(nodeId)\n\t\t\tif (!isEqual(localChildren, remoteChildren)) {\n\t\t\t\tproblems.push(` .children: [${localChildren.join(\",\")}] != [${remoteChildren.join(\",\")}]`)\n\t\t\t}\n\n\t\t\tconst localLatest = localStore.getLatest(nodeId)\n\t\t\tconst remoteLatest = remoteStore.getLatest(nodeId)\n\t\t\tconst keys = new Set<string>()\n\t\t\tfor (const key of localLatest?.keys() ?? []) keys.add(key)\n\t\t\tfor (const key of remoteLatest?.keys() ?? []) keys.add(key)\n\n\t\t\tfor (const key of keys) {\n\t\t\t\tif (key in ignoredStoreNodeProperties) continue\n\n\t\t\t\tconst localValue = localStore.getObjectKey(nodeId, key)\n\t\t\t\tconst remoteValue = remoteStore.getObjectKey(nodeId, key)\n\t\t\t\tif (hasDifferentValue(localValue, remoteValue)) {\n\t\t\t\t\tproblems.push(` .${key}: ${JSON.stringify(localValue)} != ${JSON.stringify(remoteValue)}`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (problems.length > 0) {\n\t\t\t\tdifferent = true\n\t\t\t\terrors.push(\"!node: \" + path)\n\t\t\t\terrors.push(...problems)\n\t\t\t}\n\t\t}\n\n\t\tfor (const childId of localHierarchy.getChildrenIds(nodeId)) {\n\t\t\tcompareNodes(childId, path)\n\t\t}\n\t}\n\n\tfunction addMissingNodes(nodeId: string, path: string) {\n\t\tpath += \".\" + nodeId\n\t\tif (!seen.has(nodeId)) {\n\t\t\tdifferent = true\n\t\t\terrors.push(\"+node: \" + path)\n\t\t}\n\t\tfor (const childId of remoteHierarchy.getChildrenIds(nodeId)) {\n\t\t\taddMissingNodes(childId, path)\n\t\t}\n\t}\n\n\tconst localRootId = localHierarchy.getRootId()\n\tif (localRootId) {\n\t\tcompareNodes(localRootId, \"\")\n\t}\n\n\tconst remoteRootId = remoteHierarchy.getRootId()\n\tif (remoteRootId) {\n\t\taddMissingNodes(remoteRootId, \"\")\n\t}\n\n\treturn different\n}\n", "import { MAIN_BRANCH_ID, type SerializableRow, Store } from \"@framerjs/crdtree2\"\nimport type { CompareResult } from \"@framerjs/crdtree2/src/manifest/Manifest.ts\"\nimport type { ComponentLoader } from \"@framerjs/framer-runtime\"\nimport { assert, getLogger, hostInfo, reportableError } from \"@framerjs/shared\"\nimport type {\n\tRemoteDocumentDataHandler,\n\tRemoteDocumentDataHandlerCallbacks,\n\tSendMessage,\n} from \"document/RemoteDocumentDataHandler.ts\"\nimport type { AppEnvironment } from \"document/RemoteDocumentHandler.ts\"\nimport type { CrdtDebugRecord } from \"document/crdt/CRDTDebugRecord.ts\"\nimport { canRenderPageTypeBeforeDocumentHasFullyLoaded } from \"document/firstScopeFinder.ts\"\nimport type { DocumentLoaderSettings } from \"document/loader/DocumentLoader.ts\"\nimport { CanvasTreeVersion } from \"document/models/CanvasTree/CanvasTreeVersion.ts\"\nimport type { NodeChange } from \"document/models/CanvasTree/TreeDiff.ts\"\nimport { safeLoadDocument } from \"document/models/CanvasTree/TreeRepair.ts\"\nimport type { TreeUpdater } from \"document/models/CanvasTree/TreeUpdater.ts\"\nimport type { CanvasNode, CanvasTree } from \"document/models/CanvasTree/index.ts\"\nimport { type CrdtConfirm, type CrdtUpdate, isCrdtUpdate } from \"document/models/CanvasTree/treeSyncUtils.ts\"\nimport { getTunnelId } from \"environment/getTunnelId.ts\"\nimport { accessTokenRefresher } from \"web/lib/accessTokenRefresherWeb.ts\"\nimport { getMultiplayerServiceURL } from \"web/lib/multiplayerService.ts\"\nimport { CrdtBranchStoreRegistry } from \"./CrdtBranchStoreRegistry.ts\"\nimport { CrdtDocumentLoader } from \"./CrdtDocumentLoader.ts\"\nimport { CrdtTreeCommitter } from \"./CrdtTreeCommitter.ts\"\nimport { CrdtTreeSync } from \"./CrdtTreeSync.ts\"\nimport type { LocalCache } from \"./LocalCache.ts\"\nimport { mergeBranchedRowsAndGetPatches } from \"./crdtTreeUpdater.ts\"\nimport { findStoreTreeDifferences } from \"./findStoreTreeDifferences.ts\"\n\nconst log = getLogger(\"remote:connection\")\nconst verifyLog = getLogger(\"remote:verify\")\nconst VERSION_ETAG_REGEX = /Version-(\\d+)/u\nconst CRDT_VERSION_URL_REGEX = /\\d+\\.crdt.*/u\n\n/** Total unconfirmed messages we keep in flight, before waiting to send more. */\nconst MAX_INFLIGHT_MESSAGES = 5\n\nexport class CrdtRemoteDocumentDataHandler implements RemoteDocumentDataHandler {\n\tpublic treeSync: CrdtTreeSync\n\tprivate remoteUpdates: (CrdtUpdate | CrdtConfirm)[] = []\n\tprivate ignoreTreeVerifies = false\n\tprivate ignoreTreeVerifyVersion = 0\n\tprivate shouldCrashFromDebug = false\n\n\t/**\n\t * The most recent verify message that arrived while the document was\n\t * hidden. Re-evaluated at the end of `processRemoteUpdates` once the tab\n\t * is visible again and the inbound queue has been drained.\n\t */\n\tprivate pendingTreeVerify: { url: string; version: number; hash: number } | undefined\n\n\t/**\n\t * Whether `document.hidden` is currently true. Updated from the\n\t * surrounding `RemoteDocument` on `visibilitychange`. We only buffer\n\t * verifies while the tab is hidden \u2014 in the foreground the engine\n\t * scheduler is running every frame, so verifies are evaluated\n\t * synchronously the way they always were.\n\t */\n\tprivate documentHidden = false\n\n\t/**\n\t * True while the verify path is reasoning about a state that's only\n\t * transiently a subset of the store: an initial shallow-scope load is\n\t * still in progress, a single scope is being asynchronously loaded into\n\t * the tree, or there are inbound remote rows queued that have been\n\t * written to the store but not yet applied to the tree. Verifies that\n\t * land in this window can read tree-vs-store states that look\n\t * inconsistent now but will reconcile once the loader/queue catches up,\n\t * so we buffer them and re-evaluate at the next quiescent point.\n\t */\n\tprivate get verifyIsBlockedByLoader(): boolean {\n\t\tif (this.loader?.activelyLoadingScope) return true\n\t\tif (this.treeSync.committer.isPartialLoading) return true\n\t\treturn false\n\t}\n\n\tpublic loader: CrdtDocumentLoader | undefined\n\tpublic documentSize = 0\n\tpublic loaderPromise: Promise<void> | undefined\n\n\t/** Captures the last crdt seq that was synced with the server. */\n\t// (Moved to CrdtTreeSync): lastSyncSeq/messageSeq/unconfirmedCrdtUpdates\n\n\t// Compatibility surface for tests/debugging.\n\tget unconfirmedCrdtUpdates() {\n\t\treturn this.treeSync.unconfirmedCrdtUpdates\n\t}\n\n\trecorder?: (record: CrdtDebugRecord) => void\n\n\tconstructor(\n\t\ttimeline: TreeUpdater,\n\t\tprivate readonly componentLoader: ComponentLoader,\n\t\tprivate readonly userId: string,\n\t\tprivate projectId: string,\n\t\tprivate callbacks: RemoteDocumentDataHandlerCallbacks,\n\t\tprivate readonly localCache?: LocalCache,\n\t\tprivate readonly isMPSSocket = false,\n\t\tappEnvironment?: AppEnvironment,\n\t) {\n\t\tassert(timeline instanceof CrdtTreeCommitter, \"tree updater must be a CrdtTreeCommitter\")\n\t\tthis.treeSync = new CrdtTreeSync(timeline, isMPSSocket)\n\t\tthis.treeSync.appEnvironment = appEnvironment\n\t\tthis.treeSync.waitingForTree = true\n\n\t\t// TODO: Remove after store is available in debug bar\n\t\t// @ts-expect-error Remove this line when we are done with store\n\t\twindow.store = this.store\n\t}\n\n\t// Expose necessary TreeSync properties and methods\n\tget treeVersion(): number {\n\t\treturn this.treeSync.treeVersion\n\t}\n\n\tget isReady(): boolean {\n\t\treturn this.treeSync.isReady\n\t}\n\n\tget waitingForTree(): boolean {\n\t\treturn this.treeSync.waitingForTree\n\t}\n\n\tprivate get store() {\n\t\treturn this.treeSync.committer.store\n\t}\n\n\tget isLoading() {\n\t\treturn this.treeSync.isLoading\n\t}\n\n\tget localUpdatesInFlight() {\n\t\treturn []\n\t}\n\n\tget localUpdatesAtInit() {\n\t\treturn []\n\t}\n\n\tget hasError(): boolean {\n\t\treturn this.treeSync.hasError\n\t}\n\n\tget init() {\n\t\treturn this.treeSync.init\n\t}\n\n\tget session() {\n\t\treturn this.treeSync.session\n\t}\n\n\tsetTree(tree: CanvasTree, treeVersion: number, options?: { initialChanges?: NodeChange[]; isLoading?: boolean }) {\n\t\tthis.treeSync.setTree(tree, treeVersion, options)\n\t}\n\n\tget hasUpdatesToProcess() {\n\t\treturn !this.waitingForTree && this.remoteUpdates.length > 0\n\t}\n\n\tresetSession(): void {\n\t\tthis.pendingTreeVerify = undefined\n\t\tthis.treeSync.resetSession()\n\t}\n\n\t/**\n\t * Called by `RemoteDocument` from the `visibilitychange` listener.\n\t * On the hidden\u2192visible transition we eagerly re-evaluate any deferred\n\t * verify: `processRemoteUpdates` is only scheduled when there are\n\t * inbound rows to drain, so a verify deferred against an already-quiet\n\t * queue would otherwise sit in `pendingTreeVerify` until the next row\n\t * arrives, masking real divergences in the meantime.\n\t */\n\tsetDocumentHidden(hidden: boolean): void {\n\t\tconst wasHidden = this.documentHidden\n\t\tthis.documentHidden = hidden\n\t\tif (wasHidden && !hidden) {\n\t\t\tthis.maybeEvaluatePendingTreeVerify()\n\t\t}\n\t}\n\n\t/**\n\t * Re-evaluate `pendingTreeVerify` if all the conditions that made the\n\t * original verify ambiguous have been resolved: tab visible again,\n\t * inbound queue drained, loader not mid-load, and the client tree\n\t * caught up to (or past) the version the server asked us to verify.\n\t * Called from the hidden\u2192visible transition, at the end of\n\t * `processRemoteUpdates`, and after the loader integrates a scope or\n\t * finishes loading.\n\t */\n\tprivate maybeEvaluatePendingTreeVerify(): void {\n\t\tconst pendingTreeVerify = this.pendingTreeVerify\n\t\tif (pendingTreeVerify === undefined) return\n\t\tif (this.documentHidden) return\n\t\tif (this.treeSync.hasError) return\n\t\tif (!this.treeSync.isReady) return\n\t\tif (this.remoteUpdates.length > 0) return\n\t\tif (this.verifyIsBlockedByLoader) return\n\t\tif (this.treeSync.treeVersion < pendingTreeVerify.version) return\n\n\t\tthis.pendingTreeVerify = undefined\n\t\tthis.handleTreeVerify(pendingTreeVerify.url, pendingTreeVerify.version, pendingTreeVerify.hash)\n\t}\n\n\tdebugResetSessionAndTree(tree: CanvasTree): void {\n\t\tthis.treeSync.debugResetSessionAndTree(tree)\n\t}\n\n\tdebugCrash() {\n\t\tthis.shouldCrashFromDebug = true\n\t}\n\n\tcanProcessChanges() {\n\t\tif (!this.treeSync.isReady || this.shouldCrashFromDebug) {\n\t\t\t// If this commit did not generate a diff, we can allow it. When the\n\t\t\t// treeSync is not ready, the user is in read-only mode. But as\n\t\t\t// fonts load, or code components, the tree might still take on\n\t\t\t// board changes, or do commit(), for other reasons.\n\t\t\tif (this.treeSync.hasOnlyEmptyChangesForRemote()) return false\n\n\t\t\t// On the new Multiplayer Service local CRDT edits can stay\n\t\t\t// buffered while we wait to catch up to the remote tree, instead\n\t\t\t// of erroring out.\n\t\t\tif (this.isMPSSocket && this.treeSync.waitingForTree) return false\n\n\t\t\t// But otherwise we would have a diff that we cannot send to the\n\t\t\t// server. Which is bad, so we throw an error.\n\t\t\tlet state = \"is not ready\"\n\t\t\tif (this.treeSync.hasError) {\n\t\t\t\tstate = \"had an error\"\n\t\t\t} else if (this.treeSync.waitingForTree) {\n\t\t\t\tstate = \"is waiting for tree data\"\n\t\t\t} else if (this.treeSync.waitingForInitialUpdates) {\n\t\t\t\tstate = \"is waiting for initial updates\"\n\t\t\t} else if (this.shouldCrashFromDebug) {\n\t\t\t\tthis.shouldCrashFromDebug = false\n\t\t\t\tstate = \"is doing a deliberate crash test\"\n\t\t\t}\n\t\t\tthrow this.treeSync.error(\"cannot create local updates when the document \" + state)\n\t\t}\n\n\t\treturn true\n\t}\n\n\tprocessViewOnly() {\n\t\t// When the user is a viewer, we don't expect any updates, log a warning and consume the\n\t\t// update. This prevents the user from (silently) disconnecting.\n\n\t\t// Unfortunately we do sometimes update the local tree if code modules update, or fonts\n\t\t// are loaded, etc. And if graphics containers are involved, they might compute a new\n\t\t// contentHash.\n\t\t// Protocol bookkeeping belongs to CrdtTreeSync.\n\t\t// If we're already behind the last sent seq, don't do anything.\n\t\t// Otherwise, consider everything up to current seq as synced to avoid sending viewer updates.\n\t\tif (this.store.seq <= 0) return\n\t\tthis.treeSync.onLoadedFirstData()\n\n\t\tlog.warn(\"cannot create local updates when the user is a viewer\")\n\t}\n\n\thandleRows(_id: string, update: CrdtUpdate): void {\n\t\tthis.remoteUpdates.push(update)\n\t}\n\n\thandleConfirmRows(message: CrdtConfirm): void {\n\t\tthis.remoteUpdates.push(message)\n\t}\n\n\thandleInit(treeVersion: number, initialUpdates: number) {\n\t\tthis.remoteUpdates.length = 0\n\t\t// `pendingTreeVerify` references a hash bound to the previous session's\n\t\t// tree state. Drop it on init so the next drain cannot evaluate stale\n\t\t// data against the new session and falsely report an out-of-sync error.\n\t\tthis.pendingTreeVerify = undefined\n\t\tconst needsDownload = this.treeSync.handleInit(treeVersion, initialUpdates)\n\n\t\treturn { needsDownload }\n\t}\n\n\thandleTreeUpdate() {\n\t\tthrow new Error(\"Json tree updates cannot be handled by Crdt data handler\")\n\t}\n\n\thandleTreeVerify(url: string, version: number, hash: number) {\n\t\t// Ignore verify messages while the treeSync is not ready.\n\t\tif (!this.treeSync.isReady) return\n\n\t\t// When manually verifying trees, we force a snapshot from the server,\n\t\t// which will also send a treeVerify message. That is why handling can\n\t\t// be suppressed completely, or for a specific version.\n\t\tif (this.ignoreTreeVerifies) return\n\t\tif (this.ignoreTreeVerifyVersion === version) return\n\n\t\t// While the tab is hidden the engine scheduler is throttled but the\n\t\t// websocket message handler is not, so a verify can land mid-frame \u2014\n\t\t// before `processRemoteUpdates` has drained queued rows or advanced\n\t\t// `treeVersion`. Computing the hash now would compare the server's\n\t\t// post-version state to our pre-drain state and report a false\n\t\t// mismatch. Buffer the latest verify and re-evaluate it in\n\t\t// `processRemoteUpdates` once the tab is visible again. In the\n\t\t// foreground the scheduler runs every frame, so verifies stay\n\t\t// synchronous and never wait on this path.\n\t\tif (this.documentHidden) {\n\t\t\tverifyLog.debug(\"remote tree verify deferred while document hidden\", {\n\t\t\t\tversion,\n\t\t\t\tclientVersion: this.treeSync.treeVersion,\n\t\t\t\tqueuedUpdates: this.remoteUpdates.length,\n\t\t\t})\n\t\t\t// Keep only the most recent deferred verify; older versions are\n\t\t\t// implicitly covered by the newer one once we catch up.\n\t\t\tthis.pendingTreeVerify = { url, version, hash }\n\t\t\treturn\n\t\t}\n\n\t\t// The shallow-scope loader runs asynchronously: each scope is parsed\n\t\t// from the store between scheduler yields, and only integrated into\n\t\t// the canvas tree on the next `loadOneScope` call. While that work\n\t\t// is in flight the tree is intentionally a subset of the store \u2014\n\t\t// `processRemoteUpdates` itself short-circuits when\n\t\t// `activelyLoadingScope` is true, leaving any rows already written\n\t\t// to the store unmirrored in the tree. A verify computed in this\n\t\t// window would compare the server's fully-loaded view against our\n\t\t// transient partial tree and report \"out of sync\" against a state\n\t\t// that will reconcile a few frames later. Buffer for re-evaluation\n\t\t// once the loader settles, just like the hidden-tab case.\n\t\tif (this.verifyIsBlockedByLoader) {\n\t\t\tverifyLog.debug(\"remote tree verify deferred while loader is integrating scopes\", {\n\t\t\t\tversion,\n\t\t\t\tclientVersion: this.treeSync.treeVersion,\n\t\t\t\tqueuedUpdates: this.remoteUpdates.length,\n\t\t\t\tactivelyLoadingScope: this.loader?.activelyLoadingScope ?? false,\n\t\t\t\tisPartialLoading: this.treeSync.committer.isPartialLoading,\n\t\t\t})\n\t\t\tthis.pendingTreeVerify = { url, version, hash }\n\t\t\treturn\n\t\t}\n\n\t\t// TODO: hasHierarchyChangesAfterVersion is not branch aware yet, so we'll potentially skip\n\t\t// verification if *any* branch has had hierarchy changes.\n\t\tif (this.treeSync.hasHierarchyChangesAfterVersion(version)) {\n\t\t\tverifyLog.debug(\"remote tree verify skipped due to later hierarchy changes\", { version, hash })\n\n\t\t\t// We will still verify the canvas tree with the store hierarchy.\n\t\t\tthis.verifyStoreTreeWithCanvasTree(hash)\n\t\t\treturn\n\t\t}\n\n\t\tconst mainStore = this.treeSync.committer.branches.getStore(MAIN_BRANCH_ID)\n\t\tconst localHash = mainStore.getHierarchy().computeTreeHash()\n\t\tif (localHash !== hash) {\n\t\t\tverifyLog.error(\"remote tree verify failed\", {\n\t\t\t\tversion,\n\t\t\t\thash,\n\t\t\t\tlocalHash,\n\t\t\t})\n\t\t\tthis.localCache?.abortAndClearCache()\n\n\t\t\t// Do a deeper verify to report what was different.\n\t\t\tvoid this.verifyLocalTreeWithServer(url, version)\n\n\t\t\tconst error = new Error(\"Local document out of sync with document on server.\")\n\t\t\tthis.remoteUpdates.length = 0\n\t\t\tthis.treeSync.hasError = true\n\t\t\tthis.callbacks.error(error)\n\t\t\treturn\n\t\t}\n\n\t\tif (!this.verifyStoreTreeWithCanvasTree(hash)) {\n\t\t\treturn\n\t\t}\n\n\t\tverifyLog.debug(\"tree verify passed\", { version, hash })\n\n\t\t// Always download and fully verify the tree from the server if in a tunnel or running on development.\n\t\tconst tunnel = getTunnelId()\n\t\tconst development = hostInfo.isDevelopment || hostInfo.isLocal\n\n\t\tif (tunnel || development) {\n\t\t\tvoid this.verifyLocalTreeWithServer(url, version)\n\t\t}\n\t}\n\n\t/** Compares the (partial) canvas tree with the store hierarchy. */\n\tprivate verifyStoreTreeWithCanvasTree(hash: number): boolean {\n\t\tconst error = this.treeSync.committer.verifyTree()\n\t\tif (!error) return true\n\n\t\t// Capture the specific divergence and state context so Sentry shows\n\t\t// *why* the local check failed (which node, which mismatch), not just\n\t\t// the generic \"out of sync\" message. Without this, every report\n\t\t// collapses into a single fingerprint and the actual root cause stays\n\t\t// hidden behind the toast string.\n\t\tconst context = {\n\t\t\tversion: this.treeSync.treeVersion,\n\t\t\thash,\n\t\t\tqueuedUpdates: this.remoteUpdates.length,\n\t\t\tactivelyLoadingScope: this.loader?.activelyLoadingScope ?? false,\n\t\t\tisPartialLoading: this.treeSync.committer.isPartialLoading,\n\t\t\tdocumentHidden: this.documentHidden,\n\t\t\thadPendingTreeVerify: this.pendingTreeVerify !== undefined,\n\t\t\ttreeSize: this.treeSync.committer.tree.size(),\n\t\t\tdetail: error.message,\n\t\t}\n\t\tverifyLog.error(\"local tree verify failed\", context)\n\t\tverifyLog.reportError(error, context)\n\n\t\tthis.remoteUpdates.length = 0\n\t\tthis.treeSync.hasError = true\n\t\tthis.callbacks.error(new Error(`Tree out of sync with store. ${error.message}`))\n\t\treturn false\n\t}\n\n\tprocessRemoteUpdates() {\n\t\tlog.debug(\n\t\t\t\"processRemoteUpdates: starting - waitingForTree:\",\n\t\t\tthis.treeSync.waitingForTree,\n\t\t\t\"waitingForInitialUpdates:\",\n\t\t\tthis.treeSync.waitingForInitialUpdates,\n\t\t\t\"hasError:\",\n\t\t\tthis.treeSync.hasError,\n\t\t\t\"isReady:\",\n\t\t\tthis.isReady,\n\t\t\t\"remoteUpdates.length:\",\n\t\t\tthis.remoteUpdates.length,\n\t\t)\n\n\t\tif (this.treeSync.waitingForTree) {\n\t\t\tlog.debug(\"processRemoteUpdates: exiting early - waitingForTree=true\")\n\t\t\treturn\n\t\t}\n\n\t\tif (this.loader?.activelyLoadingScope) {\n\t\t\tlog.debug(\"processRemoteUpdates: exiting early - activelyLoadingScope=true\")\n\t\t\treturn\n\t\t}\n\n\t\tlet handlingRemoteUpdate: CrdtUpdate | CrdtConfirm | undefined\n\n\t\ttry {\n\t\t\tassert(!this.treeSync.committer.tree.hasUncommittedChanges(), \"tree must not have uncommitted changes\")\n\n\t\t\tif (this.shouldCrashFromDebug) {\n\t\t\t\tthis.shouldCrashFromDebug = false\n\t\t\t\tthrow Error(\"RemoteDocument CrashTest\")\n\t\t\t}\n\n\t\t\t// Process all updates from the queue, if any.\n\t\t\twhile (this.remoteUpdates.length > 0) {\n\t\t\t\tconst update = this.remoteUpdates.shift()\n\t\t\t\tif (!update) break\n\n\t\t\t\thandlingRemoteUpdate = update\n\n\t\t\t\t// The server sending a confirm for our own rows.\n\t\t\t\tif (!isCrdtUpdate(update)) {\n\t\t\t\t\tconst confirmedRows = this.treeSync.unconfirmedCrdtUpdates.get(update.seq)?.rows\n\t\t\t\t\tconst versionBefore = this.treeSync.treeVersion\n\t\t\t\t\tthis.treeSync.handleConfirmMessage(update)\n\t\t\t\t\tif (this.localCache && confirmedRows && this.treeSync.treeVersion > versionBefore) {\n\t\t\t\t\t\tthis.localCache.addRows(confirmedRows, this.treeSync.treeVersion)\n\t\t\t\t\t}\n\t\t\t\t\t// On the new Multiplayer Service we may end up waiting for a\n\t\t\t\t\t// fresh snapshot mid-stream; stop draining so the next\n\t\t\t\t\t// init/snapshot runs before we apply more updates.\n\t\t\t\t\tif (this.isMPSSocket && this.treeSync.waitingForTree) break\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tassert(typeof update.next === \"number\", \"Update must have tree version\")\n\n\t\t\t\t// On the new Multiplayer Service, skip stale/duplicate updates\n\t\t\t\t// before mutating the store; the legacy server only sends\n\t\t\t\t// strictly consecutive updates so the check is a no-op there.\n\t\t\t\tif (this.isMPSSocket && this.treeSync.checkRemoteVersion(update.next, \"update\") === \"ignore\") {\n\t\t\t\t\t// The server counts every queued message toward `initialUpdates`,\n\t\t\t\t\t// so an ignored stale message must still advance init progress \u2014\n\t\t\t\t\t// otherwise readiness deadlocks until the next reconnect.\n\t\t\t\t\tthis.treeSync.updatesSeen += 1\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tStore.verifyBatches(update.rows)\n\n\t\t\t\tconst patches = this.writeServerUpdateToTheStore(update)\n\t\t\t\tif (patches) {\n\t\t\t\t\tconst versionBefore = this.treeSync.treeVersion\n\t\t\t\t\tconst hasHierarchyChanges = update.rows.some(row => row.key === \"parentid\")\n\t\t\t\t\tconst nodeChanges = this.treeSync.handleRemotePatches(patches, update.next, hasHierarchyChanges)\n\n\t\t\t\t\tif (nodeChanges) {\n\t\t\t\t\t\tthis.loader?.addNodeChanges(nodeChanges)\n\t\t\t\t\t}\n\n\t\t\t\t\tif (this.localCache && this.treeSync.treeVersion > versionBefore) {\n\t\t\t\t\t\tthis.localCache.addRows(update.rows, this.treeSync.treeVersion)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthis.treeSync.updatesSeen += 1\n\t\t\t}\n\n\t\t\t// Same as above, but for the new loader.\n\t\t\tif (this.loader) {\n\t\t\t\tconst scopes = this.treeSync.committer.tree.root.children\n\t\t\t\tconst hasScopeToShow = scopes.some(\n\t\t\t\t\tpage => canRenderPageTypeBeforeDocumentHasFullyLoaded(page) && page.isValid(),\n\t\t\t\t)\n\t\t\t\tif (!hasScopeToShow) {\n\t\t\t\t\t// If there is no page we can showing the user, we force load the whole document\n\t\t\t\t\t// in one go. This is a brute force solution, but should be rare.\n\t\t\t\t\tlog.info(\"cannot show any page, forcing load of next page\")\n\t\t\t\t\tconst shallowScope = scopes.find(scope => canRenderPageTypeBeforeDocumentHasFullyLoaded(scope))\n\t\t\t\t\tif (!shallowScope) throw Error(\"No scope to load\")\n\t\t\t\t\tconst scope = this.loader.loadScope(shallowScope.id)\n\t\t\t\t\tif (!scope) throw Error(\"Unable to load scope\")\n\t\t\t\t\tthis.treeSync.loadOneScope(scope, false)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.callbacks.updateProcessed(this.treeSync.committer.tree)\n\t\t} catch (errorValue) {\n\t\t\tconst error = reportableError(errorValue)\n\t\t\tthis.remoteUpdates.length = 0\n\t\t\tlog.error(\"Error processing remote updates:\", error)\n\t\t\tlog.debug(\"Last update:\", handlingRemoteUpdate)\n\t\t\tthis.callbacks.errorRecoverable()\n\t\t\tthis.treeSync.error(error.message)\n\t\t\tthrow error\n\t\t}\n\n\t\t// If a verify arrived while the document was hidden, re-evaluate it\n\t\t// now that we are at a quiescent point. While the tab is still hidden\n\t\t// the helper short-circuits \u2014 verification is intentionally disabled\n\t\t// in the background and resumes on the inactive\u2192active transition.\n\t\tthis.maybeEvaluatePendingTreeVerify()\n\t}\n\n\t/** For testing: whether a verify message is queued for re-evaluation after the next drain. */\n\tget hasPendingTreeVerify(): boolean {\n\t\treturn this.pendingTreeVerify !== undefined\n\t}\n\n\t/** For testing purposes. */\n\tasync verifyTreeWithServer(): Promise<number> {\n\t\tconst path = `/projects/${this.projectId}/tree/latest?forceSnapshot=true`\n\t\t// TODO: Remove the conditional once we've fully migrated to FramerMultiplayerService\n\t\tconst documentURL = this.isMPSSocket ? new URL(getMultiplayerServiceURL(path)) : new URL(path, window.location.href)\n\t\tlet crdtResponse: Response | undefined\n\n\t\ttry {\n\t\t\t// Temporarily ignore all tree verify messages in handleTreeVerify.\n\t\t\tthis.ignoreTreeVerifies = true\n\t\t\tcrdtResponse = await fetch(documentURL, await accessTokenRefresher.withAuthorizationHeader({}))\n\t\t} finally {\n\t\t\tthis.ignoreTreeVerifies = false\n\t\t}\n\n\t\tif (!crdtResponse.ok) {\n\t\t\tthrow Error(`unable to fetch document json: ${crdtResponse.status} ${crdtResponse.statusText}`)\n\t\t}\n\n\t\tconst etag = crdtResponse.headers.get(\"etag\") || \"\"\n\t\tconst version = Number.parseInt(etag.match(VERSION_ETAG_REGEX)?.[1] ?? \"0\", 10)\n\t\tif (!Number.isFinite(version) || version <= 0) {\n\t\t\tthrow Error(`unable to parse document tree version from: ${etag}`)\n\t\t}\n\n\t\tconst delta = this.treeSync.treeVersion - version\n\t\t// Ignore a specific version in handleTreeVerify.\n\t\tthis.ignoreTreeVerifyVersion = version\n\n\t\tconst treeData = new Uint8Array(await crdtResponse.arrayBuffer())\n\t\tconst error = this.compareLocalStoreWithServerCrdt(treeData, version)\n\t\tif (error) throw error\n\n\t\tif (!this.verifyStoreTreeWithCanvasTree(0)) {\n\t\t\tthrow Error(\"Tree out of sync with store.\")\n\t\t}\n\n\t\treturn delta\n\t}\n\n\t/** Called before page unload to create any pending updates that need to be sent */\n\tflushUpdates(remote: SendMessage): boolean {\n\t\tconst msg = this.treeSync.createMessage(this.userId)\n\t\tif (!msg) return false\n\t\tremote.sendMessage({ type: \"rows\", value: msg })\n\t\treturn true\n\t}\n\n\t/** Resends previous local updates when the network becomes ready */\n\tresendUnconfirmedUpdates(remote: SendMessage): void {\n\t\tconst messages = this.treeSync.createUnconfirmedMessages()\n\t\tfor (const msg of messages) {\n\t\t\tremote.sendMessage({ type: \"rows\", value: msg })\n\t\t}\n\t}\n\n\tcancelAndClearLoader() {\n\t\tthis.loader?.scheduler.cancel()\n\t\tthis.loader = undefined\n\t}\n\n\tmaybeSend(remote: SendMessage): \"postpone\" | \"didSend\" | \"nothingToSend\" {\n\t\tif (!this.treeSync.isReady) return \"nothingToSend\"\n\t\tif (!this.treeSync.hasMessageForRemote()) return \"nothingToSend\"\n\n\t\tconst inflight = this.treeSync.unconfirmedCrdtUpdates.size\n\t\tif (inflight >= MAX_INFLIGHT_MESSAGES) return \"postpone\"\n\n\t\tconst msg = this.treeSync.createMessage(this.userId)\n\t\tif (!msg) return \"nothingToSend\"\n\t\tremote.sendMessage({ type: \"rows\", value: msg })\n\t\treturn \"didSend\"\n\t}\n\n\tprivate writeServerUpdateToTheStore(update: CrdtUpdate) {\n\t\tconst patches = mergeBranchedRowsAndGetPatches(this.treeSync.committer.branches, update.rows)\n\n\t\tif (this.recorder) {\n\t\t\tthis.recorder({\n\t\t\t\tsource: \"remote\",\n\t\t\t\trows: update.rows,\n\t\t\t})\n\t\t}\n\n\t\treturn patches\n\t}\n\n\tcreateLoader(url: string, treeVersion: number, settings: DocumentLoaderSettings) {\n\t\tthis.loader?.scheduler.cancel()\n\t\tconst loader = new CrdtDocumentLoader(\n\t\t\tthis.store,\n\t\t\tthis.projectId,\n\t\t\tthis.componentLoader,\n\t\t\ttreeVersion,\n\t\t\turl,\n\t\t\tsettings,\n\t\t\tthis.treeSync.committer.branches,\n\t\t)\n\n\t\tthis.loader = loader\n\n\t\tloader.on(\"loadedFirstData\", () => {\n\t\t\tthis.treeSync.onLoadedFirstData()\n\t\t})\n\n\t\treturn this.loader\n\t}\n\n\tfinishLoading() {\n\t\tthis.loader = undefined\n\t}\n\n\tprivate compareLocalStoreWithServerCrdt(treeData: Uint8Array, version: number): Error | undefined {\n\t\tconst { remoteStore, compareResult } = this.createRemoteStore(treeData)\n\n\t\tverifyLog.debug(\n\t\t\t\"local:\",\n\t\t\tthis.store.getHierarchy().computeTreeHash(),\n\t\t\tthis.store.getHierarchy().sizeWithoutReplicas(),\n\t\t\t\"remote:\",\n\t\t\tremoteStore.getHierarchy().computeTreeHash(),\n\t\t\tremoteStore.getHierarchy().sizeWithoutReplicas(),\n\t\t\t\"version:\",\n\t\t\tversion,\n\t\t)\n\n\t\tlet error: Error | undefined\n\t\tconst differences: string[] = []\n\t\tconst different = findStoreTreeDifferences(this.store, remoteStore, differences)\n\t\tif (different) {\n\t\t\tverifyLog.warn(\"stores are different\\n\" + differences.join(\"\\n\"))\n\t\t\terror = Error(\"Local document different from server document.\")\n\t\t\tverifyLog.reportError(error, {\n\t\t\t\tdifferences,\n\t\t\t\tlocalOnlyRows: compareResult.extra.length,\n\t\t\t\tremoteOnlyRows: compareResult.missing.length,\n\t\t\t})\n\t\t} else if (compareResult.extra.length > 0 || compareResult.missing.length > 0) {\n\t\t\tverifyLog.debug(\"stores are same after reconciling rows\", {\n\t\t\t\tlocalOnlyRows: compareResult.extra.length,\n\t\t\t\tremoteOnlyRows: compareResult.missing.length,\n\t\t\t})\n\t\t} else {\n\t\t\tverifyLog.debug(\"stores are same\")\n\t\t}\n\n\t\treturn error\n\t}\n\n\tprivate createRemoteStore(treeData: Uint8Array) {\n\t\tconst remoteMainStore = new Store({ client: 0, user: \"\" })\n\t\tremoteMainStore.fromBuffer(treeData)\n\n\t\tconst localBranches = this.treeSync.committer.branches\n\t\tconst remoteBranches = new CrdtBranchStoreRegistry(remoteMainStore)\n\t\tconst activeBranchId = localBranches.activeBranchId\n\t\tlet remoteStore = remoteMainStore\n\t\tconst compareResult: CompareResult = { extra: [], missing: [] }\n\n\t\tfor (const branchId of localBranches.getBranchPath(activeBranchId)) {\n\t\t\tconst localStore = localBranches.getStore(branchId)\n\t\t\tremoteStore = remoteBranches.getStore(branchId)\n\t\t\tconst branchCompareResult = localStore.compare(remoteStore.manifest)\n\t\t\tcompareResult.extra.push(...branchCompareResult.extra)\n\t\t\tcompareResult.missing.push(...branchCompareResult.missing)\n\t\t\tthis.appendLocalRowsMissingFromServer(localStore, remoteStore, branchCompareResult.extra)\n\t\t}\n\n\t\treturn { remoteStore, compareResult }\n\t}\n\n\tprivate appendLocalRowsMissingFromServer(\n\t\tlocalStore: Store,\n\t\tremoteStore: Store,\n\t\tlocalOnlyTimestamps: CompareResult[\"extra\"],\n\t): void {\n\t\tif (localOnlyTimestamps.length === 0) return\n\t\tconst rowsByTimestamp = new Map<string, SerializableRow[]>()\n\t\tfor (const row of localStore.getSerializableRows()) {\n\t\t\tconst timestampKey = `${row.client}:${row.seq}`\n\t\t\tconst rows = rowsByTimestamp.get(timestampKey)\n\t\t\tif (rows) {\n\t\t\t\trows.push(row)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\trowsByTimestamp.set(timestampKey, [row])\n\t\t}\n\n\t\tconst rowsToAppend: SerializableRow[] = []\n\t\tfor (const timestamp of localOnlyTimestamps) {\n\t\t\tconst rows = rowsByTimestamp.get(`${timestamp.client}:${timestamp.seq}`)\n\t\t\tassert(rows, \"unable to find local rows for timestamp\", timestamp.client, timestamp.seq)\n\t\t\trowsToAppend.push(...rows)\n\t\t}\n\t\tremoteStore.addSerializableRows(rowsToAppend)\n\t}\n\n\tprivate async verifyLocalTreeWithServer(url: string, version: number): Promise<void> {\n\t\ttry {\n\t\t\t// TODO: Drop the conditional once we've fully migrated to FramerMultiplayerService\n\t\t\tconst pathname = url.replace(CRDT_VERSION_URL_REGEX, version + \".crdt\")\n\t\t\tconst documentURL = this.isMPSSocket ? new URL(pathname) : new URL(pathname, window.location.href)\n\t\t\tverifyLog.debug(\"verifying local tree with server crdt:\", documentURL)\n\t\t\tlet crdtResponse = await fetch(documentURL, await accessTokenRefresher.withAuthorizationHeader({}))\n\t\t\tif (!crdtResponse.ok) {\n\t\t\t\tif (crdtResponse.status === 404) {\n\t\t\t\t\tconst latestPath = `/projects/${this.projectId}/tree/latest`\n\t\t\t\t\t// TODO: Remove the conditional once we've fully migrated to FramerMultiplayerService\n\t\t\t\t\tconst latestURL = this.isMPSSocket\n\t\t\t\t\t\t? new URL(getMultiplayerServiceURL(latestPath))\n\t\t\t\t\t\t: new URL(latestPath, window.location.href)\n\t\t\t\t\tverifyLog.debug(\"404, retrying crdt with:\", latestURL)\n\t\t\t\t\tcrdtResponse = await fetch(latestURL, await accessTokenRefresher.withAuthorizationHeader({}))\n\t\t\t\t\tif (!crdtResponse.ok) {\n\t\t\t\t\t\tthrow Error(`unable to fetch latest crdt document: ${crdtResponse.status} ${crdtResponse.statusText}`)\n\t\t\t\t\t} else if (!crdtResponse.headers.get(\"etag\")?.includes(version.toString())) {\n\t\t\t\t\t\tthrow Error(`lastest tree version does not match: ${crdtResponse.headers.get(\"etag\")} ${version}`)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthrow Error(`unable to fetch crdt document: ${crdtResponse.status} ${crdtResponse.statusText}`)\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst loadedData = new Uint8Array(await crdtResponse.arrayBuffer())\n\t\t\tverifyLog.debug(\"using crdt from server\")\n\t\t\tconst error = this.compareLocalStoreWithServerCrdt(loadedData, version)\n\t\t\tif (error) throw error\n\t\t} catch (e) {\n\t\t\tverifyLog.error(\"Error:\", e)\n\t\t\tthis.callbacks.error(reportableError(e))\n\t\t}\n\t}\n\n\t/** For testing/debugging only; protocol state lives in `CrdtTreeSync`. */\n\tgetRowsToSend() {\n\t\treturn this.treeSync.committer.takePendingRows(this.userId)\n\t}\n\n\tloadedAllScopes() {\n\t\tthis.treeSync.loadedAllScopes()\n\t\t// Any verify that arrived during the initial shallow-scope load was\n\t\t// buffered; now that the tree mirrors the store we can finally honor\n\t\t// it. If the deferred verify is still ambiguous (e.g. queue not yet\n\t\t// drained) the helper short-circuits and we'll retry from\n\t\t// `processRemoteUpdates`.\n\t\tthis.maybeEvaluatePendingTreeVerify()\n\t}\n\n\tloadOneScope(scope: CanvasNode, keepChanges: boolean): CanvasTree {\n\t\tconst result = this.treeSync.loadOneScope(scope, keepChanges)\n\t\t// Each scope integration moves the tree closer to the store. Retry a\n\t\t// pending verify so we don't sit on it for the full load window when\n\t\t// the divergence the verify was guarding against has already healed.\n\t\tthis.maybeEvaluatePendingTreeVerify()\n\t\treturn result\n\t}\n\n\thasUnconfirmedChanges(): boolean {\n\t\treturn this.treeSync.hasUnconfirmedChanges()\n\t}\n\n\tresetTreesForRecovery(activeScopeId: string): CanvasTree {\n\t\tif (!this.loader) {\n\t\t\t// If there is no loader, we rebuild the tree from the store in one big operation. But\n\t\t\t// with the data only tree, there is always a loader.\n\t\t\tconst errors: string[] = []\n\t\t\tconst rootData: unknown = this.store.getObject(this.treeSync.committer.tree.root.id)\n\t\t\tconst treeData = { version: CanvasTreeVersion, root: rootData }\n\t\t\treturn safeLoadDocument(treeData, this.componentLoader, errors)\n\t\t}\n\n\t\treturn this.loader.resetForCrashRecovery(activeScopeId)\n\t}\n\n\terror(message: string, cause?: unknown) {\n\t\treturn this.treeSync.error(message, cause)\n\t}\n}\n", "import { assert, assertNever, getLogger } from \"@framerjs/shared\"\nimport { isCrdt } from \"document/crdt/isCrdt.ts\"\nimport { environment } from \"environment/index.ts\"\nimport { useCallback, useEffect, useMemo, useRef } from \"react\"\nimport { SocketStats } from \"./SocketStats.ts\"\nimport {\n\ttype ConnectHandler,\n\ttype DisconnectHandler,\n\tDisconnectReason,\n\ttype IncomingMessage,\n\ttype MessageHandler,\n\ttype MessageType,\n\ttype Socket,\n} from \"./types.ts\"\n\nconst log = getLogger(\"remote:socket\")\nconst INITIAL_RECONNECT_DELAY_MS = 25\nconst RECONNECT_TO_NEW_SERVER_DELAY_MS = 50\nconst MAX_RECONNECT_DELAY_MS = 5000\nconst RECONNECT_JITTER = 0.1\nexport const RECONNECT_ATTEMPT_RESET_DELAY_MS = 5000\n\ninterface RecoverableReconnectSchedule {\n\tdelay: number\n\tnextReconnectAttempt: number\n}\n\n/**\n * See PROTOCOL_VERSION in protocol-version.mjs.\n */\nexport const PROTOCOL_VERSION = process.env.PROTOCOL_VERSION as unknown as number\n\nexport function isRecoverableDisconnectReason(reason: DisconnectReason): boolean {\n\tswitch (reason) {\n\t\tcase DisconnectReason.AccessDenied:\n\t\tcase DisconnectReason.ClientNeedsUpdate:\n\t\tcase DisconnectReason.ClientTooNew:\n\t\tcase DisconnectReason.DocumentNotFound:\n\t\tcase DisconnectReason.UnsupportedSchema:\n\t\tcase DisconnectReason.Maintenance:\n\t\tcase DisconnectReason.UnknownPermanentError:\n\t\tcase DisconnectReason.ClientSidePermanentError:\n\t\tcase DisconnectReason.CrdtMigrationFailed:\n\t\tcase DisconnectReason.TreeModeMismatch:\n\t\t\treturn false\n\t\tcase DisconnectReason.ReconnectToNewServer:\n\t\tcase DisconnectReason.UnknownRecoverableError:\n\t\tcase DisconnectReason.ClientSideRecoverableError:\n\t\t\treturn true\n\t\tdefault:\n\t\t\treturn assertNever(reason)\n\t}\n}\n\nexport function getRecoverableReconnectDelay(reconnectAttempt: number): number {\n\treturn Math.min(INITIAL_RECONNECT_DELAY_MS * 2 ** reconnectAttempt, MAX_RECONNECT_DELAY_MS)\n}\n\nfunction getJitteredReconnectDelay(delay: number, random: () => number): number {\n\tconst jitterMultiplier = 1 - RECONNECT_JITTER + random() * RECONNECT_JITTER * 2\n\treturn Math.min(Math.round(delay * jitterMultiplier), MAX_RECONNECT_DELAY_MS)\n}\n\nexport function getRecoverableReconnectSchedule(\n\treason: DisconnectReason,\n\treconnectAttempt: number,\n\trandom = Math.random,\n): RecoverableReconnectSchedule {\n\tif (reason === DisconnectReason.ReconnectToNewServer) {\n\t\treturn {\n\t\t\tdelay: getJitteredReconnectDelay(RECONNECT_TO_NEW_SERVER_DELAY_MS, random),\n\t\t\tnextReconnectAttempt: reconnectAttempt,\n\t\t}\n\t}\n\treturn {\n\t\tdelay: getJitteredReconnectDelay(getRecoverableReconnectDelay(reconnectAttempt), random),\n\t\tnextReconnectAttempt: reconnectAttempt + 1,\n\t}\n}\n\nexport function useSocket({\n\turl,\n\tdocumentConnection,\n\ttunnel = undefined,\n\tgetSubprotocols,\n}: {\n\turl: string\n\tdocumentConnection: {\n\t\ttreeSchema: number\n\t\ttreeVersion: number\n\t\tsetSocketStats: (stats: SocketStats) => void\n\t}\n\ttunnel?: string | undefined\n\t/**\n\t * Optional async callback invoked before every (re)connect to supply the\n\t * WebSocket subprotocols.\n\t */\n\tgetSubprotocols?: () => Promise<string[] | undefined>\n}): Socket {\n\tconst ref = useRef<{ ws: WebSocket; stats: SocketStats; clientClosed: boolean } | null>(null)\n\tconst isInitialConnectionRef = useRef(true)\n\tconst handlersRef = useRef({\n\t\tonConnect: new Set<ConnectHandler>(),\n\t\tonDisconnect: new Set<DisconnectHandler>(),\n\t\tonMessage: new Set<MessageHandler>(),\n\t})\n\n\tconst webSocketURL = useRef(url)\n\tconst shouldReconnectRef = useRef(true)\n\tconst connectTimerRef = useRef<number | undefined>(undefined)\n\tconst reconnectAttemptRef = useRef(0)\n\tconst getSubprotocolsRef = useRef(getSubprotocols)\n\tgetSubprotocolsRef.current = getSubprotocols\n\tfunction cancelReconnect() {\n\t\tif (connectTimerRef.current === undefined) return\n\n\t\twindow.clearTimeout(connectTimerRef.current)\n\t\tconnectTimerRef.current = undefined\n\t}\n\n\tconst disconnect = useCallback(() => {\n\t\tshouldReconnectRef.current = false\n\t\tconst current = ref.current\n\t\tif (!current) return\n\n\t\tif (current.ws.readyState < WebSocket.CLOSING) {\n\t\t\tcurrent.clientClosed = true\n\t\t\tcurrent.ws.close()\n\t\t}\n\t}, [])\n\n\t// This callback is safe to call multiple times, it will make sure no work is done if not necessary.\n\tconst connect = useCallback(async () => {\n\t\tcancelReconnect()\n\t\tif (!shouldReconnectRef.current || ref.current) return\n\n\t\tfunction scheduleReconnect(delay: number) {\n\t\t\tif (connectTimerRef.current !== undefined) return\n\n\t\t\tconnectTimerRef.current = window.setTimeout(() => {\n\t\t\t\tconnectTimerRef.current = undefined\n\t\t\t\tif (!navigator.onLine) return\n\t\t\t\tif (document.hidden && !environment.isApiPlugin) return\n\t\t\t\tvoid connect()\n\t\t\t}, delay)\n\t\t}\n\n\t\tconst urlWithVersions = new URL(webSocketURL.current)\n\t\turlWithVersions.searchParams.set(\"v\", PROTOCOL_VERSION.toString())\n\t\turlWithVersions.searchParams.set(\"tunnel\", tunnel || \"\")\n\t\tif (isCrdt()) {\n\t\t\turlWithVersions.searchParams.set(\"mode\", \"crdt\")\n\t\t}\n\t\t// If the treeSchema is not realistic, don't try to make a\n\t\t// connection. This happens when Vekter isn't initialized yet.\n\t\tif (documentConnection.treeSchema <= 0) return\n\t\turlWithVersions.searchParams.set(\"treeSchema\", documentConnection.treeSchema.toString())\n\t\turlWithVersions.searchParams.set(\"treeVersion\", documentConnection.treeVersion.toString())\n\n\t\tconst subprotocols = await getSubprotocolsRef.current?.().catch(e => {\n\t\t\tlog.warn(\"Error resolving websocket subprotocols:\", e)\n\t\t\treturn undefined\n\t\t})\n\t\t// Another connect() may have started while awaiting the subprotocols.\n\t\tif (!shouldReconnectRef.current || ref.current) return\n\n\t\tlog.debug(\"connecting to\", urlWithVersions.href)\n\t\tconst ws = new WebSocket(urlWithVersions.href, subprotocols)\n\t\tconst stats = new SocketStats()\n\t\tconst current = { ws, stats, clientClosed: false }\n\t\tdocumentConnection.setSocketStats(stats)\n\t\tlet pingTimer = 0\n\t\tlet reconnectAttemptResetTimer = 0\n\n\t\tws.addEventListener(\"open\", () => {\n\t\t\tlog.debug(\"open\")\n\t\t\treconnectAttemptResetTimer = window.setTimeout(() => {\n\t\t\t\treconnectAttemptRef.current = 0\n\t\t\t\treconnectAttemptResetTimer = 0\n\t\t\t}, RECONNECT_ATTEMPT_RESET_DELAY_MS)\n\n\t\t\t// ensure something measures the rtt regularly\n\t\t\tpingTimer = window.setInterval(() => {\n\t\t\t\tif (performance.now() - stats.lastSend() < 1000) return\n\t\t\t\tif (stats.pendingCount(\"ping\") > 1) return\n\t\t\t\tif (ws.readyState !== WebSocket.OPEN) return\n\n\t\t\t\tconst msg = \"ping {}\"\n\t\t\t\tws.send(msg)\n\t\t\t\tstats.sent(\"ping\", msg)\n\t\t\t}, 1000)\n\n\t\t\tfor (const handler of handlersRef.current.onConnect) {\n\t\t\t\ttry {\n\t\t\t\t\thandler(isInitialConnectionRef.current)\n\t\t\t\t} catch (e) {\n\t\t\t\t\tlog.warn(\"Error in onConnect handler:\", e)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tisInitialConnectionRef.current = false\n\t\t})\n\n\t\tws.addEventListener(\"close\", event => {\n\t\t\tconst reason = getSocketDisconnectReason(event)\n\t\t\tlog.debug(\"close:\", reason, \"clientClosed:\", current.clientClosed, event)\n\n\t\t\tif (pingTimer !== 0) {\n\t\t\t\tclearInterval(pingTimer)\n\t\t\t\tpingTimer = 0\n\t\t\t}\n\t\t\tif (reconnectAttemptResetTimer !== 0) {\n\t\t\t\tclearTimeout(reconnectAttemptResetTimer)\n\t\t\t\treconnectAttemptResetTimer = 0\n\t\t\t}\n\n\t\t\t// Only change global state if this websocket is the current socket.\n\t\t\tif (ref.current !== current) return\n\n\t\t\tif (!isRecoverableDisconnectReason(reason)) {\n\t\t\t\tshouldReconnectRef.current = false\n\t\t\t}\n\n\t\t\tfor (const handler of handlersRef.current.onDisconnect) {\n\t\t\t\ttry {\n\t\t\t\t\thandler(reason)\n\t\t\t\t} catch (e) {\n\t\t\t\t\tlog.warn(\"Error in onDisconnect handler:\", e)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tref.current = null\n\n\t\t\tif (shouldReconnectRef.current) {\n\t\t\t\tconst { delay, nextReconnectAttempt } = getRecoverableReconnectSchedule(reason, reconnectAttemptRef.current)\n\t\t\t\treconnectAttemptRef.current = nextReconnectAttempt\n\t\t\t\tscheduleReconnect(delay)\n\t\t\t}\n\t\t})\n\n\t\tws.addEventListener(\"message\", event => {\n\t\t\ttry {\n\t\t\t\tconst data = event.data as string\n\t\t\t\tstats.received(data)\n\n\t\t\t\tconst message = parseSocketMessage(data)\n\n\t\t\t\tif (message.type === \"ack\") {\n\t\t\t\t\tstats.acked()\n\t\t\t\t\treturn\n\t\t\t\t} else if (message.type === \"redirect\") {\n\t\t\t\t\t// Server told us to reconnect to a new URL\n\t\t\t\t\twebSocketURL.current = message.value.url\n\t\t\t\t}\n\t\t\t\tfor (const handler of handlersRef.current.onMessage) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\thandler(message)\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tlog.warn(\"Error in onMessage handler:\", e)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tlog.warn(\"Error receiving:\", e)\n\t\t\t}\n\t\t})\n\t\tref.current = current\n\t}, [documentConnection])\n\n\tuseEffect(() => {\n\t\tvoid connect()\n\t}, [connect])\n\n\tconst handlePageVisibilityAndNetworkStatusChange = useCallback(\n\t\t({ online, visible }: { online: boolean; visible: boolean }) => {\n\t\t\tconst shouldConnect = online && (visible || environment.isApiPlugin)\n\n\t\t\tif (shouldConnect) {\n\t\t\t\tvoid connect()\n\t\t\t} else if (!environment.isApiPlugin) {\n\t\t\t\tcancelReconnect()\n\t\t\t}\n\t\t},\n\t\t[connect],\n\t)\n\n\tusePageVisibilityAndNetworkStatusChange(handlePageVisibilityAndNetworkStatusChange)\n\n\tconst socket: Socket = useMemo(() => {\n\t\treturn {\n\t\t\tgetSocketStats(): SocketStats | undefined {\n\t\t\t\treturn ref.current?.stats\n\t\t\t},\n\t\t\tconnect() {\n\t\t\t\tshouldReconnectRef.current = true\n\t\t\t\tvoid connect()\n\t\t\t},\n\t\t\tdisconnect() {\n\t\t\t\tdisconnect()\n\t\t\t},\n\t\t\tonConnect(handler) {\n\t\t\t\thandlersRef.current.onConnect.add(handler)\n\t\t\t\treturn () => {\n\t\t\t\t\thandlersRef.current.onConnect.delete(handler)\n\t\t\t\t}\n\t\t\t},\n\t\t\tonDisconnect(handler) {\n\t\t\t\thandlersRef.current.onDisconnect.add(handler)\n\t\t\t\treturn () => {\n\t\t\t\t\thandlersRef.current.onDisconnect.delete(handler)\n\t\t\t\t}\n\t\t\t},\n\t\t\tonMessage(handler) {\n\t\t\t\thandlersRef.current.onMessage.add(handler)\n\t\t\t\treturn () => {\n\t\t\t\t\thandlersRef.current.onMessage.delete(handler)\n\t\t\t\t}\n\t\t\t},\n\t\t\tsend(message) {\n\t\t\t\tif (!ref.current || ref.current.ws.readyState !== 1) {\n\t\t\t\t\tif (message.type !== \"state\") {\n\t\t\t\t\t\tlog.warn(\"Dropping\", message.type, \"message.\")\n\t\t\t\t\t}\n\t\t\t\t\t// TODO: Queue the message.\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst msg = `${message.type} ${JSON.stringify(message.value)}`\n\t\t\t\t\tref.current.ws.send(msg)\n\t\t\t\t\tref.current.stats.sent(message.type, msg)\n\t\t\t\t} catch (e) {\n\t\t\t\t\t// TODO: Queue the message if it failed due to a transient error.\n\t\t\t\t\tlog.warn(\"Error sending\", message.type, \"message:\", e)\n\t\t\t\t}\n\t\t\t},\n\t\t\tforceReconnect() {\n\t\t\t\tif (ref.current) {\n\t\t\t\t\tref.current.stats.reset()\n\t\t\t\t\tref.current.clientClosed = true\n\t\t\t\t\tref.current.ws.close()\n\t\t\t\t\tref.current = null\n\t\t\t\t}\n\t\t\t\tshouldReconnectRef.current = true\n\t\t\t\tvoid connect()\n\t\t\t},\n\t\t}\n\t}, [connect, disconnect])\n\treturn socket\n}\n\nfunction usePageVisibilityAndNetworkStatusChange(onChange: (status: { online: boolean; visible: boolean }) => void) {\n\tuseEffect(() => {\n\t\tdocument.addEventListener(\"visibilitychange\", handleChange)\n\t\twindow.addEventListener(\"online\", handleChange)\n\t\twindow.addEventListener(\"offline\", handleChange)\n\t\tfunction handleChange() {\n\t\t\tonChange({ online: navigator.onLine, visible: !document.hidden })\n\t\t}\n\n\t\treturn () => {\n\t\t\tdocument.removeEventListener(\"visibilitychange\", handleChange)\n\t\t\twindow.removeEventListener(\"online\", handleChange)\n\t\t\twindow.removeEventListener(\"offline\", handleChange)\n\t\t}\n\t}, [onChange])\n}\n\nexport function getSocketDisconnectReason(event: CloseEvent): DisconnectReason {\n\t// The reasons returned by `FramerWeb` are defined in `project_room.go`.\n\tswitch (event.reason) {\n\t\tcase \"ERR_RECONNECT_TO_NEW_SERVER\":\n\t\t\treturn DisconnectReason.ReconnectToNewServer\n\t\tcase \"ERR_ACCESS_DENIED\":\n\t\t\treturn DisconnectReason.AccessDenied\n\t\tcase \"ERR_CLIENT_NEEDS_UPDATE\":\n\t\t\treturn DisconnectReason.ClientNeedsUpdate\n\t\tcase \"ERR_DOCUMENT_NOT_FOUND\":\n\t\t\treturn DisconnectReason.DocumentNotFound\n\t\tcase \"ERR_UNSUPPORTED_SCHEMA_VERSION\":\n\t\t\treturn DisconnectReason.UnsupportedSchema\n\t\tcase \"ERR_MAINTENANCE\":\n\t\t\treturn DisconnectReason.Maintenance\n\t\tcase \"ERR_INVALID_OPERATION\":\n\t\t\treturn DisconnectReason.ClientSidePermanentError\n\t\tcase \"ERR_CRDT_MIGRATION_FAILED\":\n\t\t\treturn DisconnectReason.CrdtMigrationFailed\n\t\tcase \"ERR_UNKNOWN\":\n\t\t\treturn DisconnectReason.UnknownPermanentError\n\t}\n\n\tif (event.code === 1011) {\n\t\t// In case of an internal server error, assume the client needs an update.\n\t\treturn DisconnectReason.ClientNeedsUpdate\n\t}\n\n\t// Otherwise, assume the error is recoverable.\n\treturn DisconnectReason.UnknownRecoverableError\n}\n\nexport function parseSocketMessage(data: string): IncomingMessage {\n\tconst space1 = data.indexOf(\" \")\n\tconst space2 = data.indexOf(\" \", space1 + 1)\n\n\tassert(space1 >= 0 && space2 >= 0, \"Invalid data\")\n\n\tconst id = data.substring(0, space1)\n\tconst type = data.substring(space1 + 1, space2) as MessageType\n\tconst json = data.substring(space2 + 1)\n\tconst value = JSON.parse(json)\n\n\treturn { id, type, value }\n}\n", "// By keeping these powers of two, it's easy to do bit modulo below.\nconst maxPending = 1 << 7\nconst rttCount = 1 << 5\n\n// A global counter to give every socket stats instance a unique id.\nlet socketStatsIdCounter = 0\n\nexport class SocketStats {\n\tprivate id = ++socketStatsIdCounter\n\tprivate currentRtt = NaN\n\tprivate rtts: number[] = []\n\tprivate rttIndex = 0\n\n\tprivate pending = Array.from(Array(maxPending), () => ({ type: \"\", time: 0 }))\n\t// The index of the first (oldest) item in the pending list.\n\tprivate start = 0\n\t// The index of where the pending list ends (the index we will write to next).\n\tprivate end = 0\n\n\tprivate overflow = 0\n\tprivate lastSendTime = 0\n\n\tprivate bytesSent = 0\n\tprivate bytesReceived = 0\n\n\tread(): [bytesSent: number, bytesReceived: number, rtt: number, id: number] {\n\t\tconst { bytesSent, bytesReceived, id } = this\n\t\tthis.bytesSent = 0\n\t\tthis.bytesReceived = 0\n\t\treturn [bytesSent, bytesReceived, this.rtt(), id]\n\t}\n\n\tprivate computeRtt(): void {\n\t\tconst count = this.rtts.length\n\t\tif (count === 0) {\n\t\t\tthis.currentRtt = NaN\n\t\t\treturn\n\t\t}\n\n\t\tlet total = 0\n\t\tfor (const rtt of this.rtts) {\n\t\t\ttotal += rtt\n\t\t}\n\t\tthis.currentRtt = total / count\n\t}\n\n\tlastSend() {\n\t\treturn this.lastSendTime\n\t}\n\n\trtt(): number {\n\t\tif (Number.isNaN(this.currentRtt)) {\n\t\t\tthis.computeRtt()\n\t\t}\n\t\treturn Math.max(this.currentRtt || 0, this.pendingRtt())\n\t}\n\n\tpendingRtt(): number {\n\t\tif (this.start === this.end) return 0\n\t\t// biome-ignore lint/style/noNonNullAssertion: this.start is always within bounds (0 to maxPending-1)\n\t\tconst item = this.pending[this.start]!\n\t\treturn performance.now() - item.time\n\t}\n\n\tpendingCount(type?: string) {\n\t\tif (!type) return this.start > this.end ? maxPending - this.start + this.end : this.end - this.start\n\t\tlet count = 0\n\t\tfor (let i = this.start; i !== this.end; i = (i + 1) & (maxPending - 1)) {\n\t\t\t// biome-ignore lint/style/noNonNullAssertion: i is always within bounds (0 to maxPending-1)\n\t\t\tconst item = this.pending[i]!\n\t\t\tif (item.type === type) count++\n\t\t}\n\t\treturn count\n\t}\n\n\tsent(type: string, msg: string) {\n\t\t// This ignores extra bytes needed to send multi-byte characters, but is worth the speed\n\t\t// improvement by not exactly getting the byteLength.\n\t\tthis.bytesSent += msg.length\n\n\t\tif (this.end === (this.start === 0 ? maxPending - 1 : this.start - 1)) {\n\t\t\t// Circular buffer is full, so we discard the oldest item.\n\t\t\tthis.start = (this.start + 1) & (maxPending - 1)\n\t\t\t// Track overflow since we need to ignore incoming acks.\n\t\t\tthis.overflow++\n\t\t}\n\t\t// biome-ignore lint/style/noNonNullAssertion: this.end is always within bounds (0 to maxPending-1)\n\t\tconst item = this.pending[this.end]!\n\t\titem.type = type\n\t\titem.time = performance.now()\n\t\tthis.end = (this.end + 1) & (maxPending - 1)\n\t\tthis.lastSendTime = item.time\n\t}\n\n\treceived(msg: string) {\n\t\tthis.bytesReceived += msg.length\n\t}\n\n\treset(): void {\n\t\tthis.start = 0\n\t\tthis.end = 0\n\t\tthis.overflow = 0\n\t\tthis.rtts = []\n\t\tthis.rttIndex = 0\n\t\tthis.currentRtt = NaN\n\t}\n\n\tacked() {\n\t\tif (this.start === this.end) {\n\t\t\t// biome-ignore lint/suspicious/noConsole: <explanation>\n\t\t\tconsole.warn(\"Called SocketStats.acked() with empty buffer\")\n\t\t\treturn\n\t\t}\n\t\tif (this.overflow > 0) {\n\t\t\t// Due to overflow, this message does not have an entry in the buffer.\n\t\t\tthis.overflow--\n\t\t\treturn\n\t\t}\n\t\t// biome-ignore lint/style/noNonNullAssertion: this.start is always within bounds (0 to maxPending-1)\n\t\tconst item = this.pending[this.start]!\n\t\tconst rtt = performance.now() - item.time\n\t\tif (this.rtts.length < rttCount) {\n\t\t\tthis.rtts.push(rtt)\n\t\t} else {\n\t\t\tthis.rtts[this.rttIndex] = rtt\n\t\t\tthis.rttIndex = (this.rttIndex + 1) & (rttCount - 1)\n\t\t}\n\t\tthis.start = (this.start + 1) & (maxPending - 1)\n\t\tthis.currentRtt = NaN\n\t}\n}\n", "import { toast } from \"./toaster.ts\"\n\ninterface TreeModeMismatchToastAction {\n\ttitle: string\n\tonClick: () => void\n}\n\nexport function showTreeModeMismatchToast(action?: TreeModeMismatchToastAction) {\n\ttoast({\n\t\ttype: \"add\",\n\t\tvariant: \"warning\",\n\t\tprimaryText: \"Project format updated.\",\n\t\tsecondaryText: \"Reload to continue.\",\n\t\tkey: \"tree-mode-mismatch\",\n\t\tduration: Number.POSITIVE_INFINITY,\n\t\ticon: \"warning\",\n\t\tshowCloseButton: \"never\",\n\t\taction: action ?? {\n\t\t\ttitle: \"Reload\",\n\t\t\tonClick: () => window.top!.location.reload(),\n\t\t},\n\t})\n}\n", "import type { LocalCache } from \"document/crdt/sync/LocalCache.ts\"\nimport type {\n\tDisconnectReason,\n\tLockMessageData,\n\tMessage,\n\tSocket,\n\tTreeMessageData,\n\tTreeVerifyData,\n\tUserStateData,\n} from \"socket/types.ts\"\nimport type { LockState } from \"./RemoteLocks.ts\"\nimport type { VekterEngine } from \"./VekterEngine.ts\"\nimport type { DocumentLoader, DocumentLoaderSettings } from \"./loader/DocumentLoader.ts\"\nimport type { NodeChange } from \"./models/CanvasTree/TreeDiff.ts\"\nimport type { CanvasNode, CanvasTree } from \"./models/CanvasTree/index.ts\"\nimport type { CrdtUpdate } from \"./models/CanvasTree/treeSyncUtils.ts\"\n\nexport type AppEnvironment = \"vekter\" | \"on_page\" | \"api\"\n\n/** Vekter -> Server */\nexport interface ClientTreeUpdate {\n\tappEnvironment?: AppEnvironment\n\tsession: number\n\tseq: number\n\tchanges: NodeChange[]\n\tcount?: number\n\treasons?: string\n}\n\n/** Server -> Vekter, only to the original sender. */\ninterface ServerTreeUpdateReply {\n\tsession: number\n\tseq: number\n\tnext: number\n}\n\n/** Server -> Vekter, to all clients except the original sender. */\nexport interface ServerTreeUpdateOther {\n\tnext: number\n\tchanges: NodeChange[]\n}\n\nexport type ServerTreeUpdate = ServerTreeUpdateReply | ServerTreeUpdateOther\n\nexport function isClientTreeUpdate(o: unknown): o is ClientTreeUpdate {\n\treturn typeof o === \"object\" && o !== null && \"session\" in o && \"changes\" in o\n}\n\nexport function isServerTreeUpdate(o: unknown): o is ServerTreeUpdate {\n\treturn typeof o === \"object\" && o !== null && \"next\" in o\n}\n\nexport function isServerTreeUpdateReply(o: unknown): o is ServerTreeUpdateReply {\n\treturn isServerTreeUpdate(o) && \"session\" in o\n}\n\nexport function isServerTreeUpdateOther(o: unknown): o is ServerTreeUpdateOther {\n\treturn isServerTreeUpdate(o) && \"changes\" in o && Array.isArray(o.changes)\n}\n\ninterface SnapshotNode {\n\tid: string\n\t__class: string\n\tchildren?: SnapshotNode[]\n\t[key: string]: unknown\n}\n\nexport interface SnapshotTree {\n\tversion: number\n\troot: SnapshotNode\n}\n\nexport interface TreeSnapshotData {\n\tsession: number // a unique token per client connection\n\ttreeVersion: number // MAX(treeVersions seen)\n\ttree: SnapshotTree\n\tforce?: boolean\n\tmigration?: boolean\n}\n\nexport function isTreeSnapshotData(o: unknown): o is TreeSnapshotData {\n\treturn typeof o === \"object\" && o !== null && \"tree\" in o\n}\n\n/** Interface implemented by side that manages the underlying connection. */\nexport interface RemoteDocumentConnection {\n\trtt(): number\n\tsendMessage(msg: Message): void\n\tupdateUserState(update: Partial<UserStateData>): void\n}\n\n// we enumarate all possible prefixes here to ensure type safety and to avoid unexpected values\ntype LoaderTypePrefix =\n\t| \"FullParse\"\n\t| \"ShallowScopes\"\n\t| \"Debug+ShallowScopes\"\n\t| \"FullParse+ShallowScopes\"\n\t| \"FullParse+Debug+ShallowScopes\"\n\n/**\n * The LoaderType is used to identify the type of loader that is currently active.\n */\ntype LoaderType = \"DocumentLoader\" | `${LoaderTypePrefix}+DocumentLoader`\n\n/**\n * Interface we implement for the side that manages the connection with the server.\n * An object with this interface is returned by engine.setRemoteTreeSync(...)\n */\nexport interface RemoteDocumentHandler {\n\t// TODO: Do we need these to be public at all\n\t// they are only used for assertions\n\treadonly engine: VekterEngine\n\treadonly projectId: string\n\treadonly userId: string\n\n\tremote: RemoteDocumentConnection | null\n\treadonly treeSchema: number\n\t// TODO: Check if we can remove this\n\tloader?: DocumentLoader\n\tinitialLoad: boolean\n\treadonly documentByteSize: number\n\tsuppressLoadingForDebugging: boolean\n\n\treadonly localCache?: LocalCache\n\n\tsocket?: Socket\n\n\tsetSocket(socket: Socket): void\n\n\treadonly networkReady: boolean\n\n\tcreateLoader(url: string, version: number, settings: DocumentLoaderSettings): DocumentLoader\n\tforceServerSnapshot(options?: { forceCompact?: boolean }): Promise<void>\n\tverifyTreeWithServer(): Promise<number>\n\n\tfinishPartialLoad(): void\n\treadonly loaderType: LoaderType\n\n\tsetChatText(chat: string): void\n\tscheduleChatTextReset(): void\n\n\trequestFileLock(id: string, duration?: number): boolean\n\treleaseFileLock(id: string): void\n\tgetFileLockState(id: string): LockState | undefined\n\tmakeReadOnly(): void\n\tmakeReadWrite(): void\n\n\t// TODO: Can we move this to VekterEngineErrorRecovery\n\tcanReloadServerDocumentForRecovery(): boolean\n\n\t// Debug\n\tdebugCreateNewTreeSyncSession(tree: CanvasTree): void\n\tdebugDetachClientFromServer(options?: { keepAPIFetcherWritable?: boolean }): void\n\tdebugReattachClientToServer(): void\n\tdebugCrash(): void\n\n\tafterCommit(tree: CanvasTree): void\n\tprocessBetweenFramesAsync: (task: () => void) => Promise<void>\n\tgetAndResetTotalTime(): number\n\n\tsetRemoteConnection(remote: RemoteDocumentConnection): void\n\tsetRemoteConnectionFailure(reason: DisconnectReason): void\n\thasRemoteConnection(): boolean\n\thandleLock(userId: string, lock: LockMessageData): void\n\thandleTreeMessage(userId: string, message: TreeMessageData): void\n\thandleTreeUpdate(userId: string, update: ServerTreeUpdate): void\n\thandleTreeVerify(verify: TreeVerifyData): void\n\thandleRows(id: string, value: CrdtUpdate): void\n\thandleConfirmRows(id: string, value: { seq: number }): void\n\twaitingForSync(): boolean\n\n\ttreeVersion(): number\n\n\tloadOneScope(scope: CanvasNode, keepChanges: boolean): CanvasTree\n\n\thasUnconfirmedChanges(): boolean\n\n\tresetTreesForRecovery(activeScopeId: string): CanvasTree\n\n\t/** Send the message after flushing out any in-flight updates. */\n\tsendAfterFlush(message: Message): boolean\n\n\t/**\n\t * Mark the next disconnection as expected, and ask for a hard-reload of the window when it happens.\n\t */\n\treloadOnDisconnect(): void\n\t/**\n\t * Whether the active socket session targets the Multiplayer Service.\n\t */\n\treadonly isMPSSocket: boolean\n\t/**\n\t * Prepare for a reconnect by resetting internal state and preparing for a new connection.\n\t */\n\tprepareForReconnect(): void\n}\n", "import type { ComponentLoader } from \"@framerjs/framer-runtime\"\nimport { assert, getLogger } from \"@framerjs/shared\"\nimport { markLoadingPerf, setWsInitialUpdates } from \"utils/performanceTracker.ts\"\nimport type { CanvasTree } from \"./CanvasTree.ts\"\nimport { type NodeChange, changesApplyToNodeIds, jsonApplyChanges } from \"./TreeDiff.ts\"\nimport { RollingDiff, type TreeTimeline } from \"./TreeTimeline.ts\"\nimport { expandScopeReplicas } from \"./expandScopeReplicas.ts\"\nimport type { CanvasNode } from \"./nodes/CanvasNode.ts\"\nimport type { LocalTreeUpdate } from \"./treeSyncUtils.ts\"\n\nconst log = getLogger(\"remote:sync\")\n\n// How does the syncing work:\n// 1. local changes create local updates, those are stored and send away\n// 2. as remote updates come in, they are applied to the tree\n// 3. then any stored local updates are replayed\n// 4. when a remote update is a stored local update, it is removed from store\n//\n// This creates optimistic synchronization, where local edits are visible\n// regardless of remote edits and how long it takes to propagate changes. All it\n// requires from the server is that it puts updates from all clients in a single\n// stream with a single order, which gets sent to all clients. It requires from\n// the data structure that all updates can be re-applied and results in the same\n// data as when the server stream is re-played.\n//\n// That last requirement works for us, as we consider every node to be a bag of\n// values with its own identity.\n\nconst MAX_INTEGER = 2 ** 52\n\nexport abstract class TreeSync {\n\tprotected rollingDiff: RollingDiff | null = null\n\n\t// unique message information\n\tsession = Math.floor(Math.random() * MAX_INTEGER)\n\tseq = 0\n\n\t// tree information\n\ttreeVersion = 0\n\tupdatesSeen = 0\n\n\t// init information\n\tinit = 0\n\texpectingInitialUpdates = 0\n\n\tlocalUpdatesInFlight: LocalTreeUpdate[] = []\n\n\t/** When reconnecting, we have to resend previous local updates that were still in-flight, we then might\n\t * receive these zero or more times from the server. */\n\tlocalUpdatesAtInit: LocalTreeUpdate[] = []\n\n\t// sync information\n\thasError = false\n\twaitingForTree = false\n\n\t// computed properties\n\tget waitingForInitialUpdates(): boolean {\n\t\treturn this.expectingInitialUpdates > this.updatesSeen\n\t}\n\n\tget isLoading(): boolean {\n\t\treturn this.waitingForTree || this.waitingForInitialUpdates\n\t}\n\n\tget isReady(): boolean {\n\t\treturn !(this.hasError || this.waitingForTree || this.waitingForInitialUpdates)\n\t}\n\n\t// NOTE: Used only in tests to make it easier\n\tget tree(): CanvasTree {\n\t\treturn this.timeline.tree\n\t}\n\n\tconstructor(\n\t\tpublic readonly timeline: TreeTimeline,\n\t\tprotected readonly componentLoader: ComponentLoader,\n\t\ttreeVersion = 0,\n\t\toptions?: { initialChanges?: NodeChange[]; isLoading?: boolean },\n\t) {\n\t\tthis.setTree(timeline.tree, treeVersion, options)\n\t}\n\n\terror(message: string, cause?: unknown) {\n\t\tthis.hasError = true\n\t\treturn Error(message, { cause })\n\t}\n\n\tverify(treeVersion: number, serverHash: number): boolean {\n\t\tconst tree = this.timeline.getTreeForVersion(treeVersion)\n\t\tif (!tree) {\n\t\t\tlog.info(\"verify: unable to find tree with version\", treeVersion)\n\t\t\treturn true\n\t\t}\n\n\t\tconst localHash = tree.computeTreeHash()\n\t\tif (localHash !== serverHash) {\n\t\t\tlog.warn(\"verify: failed\", localHash, \"!==\", serverHash)\n\t\t\tif (serverHash === 0) return true\n\t\t\tlog.reportError(\"Tree verification failed\", { localHash, serverHash, treeVersion, treeSize: tree.size() })\n\t\t} else {\n\t\t\tlog.debug(\"verify: passed; hash:\", serverHash)\n\t\t}\n\t\treturn localHash === serverHash\n\t}\n\n\tsetTree(tree: CanvasTree, treeVersion: number, options?: { initialChanges?: NodeChange[]; isLoading?: boolean }) {\n\t\tlog.info(\"setTree\", treeVersion)\n\t\tthis.timeline.reset(tree, options)\n\t\tthis.setRemoteTreeVersion(treeVersion)\n\n\t\tconst isLoading = !!options?.isLoading\n\t\tif (isLoading) {\n\t\t\tthis.rollingDiff = new RollingDiff()\n\t\t\tthis.rollingDiff.addChanges(options?.initialChanges)\n\t\t}\n\n\t\tthis.treeVersion = treeVersion\n\t\tthis.waitingForTree = false\n\t\tthis.hasError = false\n\n\t\t// When we have downloaded a new tree, there are no local updates on top of it, and we discard the\n\t\t// localUpdatesInFlight. Through the localUpdatesAtInit mechanism, we will still be sending them to\n\t\t// the server. And when we receive them back, they will be treated as remote updates.\n\n\t\t// Note however, that doing yet another init quickly after this one will leave the\n\t\t// localUpdatesAtInit empty. That might drop local edits, and might have the client process unknown\n\t\t// local updates causing red-bar errors.\n\t\tthis.localUpdatesInFlight = []\n\t}\n\n\t/** Used to not resume an old connection, but to restart after a disconnect. */\n\tresetSession() {\n\t\tthis.treeVersion = 0\n\t\tthis.session = Math.floor(Math.random() * MAX_INTEGER)\n\t\tthis.localUpdatesInFlight = []\n\t\tthis.localUpdatesAtInit = []\n\t}\n\n\tdebugResetSessionAndTree(tree: CanvasTree) {\n\t\tthis.resetSession()\n\t\tthis.setTree(tree, 0)\n\t}\n\n\t// When we are sent an init, we've already indicated our treeVersion, and will be sent only updates that match it.\n\thandleInit(treeVersion: number, initialUpdates: number) {\n\t\tthis.init += 1\n\t\tif (this.init === 1) {\n\t\t\tmarkLoadingPerf(\"wsTreeInitMessages\")\n\t\t\tsetWsInitialUpdates(initialUpdates)\n\t\t}\n\t\tlog.info(\"init\", this.init, { treeVersion, initialUpdates, localTreeVersion: this.treeVersion })\n\t\tlog.debug(\"init updates:\", {\n\t\t\tseen: this.updatesSeen,\n\t\t\tinFlight: this.localUpdatesInFlight.length,\n\t\t\tprevious: this.localUpdatesAtInit.length,\n\t\t})\n\n\t\t// start batching the initial updates\n\t\tthis.hasError = false\n\t\tthis.expectingInitialUpdates = initialUpdates\n\t\tthis.updatesSeen = 0\n\n\t\t// Copy the local updates in flight, so we can deal with receiving them more then once. Also the\n\t\t// localUpdatesAtInit is the list of updates we are going to re-send to the server.\n\t\tthis.localUpdatesAtInit = this.localUpdatesInFlight.slice()\n\n\t\t// If the server treeVersion is not the one we are at, we have to download the document, if possible\n\t\tif (this.treeVersion !== treeVersion || this.waitingForTree) {\n\t\t\tthis.waitingForTree = true\n\t\t\treturn true\n\t\t}\n\n\t\treturn false\n\t}\n\n\t/** When shallow loading with a rolling diff, or right after shallow loading is done, we trim\n\t * the timeline aggressively. There is no need to hold on to the trees that insert scopes,\n\t * because we cannot compare partially loaded trees with server snapshots to detect out of sync\n\t * errors. */\n\tprotected trimForShallowLoading() {\n\t\tconst timeline = this.timeline\n\n\t\t// Trim the entries buffer, leaving 3 remote entries. That way forward cursors can still be\n\t\t// computed if loading involved rebases.\n\t\tconst count = this.getRemoteIndex() - 3\n\t\tif (count <= 0) return\n\n\t\ttimeline.trimmed += count\n\t\tlog.debug(\"trim\", count, \"new offset:\", timeline.trimmed, \"entries.length:\", timeline.entries.length, \"after load\")\n\t\ttimeline.entries.splice(0, count)\n\t\tassert(\n\t\t\tthis.timeline.remoteTreeIndex === 0 || this.getRemoteIndex() >= 0,\n\t\t\t\"must have some buffer before remoteTreeIndex\",\n\t\t)\n\t}\n\n\tloadedAllScopes() {\n\t\tconst timeline = this.timeline\n\t\tlog.info(\"done loading, took:\", Math.round((performance.now() - timeline.resetTime) / 100) / 10, \"seconds\")\n\t\tassert(timeline.isPartialLoading, \"Must be in loading mode\")\n\t\ttimeline.isPartialLoading = false\n\t\tthis.rollingDiff = null\n\n\t\tconst remote = this.getRemoteEntry()\n\t\tif (!remote) return\n\n\t\tremote.version = timeline.remoteTreeVersion\n\n\t\tthis.trimForShallowLoading()\n\t}\n\n\t/** Insert one scope on top of the remote tree, before any local tree, because it just got loaded. */\n\tloadOneScope(scope: CanvasNode, keepChanges: boolean) {\n\t\tconst timeline = this.timeline\n\t\tlog.debug(\"loadOneScope:\", scope.id)\n\n\t\tassert(timeline.isPartialLoading, \"Must be loading\")\n\t\tassert(!scope.cache.isShallowLoad, \"Scope must not be shallow\")\n\n\t\t// We start at the current remote tree.\n\t\tconst remote = this.getRemoteEntry()\n\t\tassert(remote, \"remote tree is missing\")\n\n\t\tconst isViewOnly = timeline.tree.isViewOnly\n\t\tremote.tree.editClosed = false\n\t\tremote.tree.isViewOnly = false\n\t\tremote.tree.inEditor = false\n\t\tremote.tree.makeLatest()\n\n\t\tconst scopeNodesAdded = new Set<string>()\n\t\tconst position = remote.tree.root.children.findIndex(node => node.id === scope.id)\n\t\tif (scope.__class === \"WebPageNode\" || scope.__class === \"SmartComponentNode\") {\n\t\t\texpandScopeReplicas(scope)\n\t\t\tremote.tree = remote.tree.commitWithLoadedScope(this.componentLoader, scope)\n\t\t\tfor (const node of scope.walk()) {\n\t\t\t\ttimeline.trackChange(node.id)\n\t\t\t\tscopeNodesAdded.add(node.id)\n\t\t\t}\n\t\t} else {\n\t\t\tremote.tree.remove(scope.id)\n\t\t\tremote.tree.insertNode(scope, remote.tree.root.id, position)\n\t\t}\n\n\t\t// Apply all the changes between the loaded tree upto the current remote tree.\n\t\tif (this.rollingDiff) {\n\t\t\tconst changes = this.rollingDiff.getChanges()\n\t\t\tif (scopeNodesAdded.size > 0) {\n\t\t\t\tif (changesApplyToNodeIds(changes, scopeNodesAdded)) {\n\t\t\t\t\tjsonApplyChanges(remote.tree, changes)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tjsonApplyChanges(remote.tree, changes)\n\t\t\t}\n\t\t} else {\n\t\t\tlet index = 0\n\t\t\tlet canSkipChanges = scopeNodesAdded.size > 0\n\t\t\tconst remoteIdx = this.getRemoteIndex()\n\t\t\tfor (const entry of timeline.entries) {\n\t\t\t\tif (index > remoteIdx) break\n\t\t\t\tindex++\n\n\t\t\t\t// We can skip scope insert changes.\n\t\t\t\tif (entry.wasScopeInsert) continue\n\t\t\t\tif (canSkipChanges && !changesApplyToNodeIds(entry.changes, scopeNodesAdded)) continue\n\n\t\t\t\tcanSkipChanges = false\n\t\t\t\tjsonApplyChanges(remote.tree, entry.changes)\n\t\t\t}\n\t\t}\n\n\t\tif (position === -1) {\n\t\t\t// Between the snapshot and the remote tree, the scope was deleted. We did add it to the\n\t\t\t// tree before replaying the diffs. But the diffs should have deleted it again.\n\t\t\tassert(!remote.tree.get(scope.id), \"Scope must have been deleted by remote diffs\")\n\t\t} else {\n\t\t\tremote.tree.loadReplicasAndCodeComponents(scope)\n\t\t}\n\n\t\t// As we commit, we don't collect any diffs, only signal these nodes have changed.\n\t\tconst tree = remote.tree.commit(\n\t\t\tthis.componentLoader,\n\t\t\t(current: CanvasNode | undefined, draft: CanvasNode | undefined) => {\n\t\t\t\tconst id = current?.id ?? draft?.id\n\t\t\t\tif (id) {\n\t\t\t\t\ttimeline.trackChange(id)\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\n\t\t// Restore inEditor\n\t\tremote.tree.inEditor = true\n\t\ttree.inEditor = true\n\n\t\t// Update accounting and maybe rebase if there are local unconfirmed changes.\n\t\tthis.incrementRemoteTreeIndex()\n\n\t\tif (!keepChanges) {\n\t\t\ttimeline.latestReversibleNodeChanges = null\n\t\t}\n\n\t\tthis.addTreeToTimeline(tree)\n\n\t\t// When in legacy mode, we are not creating a diff for each scope load, so we need to inform\n\t\t// every cursor that they need to restart, or they will be missing content.\n\t\tif (timeline.legacyMode) {\n\t\t\ttimeline.invalidateAllCursors()\n\t\t}\n\n\t\t// Restore isViewOnly on the tree we're returning\n\t\ttimeline.tree.isViewOnly = isViewOnly\n\t\tif (this.rollingDiff) {\n\t\t\tthis.trimForShallowLoading()\n\t\t}\n\t\treturn timeline.tree\n\t}\n\n\tpublic getRemoteEntry() {\n\t\treturn this.timeline.getEntry(this.getRemoteIndex())\n\t}\n\n\tsetRemoteTreeVersion(version: number) {\n\t\tthis.timeline.remoteTreeVersion = version\n\t\tif (this.timeline.isPartialLoading) return\n\t\tconst remoteTreeEntry = this.getRemoteEntry()\n\t\tassert(remoteTreeEntry, \"remote tree is missing\")\n\t\tremoteTreeEntry.version = version\n\t}\n\n\tabstract getRemoteIndex(): number\n\tabstract addTreeToTimeline(tree: CanvasTree): void\n\tabstract incrementRemoteTreeIndex(): void\n}\n", "import { environment } from \"@framerjs/framer-environment\"\nimport { assert, getLogger } from \"@framerjs/shared\"\nimport type { AppEnvironment, ServerTreeUpdate } from \"document/RemoteDocumentHandler.ts\"\nimport { isServerTreeUpdateOther, isServerTreeUpdateReply } from \"document/RemoteDocumentHandler.ts\"\nimport type { CanvasTree } from \"./CanvasTree.ts\"\nimport { type NodeChange, type ReversibleNodeChange, jsonApplyChanges, nodeChange } from \"./TreeDiff.ts\"\nimport { TreeSync } from \"./TreeSync.ts\"\nimport type { ReversibleNodeChangesById, TimelineEntry } from \"./TreeTimeline.ts\"\nimport { type LocalTreeUpdate, makeServerTreeUpdate } from \"./treeSyncUtils.ts\"\n\nconst log = getLogger(\"remote:sync\")\n\n// How does the syncing work:\n// 1. local changes create local updates, those are stored and send away\n// 2. as remote updates come in, they are applied to the tree\n// 3. then any stored local updates are replayed\n// 4. when a remote update is a stored local update, it is removed from store\n//\n// This creates optimistic synchronization, where local edits are visible\n// regardless of remote edits and how long it takes to propagate changes. All it\n// requires from the server is that it puts updates from all clients in a single\n// stream with a single order, which gets sent to all clients. It requires from\n// the data structure that all updates can be re-applied and results in the same\n// data as when the server stream is re-played.\n//\n// That last requirement works for us, as we consider every node to be a bag of\n// values with its own identity.\n\nexport class JsonTreeSync extends TreeSync {\n\tappEnvironment: AppEnvironment | undefined\n\tpublic localChangesSentToRemote = 0\n\n\toverride setTree(\n\t\ttree: CanvasTree,\n\t\ttreeVersion: number,\n\t\toptions?: { initialChanges?: NodeChange[]; isLoading?: boolean },\n\t): void {\n\t\tsuper.setTree(tree, treeVersion, options)\n\t\tthis.localChangesSentToRemote = 0\n\t}\n\n\thandleRemoteUpdate(update: ServerTreeUpdate): void {\n\t\tif (this.hasError || this.waitingForTree) return\n\t\tassert(typeof update.next === \"number\", \"must be a valid tree update\")\n\n\t\tconst treeVersion = update.next\n\n\t\tlog.trace(\"this:\", this.session, this.seq, \"at:\", this.treeVersion, \"update:\", update)\n\n\t\t// If we are skipping versions, something is wrong\n\t\tif (treeVersion !== this.treeVersion + 1) {\n\t\t\tif (treeVersion <= this.treeVersion) {\n\t\t\t\tlog.debug(\"ignoring old update:\", treeVersion, \" <= \", this.treeVersion)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthrow this.error(\"missing update: \" + this.treeVersion + \" + 1 != \" + treeVersion)\n\t\t}\n\n\t\tthis.updatesSeen += 1\n\t\tthis.treeVersion = treeVersion\n\n\t\tif (isServerTreeUpdateReply(update) && update.session === this.session) {\n\t\t\t// An update that started locally, normally this should be our first in-flight local update.\n\t\t\tconst first = this.localUpdatesInFlight[0]\n\t\t\tif (first?.seq === update.seq) {\n\t\t\t\tthis.localUpdatesInFlight.shift()\n\t\t\t\tthis.confirmLocalChangesByRemote(first.count, treeVersion)\n\t\t\t\tfirst.confirmed = true\n\t\t\t} else {\n\t\t\t\t// Otherwise, when reconnecting, we must deal with, potentially many, duplicate local updates.\n\t\t\t\t// We treat them as if they are remote updates. That will keep our tree the same as the server\n\t\t\t\t// side tree.\n\t\t\t\tconst previous = this.localUpdatesAtInit.find(u => u.seq === update.seq)\n\t\t\t\tif (previous) {\n\t\t\t\t\tthis.insertRemoteChanges(previous.changes, treeVersion)\n\t\t\t\t\tprevious.confirmed = true\n\t\t\t\t} else {\n\t\t\t\t\t// But if both cases fail, something is wrong with the connection, message ordering, or\n\t\t\t\t\t// we are reconnecting too fast too often, and we must error out.\n\t\t\t\t\tconst index = this.localUpdatesInFlight.findIndex(u => u.seq === update.seq)\n\t\t\t\t\tconst error =\n\t\t\t\t\t\tindex === -1\n\t\t\t\t\t\t\t? \"unknown local update: \" + update.seq + \" != \" + first?.seq\n\t\t\t\t\t\t\t: \"missing local update: \" + update.seq + \" != \" + first?.seq + \", is index: \" + index\n\t\t\t\t\tthrow this.error(error)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (isServerTreeUpdateOther(update)) {\n\t\t\t// An update from other clients.\n\t\t\tif (update.changes.length > 0) {\n\t\t\t\tthis.insertRemoteChanges(update.changes, treeVersion)\n\t\t\t}\n\t\t} else {\n\t\t\t// Should not happen.\n\t\t\tlog.reportErrorOncePerMinute(new Error(\"Unknown remote update\"), { update })\n\t\t}\n\t}\n\n\tconfirmLocalChangesByRemote(count: number, version = 0): CanvasTree {\n\t\tconst timeline = this.timeline\n\t\tassert(count >= 1, \"cannot confirm less than one change\")\n\t\tassert(this.localChangesSentToRemote >= count, \"cannot confirm local changes that have not been sent\")\n\t\tassert(timeline.remoteTreeIndex < timeline.localTreeIndex, \"must have unconfirmed local changes\")\n\n\t\tif (this.rollingDiff) {\n\t\t\tfor (let i = 1; i <= count; i++) {\n\t\t\t\tthis.rollingDiff.addChanges(timeline.getEntry(timeline.remoteTreeIndex + i)?.changes)\n\t\t\t}\n\t\t}\n\n\t\tthis.localChangesSentToRemote -= count\n\t\ttimeline.incrementRemoteTreeIndex(count)\n\n\t\tthis.setRemoteTreeVersion(version)\n\t\treturn timeline.tree\n\t}\n\n\tinsertRemoteChanges(changes: NodeChange[], version = 0): CanvasTree {\n\t\tconst timeline = this.timeline\n\t\tlog.debug(\"insertRemoteChanges:\", changes.length)\n\t\tassert(timeline.tree === timeline.getLastEntry().tree, \"tree out of sync\")\n\t\tassert(timeline.remoteTreeIndex <= timeline.localTreeIndex, \"remote tree too far ahead\")\n\n\t\tif (this.rollingDiff) {\n\t\t\tthis.rollingDiff.addChanges(changes)\n\t\t}\n\n\t\tconst remote = this.getRemoteEntry()\n\t\tassert(remote, \"remote tree is missing\")\n\n\t\tconst isViewOnly = timeline.tree.isViewOnly\n\t\tremote.tree.editClosed = false\n\t\tremote.tree.isViewOnly = false\n\t\tremote.tree.makeLatest()\n\t\tremote.tree.beginAllowPartialScopeAccess()\n\t\tjsonApplyChanges(remote.tree, changes)\n\t\tconst tree = remote.tree.commitDiffs(this.componentLoader)\n\n\t\tfor (const change of changes) {\n\t\t\ttimeline.trackChange(change.id, change)\n\t\t}\n\t\tfor (const node of remote.tree.getNodesChangedByCommit()) {\n\t\t\ttimeline.trackChange(node.id)\n\t\t}\n\n\t\ttimeline.incrementRemoteTreeIndex(1)\n\t\ttimeline.latestReversibleNodeChanges = null\n\n\t\tconst rebase = timeline.entries.length - this.getRemoteIndex()\n\t\tassert(rebase >= 0, \"computed rebase is off\")\n\n\t\tif (rebase === 0) {\n\t\t\tthis.addRemoteTreeWithChanges(tree, changes)\n\t\t} else {\n\t\t\tthis.rebaseRemoteTreeWithChanges(tree, changes, rebase)\n\t\t}\n\t\tthis.trim()\n\n\t\tthis.setRemoteTreeVersion(version)\n\t\tremote.tree.endAllowPartialScopeAccess()\n\n\t\t// Restore isViewOnly\n\t\ttimeline.tree.isViewOnly = isViewOnly\n\t\treturn timeline.tree\n\t}\n\n\tprivate addRemoteTreeWithChanges(tree: CanvasTree, changes: NodeChange[]) {\n\t\tlog.trace(\"addRemoteTreeWithChanges:\", changes.length)\n\t\tconst lastEntry = this.timeline.getLastEntry()\n\t\tassert(tree.lineage === lastEntry.tree.lineage, \"Trees must belong to the same line.\")\n\t\tassert(!tree.hasUncommittedChanges(), \"Tree cannot have uncommitted changes.\")\n\n\t\tif (lastEntry.tree !== tree) {\n\t\t\tlastEntry.tree.releaseMemory()\n\t\t}\n\n\t\treturn this.timeline.addEntry(tree, changes)\n\t}\n\n\t// Think of this like a git-rebase, the local changes are replayed on top of the remote tree.\n\tprivate rebaseRemoteTreeWithChanges(tree: CanvasTree, changes: NodeChange[], rebase: number) {\n\t\tconst timeline = this.timeline\n\t\tlog.debug(\"rebaseRemoteTreeWithChanges:\", rebase, \"changes:\", changes.length)\n\t\tassert(tree.lineage === timeline.getLastEntry().tree.lineage, \"Trees must belong to the same line.\")\n\t\tassert(!tree.hasUncommittedChanges(), \"Tree cannot have uncommitted changes.\")\n\t\tassert(timeline.entries.length >= rebase, \"rebase\", rebase, \"> commits\", timeline.entries.length)\n\n\t\tconst entriesToRebase = timeline.entries.splice(timeline.entries.length - rebase, rebase)\n\t\tassert(entriesToRebase.length === rebase, \"must have\", rebase, \"entries to process\")\n\n\t\tconst entry = timeline.addEntry(tree, changes, [], true /* wasRebase */)\n\n\t\tlet previousTree = tree\n\t\tfor (let i = 0; i < rebase; i++) {\n\t\t\tconst toRebase = entriesToRebase[i]!\n\t\t\tjsonApplyChanges(tree, toRebase.changes)\n\t\t\ttree = tree.commitDiffs(this.componentLoader)\n\t\t\tfor (const change of toRebase.changes) {\n\t\t\t\ttimeline.trackChange(change.id, change)\n\t\t\t}\n\t\t\tfor (const node of previousTree.getNodesChangedByCommit()) {\n\t\t\t\ttimeline.trackChange(node.id)\n\t\t\t}\n\n\t\t\ttimeline.addEntry(tree, toRebase.changes, toRebase.editReasons, toRebase.wasRebase)\n\t\t\tif (tree !== previousTree) {\n\t\t\t\tpreviousTree.releaseMemory()\n\t\t\t\tpreviousTree = tree\n\t\t\t}\n\t\t}\n\n\t\t// TODO: We should convert `tree` to getter\n\t\ttimeline.tree = tree\n\t\treturn entry\n\t}\n\n\taddTreeToTimeline(tree: CanvasTree) {\n\t\tconst timeline = this.timeline\n\t\tconst rebase = timeline.entries.length - this.getRemoteIndex()\n\t\tassert(rebase >= 0, \"computed rebase is off\")\n\n\t\tlet entry: TimelineEntry\n\t\tif (rebase === 0) {\n\t\t\tentry = this.addRemoteTreeWithChanges(tree, [])\n\t\t} else {\n\t\t\tentry = this.rebaseRemoteTreeWithChanges(tree, [], rebase)\n\t\t}\n\t\tentry.wasScopeInsert = true\n\t}\n\n\t/** Load a tree based on the starting tree, will replay all changes against that full tree, and\n\t * will re-enable trimming the local buffer. */\n\tloadCompleteTree(tree: CanvasTree, version = 0) {\n\t\tconst timeline = this.timeline\n\t\tlog.debug(\n\t\t\t\"load complete tree:\",\n\t\t\ttimeline.tree.sizeAtStart(),\n\t\t\t\"->\",\n\t\t\ttree.size(),\n\t\t\t\"entries:\",\n\t\t\ttimeline.entries.length,\n\t\t)\n\t\tassert(timeline.trimmed === 0, \"cannot load complete tree while having local changes\")\n\t\tassert(!tree.hasUncommittedChanges(), \"tree should be clean\")\n\n\t\ttimeline.entries.forEach((entry, index) => {\n\t\t\tif (index > timeline.remoteTreeIndex) return\n\t\t\tjsonApplyChanges(tree, entry.changes)\n\t\t})\n\n\t\tif (tree.hasUncommittedChanges()) {\n\t\t\ttree = tree.commitDiffs(this.componentLoader)\n\t\t}\n\n\t\tlet changes: ReversibleNodeChange[] = []\n\t\tconst oldTree = timeline.tree\n\n\t\t// If the change set is not too large, we calculate it, otherwise we resend the whole tree.\n\t\tif (oldTree.sizeAtStart() * 2 > tree.size()) {\n\t\t\tconst changesById: ReversibleNodeChangesById = {}\n\t\t\tfor (const node of tree.root.walk()) {\n\t\t\t\tconst oldNode = oldTree.getNodeAtStart(node.id) || undefined\n\t\t\t\tconst change = nodeChange(oldNode, node)\n\t\t\t\tif (change) {\n\t\t\t\t\tchangesById[change.id] = change\n\t\t\t\t}\n\t\t\t\ttimeline.trackChange(node.id, change)\n\t\t\t}\n\t\t\tchanges = Object.values(changesById)\n\n\t\t\tlog.debug(\"load complete tree, diff:\", changes.length)\n\t\t} else {\n\t\t\ttimeline.invalidateAllCursors()\n\t\t\tlog.debug(\"load complete tree, resending:\", timeline.tree.size())\n\t\t}\n\n\t\ttimeline.incrementRemoteTreeIndex(1)\n\t\ttimeline.latestReversibleNodeChanges = null\n\n\t\tconst rebase = timeline.entries.length - timeline.remoteTreeIndex\n\t\tassert(rebase >= 0, \"computed rebase is off\")\n\n\t\t// If the tree used to load the document is no longer the current tree, we reset to it,\n\t\t// instead of patching up. This can happen if crash recovery ran before partial document\n\t\t// loading was done.\n\t\tif (tree.lineage !== timeline.tree.lineage) {\n\t\t\ttimeline.reset(tree)\n\t\t\tthis.setRemoteTreeVersion(version)\n\t\t\treturn timeline.tree\n\t\t}\n\n\t\tif (rebase === 0) {\n\t\t\tthis.addRemoteTreeWithChanges(tree, changes)\n\t\t} else {\n\t\t\tthis.rebaseRemoteTreeWithChanges(tree, changes, rebase)\n\t\t}\n\n\t\tthis.setRemoteTreeVersion(version)\n\t\tthis.trim()\n\n\t\ttimeline.tree.forEachNode(n => timeline.trackChange(n.id))\n\n\t\treturn timeline.tree\n\t}\n\n\tincrementRemoteTreeIndex(): void {\n\t\tthis.timeline.incrementRemoteTreeIndex(1)\n\t}\n\n\tgetRemoteIndex() {\n\t\treturn this.timeline.remoteTreeIndex - this.timeline.trimmed\n\t}\n\n\tgetUnconfirmedChangeCount(): number {\n\t\treturn this.timeline.localTreeIndex - this.timeline.remoteTreeIndex\n\t}\n\n\thasChangesForRemote(): boolean {\n\t\tconst startIndex = this.timeline.remoteTreeIndex + this.localChangesSentToRemote\n\t\tconst endIndex = this.timeline.localTreeIndex\n\t\tassert(startIndex <= endIndex, \"inconsistency in getting local changes to send\")\n\t\treturn startIndex < endIndex\n\t}\n\n\thasOnlyEmptyChangesForRemote(): boolean {\n\t\tconst startIndex = this.timeline.remoteTreeIndex + this.localChangesSentToRemote\n\t\tconst endIndex = this.timeline.localTreeIndex\n\t\tif (startIndex >= endIndex) return true\n\t\tconst changes = this.timeline.computeForwardChanges(startIndex, endIndex)\n\t\treturn changes.length === 0\n\t}\n\n\tcreateUpdateToSend() {\n\t\tif (!this.isReady) throw Error(\"cannot create updates while not ready\")\n\t\tif (!this.hasChangesForRemote()) return null\n\n\t\tconst { changes, count, reasons } = this.getForwardChangesForRemote()\n\t\tconst seq = ++this.seq\n\t\tconst update: LocalTreeUpdate = {\n\t\t\tappEnvironment: this.appEnvironment,\n\t\t\tsession: this.session,\n\t\t\tseq,\n\t\t\tchanges,\n\t\t\tcount,\n\t\t\treasons,\n\t\t\tconfirmed: false,\n\t\t}\n\n\t\tthis.localUpdatesInFlight.push(update)\n\t\treturn update\n\t}\n\n\tgetForwardChangesForRemote() {\n\t\tconst startIndex = this.timeline.remoteTreeIndex + this.localChangesSentToRemote\n\t\tconst endIndex = this.timeline.localTreeIndex\n\t\tconst changes = this.timeline.getChangesBetweenEntries(startIndex, endIndex)\n\n\t\tthis.localChangesSentToRemote += changes.count\n\n\t\treturn changes\n\t}\n\n\t// TODO for testing only\n\tcommitAndCreateUpdate(next = 0) {\n\t\tassert(environment.isTest)\n\t\tthis.timeline.commitLocalTree()\n\t\tconst update = this.createUpdateToSend()\n\t\tif (!update) return null\n\t\treturn makeServerTreeUpdate(update, next)\n\t}\n\n\tresetTreesForRecovery() {\n\t\tlog.info(\n\t\t\t\"reset trees for recovery, remote:\",\n\t\t\tthis.getRemoteIndex(),\n\t\t\t\"last index:\",\n\t\t\tthis.timeline.localTreeIndex,\n\t\t\t\"number of entries to reapply to remote tree\",\n\t\t\tthis.localChangesSentToRemote,\n\t\t)\n\t\treturn this.timeline.resetTreesForRecovery(this.getRemoteIndex(), this.localChangesSentToRemote)\n\t}\n\n\ttrim() {\n\t\tif (this.timeline.isPartialLoading) return\n\n\t\t// Trim the entries buffer to keep it around 100 entries.\n\t\tlet count = 0\n\t\tif (this.timeline.remoteTreeIndex > 0) {\n\t\t\t// If we are tracking a remote tree, use its offset instead.\n\t\t\tcount = this.getRemoteIndex() - 100\n\t\t} else {\n\t\t\tcount = this.timeline.localTreeIndex - this.timeline.trimmed - 100\n\t\t}\n\n\t\t// Only actually do the work if we are going to remove a decent amount of entries.\n\t\tif (count <= 75) return\n\n\t\tthis.timeline.trimmed += count\n\t\tlog.debug(\"trim\", count, \"new offset:\", this.timeline.trimmed, \"entries.length:\", this.timeline.entries.length)\n\t\tthis.timeline.entries.splice(0, count)\n\n\t\tassert(\n\t\t\tthis.timeline.remoteTreeIndex === 0 || this.getRemoteIndex() >= 0,\n\t\t\t\"must have some buffer before remoteTreeIndex\",\n\t\t)\n\t}\n}\n", "import { assert } from \"@framerjs/shared\"\nimport { isEqual } from \"library/index.ts\"\nimport type { CanvasTree } from \"../CanvasTree.ts\"\nimport type { NodeChange } from \"../TreeDiff.ts\"\nimport type { CanvasNode } from \"../nodes/CanvasNode.ts\"\nimport { isReplicaChild } from \"../traits/WithTemplate.ts\"\n\nconst ignoredNodeProperties = {\n\tcache: true,\n\tupdate: true,\n\tmutable: true,\n\tchildren: true,\n}\n\nfunction filterCache(key: string, value: any) {\n\tif (key === \"cached\") return undefined\n\treturn value\n}\n\nfunction normalize(value: any) {\n\treturn JSON.parse(JSON.stringify(value, filterCache))\n}\n\ninterface TreeLike {\n\troot: CanvasNode\n\tget(id: string): CanvasNode | null\n}\n\nexport function findTreeDifferences(\n\ttree1: CanvasTree | TreeLike,\n\ttree2: CanvasTree | TreeLike,\n\terrors: string[],\n): boolean {\n\tconst seen = new Set<string>()\n\tlet different = false\n\n\tfunction compareNodes(node1: CanvasNode, path: string) {\n\t\tseen.add(node1.id)\n\t\tpath += \".\" + node1.id\n\n\t\tconst node2 = tree2.get(node1.id)\n\t\tif (!node2) {\n\t\t\tdifferent = true\n\t\t\terrors.push(\"-node: \" + path + (isReplicaChild(node1) ? \" (replica child)\" : \"\"))\n\t\t} else {\n\t\t\tconst problems: string[] = []\n\n\t\t\t// compare children lists\n\t\t\tconst c1 = node1.children?.map(c => c.id).join(\",\")\n\t\t\tconst c2 = node2.children?.map(c => c.id).join(\",\")\n\t\t\tif (c1 !== c2) {\n\t\t\t\tproblems.push(\" .children: [\" + c1 + \"] != [\" + c2 + \"]\")\n\t\t\t}\n\n\t\t\t// compare properties\n\t\t\tfor (const [key, value] of node1.entries()) {\n\t\t\t\tif (key in ignoredNodeProperties) continue\n\t\t\t\tconst otherValue = node2[key as keyof CanvasNode]\n\t\t\t\tif (!isEqual(value, otherValue)) {\n\t\t\t\t\tconst hasUndefined = value === undefined || otherValue === undefined\n\t\t\t\t\tif (hasUndefined || !isEqual(normalize(value), normalize(otherValue))) {\n\t\t\t\t\t\tif (key !== \"contentHash\") {\n\t\t\t\t\t\t\tproblems.push(\" .\" + key + \": \" + JSON.stringify(value) + \" != \" + JSON.stringify(otherValue))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (\n\t\t\t\t\tvalue &&\n\t\t\t\t\ttypeof value === \"object\" &&\n\t\t\t\t\tvalue.__proto__ !==\n\t\t\t\t\t\t(typeof otherValue === \"object\" && otherValue && \"__proto__\" in otherValue\n\t\t\t\t\t\t\t? otherValue.__proto__\n\t\t\t\t\t\t\t: undefined)\n\t\t\t\t) {\n\t\t\t\t\tproblems.push(\" .\" + key + \": different prototypes\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (problems.length > 0) {\n\t\t\t\tdifferent = true\n\t\t\t\terrors.push(\"!node: \" + path + (isReplicaChild(node1) ? \" (replica child)\" : \"\"))\n\t\t\t\terrors.push(...problems)\n\t\t\t}\n\t\t}\n\n\t\tfor (const child of node1.children ?? []) {\n\t\t\tcompareNodes(child, path)\n\t\t}\n\t}\n\n\tfunction addMissingNodes(node2: CanvasNode, path: string) {\n\t\tpath += \".\" + node2.id\n\t\tif (!seen.has(node2.id)) {\n\t\t\tdifferent = true\n\t\t\terrors.push(\"+node: \" + path + (isReplicaChild(node2) ? \" (replica child)\" : \"\"))\n\t\t}\n\t\tfor (const child of node2.children ?? []) {\n\t\t\taddMissingNodes(child, path)\n\t\t}\n\t}\n\n\tcompareNodes(tree1.root, \"\")\n\taddMissingNodes(tree2.root, \"\")\n\treturn different\n}\n\nexport function assertTreeEqual(node1: CanvasNode, node2: CanvasNode, path = \"\"): boolean {\n\tif (node1.id !== node2.id) {\n\t\tthrow Error(\"Tree Assertion Error: \" + path + \" different ids:\" + node1.id + \" != \" + node2.id)\n\t}\n\n\tpath += \".\" + node1.id\n\n\tconst error: string[] = []\n\tconst c1 = node1.children?.map(c => c.id).join(\",\")\n\tconst c2 = node2.children?.map(c => c.id).join(\",\")\n\tif (c1 !== c2) {\n\t\terror.push(\"children: \" + c1 + \" != \" + c2)\n\t}\n\tfor (const [key, value] of node1.entries()) {\n\t\tif (key in ignoredNodeProperties) continue\n\t\tconst otherValue = node2[key as keyof CanvasNode]\n\t\tif (!isEqual(value, otherValue)) {\n\t\t\tconst hasUndefined = value === undefined || otherValue === undefined\n\t\t\tif (hasUndefined || !isEqual(normalize(value), normalize(otherValue))) {\n\t\t\t\terror.push(\".\" + key + \": \" + JSON.stringify(value) + \" != \" + JSON.stringify(otherValue))\n\t\t\t}\n\t\t}\n\t}\n\tif (error.length > 0) {\n\t\tthrow Error(\"Tree Assertion Error: \" + path + \"\\n\" + error.join(\"\\n\"))\n\t}\n\n\tif (!node1.children) return true\n\tassert(node2.children, \"bug\")\n\tnode1.children.forEach((node1Child, i) => {\n\t\tconst node2Child = node2.children?.at(i)\n\t\tassert(node2Child, \"node1 and node2 mush have equal children\")\n\t\tassertTreeEqual(node1Child, node2Child, path)\n\t})\n\treturn true\n}\n\nexport function assertChangesSuperSetOf(changes: NodeChange[], base: NodeChange[]): boolean {\n\tconst changesById: { [key: string]: NodeChange } = {}\n\tfor (const change of changes) {\n\t\tchangesById[change.id] = change\n\t}\n\n\tfor (const baseChange of base) {\n\t\tconst change = changesById[baseChange.id]\n\t\tif (!change) throw Error(\"missing change: \" + baseChange.id)\n\n\t\tif (baseChange.toChildren && !isEqual(change.toChildren, baseChange.toChildren)) {\n\t\t\tthrow Error(\"'toChildren' different for: \" + baseChange.id)\n\t\t}\n\n\t\tif (baseChange.added && !isEqual(change.added, baseChange.added)) {\n\t\t\tthrow Error(\"'added' different for: \" + baseChange.id)\n\t\t}\n\n\t\tif (baseChange.removed && !isEqual(change.removed, baseChange.removed)) {\n\t\t\tthrow Error(\"'removed' different for: \" + baseChange.id)\n\t\t}\n\n\t\tconst to = change.to\n\t\tfor (const [key, value] of Object.entries(baseChange.to)) {\n\t\t\tif (!isEqual(to[key], value)) {\n\t\t\t\tthrow Error(\"'\" + key + \"' different for: \" + baseChange.id)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true\n}\n", "import type { ComponentLoader } from \"@framerjs/framer-runtime\"\nimport { assert, getLogger, hostInfo, reportableError } from \"@framerjs/shared\"\nimport { getTunnelId } from \"environment/getTunnelId.ts\"\nimport { isString } from \"utils/typeChecks.ts\"\nimport { accessTokenRefresher } from \"web/lib/accessTokenRefresherWeb.ts\"\nimport { getMultiplayerServiceURL } from \"web/lib/multiplayerService.ts\"\nimport type {\n\tRemoteDocumentDataHandler,\n\tRemoteDocumentDataHandlerCallbacks,\n\tSendMessage,\n} from \"./RemoteDocumentDataHandler.ts\"\nimport type { AppEnvironment, ServerTreeUpdate } from \"./RemoteDocumentHandler.ts\"\nimport { isServerTreeUpdateOther, isServerTreeUpdateReply } from \"./RemoteDocumentHandler.ts\"\nimport { canRenderPageTypeBeforeDocumentHasFullyLoaded } from \"./firstScopeFinder.ts\"\nimport type { DocumentLoaderSettings } from \"./loader/DocumentLoader.ts\"\nimport { DocumentLoader } from \"./loader/DocumentLoader.ts\"\nimport { JsonTreeSync } from \"./models/CanvasTree/JsonTreeSync.ts\"\nimport type { NodeChange } from \"./models/CanvasTree/TreeDiff.ts\"\nimport { type TimelineEntry, TreeTimeline } from \"./models/CanvasTree/TreeTimeline.ts\"\nimport type { TreeUpdater } from \"./models/CanvasTree/TreeUpdater.ts\"\nimport type { CanvasNode, CanvasTree } from \"./models/CanvasTree/index.ts\"\nimport { asClientTreeUpdate } from \"./models/CanvasTree/treeSyncUtils.ts\"\nimport { findTreeDifferences } from \"./models/CanvasTree/utils/treeAsserts.ts\"\n\nconst log = getLogger(\"remote:connection\")\nconst verifyLog = getLogger(\"remote:verify\")\n\n/** Total unconfirmed messages we keep in flight, before waiting to send more. */\nconst MAX_INFLIGHT_MESSAGES = 5\n\nexport class JsonRemoteDocumentDataHandler implements RemoteDocumentDataHandler {\n\tpublic treeSync: JsonTreeSync\n\tprivate remoteUpdates: ServerTreeUpdate[] = []\n\tprivate ignoreTreeVerifies = false\n\tprivate ignoreTreeVerifyVersion = 0\n\tprivate shouldCrashFromDebug = false\n\n\tpublic loader: DocumentLoader | undefined\n\n\tconstructor(\n\t\ttimeline: TreeUpdater,\n\t\tprivate readonly componentLoader: ComponentLoader,\n\t\tprivate projectId: string,\n\t\tprivate callbacks: RemoteDocumentDataHandlerCallbacks,\n\t\tprivate readonly isMPSSocket = false,\n\t\tappEnvironment?: AppEnvironment,\n\t) {\n\t\tassert(timeline instanceof TreeTimeline, \"timeline must be a TreeTimeline\")\n\t\t// Create and manage TreeSync internally\n\t\tthis.treeSync = new JsonTreeSync(timeline, componentLoader)\n\t\tthis.treeSync.appEnvironment = appEnvironment\n\t\tthis.treeSync.waitingForTree = true\n\t}\n\n\tget init() {\n\t\treturn this.treeSync.init\n\t}\n\n\tsetTree(tree: CanvasTree, treeVersion: number, options?: { initialChanges?: NodeChange[]; isLoading?: boolean }) {\n\t\tthis.treeSync.setTree(tree, treeVersion, options)\n\t}\n\n\tprivate get timeline() {\n\t\treturn this.treeSync.timeline\n\t}\n\n\t// Expose necessary TreeSync properties and methods\n\tget treeVersion(): number {\n\t\treturn this.treeSync.treeVersion\n\t}\n\n\tget isReady(): boolean {\n\t\treturn this.treeSync.isReady\n\t}\n\n\tget waitingForTree(): boolean {\n\t\treturn this.treeSync.waitingForTree\n\t}\n\n\tget isLoading() {\n\t\treturn this.treeSync.isLoading\n\t}\n\n\tget session() {\n\t\treturn this.treeSync.session\n\t}\n\n\tresetSession(): void {\n\t\tthis.treeSync.resetSession()\n\t}\n\n\tdebugResetSessionAndTree(tree: CanvasTree): void {\n\t\tthis.treeSync.debugResetSessionAndTree(tree)\n\t}\n\n\tdebugCrash() {\n\t\tthis.shouldCrashFromDebug = true\n\t}\n\n\tcanProcessChanges() {\n\t\tthis.treeSync.trim()\n\t\tif (!this.treeSync.isReady || this.shouldCrashFromDebug) {\n\t\t\t// If this commit did not generate a diff, we can allow it. When the\n\t\t\t// treeSync is not ready, the user is in read-only mode. But as\n\t\t\t// fonts load, or code components, the tree might still take on\n\t\t\t// board changes, or do commit(), for other reasons.\n\t\t\tif (this.treeSync.hasOnlyEmptyChangesForRemote()) return false\n\n\t\t\t// But otherwise we would have a diff that we cannot send to the\n\t\t\t// server. Which is bad, so we throw an error.\n\t\t\tlet state = \"is not ready\"\n\t\t\tif (this.treeSync.hasError) {\n\t\t\t\tstate = \"had an error\"\n\t\t\t} else if (this.treeSync.waitingForTree) {\n\t\t\t\tstate = \"is waiting for tree data\"\n\t\t\t} else if (this.treeSync.waitingForInitialUpdates) {\n\t\t\t\tstate = \"is waiting for initial updates\"\n\t\t\t} else if (this.shouldCrashFromDebug) {\n\t\t\t\tthis.shouldCrashFromDebug = false\n\t\t\t\tstate = \"is doing a deliberate crash test\"\n\t\t\t}\n\t\t\tthrow this.treeSync.error(\"cannot create local updates when the document \" + state)\n\t\t}\n\n\t\treturn true\n\t}\n\n\tprocessViewOnly(): void {\n\t\t// When the user is a viewer, we don't expect any updates, log a warning and consume the\n\t\t// update. This prevents the user from (silently) disconnecting.\n\n\t\t// Unfortunately we do sometimes update the local tree if code modules update, or fonts\n\t\t// are loaded, etc. And if graphics containers are involved, they might compute a new\n\t\t// contentHash.\n\t\tif (!this.treeSync.hasChangesForRemote()) return\n\n\t\tconst { changes, count } = this.treeSync.getForwardChangesForRemote()\n\t\tlog.warn(\"cannot create local updates when the user is a viewer, ignoring:\", changes)\n\t\tthis.treeSync.confirmLocalChangesByRemote(count)\n\t}\n\n\tmaybeSend(remote: SendMessage) {\n\t\tif (!this.treeSync.isReady) return \"nothingToSend\"\n\t\tif (!this.treeSync.hasChangesForRemote()) return \"nothingToSend\"\n\n\t\tconst inflight = this.treeSync.localUpdatesInFlight.length\n\t\tif (inflight >= MAX_INFLIGHT_MESSAGES) {\n\t\t\treturn \"postpone\"\n\t\t}\n\n\t\tconst update = this.treeSync.createUpdateToSend()\n\t\tif (update) {\n\t\t\tlog.debug(\"sending update:\", inflight, update.changes.length, update.reasons)\n\t\t\tremote.sendMessage({ type: \"treeUpdate\", value: asClientTreeUpdate(update) })\n\t\t\treturn \"didSend\"\n\t\t}\n\n\t\treturn \"nothingToSend\"\n\t}\n\n\thandleRows(): void {\n\t\tthrow Error(\"Crdt tree updates cannot be handled by Json data handler\")\n\t}\n\n\thandleConfirmRows(): void {\n\t\tthrow Error(\"Crdt tree updates cannot be handled by Json data handler\")\n\t}\n\n\tget hasUpdatesToProcess() {\n\t\treturn !this.waitingForTree && this.remoteUpdates.length > 0\n\t}\n\n\thandleInit(treeVersion: number, initialUpdates: number) {\n\t\tthis.remoteUpdates.length = 0\n\t\tconst needsDownload = this.treeSync.handleInit(treeVersion, initialUpdates)\n\t\treturn { needsDownload }\n\t}\n\n\thandleTreeVerify(url: string, version: number, hash: number) {\n\t\t// Ignore verify messages while the treeSync is not ready.\n\t\tif (!this.treeSync.isReady) return\n\n\t\t// When manually verifying trees, we force a snapshot from the server,\n\t\t// which will also send a treeVerify message. That is why handling can\n\t\t// be suppressed completely, or for a specific version.\n\t\tif (this.ignoreTreeVerifies) return\n\t\tif (this.ignoreTreeVerifyVersion === version) return\n\n\t\tif (!this.treeSync.verify(version, hash)) {\n\t\t\t// Report to the console a deeper check as to what is different.\n\t\t\tconst localTree = this.treeSync.timeline.getTreeForVersion(version)\n\t\t\tif (localTree) {\n\t\t\t\tconst entries = this.treeSync.timeline.entries.slice()\n\t\t\t\tvoid this.verifyLocalTreeWithServer(url, localTree, version, entries)\n\t\t\t}\n\n\t\t\tthis.remoteUpdates.length = 0\n\t\t\tthis.treeSync.hasError = true\n\t\t\tconst error = new Error(\"Local document out of sync with document on server.\")\n\t\t\tthis.callbacks.error(error)\n\t\t\treturn\n\t\t}\n\n\t\t// Always download and fully verify the tree from the server if in a tunnel or running on development.\n\t\tconst tunnel = getTunnelId()\n\t\tconst development = hostInfo.isDevelopment || hostInfo.isLocal\n\n\t\tif (tunnel || development) {\n\t\t\tconst localTree = this.treeSync.timeline.getTreeForVersion(version)\n\t\t\tif (localTree) {\n\t\t\t\tconst entries = this.treeSync.timeline.entries.slice()\n\t\t\t\tvoid this.verifyLocalTreeWithServer(url, localTree, version, entries)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async verifyLocalTreeWithServer(\n\t\turl: string,\n\t\tlocalTree: CanvasTree,\n\t\tversion: number,\n\t\tentries: TimelineEntry[],\n\t): Promise<void> {\n\t\ttry {\n\t\t\tconst documentURL = url.replace(/\\d+\\.json/u, version + \".json\")\n\t\t\tconst jsonResponse = await fetch(documentURL, await accessTokenRefresher.withAuthorizationHeader({}))\n\n\t\t\tif (!jsonResponse.ok) {\n\t\t\t\tthrow Error(`unable to fetch document json: ${jsonResponse.status} ${jsonResponse.statusText}`)\n\t\t\t}\n\t\t\tconst loadedData = await jsonResponse.text()\n\t\t\tconst remoteTree = await this.loadServerTree(loadedData, documentURL, version)\n\t\t\tthis.compareTreeWithServerJson(localTree, remoteTree, version, entries)\n\t\t} catch (e) {\n\t\t\tverifyLog.error(\"Error:\", e)\n\t\t}\n\t}\n\n\tprivate compareTreeWithServerJson(\n\t\tlocalTree: CanvasTree,\n\t\tremoteTree: CanvasTree,\n\t\tversion: number,\n\t\tentries?: TimelineEntry[],\n\t): Error | undefined {\n\t\tverifyLog.debug(\n\t\t\t\"local:\",\n\t\t\tlocalTree.computeTreeHash(),\n\t\t\tlocalTree.size(),\n\t\t\t\"remote:\",\n\t\t\tremoteTree.computeTreeHash(),\n\t\t\tremoteTree.size(),\n\t\t\t\"version:\",\n\t\t\tversion,\n\t\t)\n\n\t\tlet error: Error | undefined\n\t\tconst differences: string[] = []\n\t\tconst different = findTreeDifferences(localTree, remoteTree, differences)\n\t\tif (different) {\n\t\t\tverifyLog.warn(\"trees are different\\n\" + differences.join(\"\\n\"))\n\t\t\tif (entries) {\n\t\t\t\tverifyLog.debug(\"timeline.entries\", entries)\n\t\t\t}\n\t\t\terror = Error(\"Local document different from server document.\")\n\t\t\tverifyLog.reportError(error, { differences, changes: entries?.slice(-25).map(e => e.changes) })\n\t\t} else if (differences.length > 0) {\n\t\t\tverifyLog.debug(\"trees have warnings:\\n\" + differences.join(\"\\n\"))\n\t\t} else {\n\t\t\tverifyLog.debug(\"trees are same\")\n\t\t}\n\t\treturn error\n\t}\n\n\thandleTreeUpdate(update: ServerTreeUpdate) {\n\t\tthis.remoteUpdates.push(update)\n\t}\n\n\tprocessRemoteUpdates() {\n\t\tif (this.treeSync.waitingForTree) return\n\n\t\tlet handlingRemoteUpdate: ServerTreeUpdate | undefined\n\n\t\ttry {\n\t\t\tassert(!this.timeline.tree.hasUncommittedChanges(), \"tree must not have uncommitted changes\")\n\n\t\t\tif (this.shouldCrashFromDebug) {\n\t\t\t\tthis.shouldCrashFromDebug = false\n\t\t\t\tthrow Error(\"RemoteDocument CrashTest\")\n\t\t\t}\n\n\t\t\t// Process all updates from the queue, if any.\n\t\t\twhile (this.remoteUpdates.length > 0) {\n\t\t\t\tconst update = this.remoteUpdates.shift()\n\t\t\t\tif (!update) break\n\n\t\t\t\thandlingRemoteUpdate = update\n\t\t\t\tthis.ensureAllScopesAreLoaded(update)\n\t\t\t\tthis.treeSync.handleRemoteUpdate(update)\n\n\t\t\t\tif (this.loader && !isServerTreeUpdateReply(update)) {\n\t\t\t\t\tthis.loader.addNodeChanges(update.changes)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Same as above, but for the new loader.\n\t\t\tif (this.loader) {\n\t\t\t\tconst scopes = this.timeline.tree.root.children\n\t\t\t\tconst hasScopeToShow = scopes.some(\n\t\t\t\t\tpage => canRenderPageTypeBeforeDocumentHasFullyLoaded(page) && page.isValid(),\n\t\t\t\t)\n\t\t\t\tif (!hasScopeToShow) {\n\t\t\t\t\t// If there is no page we can showing the user, we force load the whole document\n\t\t\t\t\t// in one go. This is a brute force solution, but should be rare.\n\t\t\t\t\tlog.info(\"cannot show any page, forcing load of next page\")\n\t\t\t\t\tconst shallowScope = scopes.find(scope => canRenderPageTypeBeforeDocumentHasFullyLoaded(scope))\n\t\t\t\t\tif (!shallowScope) throw Error(\"No scope to load\")\n\t\t\t\t\tconst scope = this.loader.loadScope(shallowScope.id)\n\t\t\t\t\tif (!scope) throw Error(\"Unable to load scope\")\n\t\t\t\t\tthis.treeSync.loadOneScope(scope, false)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.callbacks.updateProcessed(this.timeline.tree)\n\t\t} catch (errorValue) {\n\t\t\tconst error = reportableError(errorValue)\n\t\t\tthis.remoteUpdates.length = 0\n\t\t\tlog.error(\"Error processing remote updates:\", error)\n\t\t\tlog.debug(\"Last update:\", handlingRemoteUpdate)\n\t\t\tthis.treeSync.error(error.message)\n\n\t\t\tthis.callbacks.errorRecoverable()\n\t\t\tthrow error\n\t\t}\n\t}\n\n\tprivate ensureAllScopesAreLoaded(update: ServerTreeUpdate) {\n\t\tif (!this.loader) return\n\t\tif (!isServerTreeUpdateOther(update)) return\n\n\t\tconst scopesToLoad = new Set<string>()\n\n\t\tfor (const changes of update.changes) {\n\t\t\tif (!changes.previousScope) continue\n\t\t\tscopesToLoad.add(changes.previousScope)\n\t\t\tscopesToLoad.add(changes.to.parentid)\n\t\t}\n\n\t\tfor (const scope of scopesToLoad) {\n\t\t\tif (this.loader.hasLoadedScope(scope)) continue\n\t\t\tconst scopeData = this.loader.loadScope(scope)\n\t\t\tif (!scopeData) continue\n\t\t\tthis.treeSync.loadOneScope(scopeData, false)\n\t\t}\n\t}\n\n\tcreateLoader(url: string, treeVersion: number, settings: DocumentLoaderSettings) {\n\t\tthis.loader?.scheduler.cancel()\n\t\tconst loader = new DocumentLoader(this.componentLoader, treeVersion, url, settings)\n\n\t\tthis.loader = loader\n\t\treturn this.loader\n\t}\n\n\t/** For testing purposes. */\n\tasync verifyTreeWithServer(): Promise<number> {\n\t\tconst path = `/projects/${this.projectId}/tree/latest?forceSnapshot=true`\n\t\t// TODO: Remove the conditional once we've fully migrated to FramerMultiplayerService\n\t\tconst documentURL = this.isMPSSocket ? new URL(getMultiplayerServiceURL(path)) : new URL(path, window.location.href)\n\t\tlet jsonResponse: Response\n\n\t\ttry {\n\t\t\t// Temporarily ignore all tree verify messages in handleTreeVerify.\n\t\t\tthis.ignoreTreeVerifies = true\n\t\t\tjsonResponse = await fetch(documentURL, await accessTokenRefresher.withAuthorizationHeader({}))\n\t\t} finally {\n\t\t\tthis.ignoreTreeVerifies = false\n\t\t}\n\n\t\tif (!jsonResponse.ok) {\n\t\t\tthrow Error(`unable to fetch document json: ${jsonResponse.status} ${jsonResponse.statusText}`)\n\t\t}\n\n\t\tconst etag = jsonResponse.headers.get(\"etag\") || \"\"\n\t\tconst version = Number.parseInt(etag.match(/Version-(\\d+)/u)?.[1] ?? \"0\", 10)\n\t\tif (!Number.isFinite(version) || version <= 0) {\n\t\t\tthrow Error(`unable to parse document tree version from: ${etag}`)\n\t\t}\n\n\t\tconst delta = this.treeSync.treeVersion - version\n\t\tconst localTree = this.treeSync.timeline.getTreeForVersion(version)\n\t\tif (!localTree) {\n\t\t\tthrow Error(`unable to get the local tree for version ${version}`)\n\t\t}\n\n\t\t// Ignore a specific version in handleTreeVerify.\n\t\tthis.ignoreTreeVerifyVersion = version\n\t\tconst treeData = await jsonResponse.text()\n\t\tconst remoteTree = await this.loadServerTree(treeData, documentURL.toString(), version)\n\t\tconst error = this.compareTreeWithServerJson(localTree, remoteTree, version)\n\t\tif (error) throw error\n\t\treturn delta\n\t}\n\n\tprivate async loadServerTree(treeData: string | Uint8Array, url: string, version: number): Promise<CanvasTree> {\n\t\tassert(!treeData || isString(treeData), \"treeData must be a string\")\n\n\t\tlet remoteTree: CanvasTree | undefined = undefined\n\t\tconst loader = new DocumentLoader(this.componentLoader, version, url, {\n\t\t\tpartialParsing: true,\n\t\t\tloadInBackground: true,\n\t\t\tloadedData: treeData,\n\t\t\trequestIdleCallback: this.callbacks.requestIdleCallback,\n\t\t})\n\t\tloader.on(\"loadedFirstData\", tree => {\n\t\t\tloader.on(\"loadedScope\", scope => {\n\t\t\t\tconst position = tree.root.children.findIndex(node => node.id === scope.id)\n\t\t\t\ttree.remove(scope.id)\n\t\t\t\ttree.insertNode(scope, tree.root.id, position)\n\t\t\t\ttree = tree.commit(this.componentLoader)\n\t\t\t})\n\t\t\tloader.on(\"loadedAllData\", () => {\n\t\t\t\tremoteTree = tree\n\t\t\t})\n\t\t})\n\t\tawait loader.start()\n\t\tassert(remoteTree, \"loadedAllData not called\")\n\t\treturn remoteTree\n\t}\n\n\t// Expose TreeSync methods needed by RemoteDocument\n\tget localUpdatesInFlight() {\n\t\treturn this.treeSync.localUpdatesInFlight\n\t}\n\n\tget localUpdatesAtInit() {\n\t\treturn this.treeSync.localUpdatesAtInit\n\t}\n\n\tget hasError(): boolean {\n\t\treturn this.treeSync.hasError\n\t}\n\n\t/** Called before page unload to create any pending updates that need to be sent */\n\tflushUpdates(remote: SendMessage): boolean {\n\t\tif (this.localUpdatesInFlight.length === 0) return false\n\n\t\tconst update = this.treeSync.createUpdateToSend()\n\t\tif (update) {\n\t\t\tremote.sendMessage({ type: \"treeUpdate\", value: asClientTreeUpdate(update) })\n\t\t}\n\n\t\treturn true\n\t}\n\n\t/** Resends previous local updates when network becomes ready */\n\tresendUnconfirmedUpdates(remote: SendMessage): void {\n\t\tassert(this.isReady)\n\n\t\tconst previousLocalUpdates = this.localUpdatesAtInit.filter(u => !u.confirmed)\n\t\tif (previousLocalUpdates.length === 0) return\n\n\t\t// At this point, we also re-send all previous inflight updates. While they have been sent, we don't\n\t\t// know when and why the connection dropped and if the server has received and/or processed these.\n\t\tlog.debug(\"resending local updates:\", previousLocalUpdates.length)\n\t\tfor (const update of previousLocalUpdates) {\n\t\t\tremote.sendMessage({ type: \"treeUpdate\", value: asClientTreeUpdate(update) })\n\t\t}\n\t}\n\n\tcancelAndClearLoader() {\n\t\tthis.loader?.scheduler.cancel()\n\t\tthis.loader = undefined\n\t}\n\n\tfinishLoading() {\n\t\tthis.loader = undefined\n\t}\n\n\tloadOneScope(scope: CanvasNode, keepChanges: boolean): CanvasTree {\n\t\treturn this.treeSync.loadOneScope(scope, keepChanges)\n\t}\n\n\tloadedAllScopes() {\n\t\tthis.treeSync.loadedAllScopes()\n\t}\n\n\thasUnconfirmedChanges(): boolean {\n\t\treturn this.treeSync.getUnconfirmedChangeCount() > 0\n\t}\n\n\tresetTreesForRecovery(_activeScopeId: string): CanvasTree {\n\t\treturn this.treeSync.resetTreesForRecovery()\n\t}\n\n\terror(message: string) {\n\t\treturn this.treeSync.error(message)\n\t}\n}\n", "import { getDayRelativeToToday } from \"./getDayRelativeToToday.ts\"\n\nexport const dateRangePresets = {\n\tToday: 0,\n\tYesterday: 1,\n\t\"Last 7 days\": 7,\n\t\"Last 30 days\": 30,\n\t\"Last 90 days\": 90,\n} as const\n\nexport type DateRangePreset = keyof typeof dateRangePresets\n\nexport function getDateRangeForPreset(preset: DateRangePreset): [fromDay: string, toDay: string] {\n\tconst days = dateRangePresets[preset]\n\tconst today = getDayRelativeToToday(0)\n\n\tif (preset === \"Yesterday\") {\n\t\tconst yesterday = getDayRelativeToToday(-1)\n\t\treturn [yesterday, yesterday]\n\t}\n\n\tif (days === 0) {\n\t\treturn [today, today]\n\t}\n\n\treturn [getDayRelativeToToday(-days), today]\n}\n", "import { type ReversibleNodeChange, collapseChanges } from \"document/models/CanvasTree/TreeDiff.ts\"\nimport type { TreeUpdater } from \"document/models/CanvasTree/TreeUpdater.ts\"\nimport { isUndefined } from \"utils/typeChecks.ts\"\n\nexport interface BaseUndoRedoEntry {\n\tchanges: ReversibleNodeChange[]\n}\n\nexport abstract class BaseUndoRedoManager<UndoRedoEntry extends BaseUndoRedoEntry> {\n\tprotected readonly undoBuffer: UndoRedoEntry[] = []\n\tprotected readonly redoBuffer: UndoRedoEntry[] = []\n\n\tprotected readonly undoGroup: number[] = []\n\tprotected scheduledEndUndoGroup: number | undefined\n\n\tcanUndo() {\n\t\treturn this.undoBuffer.length > 0\n\t}\n\n\tpeekUndo() {\n\t\treturn this.undoBuffer.at(-1)\n\t}\n\n\tundo(updater: TreeUpdater, metadata?: Omit<Partial<UndoRedoEntry>, \"changes\">) {\n\t\tconst entry = this.undoBuffer.pop()\n\t\tif (isUndefined(entry)) return\n\n\t\tupdater.applyReverseChanges(entry.changes)\n\n\t\tthis.redoBuffer.push({ ...entry, ...metadata })\n\n\t\t// push undo group boundaries back if there are any open groups\n\t\tconst max = this.undoBuffer.length\n\t\tthis.undoGroup.forEach((group, index) => {\n\t\t\tthis.undoGroup[index] = Math.min(group, max)\n\t\t})\n\n\t\treturn entry\n\t}\n\n\tcanRedo() {\n\t\treturn this.redoBuffer.length > 0\n\t}\n\n\tpeekRedo() {\n\t\treturn this.redoBuffer.at(-1)\n\t}\n\n\tredo(updater: TreeUpdater, metadata?: Omit<Partial<UndoRedoEntry>, \"changes\">) {\n\t\tconst entry = this.redoBuffer.pop()\n\t\tif (isUndefined(entry)) return\n\n\t\tupdater.applyChanges(entry.changes)\n\n\t\tthis.undoBuffer.push({ ...entry, ...metadata })\n\n\t\treturn entry\n\t}\n\n\tbeginUndoGroup() {\n\t\tthis.undoGroup.push(this.undoBuffer.length)\n\t}\n\n\t/**\n\t * Discards all changes in the current undo group and reverts the tree back to the start.\n\t */\n\tdiscardUndoGroup(updater: TreeUpdater) {\n\t\tconst group = this.undoGroup.pop()\n\t\tif (isUndefined(group)) return\n\t\tif (group >= this.undoBuffer.length) return\n\n\t\tconst edits = this.undoBuffer.splice(group)\n\t\tconst changes = collapseChanges(edits)\n\n\t\tupdater.applyReverseChanges(changes)\n\n\t\treturn edits[0]\n\t}\n\n\t/**\n\t * Schedules the end of the current undo group.\n\t */\n\tscheduleEndUndoGroup() {\n\t\tconst group = this.undoGroup.pop()\n\t\tif (isUndefined(group)) return\n\t\tif (group >= this.undoBuffer.length) return\n\n\t\tthis.scheduledEndUndoGroup = group\n\t}\n\n\t/**\n\t * Processes the scheduled end of the undo group, collapsing all changes into a single entry.\n\t */\n\tprocessScheduledEndUndoGroup(metadata: Omit<UndoRedoEntry, \"changes\">) {\n\t\tconst group = this.scheduledEndUndoGroup\n\t\tthis.scheduledEndUndoGroup = undefined\n\n\t\tif (isUndefined(group)) return\n\t\tif (group >= this.undoBuffer.length) return\n\n\t\tconst edits = this.undoBuffer.splice(group)\n\t\tconst changes = collapseChanges(edits)\n\t\tthis.undoBuffer.push({ changes, ...metadata } as UndoRedoEntry)\n\t}\n\n\tclearUndoStack() {\n\t\tthis.undoBuffer.length = 0\n\t\tthis.redoBuffer.length = 0\n\t\tthis.undoGroup.length = 0\n\t\tthis.scheduledEndUndoGroup = undefined\n\t}\n\n\taddUndoEntry(entry: UndoRedoEntry) {\n\t\tthis.undoBuffer.push(entry)\n\t\tthis.redoBuffer.length = 0\n\t}\n\n\tgetUndoBufferSize(): number {\n\t\treturn this.undoBuffer.length\n\t}\n}\n"],
  "mappings": "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",
  "names": ["log", "scope", "log", "log", "log", "filterCache", "normalize", "log", "verifyLog", "MAX_INFLIGHT_MESSAGES", "scope"]
}
