{
  "version": 3,
  "sources": ["../../src/canvas-sandbox-renderer/sandboxConfig.ts", "../../src/document/models/CanvasTree/PartialTreeSender.ts", "../../src/app/FlagsUpdater.ts"],
  "sourcesContent": ["import { assertAllowSameOriginForSandboxApp, sandboxAppRelativeToEditor } from \"@framerjs/framer-environment/domains.ts\"\nimport { assert, Lazy, getServiceMap } from \"@framerjs/shared\"\nimport { environment } from \"environment/index.ts\"\nimport { editorPath } from \"utils/platformPaths.ts\"\n\n/**\n * Configuration for a sandbox entrypoint. Note that correctly supporting `origin` is crucial for security.\n */\nexport interface SandboxConfig {\n\turl: string\n\torigin: string\n\tisCrossOriginProtected: boolean\n}\n\n/**\n * Configuration for standard sandbox entrypoints\n */\nexport const sandboxConfig = Lazy({\n\tcanvas: (): SandboxConfig => {\n\t\tconst sandboxPath = relativePathToCanvasSandbox()\n\t\tassertAllowSameOriginForSandboxApp(sandboxPath)\n\t\treturn sandboxAppRelativeToEditor(sandboxPath)\n\t},\n\tonPageCanvas: (): SandboxConfig => {\n\t\tconst sandboxPath = relativePathToOnPageCanvasSandbox()\n\t\tassertAllowSameOriginForSandboxApp(sandboxPath)\n\n\t\tconst { url, isCrossOriginProtected } = sandboxAppRelativeToEditor(sandboxPath)\n\n\t\t// In Vekter, FramerWeb partitions the canvas domain by project ID\n\t\t// On-Page, FramerWeb is not aware of which project is being requested,\n\t\t// therefore we partition client-side.\n\t\tconst partitionedUrl = partitionOnPageCanvasSandboxDomain(url)\n\n\t\treturn {\n\t\t\turl: partitionedUrl,\n\t\t\torigin: new URL(partitionedUrl).origin,\n\t\t\tisCrossOriginProtected,\n\t\t}\n\t},\n\tpatch: (): SandboxConfig => {\n\t\tconst sandboxPath = relativePathToPatchSandbox()\n\t\tassertAllowSameOriginForSandboxApp(sandboxPath)\n\t\treturn sandboxAppRelativeToEditor(sandboxPath)\n\t},\n})\n\n// Returns the relative location of the sandbox entry point, configured with the necessary services.\n// Should be used in conjunction with sandboxAppRelativeToEditor.\nfunction relativePathToCanvasSandbox() {\n\tconst filename = environment.isDebugBuild ? \"canvas-sandbox.debug.html\" : \"canvas-sandbox.html\"\n\tconst path = editorPath(`./${filename}`)\n\tconst servicesJSON = JSON.stringify(getServiceMap())\n\treturn path + `#services=${encodeURIComponent(servicesJSON)}`\n}\n\nfunction relativePathToOnPageCanvasSandbox() {\n\tconst filename = environment.isDebugBuild ? \"canvas-sandbox-on-page.debug.html\" : \"canvas-sandbox-on-page.html\"\n\tconst path = editorPath(`./${filename}`)\n\tconst servicesJSON = JSON.stringify(getServiceMap())\n\treturn path + `#services=${encodeURIComponent(servicesJSON)}`\n}\n\nfunction relativePathToPatchSandbox() {\n\tconst filename = environment.isDebugBuild ? \"patch-sandbox.debug.html\" : \"patch-sandbox.html\"\n\tconst path = editorPath(`./${filename}`)\n\tconst servicesJSON = JSON.stringify(getServiceMap())\n\treturn path + `#services=${encodeURIComponent(servicesJSON)}`\n}\n\nexport const MAX_HOSTNAME_PART_LENGTH = 63\n\nfunction partitionOnPageCanvasSandboxDomain(original: string): string {\n\tconst url = new URL(original)\n\n\t// Don't partition on localhost. When running in debug or integration tests.\n\tif (url.hostname === \"localhost\" || url.hostname === \"127.0.0.1\") return original\n\n\tconst subdomainDepth = url.hostname.split(\".\").length - 2\n\n\t// Don't partition on preview links (`****.beta.framercanvas.com`)\n\tif (subdomainDepth >= 2) return original\n\n\tconst framerSiteId = new URLSearchParams(window.location.search).get(\"framerSiteId\")\n\tassert(framerSiteId, \"onPageCanvas must be loaded with a framerSiteId\")\n\n\tconst partitionId = `site-${framerSiteId}`.slice(0, MAX_HOSTNAME_PART_LENGTH)\n\turl.hostname = `${partitionId}.${url.hostname}`\n\n\treturn url.toString()\n}\n", "import { assert, ResolvablePromise, emptyArray, getLogger } from \"@framerjs/shared\"\nimport { CanvasTreeVersion } from \"document/models/CanvasTree/CanvasTreeVersion.ts\"\nimport type { NodeChange } from \"document/models/CanvasTree/TreeDiff.ts\"\nimport type { ChangeTrackingCursor, TreeUpdater } from \"document/models/CanvasTree/TreeUpdater.ts\"\nimport type { CanvasNode, CanvasTree, NodeID } from \"document/models/CanvasTree/index.ts\"\nimport { isScopeNode } from \"document/models/CanvasTree/index.ts\"\nimport { treeLibrary } from \"./FastTreeLibrary.ts\"\nimport { COLOR_STYLE_TOKEN_LIST_NODE_ID } from \"./nodes/ColorStyleTokenListNode.ts\"\nimport { CONTENT_MANAGEMENT_ID } from \"./nodes/ContentManagementNode.ts\"\nimport { EXTERNAL_MODULES_LIST_ID } from \"./nodes/ExternalModuleNode.ts\"\nimport { LOCAL_MODULES_LIST_ID } from \"./nodes/LocalModuleNode.ts\"\nimport { specialSerializationProperties } from \"./nodes/MutableNode.ts\"\nimport { PRESETS_LIST_NODE_ID } from \"./nodes/PresetsListNode.ts\"\nimport { ROUTE_SEGMENT_ROOT_NODE_ID } from \"./nodes/RouteSegmentRootNode.ts\"\n\nconst MAX_SCOPE_BUFFER_SIZE = 3\n\nconst log = getLogger(\"PartialTreeSender\")\n\nconst emptyChildren = emptyArray<AnyTreeNode>()\n\ninterface AnyTree {\n\tversion: number\n\troot: AnyTreeNode\n\t[key: string]: any\n}\n\nexport interface AnyTreeNode {\n\tid: string\n\t__class: string\n\tchildren?: AnyTreeNode[]\n\t[key: string]: any\n}\n\nexport interface TreeChunk {\n\tchunkId: string\n\tchunkIndex: number\n\ttotalChunks: number\n\tnodes: Map<string, AnyTreeNode>\n\tchildrenMap: Map<string, string[]>\n\trootId?: string\n}\n\nexport interface PartialTreeUpdate {\n\tname?: string\n\ttree?: AnyTree\n\tchanges?: NodeChange[]\n\tscopes?: AnyTreeNode[]\n\ttimestamp?: number\n\ttreeChunks?: TreeChunk\n}\n\ninterface ChunkingConfig {\n\tmaxNodesPerChunk: number\n}\n\n/*\n We have a custom tick function to ensure we yield control back to the event loop\n so that chunks are processed in a timely manner by the iframe,\n using shared scheduler yield here will cause all chunks be sent in one go, and then iframe will receive them all at once.\n\n We need to ensure that the iframe has a chance to process each chunk before the next one:\n        [PartialTreeSender] sandbox-754 sending chunk 1 of 16\n        [PartialTreeSender] sandbox-754 sending chunk 2 of 16\n        [canvas-sandbox:PartialTreeReceiver] sandbox-754 starting new chunked transfer: 16 chunks expected\n        [canvas-sandbox:PartialTreeReceiver] sandbox-754 received chunk 1/16\n        [canvas-sandbox:PartialTreeReceiver] sandbox-754 received chunk 2/16\n        [PartialTreeSender] sandbox-754 sending chunk 3 of 16\n        [PartialTreeSender] sandbox-754 sending chunk 4 of 16\n        [PartialTreeSender] sandbox-754 sending chunk 5 of 16\n        [canvas-sandbox:PartialTreeReceiver] sandbox-754 received chunk 3/16\n        [canvas-sandbox:PartialTreeReceiver] sandbox-754 received chunk 4/16\n        [PartialTreeSender] sandbox-754 sending chunk 6 of 16\n*/\nfunction tick() {\n\treturn new Promise<void>((resolve, reject) => {\n\t\tif (typeof MessageChannel !== \"undefined\") {\n\t\t\tconst channel = new MessageChannel()\n\t\t\tchannel.port1.onmessage = () => resolve()\n\t\t\tchannel.port1.onmessageerror = () => reject()\n\t\t\tchannel.port2.postMessage(null)\n\t\t} else {\n\t\t\tsetTimeout(resolve, 0)\n\t\t}\n\t})\n}\n\ntype Chunk = PartialTreeUpdate & {\n\ttreeChunks: TreeChunk\n}\n\n/** This class takes a timeline, the current scope, and generates a serialized tree and node\n * changes. But in such a way that the recever is maintaining a tree with only a few scopes. We use\n * this for the sandbox and preview iframes. These don't need a full tree to function, and\n * communicating huge trees takes a long time, and then take a lot of memory.\n *\n * Not to be confused with a feature called Partial Document Loading.\n * */\nexport class PartialTreeSender {\n\t// A unique name so we can differentiate the different senders and receivers as they log.\n\tprivate readonly name\n\n\tcurrentScopeId: NodeID = \"\"\n\tprivate timelineCursor: ChangeTrackingCursor | undefined\n\tprivate scopeBufferMap = new Map<NodeID, number>()\n\n\t// Chunking state for large tree transfers\n\tprivate chunkQueue: Chunk[] = []\n\tprivate chunkIndex = 0\n\tprivate chunkingConfig: ChunkingConfig\n\tdrainingPromise?: ResolvablePromise<void>\n\n\t/** Requires a timeline and a name. The name is only used in the logs. */\n\tconstructor(\n\t\tprivate timeline: TreeUpdater,\n\t\tname: string,\n\t\tchunkingConfig?: Partial<ChunkingConfig>,\n\t) {\n\t\tthis.name = name + \"-\" + String(Math.round(Math.random() * 1000))\n\t\tthis.chunkingConfig = {\n\t\t\tmaxNodesPerChunk: chunkingConfig?.maxNodesPerChunk ?? 1000,\n\t\t}\n\t}\n\n\tprivate shouldUseChunking(tree: CanvasTree, scopeId?: NodeID): boolean {\n\t\t// Get chunking hints from tree (persists after loader is removed)\n\t\tif (this.timelineCursor) return false // No chunking if we have a cursor, we are in diff mode\n\t\tconst chunkingHints = tree.chunkingHints\n\n\t\t// If no hints provided, default to no chunking\n\t\tif (!chunkingHints || chunkingHints.size === 0) {\n\t\t\treturn false\n\t\t}\n\n\t\t// Check if current scope or any scope we're sending needs chunking\n\t\tconst scopeIds = getScopesToAlwaysInclude()\n\t\tif (scopeId) scopeIds.add(scopeId)\n\n\t\tfor (const id of scopeIds) {\n\t\t\tif (chunkingHints.has(id)) {\n\t\t\t\tlog.debug(this.name, `chunking required - large page hint: ${id}`)\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\n\t\treturn false\n\t}\n\n\tprivate serializeTreeChunks(tree: CanvasTree, scopeId?: NodeID): Chunk[] {\n\t\tconst chunks: Chunk[] = []\n\t\tconst chunkId = crypto.randomUUID()\n\t\tlet currentNodes = new Map<string, AnyTreeNode>()\n\t\tlet currentChildrenMap = new Map<string, string[]>()\n\t\tlet totalNodes = 0\n\n\t\t// Get scopes to serialize (same logic as serializeTree)\n\t\tconst scopeIds = getScopesToAlwaysInclude()\n\t\tif (scopeId) scopeIds.add(scopeId)\n\n\t\t// Helper to flush current chunk\n\t\tconst flushChunk = () => {\n\t\t\tif (currentNodes.size > 0) {\n\t\t\t\tchunks.push({\n\t\t\t\t\tname: this.name,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\ttreeChunks: {\n\t\t\t\t\t\tchunkId,\n\t\t\t\t\t\tchunkIndex: chunks.length,\n\t\t\t\t\t\ttotalChunks: -1, // Will be set after all chunks created\n\t\t\t\t\t\tnodes: currentNodes,\n\t\t\t\t\t\tchildrenMap: currentChildrenMap,\n\t\t\t\t\t\trootId: chunks.length === 0 ? tree.root.id : undefined,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tcurrentNodes = new Map()\n\t\t\t\tcurrentChildrenMap = new Map()\n\t\t\t}\n\t\t}\n\t\tconst traverseNode = (node: CanvasNode) => {\n\t\t\t// Serialize node only when adding to chunk\n\t\t\tconst serialized = treeLibrary.valueFromNode(node)\n\t\t\tcurrentNodes.set(node.id, serialized)\n\n\t\t\t// Track children relationships\n\t\t\tconst childIds = node.children ? node.children.map(c => c.id) : []\n\t\t\tcurrentChildrenMap.set(node.id, childIds)\n\t\t\ttotalNodes++\n\n\t\t\t// Clear children from serialized node to avoid duplication\n\t\t\tif (serialized.children) {\n\t\t\t\tserialized.children = emptyChildren as AnyTreeNode[]\n\t\t\t}\n\n\t\t\t// Flush chunk if it's full\n\t\t\tif (currentNodes.size >= this.chunkingConfig.maxNodesPerChunk) {\n\t\t\t\tflushChunk()\n\t\t\t}\n\n\t\t\t// Recursively process children\n\t\t\tif (node.children) {\n\t\t\t\tfor (const child of node.children) {\n\t\t\t\t\ttraverseNode(child)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// First, create and add the root node (same as serializeTree does)\n\t\tconst root = tree.root\n\t\tlet key: keyof typeof root\n\t\tconst rootProperties: Record<string, unknown> = {}\n\t\tfor (key in root) {\n\t\t\tif (specialSerializationProperties[key]) continue\n\t\t\trootProperties[key] = root[key]\n\t\t}\n\n\t\tconst scopeNodes = tree.getNodes(scopeIds)\n\t\tconst rootNode: AnyTreeNode = {\n\t\t\t...rootProperties,\n\t\t\t__class: \"RootNode\",\n\t\t\tid: tree.root.id,\n\t\t\tchildren: emptyChildren as AnyTreeNode[], // Will be cleared anyway in chunks\n\t\t}\n\n\t\tcurrentNodes.set(tree.root.id, rootNode)\n\t\tcurrentChildrenMap.set(\n\t\t\ttree.root.id,\n\t\t\tscopeNodes.map(scope => scope.id),\n\t\t)\n\t\ttotalNodes++\n\n\t\t// Check if we need to flush after adding root\n\t\tif (currentNodes.size >= this.chunkingConfig.maxNodesPerChunk) {\n\t\t\tflushChunk()\n\t\t}\n\n\t\t// Then traverse and serialize each scope\n\t\tfor (const scopeNode of scopeNodes) {\n\t\t\ttraverseNode(scopeNode)\n\t\t}\n\n\t\t// Flush any remaining nodes\n\t\tflushChunk()\n\n\t\t// Update total chunks count\n\t\tconst totalChunks = chunks.length\n\t\tfor (const chunk of chunks) {\n\t\t\tchunk.treeChunks.totalChunks = totalChunks\n\t\t}\n\n\t\tlog.debug(this.name, `directly chunked tree into ${chunks.length} chunks with ${totalNodes} total nodes`)\n\t\treturn chunks\n\t}\n\n\tgetNextChunk(): Chunk | undefined {\n\t\tif (this.chunkQueue.length === 0) return undefined\n\n\t\tconst chunk = this.chunkQueue[this.chunkIndex]\n\t\tthis.chunkIndex++\n\n\t\tif (this.chunkIndex >= this.chunkQueue.length) {\n\t\t\t// All chunks sent, reset state\n\t\t\tthis.chunkQueue = []\n\t\t\tthis.chunkIndex = 0\n\t\t}\n\n\t\treturn chunk\n\t}\n\n\thasMoreChunks(): boolean {\n\t\tassert(this.chunkIndex >= 0, \"Chunk index should not be negative\")\n\t\tassert(this.chunkIndex <= this.chunkQueue.length, \"Chunk index should not exceed queue length\")\n\t\treturn this.chunkIndex < this.chunkQueue.length\n\t}\n\n\tasync *drainChunks(signal?: AbortSignal): AsyncGenerator<PartialTreeUpdate> {\n\t\tif (!this.hasMoreChunks()) return // No chunks to drain\n\n\t\tconst existingDrainingPromise = this.drainingPromise\n\t\tthis.drainingPromise = new ResolvablePromise()\n\t\tif (existingDrainingPromise) {\n\t\t\tlog.debug(this.name, \"drainChunks already in progress, waiting for it to finish\")\n\t\t\tawait existingDrainingPromise\n\t\t}\n\n\t\tlog.debug(this.name, \"drainChunks started\")\n\n\t\tlet totalChunks = 0\n\t\tconst startTime = performance.now()\n\t\ttry {\n\t\t\twhile (this.hasMoreChunks()) {\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\tlog.debug(this.name, \"drainChunks aborted, clearing chunk queue\")\n\t\t\t\t\tthis.chunkQueue = []\n\t\t\t\t\tthis.chunkIndex = 0\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst chunk = this.getNextChunk()\n\t\t\t\tif (chunk) {\n\t\t\t\t\ttotalChunks++\n\t\t\t\t\tlog.debug(this.name, `sending chunk ${chunk.treeChunks.chunkIndex + 1} of ${chunk.treeChunks.totalChunks}`)\n\t\t\t\t\tyield chunk\n\t\t\t\t}\n\t\t\t\tawait tick()\n\t\t\t}\n\t\t} finally {\n\t\t\tawait tick()\n\t\t\t// now we have the full tree, invalidate the cursor so we can start sending diffs again\n\t\t\tthis.timelineCursor = this.timeline.getChangeTrackingCursor()\n\t\t\tconst sendDuration = performance.now() - startTime\n\t\t\tconst durationStr = sendDuration > 1000 ? `${(sendDuration / 1000).toFixed(2)}s` : `${Math.round(sendDuration)}ms`\n\t\t\tlog.debug(this.name, `completed sending ${totalChunks} chunks in ${durationStr}`)\n\t\t\tlog.debug(this.name, \"drainChunks completed\")\n\t\t\tthis.drainingPromise?.resolve()\n\t\t\tthis.drainingPromise = undefined\n\t\t}\n\t}\n\n\tprivate resetScopeBuffer(scopeId: NodeID | undefined) {\n\t\tthis.scopeBufferMap.clear()\n\t\tthis.currentScopeId = scopeId ?? \"\"\n\t\tif (!scopeId) return\n\t\tthis.timelineCursor = undefined\n\t\tthis.scopeBufferMap.set(scopeId, performance.now())\n\t}\n\n\tprivate updateScopeBuffer(scopeId: NodeID): [toRemove: NodeID | undefined, toAdd: NodeID | undefined] {\n\t\tif (this.currentScopeId === scopeId) return [undefined, undefined]\n\n\t\t// Never switch to the content management scope.\n\t\tif (scopeId === CONTENT_MANAGEMENT_ID) {\n\t\t\treturn [undefined, undefined]\n\t\t}\n\n\t\tthis.currentScopeId = scopeId\n\t\tif (this.scopeBufferMap.has(this.currentScopeId)) {\n\t\t\t// The scope already exists in the buffer, still update it's last use time.\n\t\t\tthis.scopeBufferMap.set(this.currentScopeId, performance.now())\n\t\t\treturn [undefined, undefined]\n\t\t}\n\n\t\tlet toRemove: NodeID | undefined = undefined\n\n\t\t// Remove the least recently used scope if needed.\n\t\tif (this.scopeBufferMap.size >= MAX_SCOPE_BUFFER_SIZE) {\n\t\t\t// Never remove scopes that must always be included.\n\t\t\tconst scopesToAlwaysInclude = getScopesToAlwaysInclude()\n\n\t\t\tlet oldestTime = Infinity\n\t\t\tlet oldestId: string | undefined = undefined\n\t\t\tfor (const [id, time] of this.scopeBufferMap) {\n\t\t\t\tif (scopesToAlwaysInclude.has(id)) continue\n\t\t\t\tif (oldestTime > time) {\n\t\t\t\t\toldestTime = time\n\t\t\t\t\toldestId = id\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (oldestId) {\n\t\t\t\tthis.scopeBufferMap.delete(oldestId)\n\t\t\t\ttoRemove = oldestId\n\t\t\t}\n\t\t}\n\n\t\t// Add the current scope to the buffer.\n\t\tthis.scopeBufferMap.set(this.currentScopeId, performance.now())\n\t\treturn [toRemove, scopeId]\n\t}\n\n\treset(scopeId?: NodeID): AnyTree | null {\n\t\tconst tree = this.timeline.tree\n\t\tthis.resetScopeBuffer(scopeId)\n\n\t\t// Check if chunking is needed using hints (no serialization)\n\t\tif (this.shouldUseChunking(tree, scopeId)) {\n\t\t\t// Directly chunk the tree without serializing first\n\t\t\tthis.chunkQueue = this.serializeTreeChunks(tree, scopeId)\n\t\t\tthis.chunkIndex = 0\n\t\t\tlog.debug(this.name, `initiated direct chunked transfer for tree`)\n\t\t\treturn null\n\t\t}\n\n\t\t// Only serialize if NOT chunking\n\t\tconst serialized = serializeTree(tree, scopeId)\n\t\tthis.chunkQueue = []\n\t\tthis.chunkIndex = 0\n\n\t\treturn serialized\n\t}\n\n\tupdate(scopeId: NodeID | undefined): PartialTreeUpdate {\n\t\tif (!scopeId) return {}\n\n\t\t// If we have chunks queued - wait for them to be drained first\n\t\tif (this.hasMoreChunks()) return {}\n\n\t\tconst tree = this.timeline.tree\n\n\t\t// Get the changes from the timeline, resetting the cursor if we cannot.\n\t\tlet changes = this.timeline.fetchForwardChanges(this.timelineCursor)\n\n\t\tif (!changes) {\n\t\t\t// No need to resend the tree if it was invalidated by load completed document.\n\t\t\tif (this.timeline.invalidatedByLoadCompletedDocument(this.timelineCursor)) {\n\t\t\t\tlog.debug(this.name, \"cursor invalidated, sending empty update for load completed document\")\n\t\t\t\tthis.timelineCursor = this.timeline.getChangeTrackingCursor()\n\t\t\t\treturn {}\n\t\t\t}\n\n\t\t\t// ensure we have a scope buffer for the current scope\n\t\t\tthis.resetScopeBuffer(scopeId)\n\n\t\t\t// Check if we need chunking BEFORE serializing\n\t\t\tif (this.shouldUseChunking(tree, scopeId)) {\n\t\t\t\t// Prepare chunks directly from tree\n\t\t\t\tthis.chunkQueue = this.serializeTreeChunks(tree, scopeId)\n\t\t\t\tthis.chunkIndex = 0\n\n\t\t\t\tconst firstChunk = this.getNextChunk()\n\t\t\t\tif (firstChunk) {\n\t\t\t\t\tlog.debug(this.name, `starting direct chunked resend with ${this.chunkQueue.length} chunks`)\n\t\t\t\t\treturn firstChunk\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Only serialize if not chunking\n\t\t\tconst serialized = serializeTree(tree, scopeId)\n\n\t\t\t// If we are not chunking, we can send the full tree immediately\n\t\t\t// we only invalidate the cursor if we are sending a full tree\n\t\t\t// if we are chunking, we will invalidate the cursor in drainChunks when done\n\t\t\tlog.debug(this.name, \"cursor invalidated, sending tree with scope:\", scopeId)\n\t\t\tthis.timelineCursor = this.timeline.getChangeTrackingCursor()\n\n\t\t\treturn { name: this.name, tree: serialized, timestamp: Date.now() }\n\t\t}\n\n\t\tconst [toRemove, toAdd] = this.updateScopeBuffer(scopeId)\n\t\tif (toRemove) {\n\t\t\tlog.debug(this.name, \"deleting scope by diff:\", toRemove)\n\t\t\tremoveSubTree(toRemove, changes)\n\t\t}\n\t\tif (changes.length === 0) {\n\t\t\tchanges = undefined\n\t\t}\n\n\t\tlet scope: AnyTreeNode | undefined\n\n\t\tif (toAdd) {\n\t\t\tlog.debug(this.name, \"adding scope by subtree:\", toAdd)\n\t\t\tscope = this.getScopeSubTree(tree, toAdd)\n\t\t}\n\n\t\tif (changes) {\n\t\t\tconst scopeIDsAfterCrossScopeMove = this.getAffectedScopeIDsAfterCrossScopeMove(tree, changes)\n\n\t\t\tfor (const crossScopeId of scopeIDsAfterCrossScopeMove) {\n\t\t\t\tif (crossScopeId !== this.currentScopeId) {\n\t\t\t\t\tif (this.scopeBufferMap.has(crossScopeId)) {\n\t\t\t\t\t\tlog.debug(this.name, \"deleting scope due to cross-scope move:\", crossScopeId)\n\t\t\t\t\t\tremoveSubTree(crossScopeId, changes)\n\t\t\t\t\t\tthis.scopeBufferMap.delete(crossScopeId)\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// if we have the scope already we don't need to add it again\n\t\t\t\tif (scope) continue\n\n\t\t\t\tlog.debug(this.name, \"resending tree with scope due to cross-scope move:\", crossScopeId)\n\t\t\t\tscope = this.getScopeSubTree(tree, crossScopeId)\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tchanges,\n\t\t\tscopes: scope ? [scope] : undefined,\n\t\t\ttimestamp: Date.now(),\n\t\t}\n\t}\n\n\tprivate getScopeSubTree(tree: CanvasTree, scopeId: NodeID | undefined): AnyTreeNode | undefined {\n\t\tconst scopeNode = tree.get(scopeId)\n\t\tif (isScopeNode(scopeNode)) {\n\t\t\tassert(scopeNode.isLoaded(), \"Scope must be loaded\")\n\t\t}\n\t\treturn addSubTree(scopeNode)\n\t}\n\n\t/**\n\t ** This gets a set of scopes that were previous parents of nodes that were moved to different scopes\n\t */\n\tprivate getAffectedScopeIDsAfterCrossScopeMove(tree: CanvasTree, changes: NodeChange[]) {\n\t\tconst affectedScopes = new Set<NodeID>()\n\t\tfor (const change of changes) {\n\t\t\tif (!change.previousScope) continue\n\t\t\tif (!change.to.parentid) continue\n\t\t\tconst node = tree.get(change.id)\n\t\t\tconst nextScope = tree.getScopeNodeFor(node)\n\t\t\tif (!nextScope) continue\n\n\t\t\t// we only need to attach the next scope, any previous scope will be removed from the buffer in the update\n\t\t\taffectedScopes.add(nextScope.id)\n\t\t}\n\n\t\treturn affectedScopes\n\t}\n}\n\nfunction removeSubTree(scopeId: NodeID, changes: NodeChange[]) {\n\tchanges.push({\n\t\tid: scopeId,\n\t\tremoved: \"CanvasNode\",\n\t\tto: {},\n\t})\n}\n\nfunction addSubTree(scope: CanvasNode | null | undefined): AnyTreeNode | undefined {\n\tif (!scope) return\n\treturn treeLibrary.valueFromNode(scope)\n}\n\nfunction getScopesToAlwaysInclude(): Set<NodeID> {\n\tconst scopeIds = new Set([\n\t\tPRESETS_LIST_NODE_ID,\n\t\tCOLOR_STYLE_TOKEN_LIST_NODE_ID,\n\t\tROUTE_SEGMENT_ROOT_NODE_ID,\n\t\tLOCAL_MODULES_LIST_ID,\n\t\tEXTERNAL_MODULES_LIST_ID,\n\t\tCONTENT_MANAGEMENT_ID,\n\t])\n\n\treturn scopeIds\n}\n\nfunction serializeTree(tree: CanvasTree, scopeId: NodeID | undefined): AnyTree | (AnyTree & { root: string }) {\n\t// We are always sending the text style presets list nodes. They cannot become the current\n\t// scope, thus never feature in the scopeBufferMap, thus never get removed. But because we are\n\t// sending all diffs, they will get created and updated as needed.\n\tconst scopeIds = getScopesToAlwaysInclude()\n\n\tif (scopeId) {\n\t\tscopeIds.add(scopeId)\n\t}\n\n\tconst children: AnyTreeNode[] = tree.getNodes(scopeIds).map(scope => treeLibrary.valueFromNode(scope))\n\n\tconst root = tree.root\n\tlet key: keyof typeof root\n\tconst rootProperties: Record<string, unknown> = {}\n\tfor (key in root) {\n\t\tif (specialSerializationProperties[key]) continue\n\t\trootProperties[key] = root[key]\n\t}\n\n\treturn {\n\t\tversion: CanvasTreeVersion,\n\t\troot: {\n\t\t\t...rootProperties,\n\t\t\t__class: \"RootNode\",\n\t\t\tid: tree.root.id,\n\t\t\tchildren,\n\t\t},\n\t}\n}\n", "import type { EmployeesOnlySettingsName, ExperimentName } from \"@framerjs/app-config\"\nimport { defaultEmployeesOnlySettings, stableExperiments } from \"@framerjs/app-config\"\nimport { unhandledError } from \"@framerjs/shared\"\nimport { employeesOnlySettings } from \"./employeesOnlySettings.ts\"\nimport { experiments } from \"./experiments.ts\"\nimport type { ProjectFeatureName } from \"./projectFeatures.ts\"\nimport { defaultProjectFeatures, projectFeatures } from \"./projectFeatures.ts\"\n\nexport interface FlagsValues {\n\t[_: string]: string\n}\n\nexport interface FlagsUpdates {\n\tupdateExperiments(experiments: FlagsValues): void\n\tupdateEmployeesOnlySettings(employeesOnlySettings: FlagsValues): void\n\tupdateProjectFeatures(projectFeatures: FlagsValues): void\n}\n\nexport class FlagsUpdater {\n\tprivate readonly experimentListeners = new Map<ExperimentName, (variant: string) => void>()\n\tprivate readonly employeesOnlySettingsListeners = new Map<EmployeesOnlySettingsName, (variant: string) => void>()\n\tprivate readonly projectFeaturesListeners = new Map<ProjectFeatureName, (variant: string) => void>()\n\n\tconstructor(private callbacks: FlagsUpdates) {}\n\n\tstartUpdatesStream() {\n\t\tObject.keys(stableExperiments).forEach((name: ExperimentName) => {\n\t\t\tconst listener = (variant: string) => {\n\t\t\t\tthis.callbacks.updateExperiments({ [name]: variant })\n\t\t\t}\n\t\t\texperiments.addListener(name, listener)\n\t\t\tthis.experimentListeners.set(name, listener)\n\t\t})\n\n\t\tObject.keys(defaultEmployeesOnlySettings).forEach((name: EmployeesOnlySettingsName) => {\n\t\t\tconst listener = (variant: string) => {\n\t\t\t\tthis.callbacks.updateEmployeesOnlySettings({ [name]: variant })\n\t\t\t}\n\t\t\temployeesOnlySettings.addListener(name, listener)\n\t\t\tthis.employeesOnlySettingsListeners.set(name, listener)\n\t\t})\n\t}\n\n\tgetInitialExperiments() {\n\t\t// initial update\n\t\tconst initialExperiments: { [_: string]: string } = {}\n\t\tObject.keys(stableExperiments).forEach((name: ExperimentName) => {\n\t\t\tinitialExperiments[name] = experiments.get(name)\n\t\t})\n\t\treturn initialExperiments\n\t}\n\n\tgetInitialEmployeesOnlySettings() {\n\t\tconst initialEmployeesOnlySettings: { [_: string]: string } = {}\n\t\tObject.keys(defaultEmployeesOnlySettings).forEach((name: EmployeesOnlySettingsName) => {\n\t\t\tinitialEmployeesOnlySettings[name] = employeesOnlySettings.get(name)\n\t\t})\n\t\treturn initialEmployeesOnlySettings\n\t}\n\n\tinitProjectFeatures() {\n\t\tprojectFeatures.updated\n\t\t\t.then(() => {\n\t\t\t\t// Emit the first update\n\t\t\t\tconst result: { [_: string]: string } = {}\n\t\t\t\tObject.keys(defaultProjectFeatures).forEach((name: ProjectFeatureName) => {\n\t\t\t\t\tresult[name] = projectFeatures.get(name)\n\t\t\t\t})\n\t\t\t\tthis.callbacks.updateProjectFeatures(result)\n\n\t\t\t\t// Install handlers if they are not there\n\t\t\t\tif (this.projectFeaturesListeners.size === 0) {\n\t\t\t\t\tObject.keys(defaultProjectFeatures).forEach((name: ProjectFeatureName) => {\n\t\t\t\t\t\tconst listener = (variant: string) => {\n\t\t\t\t\t\t\tthis.callbacks.updateProjectFeatures({ [name]: variant })\n\t\t\t\t\t\t}\n\t\t\t\t\t\tprojectFeatures.addListener(name, listener)\n\t\t\t\t\t\tthis.projectFeaturesListeners.set(name, listener)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch(unhandledError)\n\t}\n\n\t/** Will unsubscribe from experiment updates and stop updating the stream. */\n\tstopUpdatesStream() {\n\t\tfor (const [name, listener] of this.experimentListeners) {\n\t\t\texperiments.removeListener(name, listener)\n\t\t}\n\t\tfor (const [name, listener] of this.employeesOnlySettingsListeners) {\n\t\t\temployeesOnlySettings.removeListener(name, listener)\n\t\t}\n\t\tfor (const [name, listener] of this.projectFeaturesListeners) {\n\t\t\tprojectFeatures.removeListener(name, listener)\n\t\t}\n\t\tthis.experimentListeners.clear()\n\t\tthis.employeesOnlySettingsListeners.clear()\n\t\tthis.projectFeaturesListeners.clear()\n\t}\n}\n\nexport class FlagsUpdaterForRPC {\n\tconstructor(\n\t\tprivate remoteFlags: FlagsUpdates,\n\t\tabortSignal?: AbortSignal,\n\t) {\n\t\tconst flagsUpdater = new FlagsUpdater(this.remoteFlags)\n\t\tflagsUpdater.startUpdatesStream()\n\t\tthis.remoteFlags.updateExperiments(flagsUpdater.getInitialExperiments())\n\t\tthis.remoteFlags.updateEmployeesOnlySettings(flagsUpdater.getInitialEmployeesOnlySettings())\n\t\tabortSignal?.addEventListener(\"abort\", () => flagsUpdater.stopUpdatesStream(), { once: true })\n\t}\n}\n"],
  "mappings": "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",
  "names": []
}
