solve-engine 2.38.25 → 2.38.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-UPQDEN72.js → chunk-44E7U4O5.js} +3 -3
- package/dist/{chunk-UPQDEN72.js.map → chunk-44E7U4O5.js.map} +1 -1
- package/dist/chunk-L2JQQ5EO.js +5 -0
- package/dist/chunk-L2JQQ5EO.js.map +1 -0
- package/dist/{chunk-ABOVDTHN.cjs → chunk-QT4IQADG.cjs} +3 -3
- package/dist/{chunk-ABOVDTHN.cjs.map → chunk-QT4IQADG.cjs.map} +1 -1
- package/dist/{chunk-QKNXCIM3.cjs → chunk-R6NGKIBC.cjs} +2 -2
- package/dist/{chunk-QKNXCIM3.cjs.map → chunk-R6NGKIBC.cjs.map} +1 -1
- package/dist/{chunk-A5OVVFJE.js → chunk-TCDB45AW.js} +2 -2
- package/dist/{chunk-A5OVVFJE.js.map → chunk-TCDB45AW.js.map} +1 -1
- package/dist/chunk-ZWDLC4MR.cjs +5 -0
- package/dist/chunk-ZWDLC4MR.cjs.map +1 -0
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/engine.cjs +1 -1
- package/dist/engine.js +1 -1
- package/dist/engine.worker.cjs +1 -1
- package/dist/engine.worker.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/testing.cjs +2 -2
- package/dist/testing.js +1 -1
- package/dist/worker.cjs +2 -2
- package/dist/worker.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-MY3UW5CC.js +0 -5
- package/dist/chunk-MY3UW5CC.js.map +0 -1
- package/dist/chunk-OVC4LAQ4.cjs +0 -5
- package/dist/chunk-OVC4LAQ4.cjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/engine/SegmentTree.ts","../src/engine/DocumentModel.ts","../src/engine/CompilationWorkerManager.ts","../src/engine/PageManager.ts","../src/engine/ThreeTierEvaluator.ts","../src/engine/evaluateDocument.ts"],"names":["SegmentTree","index","lineId","left","right","split","merge","createNode","midRight","mid","removed","startIndex","deleteCount","newIds","clampedStart","clampedCount","collectInOrder","newTree","buildTreap","lineIds","endIndex","clampedEnd","result","node","leftSize","targetStart","targetEnd","offset","nodeIndex","updateSize","collect","start","end","priority","pseudoRandom","seed","x","DocumentModel","maxLines","DEFAULT_CONFIG","text","countLines","ErrorFactory","rawLines","i","djb2Hash","id","changes","inserted","sorted","a","b","change","startIdx","removedIds","atLine","texts","startLine","endLine","lineNumber","newText","state","newHash","tag","ids","positions","position","memberTagsOf","idx","pos","compiledAgainstHash","results","bytecodes","expressions","reads","writes","isVariableDef","inlineSolveCount","s","CompilationWorkerManager","factory","engineWorkerFactory","event","data","pending","raw","err","items","worker","resolve","reject","doc","byLineId","batch","r","w","stored","opcodes","numbers","program","PAGE_SIZE","HOT_PAGE_RADIUS","WARM_PAGE_RADIUS","PRELOAD_PAGE_COUNT","COLD_EVICT_BUFFER","PageManager","_PageManager","pageNum","docLineCount","newViewport","viewport","viewportStartPage","viewportEndPage","lastPage","hotStart","hotEnd","warmStart","warmEnd","p","coldLeftEnd","coldLeftStart","coldRightStart","coldRightEnd","direction","pages","startPage","endPage","firstPreloadPage","range","expression","inlineSpans","sharedLexer","span","EvalTier","functionsDefinedBy","names","body","readsItself","name","seen","read","ThreeTierEvaluator","engine","checkpointer","VMCheckpointer","sharedGlobalVariableStore","globalDagKey","signal","enableValueArena","unitsBefore","lines","resultMap","tierCounts","docEnd","evalEnd","states","inViewport","lineResult","unitsAfter","disableValueArena","startPos","_err","allWrites","lineNum","downstreamLineIds","positionalReaderLineIds","reader","writeVar","affected","written","earliestChange","lowest","recordedFor","gained","roots","line","key","isPrefixedEdgeKey","lineCount","low","onStack","stack","frames","visited","enter","setMembership","onCycle","root","frame","target","parent","members","member","out","stepper","from","moved","baseResult","isEmptyLine","edges","withTagEdges","orphaned","allResults","allBytecodes","allReads","hasVariableDef","lastValue","firstError","anyFailed","anyPending","definedEarlierOnThisLine","selfReading","value","entry","evaluated","e","errorMessage","v","errorValue","failed","compileErr","EngineError","errReads","errWrites","cache","bytecode","writtenHere","isArenaActive","group","persistentValue","lastResult","compiled","targets","trimmed","errorMessageOf","evaluateDocument","input","_options","previousDocumentModel","evaluator","errors","n","startPosition","endPosition","hasInlineSolves","inlineSolves","findInlineSolvesInLine","message"],"mappings":"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","file":"chunk-UPQDEN72.js","sourcesContent":["/**\n * SegmentTree, an order-statistic Treap (randomized BST).\n *\n * Replaces the flat `lineOrder: number[]` array in DocumentModel with a\n * balanced tree that supports O(log N) insert, delete, and get-at-index\n * operations. Each node stores a `lineId` and its subtree size, enabling\n * order-statistic queries (find the k-th element by position).\n *\n * Key operations:\n * getAt(index), O(log N) expected\n * insertAt(index), O(log N) expected\n * deleteAt(index), O(log N) expected\n * spliceAt(index, deleteCount, newIds), O(log N + deleteCount + insertCount)\n * indexOf(lineId), O(N) without reverse map; cached lazily in DocumentModel\n *\n * Implementation: Treap (Tree + Heap) with implicit keys.\n * Nodes are ordered by position (in-order traversal), not by lineId value.\n * The `size` field enables O(log N) order statistics.\n * Random priorities maintain balance with high probability (O(log N) depth).\n *\n * Memory: ~40 bytes per node (lineId + size + priority + left + right).\n * For 100K lines: ~4MB for tree structure (acceptable).\n */\n\n// ── Types ──────────────────────────────────────────────────────────────────\n\ninterface Node {\n\tlineId: number;\n\t/** Total number of nodes in this subtree (including self). */\n\tsize: number;\n\t/** Random priority for heap property (maintains balance). */\n\tpriority: number;\n\tleft: Node | null;\n\tright: Node | null;\n}\n\n// ── SegmentTree ────────────────────────────────────────────────────────────\n\n/**\n * Maps document offsets to line numbers in logarithmic time.\n *\n * A document is edited constantly, and every edit shifts the offsets of every\n * line after it. Recomputing them linearly on each keystroke is what this\n * avoids.\n */\nexport class SegmentTree {\n\tprivate root: Node | null = null;\n\n\t// ── Public API ───────────────────────────────────────────────────────\n\n\t/** Total number of elements in the tree. */\n\tget length(): number {\n\t\treturn this.root ? this.root.size : 0;\n\t}\n\n\t/** Whether the tree is empty. */\n\tget isEmpty(): boolean {\n\t\treturn this.root === null;\n\t}\n\n\t/**\n\t * Get the lineId at the given 0-based index.\n\t * Returns undefined if index is out of bounds.\n\t * O(log N) expected.\n\t */\n\tgetAt(index: number): number | undefined {\n\t\tif (index < 0 || index >= (this.root?.size ?? 0)) return undefined;\n\t\treturn this.nodeAt(this.root, index).lineId;\n\t}\n\n\t/**\n\t * Insert a single lineId at the given 0-based index.\n\t * O(log N) expected.\n\t */\n\tinsertAt(index: number, lineId: number): void {\n\t\tconst [left, right] = split(this.root, index);\n\t\tthis.root = merge(merge(left, createNode(lineId)), right);\n\t}\n\n\t/**\n\t * Delete a single element at the given 0-based index.\n\t * Returns the removed lineId, or undefined if out of bounds.\n\t * O(log N) expected.\n\t */\n\tdeleteAt(index: number): number | undefined {\n\t\tif (index < 0 || index >= (this.root?.size ?? 0)) return undefined;\n\n\t\tconst [left, midRight] = split(this.root, index);\n\t\tconst [mid, right] = split(midRight, 1);\n\n\t\tconst removed = mid ? mid.lineId : undefined;\n\t\tthis.root = merge(left, right);\n\t\treturn removed;\n\t}\n\n\t/**\n\t * Bulk splice: delete `deleteCount` elements at `startIndex` (0-based),\n\t * then insert `newIds` in their place.\n\t *\n\t * Returns the removed lineId array.\n\t * O(log N + deleteCount + insertCount) expected.\n\t */\n\tspliceAt(\n\t\tstartIndex: number,\n\t\tdeleteCount: number,\n\t\tnewIds: number[]\n\t): number[] {\n\t\tconst clampedStart = Math.max(0, startIndex);\n\t\tconst clampedCount = Math.min(\n\t\t\tMath.max(0, deleteCount),\n\t\t\t(this.root?.size ?? 0) - clampedStart\n\t\t);\n\n\t\t// Split into three parts: [0, start) [start, start+count) [start+count, end)\n\t\tconst [left, midRight] = split(this.root, clampedStart);\n\t\tconst [mid, right] = split(midRight, clampedCount);\n\n\t\t// Collect removed IDs\n\t\tconst removed = collectInOrder(mid);\n\n\t\t// Build a treap from the new IDs and merge\n\t\tconst newTree = buildTreap(newIds, 0, newIds.length);\n\t\tthis.root = merge(merge(left, newTree), right);\n\n\t\treturn removed;\n\t}\n\n\t/**\n\t * Replace the entire tree with a new set of lineIds.\n\t * O(N), builds a balanced treap from a flat array.\n\t */\n\treplaceAll(lineIds: number[]): void {\n\t\tthis.root = buildTreap(lineIds, 0, lineIds.length);\n\t}\n\n\t/**\n\t * Get a contiguous range of lineIds by index.\n\t * O(rangeSize + log N), single in-order walk instead of N × O(log N).\n\t *\n\t * This is the hot path for viewport rendering in DocumentModel.getVisibleLines().\n\t */\n\tgetRange(startIndex: number, endIndex: number): number[] {\n\t\tconst clampedStart = Math.max(0, startIndex);\n\t\tconst clampedEnd = Math.min((this.root?.size ?? 0) - 1, endIndex);\n\t\tif (clampedStart > clampedEnd) return [];\n\n\t\tconst result: number[] = [];\n\t\tthis.collectRange(this.root, clampedStart, clampedEnd, 0, result);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Clear the tree.\n\t */\n\tclear(): void {\n\t\tthis.root = null;\n\t}\n\n\t// ── Iteration ─────────────────────────────────────────────────────────\n\n\t/**\n\t * In-order iterator yielding all lineIds.\n\t */\n\t*[Symbol.iterator](): IterableIterator<number> {\n\t\tyield* this.inOrder(this.root);\n\t}\n\n\tprivate *inOrder(node: Node | null): IterableIterator<number> {\n\t\tif (!node) return;\n\t\tyield* this.inOrder(node.left);\n\t\tyield node.lineId;\n\t\tyield* this.inOrder(node.right);\n\t}\n\n\t// ── Private helpers ───────────────────────────────────────────────────\n\n\tprivate nodeAt(node: Node | null, index: number): Node {\n\t\t// This is only called with valid indices, so node is non-null\n\t\tconst leftSize = node!.left ? node!.left.size : 0;\n\n\t\tif (index < leftSize) {\n\t\t\treturn this.nodeAt(node!.left, index);\n\t\t} else if (index === leftSize) {\n\t\t\treturn node!;\n\t\t} else {\n\t\t\treturn this.nodeAt(node!.right, index - leftSize - 1);\n\t\t}\n\t}\n\n\t/**\n\t * Collect all node values in the range [targetStart, targetEnd] into result.\n\t * `offset` is the 0-based start of the current subtree within the full tree.\n\t * O(rangeSize + log N), skips entire subtrees outside the target range.\n\t */\n\tprivate collectRange(\n\t\tnode: Node | null,\n\t\ttargetStart: number,\n\t\ttargetEnd: number,\n\t\toffset: number,\n\t\tresult: number[]\n\t): void {\n\t\tif (!node) return;\n\n\t\tconst leftSize = node.left ? node.left.size : 0;\n\t\tconst nodeIndex = offset + leftSize;\n\n\t\t// Traverse left subtree if it overlaps the target range\n\t\tif (nodeIndex > targetStart && node.left) {\n\t\t\tthis.collectRange(node.left, targetStart, targetEnd, offset, result);\n\t\t}\n\n\t\t// Include this node if within range\n\t\tif (nodeIndex >= targetStart && nodeIndex <= targetEnd) {\n\t\t\tresult.push(node.lineId);\n\t\t}\n\n\t\t// Traverse right subtree if it overlaps the target range\n\t\tif (nodeIndex < targetEnd && node.right) {\n\t\t\tthis.collectRange(node.right, targetStart, targetEnd, nodeIndex + 1, result);\n\t\t}\n\t}\n}\n\n// ── Free functions ──────────────────────────────────────────────────────────\n\n/** Create a new treap node. */\nfunction createNode(lineId: number): Node {\n\treturn {\n\t\tlineId,\n\t\tsize: 1,\n\t\tpriority: Math.random(),\n\t\tleft: null,\n\t\tright: null,\n\t};\n}\n\n/** Update a node's size from its children. */\nfunction updateSize(node: Node): void {\n\tnode.size = 1 + (node.left ? node.left.size : 0) + (node.right ? node.right.size : 0);\n}\n\n/**\n * Merge two treaps. All keys in `left` must be ≤ all keys in `right`.\n * O(log N) expected.\n */\nfunction merge(left: Node | null, right: Node | null): Node | null {\n\tif (!left) return right;\n\tif (!right) return left;\n\n\tif (left.priority > right.priority) {\n\t\tleft.right = merge(left.right, right);\n\t\tupdateSize(left);\n\t\treturn left;\n\t} else {\n\t\tright.left = merge(left, right.left);\n\t\tupdateSize(right);\n\t\treturn right;\n\t}\n}\n\n/**\n * Split a treap at `index` (0-based).\n * Returns [left, right] where `left` contains the first `index` elements\n * and `right` contains the rest.\n * O(log N) expected.\n */\nfunction split(\n\tnode: Node | null,\n\tindex: number\n): [Node | null, Node | null] {\n\tif (!node) return [null, null];\n\n\tconst leftSize = node.left ? node.left.size : 0;\n\n\tif (index <= leftSize) {\n\t\tconst [left, right] = split(node.left, index);\n\t\tnode.left = right;\n\t\tupdateSize(node);\n\t\treturn [left, node];\n\t} else {\n\t\tconst [left, right] = split(node.right, index - leftSize - 1);\n\t\tnode.right = left;\n\t\tupdateSize(node);\n\t\treturn [node, right];\n\t}\n}\n\n/**\n * Collect all lineIds from a treap in-order.\n * O(N).\n */\nfunction collectInOrder(node: Node | null): number[] {\n\tconst result: number[] = [];\n\tcollect(node, result);\n\treturn result;\n}\n\nfunction collect(node: Node | null, result: number[]): void {\n\tif (!node) return;\n\tcollect(node.left, result);\n\tresult.push(node.lineId);\n\tcollect(node.right, result);\n}\n\n/**\n * Build a balanced treap from a flat array of lineIds.\n * Uses divide-and-conquer with pre-sorted priorities to produce\n * a heap-ordered tree in O(N) time.\n * O(N).\n */\nfunction buildTreap(lineIds: number[], start: number, end: number): Node | null {\n\tif (start >= end) return null;\n\n\tconst mid = Math.floor((start + end) / 2);\n\n\t// The priority comes from the line's own id, not from its position in this\n\t// build. Position carries no entropy across separate builds: an insert of a\n\t// single line is always `buildTreap(ids, 0, 1)`, so `mid` is always 0, so\n\t// every singly-inserted node got the same priority and they chained into a\n\t// list instead of a tree. The id is unique and stable, so the same set of\n\t// lines still produces the same shape, which is what the determinism here\n\t// was for.\n\tconst priority = pseudoRandom(lineIds[mid]);\n\n\tconst node: Node = {\n\t\tlineId: lineIds[mid],\n\t\tsize: end - start,\n\t\tpriority,\n\t\tleft: buildTreap(lineIds, start, mid),\n\t\tright: buildTreap(lineIds, mid + 1, end),\n\t};\n\n\treturn node;\n}\n\n/**\n * Simple pseudo-random number generator for deterministic treap building.\n * Uses a multiplicative hash of the input.\n *\n * Zero used to be a fixed point: it survives the shift, the xor and the\n * multiply unchanged, so `pseudoRandom(0)` was exactly `0`, the lowest priority\n * a node can have. The seed is offset by a large odd constant first, so no input\n * maps to itself, and the result is read as unsigned so the range really is\n * `[0, 1)` rather than straddling zero.\n */\nfunction pseudoRandom(seed: number): number {\n\tlet x = (seed + 0x9e3779b9) | 0;\n\tx = Math.imul((x >>> 16) ^ x, 0x45d9f3b);\n\tx = Math.imul((x >>> 16) ^ x, 0x45d9f3b);\n\tx = ((x >>> 16) ^ x) >>> 0;\n\treturn x / 0x100000000; // normalize to [0, 1)\n}\n","import { Value } from \"@solve-js/vm/Value\";\nimport { BytecodeProgram } from \"@solve-js/parser/BytecodeBuilder\";\nimport { djb2Hash } from \"@solve-js/utilities/Hash\";\nimport { SegmentTree } from \"@solve-js/engine/SegmentTree\";\nimport { DEFAULT_CONFIG } from \"@solve-js/constants/Configuration\";\nimport { countLines } from \"@solve-js/utilities/Strings\";\nimport { memberTagsOf } from \"@solve-js/packages/tags/TagScanner\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n// ── LineState ──────────────────────────────────────────────────────────────\n\n/**\n * Persistent per-line state tracked by the DocumentModel.\n *\n * Each line receives an immutable `lineId` that survives structural edits\n * (insertions, deletions, line shifts). This allows caches, the dependency\n * graph, and VM checkpoints to reference lines by ID instead of by volatile\n * line numbers.\n *\n * ── Multi-expression support ─────────────────────────────────────────\n * A single document line may contain multiple inline solves (`s\\`...\\``).\n * To support this without breaking the 1:1 line-to-DAG-node contract:\n * - `expressions[]` holds all extracted expression strings (1 entry for\n * full-line expressions, N entries for N inline solves).\n * - `bytecodes[]` holds compiled bytecode in corresponding order.\n * - `results[]` holds evaluation **result groups** in corresponding order.\n * Each element is a `Value[]`, a group of Values produced by that expression.\n * For variable definitions, the group always has exactly 1 element.\n * For multi-output expressions (e.g., currency conversion), the group\n * may have multiple Values (one per target unit).\n * - `reads[]` and `writes[]` are aggregated across ALL expressions on\n * the line, the DAG treats the line as a single dependency node.\n * - `inlineSolveCount` is 0 for full-line expressions, >0 for inline solves.\n */\nexport interface LineState {\n\t/** Immutable unique identifier, survives all structural edits. */\n\treadonly lineId: number;\n\n\t/** djb2 hash of the line text, used for O(1) change detection. */\n\ttextHash: number;\n\n\t/** The full line text (may include markdown). */\n\ttext: string;\n\n\t/**\n\t * Extracted expression texts.\n\t * - Full-line expressions: one entry (the trimmed text).\n\t * - Inline solve lines: one entry per `s\\`...\\`` span, in left-to-right order.\n\t * - Markdown-only lines: empty array.\n\t */\n\texpressions: string[];\n\n\t/**\n\t * Compiled bytecode for each expression, in corresponding order.\n\t * Parallel to {@link expressions}.\n\t */\n\tbytecodes: BytecodeProgram[];\n\n\t/** Variables this line reads (for dependency tracking). Aggregated across all expressions. */\n\treads: string[];\n\n\t/** Variables this line writes (empty if not a variable definition). Aggregated across all expressions. */\n\twrites: string[];\n\n\t/**\n\t * Evaluation result groups for each expression, in corresponding order.\n\t * Each `Value[]` is a group of Values produced by one expression.\n\t * Variable definitions always produce exactly 1 Value per group.\n\t * Multi-output expressions (e.g. currency conversion) may produce N Values.\n\t */\n\tresults: Value[][];\n\n\t/**\n\t * Convenience accessor for the first expression's first result.\n\t * Equals `results[0]?.[0] ?? null`. For single-expression lines,\n\t * this is the primary result. For multi-expression (inline solve) lines,\n\t * prefer accessing `results[i][j]` directly.\n\t */\n\tresult: Value | null;\n\n\t/** True if this line needs re-evaluation. */\n\tdirty: boolean;\n\n\t/** True if any expression on this line defines a variable (never evict bytecode). */\n\tisVariableDef: boolean;\n\n\t/** True if this line contains only markdown (no evaluable expression). */\n\tisEmpty: boolean;\n\n\t/**\n\t * Number of inline solve expressions on this line.\n\t * 0 = full-line expression (or markdown-only).\n\t * >0 = N inline solves embedded in markdown text.\n\t */\n\tinlineSolveCount: number;\n}\n\n// ── ViewportRange ──────────────────────────────────────────────────────────\n\n/** The visible line range, used to decide which lines are evaluated eagerly. */\nexport interface ViewportRange {\n\tstartLine: number; // 1-based, inclusive\n\tendLine: number; // 1-based, inclusive\n}\n\n// ── LineChange ─────────────────────────────────────────────────────────────\n\n/** Describes a structural change to the document's line list. */\nexport interface LineChange {\n\t/** 1-based line number where the change starts. */\n\tstartLine: number;\n\t/** Number of lines deleted (0 for pure insertion). */\n\tdeleteCount: number;\n\t/** New line texts inserted in place of deleted lines. */\n\tinsertLines: string[];\n}\n\n// ── ApplyChangesResult ─────────────────────────────────────────────────────\n\n/** Result of applying changes to the document model. */\nexport interface ApplyChangesResult {\n\t/** Line IDs of newly inserted lines. */\n\tinserted: number[];\n\t/** Line IDs that were removed. */\n\tremoved: number[];\n}\n\n// ── DocumentModel ──────────────────────────────────────────────────────────\n\n/**\n * Persistent document model with O(log N) line lookups and structural edits.\n *\n * Design:\n * - Each line has an immutable `lineId` (monotonically increasing counter).\n * - `LineState` objects are stored in a `Map<lineId, LineState>` for O(1) access.\n * - Line ordering is maintained in a `SegmentTree` (order-statistic Treap) that\n * supports O(log N) insert, delete, and get-at-index operations.\n * - A lazy position cache (`Map<lineId, number>`) provides O(1) position lookups\n * after the first `getLinePosition()` call and is invalidated on structural edits.\n *\n * Key invariant: line IDs never change, only their positions in the order tree.\n * This means cached bytecode, dependency graph entries, and VM checkpoints\n * keyed by lineId remain valid across all structural edits.\n */\nexport class DocumentModel {\n\t/** Persistent line ID → LineState. */\n\tprivate lines: Map<number, LineState> = new Map();\n\n\t/** Order-statistic treap representing the current document line order. */\n\tprivate orderTree: SegmentTree = new SegmentTree();\n\n\t/** Monotonically increasing counter for new line IDs. */\n\tprivate nextLineId: number = 1;\n\n\t/**\n\t * Lazy position cache: lineId → 1-based position.\n\t * Built on first `getLinePosition()` call, invalidated on structural edits.\n\t */\n\tprivate _positionCache: Map<number, number> | null = null;\n\n\t/**\n\t * Line IDs currently marked dirty, maintained alongside every\n\t * `state.dirty` mutation (in this class and in every other module that\n\t * holds a direct `LineState` reference: ThreeTierEvaluator, PageManager).\n\t * Lets {@link hasAnyDirtyLineBefore} answer \"is anything before position\n\t * X dirty\" in O(d log N), d = current dirty count, typically tiny once\n\t * a document has settled after its initial evaluation, instead of\n\t * O(N log N), which used to mean every scroll event re-walked the WHOLE\n\t * document via `getLineAt()` regardless of how little of it was actually\n\t * dirty. Benchmarked: ~10.6ms per setViewport() call scrolled near the\n\t * bottom of a 20k-line document before this fix.\n\t */\n\tprivate dirtyLineIds: Set<number> = new Set();\n\n\t/**\n\t * Lower-cased category tag to the ids of the lines carrying it, or `null`\n\t * while nothing has asked.\n\t *\n\t * `total of #food` used to walk the whole document looking for `#food`, so a\n\t * document of members and totals cost one walk per total: D aggregates over\n\t * N lines is D x N, and a notepad of a few thousand tagged lines took\n\t * minutes. This turns an aggregate into a lookup of its own members.\n\t *\n\t * Built on the first query rather than with the document, so a document with\n\t * no tags in it, which is most of them, never scans for any. Once built it is\n\t * kept in step by the four places that change a line's text: {@link setDocument},\n\t * {@link applyChanges}, {@link editLine} and {@link clear}. Keyed by line id\n\t * rather than position, so inserting or deleting a line above a tagged one\n\t * does not touch it.\n\t */\n\tprivate tagIndex: Map<string, Set<number>> | null = null;\n\n\t/**\n\t * Most lines this model will hold. See `constants/Configuration.ts`'s\n\t * `performance.maxDocumentLines`, which is where the default comes from and\n\t * which a host raises through its engine config; a host that builds a\n\t * DocumentModel directly passes it here instead.\n\t */\n\tprivate readonly maxLines: number;\n\n\t/**\n\t * @param maxLines - Ceiling on the line count, defaulting to the engine's\n\t * configured one. Every line costs a LineState with six arrays in it\n\t * whatever the line says, so the cost of a document is its line count and\n\t * nothing else bounds it: two hundred thousand lines of `1 + 1` exhausted\n\t * the heap here, before a single expression had been looked at.\n\t */\n\tconstructor(maxLines: number = DEFAULT_CONFIG.performance.maxDocumentLines) {\n\t\tthis.maxLines = maxLines;\n\t}\n\n\t// ── Initialization ──────────────────────────────────────────────────\n\n\t/**\n\t * Initialize or replace the entire document from a text blob.\n\t * Clears all existing state and assigns new persistent line IDs.\n\t *\n\t * @throws `DOCUMENT_TOO_LARGE` for a document past {@link maxLines}, before\n\t * any of it is stored. Recoverable: nothing has been replaced yet, so the\n\t * model still holds whatever it held.\n\t */\n\tsetDocument(text: string): void {\n\t\t// Counted before anything is cleared, so a refused document leaves the\n\t\t// previous one intact rather than half-replaced.\n\t\tconst lineCount = countLines(text, this.maxLines);\n\t\tif (lineCount > this.maxLines) {\n\t\t\tthrow ErrorFactory.execution(\n\t\t\t\t\"DOCUMENT_TOO_LARGE\",\n\t\t\t\t`This document has more than ${this.maxLines.toLocaleString(\"en-US\")} lines, which is the most the engine will hold at once`,\n\t\t\t\t{ maxLines: this.maxLines },\n\t\t\t);\n\t\t}\n\t\tthis.lines.clear();\n\t\tthis.orderTree.clear();\n\t\tthis._positionCache = null;\n\t\tthis.dirtyLineIds.clear();\n\t\tthis.nextLineId = 1;\n\t\tthis.tagIndex = null;\n\n\t\tconst rawLines = text.split(\"\\n\");\n\t\tconst lineIds = new Array<number>(rawLines.length);\n\n\t\tfor (let i = 0; i < rawLines.length; i++) {\n\t\t\tconst lineId = this.nextLineId++;\n\t\t\tlineIds[i] = lineId;\n\t\t\tthis.lines.set(lineId, {\n\t\t\t\tlineId,\n\t\t\t\ttextHash: djb2Hash(rawLines[i]),\n\t\t\t\ttext: rawLines[i],\n\t\t\t\texpressions: [],\n\t\t\t\tbytecodes: [],\n\t\t\t\treads: [],\n\t\t\t\twrites: [],\n\t\t\t\tresults: [],\n\t\t\t\tresult: null,\n\t\t\t\tdirty: true,\n\t\t\t\tisVariableDef: false,\n\t\t\t\tisEmpty: rawLines[i].trim().length === 0,\n\t\t\t\tinlineSolveCount: 0,\n\t\t\t});\n\t\t}\n\n\t\t// O(N) balanced treap build from flat array\n\t\tthis.orderTree.replaceAll(lineIds);\n\n\t\t// Every line starts dirty (see the object literal above), seed the\n\t\t// tracking set to match. This is the one place the set is legitimately\n\t\t// O(N): a fresh document needs full evaluation anyway.\n\t\tfor (const id of lineIds) this.dirtyLineIds.add(id);\n\t}\n\n\t// ── Structural edits ────────────────────────────────────────────────\n\n\t/**\n\t * Apply one or more line-level changes to the document.\n\t *\n\t * **Precondition:** Changes must be **non-overlapping** in their line ranges.\n\t * If two changes target the same or adjacent lines, the reverse-order\n\t * processing may produce incorrect results because the first-applied\n\t * change shifts the line numbers that the second change references.\n\t *\n\t * Changes are applied in **reverse order** (highest startLine first) so\n\t * that earlier changes in the document don't shift the indices of later\n\t * changes during processing.\n\t *\n\t * Returns both the newly inserted line IDs and the removed line IDs.\n\t * Callers should use `removed` to clean up the dependency graph and\n\t * other data structures keyed by lineId.\n\t */\n\tapplyChanges(changes: LineChange[]): ApplyChangesResult {\n\t\tconst inserted: number[] = [];\n\t\tconst removed: number[] = [];\n\n\t\t// Sort descending by startLine so earlier changes don't shift later indices\n\t\tconst sorted = [...changes].sort((a, b) => b.startLine - a.startLine);\n\n\t\tfor (const change of sorted) {\n\t\t\tconst startIdx = change.startLine - 1; // convert to 0-based\n\n\t\t\t// Create new LineState entries for inserted lines\n\t\t\tconst newIds: number[] = [];\n\t\t\tfor (const text of change.insertLines) {\n\t\t\t\tconst lineId = this.nextLineId++;\n\t\t\t\tnewIds.push(lineId);\n\t\t\t\tinserted.push(lineId);\n\t\t\t\tthis.lines.set(lineId, {\n\t\t\t\t\tlineId,\n\t\t\t\t\ttextHash: djb2Hash(text),\n\t\t\t\t\ttext,\n\t\t\t\t\texpressions: [],\n\t\t\t\t\tbytecodes: [],\n\t\t\t\t\treads: [],\n\t\t\t\t\twrites: [],\n\t\t\t\t\tresults: [],\n\t\t\t\t\tresult: null,\n\t\t\t\t\tdirty: true,\n\t\t\t\t\tisVariableDef: false,\n\t\t\t\t\tisEmpty: text.trim().length === 0,\n\t\t\t\t\tinlineSolveCount: 0,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// New lines start dirty (see the object literal above), track them.\n\t\t\tfor (const id of newIds) this.dirtyLineIds.add(id);\n\n\n\t\t\t// Only when something has already asked for one: an index nothing has\n\t\t\t// built yet describes no lines, so it has none to learn about either.\n\t\t\tif (this.tagIndex !== null) {\n\t\t\t\tfor (const id of newIds) this.indexTags(id, this.lines.get(id)!.text);\n\t\t\t}\n\n\t\t\t// O(log N) splice: delete old IDs, insert new IDs\n\t\t\tconst removedIds = this.orderTree.spliceAt(\n\t\t\t\tstartIdx,\n\t\t\t\tchange.deleteCount,\n\t\t\t\tnewIds\n\t\t\t);\n\t\t\tfor (const id of removedIds) {\n\t\t\t\tremoved.push(id);\n\t\t\t\tif (this.tagIndex !== null) this.unindexTags(id, this.lines.get(id)?.text ?? \"\");\n\t\t\t\tthis.lines.delete(id);\n\t\t\t\tthis.dirtyLineIds.delete(id);\n\t\t\t}\n\t\t}\n\n\t\t// Invalidate position cache, positions shifted for all lines\n\t\tthis._positionCache = null;\n\n\t\treturn { inserted, removed };\n\t}\n\n\t/**\n\t * Insert new lines at the given 1-based position.\n\t * Convenience wrapper around applyChanges.\n\t */\n\tinsertLines(atLine: number, texts: string[]): number[] {\n\t\tconst change: LineChange = {\n\t\t\tstartLine: atLine,\n\t\t\tdeleteCount: 0,\n\t\t\tinsertLines: texts,\n\t\t};\n\t\tconst result = this.applyChanges([change]);\n\t\treturn result.inserted;\n\t}\n\n\t/**\n\t * Delete lines in the given 1-based range [startLine, endLine] inclusive.\n\t * Convenience wrapper around applyChanges.\n\t */\n\tdeleteLines(startLine: number, endLine: number): number[] {\n\t\tconst change: LineChange = {\n\t\t\tstartLine,\n\t\t\tdeleteCount: endLine - startLine + 1,\n\t\t\tinsertLines: [],\n\t\t};\n\t\tconst result = this.applyChanges([change]);\n\t\treturn result.removed;\n\t}\n\n\t/**\n\t * Update the text of a single line in place.\n\t * If the text hash differs, marks the line dirty and clears its\n\t * bytecode/result so it gets re-evaluated.\n\t *\n\t * Returns true if the text actually changed (hash mismatch).\n\t */\n\teditLine(lineNumber: number, newText: string): boolean {\n\t\tconst state = this.getLineAt(lineNumber);\n\t\tif (!state) return false;\n\n\t\tconst newHash = djb2Hash(newText);\n\t\tif (newHash === state.textHash) return false;\n\n\t\tif (this.tagIndex !== null) {\n\t\t\tthis.unindexTags(state.lineId, state.text);\n\t\t\tthis.indexTags(state.lineId, newText);\n\t\t}\n\t\tstate.text = newText;\n\t\tstate.textHash = newHash;\n\t\tstate.expressions = [];\n\t\tstate.bytecodes = [];\n\t\tstate.results = [];\n\t\tstate.result = null;\n\t\tstate.inlineSolveCount = 0;\n\t\tstate.dirty = true;\n\t\tthis.dirtyLineIds.add(state.lineId);\n\t\tstate.isEmpty = newText.trim().length === 0;\n\t\treturn true;\n\t}\n\n\t// ── Queries ─────────────────────────────────────────────────────────\n\n\t/**\n\t * The 1-based positions of the lines carrying `#tag`, in document order.\n\t *\n\t * What `total of #tag` reads. Case-insensitive, matching how a tag is read\n\t * on a line, and it names membership only: a line that merely asks about the\n\t * group (`total of #tag`) is not in it, and neither is a `#heading`.\n\t *\n\t * Ascending, because an aggregate reports the first line it cannot read and\n\t * the line it names has to be the first one in the document, not whichever\n\t * happened to be indexed first.\n\t *\n\t * @param tag - The tag name without its `#`.\n\t * @returns Its lines' positions, ascending; empty when no line carries it.\n\t */\n\tlinesCarryingTag(tag: string): number[] {\n\t\tconst ids = this.ensureTagIndex().get(tag.toLowerCase());\n\t\tif (ids === undefined || ids.size === 0) return [];\n\t\tconst positions: number[] = [];\n\t\tfor (const id of ids) {\n\t\t\tconst position = this.getLinePosition(id);\n\t\t\t// A line still in the index but no longer in the document cannot\n\t\t\t// happen through the maintained paths; guarded rather than trusted,\n\t\t\t// since the alternative is aggregating over position -1.\n\t\t\tif (position > 0) positions.push(position);\n\t\t}\n\t\tpositions.sort((a, b) => a - b);\n\t\treturn positions;\n\t}\n\n\t/** The tag index, built over every line the first time one is asked for. */\n\tprivate ensureTagIndex(): Map<string, Set<number>> {\n\t\tif (this.tagIndex !== null) return this.tagIndex;\n\t\tthis.tagIndex = new Map();\n\t\tfor (const state of this.lines.values()) this.indexTags(state.lineId, state.text);\n\t\treturn this.tagIndex;\n\t}\n\n\t/** Records `lineId` as a member of every group its text joins. */\n\tprivate indexTags(lineId: number, text: string): void {\n\t\tif (text.indexOf(\"#\") < 0) return; // the overwhelmingly common line\n\t\tfor (const tag of memberTagsOf(text)) {\n\t\t\tconst ids = this.tagIndex!.get(tag);\n\t\t\tif (ids === undefined) this.tagIndex!.set(tag, new Set([lineId]));\n\t\t\telse ids.add(lineId);\n\t\t}\n\t}\n\n\t/** Drops `lineId` from every group the text it used to hold joined. */\n\tprivate unindexTags(lineId: number, text: string): void {\n\t\tif (text.indexOf(\"#\") < 0) return;\n\t\tfor (const tag of memberTagsOf(text)) {\n\t\t\tconst ids = this.tagIndex!.get(tag);\n\t\t\tif (ids === undefined) continue;\n\t\t\tids.delete(lineId);\n\t\t\tif (ids.size === 0) this.tagIndex!.delete(tag);\n\t\t}\n\t}\n\n\t/**\n\t * Get the LineState at the given 1-based line position. O(1).\n\t */\n\tgetLineAt(position: number): LineState | undefined {\n\t\tconst idx = position - 1;\n\t\tconst lineId = this.orderTree.getAt(idx);\n\t\tif (lineId === undefined) return undefined;\n\t\treturn this.lines.get(lineId);\n\t}\n\n\t/**\n\t * Get the 1-based position of a line by its persistent ID.\n\t * Returns -1 if the line ID is not in the document.\n\t *\n\t * Uses a lazy position cache: O(N) on first call after structural edit,\n\t * O(1) on subsequent calls. The cache is invalidated by any structural edit.\n\t */\n\tgetLinePosition(lineId: number): number {\n\t\tif (this._positionCache) {\n\t\t\treturn this._positionCache.get(lineId) ?? -1;\n\t\t}\n\n\t\t// Build position cache on first call after invalidation\n\t\tthis._positionCache = new Map();\n\t\tlet pos = 1;\n\t\tfor (const id of this.orderTree) {\n\t\t\tthis._positionCache.set(id, pos++);\n\t\t}\n\n\t\treturn this._positionCache.get(lineId) ?? -1;\n\t}\n\n\t/**\n\t * Get all LineState entries within the given viewport range (1-based, inclusive).\n\t * Uses SegmentTree.getRange() for O(viewport + log N) collection instead of\n\t * O(viewport × log N) per-line lookups.\n\t */\n\tgetVisibleLines(startLine: number, endLine: number): LineState[] {\n\t\tconst lineIds = this.orderTree.getRange(startLine - 1, endLine - 1);\n\t\tconst result: LineState[] = [];\n\t\tfor (const lineId of lineIds) {\n\t\t\tconst state = this.lines.get(lineId);\n\t\t\tif (state) result.push(state);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * The lines from `startLine` to `endLine`, one entry per position.\n\t *\n\t * The same lines {@link getVisibleLines} returns, except that a position\n\t * whose line is missing keeps its place as `undefined` rather than closing\n\t * the gap, so the caller can index by position.\n\t *\n\t * Resolving the ids here rather than handing them back is deliberate and\n\t * measured: the loop below is monomorphic over this class's own map, and\n\t * asking {@link getLineById} once per position instead was 20% slower on a\n\t * twenty-thousand-line document.\n\t *\n\t * One in-order walk of the order tree, O(span + log N), against the\n\t * O(log N) descent per position that asking {@link getLineAt} in a loop\n\t * costs. The evaluator walks from line 1 to the end of the viewport on\n\t * every pass, so that descent was a third of the cost of an edit on a long\n\t * document scrolled near the bottom.\n\t *\n\t * @param startLine - First position, 1-based, inclusive.\n\t * @param endLine - Last position, 1-based, inclusive.\n\t * @returns One entry per position in the span, in document order.\n\t */\n\tgetLineStatesInRange(startLine: number, endLine: number): (LineState | undefined)[] {\n\t\tconst lineIds = this.orderTree.getRange(startLine - 1, endLine - 1);\n\t\tconst result = new Array<LineState | undefined>(lineIds.length);\n\t\tfor (let i = 0; i < lineIds.length; i++) result[i] = this.lines.get(lineIds[i]);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get all LineState entries in order. Useful for batch processing.\n\t */\n\tgetAllLines(): LineState[] {\n\t\treturn this.getVisibleLines(1, this.lineCount);\n\t}\n\n\t/**\n\t * Get a LineState by its persistent line ID. O(1).\n\t */\n\tgetLineById(lineId: number): LineState | undefined {\n\t\treturn this.lines.get(lineId);\n\t}\n\n\t/**\n\t * Get all lines that are marked dirty.\n\t */\n\tgetDirtyLines(): LineState[] {\n\t\tconst result: LineState[] = [];\n\t\tfor (const state of this.lines.values()) {\n\t\t\tif (state.dirty) result.push(state);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Whether any line before `position` (1-based, exclusive) is dirty.\n\t *\n\t * Used by ThreeTierEvaluator.setViewport() to decide whether cached\n\t * checkpoint state might be stale and a full evaluate() (from line 1) is\n\t * needed instead of the cheap viewport-only path.\n\t *\n\t * O(d log N) where d = current dirty line count via {@link dirtyLineIds},\n\t * not O(N log N), a document that's mostly clean (the steady state after\n\t * initial load) answers this in the cost of resolving a handful of\n\t * lineIds to positions, not walking every line up to `position`.\n\t */\n\thasAnyDirtyLineBefore(position: number): boolean {\n\t\tfor (const lineId of this.dirtyLineIds) {\n\t\t\tconst pos = this.getLinePosition(lineId);\n\t\t\tif (pos >= 1 && pos < position) return true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Whether any **variable-definition** line before `position` (1-based,\n\t * exclusive) is dirty.\n\t *\n\t * Narrower than {@link hasAnyDirtyLineBefore}: `VMCheckpointer.snapshot()`\n\t * only ever records state for lines with `writes.length > 0` (see\n\t * VMCheckpoints.ts), so a dirty plain-expression line before the viewport\n\t * cannot have invalidated any checkpoint, there's no checkpoint entry\n\t * for it to invalidate. Only a dirty variable-def line can mean the VM\n\t * state a checkpoint would restore is stale.\n\t *\n\t * This distinction matters because `PageManager.evictPageBytecode()`\n\t * marks evicted non-variable-def lines dirty (so they get Tier 1 if\n\t * scrolled back into view), and Tier 3's compile-only path never clears\n\t * `dirty` for non-variable-def lines by design. Using the broader\n\t * `hasAnyDirtyLineBefore` here meant scrolling far into a large,\n\t * variable-def-free document would trip `setViewport()`'s fallback to\n\t * `evaluate()` on every single call, evaluate() reprocesses the evicted\n\t * lines via Tier 3, which recompiles their bytecode without clearing\n\t * dirty, so the very next `maintainAfterEval()` re-evicts and re-dirties\n\t * the same lines, forever re-triggering the fallback on an otherwise\n\t * unchanged viewport.\n\t */\n\thasAnyDirtyVariableDefLineBefore(position: number): boolean {\n\t\tfor (const lineId of this.dirtyLineIds) {\n\t\t\tconst state = this.lines.get(lineId);\n\t\t\tif (!state || !state.isVariableDef) continue;\n\t\t\tconst pos = this.getLinePosition(lineId);\n\t\t\tif (pos >= 1 && pos < position) return true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Number of lines currently marked dirty. For diagnostics/tests. */\n\tget dirtyCount(): number {\n\t\treturn this.dirtyLineIds.size;\n\t}\n\n\t// ── Thread-safety validation ────────────────────────────────────────\n\n\t/**\n\t * Verify that bytecode compiled by a worker is still valid for this line.\n\t *\n\t * When Phase 5.2h sends compilation to a worker, the worker posts back\n\t * `{lineId, bytecode, reads, writes, compiledAgainstHash}`. Between dispatch\n\t * and response, the user may have edited the line. This method lets the\n\t * main thread check whether the bytecode is still applicable.\n\t *\n\t * @returns true if the line still exists and its text hash matches.\n\t */\n\tisBytecodeValid(lineId: number, compiledAgainstHash: number): boolean {\n\t\tconst state = this.lines.get(lineId);\n\t\treturn state !== undefined && state.textHash === compiledAgainstHash;\n\t}\n\n\t// ── State mutations ─────────────────────────────────────────────────\n\n\t/**\n\t * Mark a line as clean (re-evaluated successfully).\n\t */\n\t/**\n\t * Forget what a line answered, without forgetting the line.\n\t *\n\t * For a line whose answer was computed about a document that no longer\n\t * exists. Marking it dirty says it must run again; this says that until it\n\t * does, it has nothing to tell anyone who asks, which is the state a pass\n\t * over the text from scratch would be in.\n\t *\n\t * The bytecode is left alone. It is compiled from the line's own text, which\n\t * a structural edit does not change, and dropping it would recompile the\n\t * document for nothing.\n\t *\n\t * @param lineId - Persistent line identifier.\n\t */\n\tforgetResult(lineId: number): void {\n\t\tconst state = this.lines.get(lineId);\n\t\tif (!state) return;\n\t\tstate.result = null;\n\t\tstate.results = [];\n\t}\n\n\tmarkClean(lineId: number): void {\n\t\tconst state = this.lines.get(lineId);\n\t\tif (state) {\n\t\t\tstate.dirty = false;\n\t\t\tthis.dirtyLineIds.delete(lineId);\n\t\t}\n\t}\n\n\t/**\n\t * Mark a line as dirty (needs re-evaluation) by its 1-based position.\n\t * Convenience for callers that have line numbers instead of line IDs.\n\t */\n\tmarkDirtyByLineNumber(lineNumber: number): void {\n\t\tconst state = this.getLineAt(lineNumber);\n\t\tif (state) {\n\t\t\tstate.dirty = true;\n\t\t\tthis.dirtyLineIds.add(state.lineId);\n\t\t}\n\t}\n\n\t/**\n\t * Mark a line as dirty (needs re-evaluation).\n\t */\n\tmarkDirty(lineId: number): void {\n\t\tconst state = this.lines.get(lineId);\n\t\tif (state) {\n\t\t\tstate.dirty = true;\n\t\t\tthis.dirtyLineIds.add(lineId);\n\t\t}\n\t}\n\n\t/**\n\t * Mark all lines as dirty (e.g., after plugin register/unregister).\n\t */\n\tinvalidateAll(): void {\n\t\tfor (const state of this.lines.values()) {\n\t\t\tstate.dirty = true;\n\t\t}\n\t\tthis.dirtyLineIds = new Set(this.lines.keys());\n\t}\n\n\t/**\n\t * Update a line's evaluation state after successful execution (Tier 1 / Tier 2).\n\t *\n\t * Sets results, bytecodes, reads, writes, and marks the line clean.\n\t * Supports multi-expression lines (inline solves) via parallel arrays.\n\t *\n\t * @param lineId - Persistent line identifier.\n\t * @param results - Evaluation result groups for each expression (in order). Each element is a Value[].\n\t * @param bytecodes - Compiled bytecode for each expression (in order).\n\t * @param expressions - Extracted expression strings (in order).\n\t * @param reads - Aggregated read variables across all expressions.\n\t * @param writes - Aggregated write variables across all expressions.\n\t * @param isVariableDef - True if any expression defines a variable.\n\t * @param inlineSolveCount - Number of inline solves (0 for full-line).\n\t */\n\tupdateLineResult(\n\t\tlineId: number,\n\t\tresults: Value[][],\n\t\tbytecodes: BytecodeProgram[],\n\t\texpressions: string[],\n\t\treads: string[],\n\t\twrites: string[],\n\t\tisVariableDef: boolean,\n\t\tinlineSolveCount: number = 0,\n\t): void {\n\t\tconst state = this.lines.get(lineId);\n\t\tif (!state) return;\n\t\tstate.results = results;\n\t\tstate.result = results[0]?.[0] ?? null;\n\t\tstate.bytecodes = bytecodes;\n\t\tstate.expressions = expressions;\n\t\tstate.reads = reads;\n\t\tstate.writes = writes;\n\t\tstate.isVariableDef = isVariableDef;\n\t\tstate.inlineSolveCount = inlineSolveCount;\n\t\tstate.dirty = false;\n\t\tthis.dirtyLineIds.delete(lineId);\n\t}\n\n\t/**\n\t * Update a line's compile-only state (Tier 3: background compilation).\n\t *\n\t * Stores expressions, bytecodes, reads, and writes. Does NOT set results\n\t * and does NOT mark the line clean, it still needs execution (Tier 1 or\n\t * Tier 2) to produce results. This distinction allows the three-tier\n\t * evaluation strategy: compile invisible lines in the background without\n\t * executing them, then execute from cached bytecode when scrolled into view.\n\t *\n\t * @param lineId - Persistent line identifier.\n\t * @param expressions - Extracted expression strings (in order).\n\t * @param bytecodes - Compiled bytecode for each expression (in order).\n\t * @param reads - Aggregated read variables across all expressions.\n\t * @param writes - Aggregated write variables across all expressions.\n\t * @param isVariableDef - True if any expression defines a variable.\n\t * @param inlineSolveCount - Number of inline solves (0 for full-line).\n\t */\n\tupdateLineCompiled(\n\t\tlineId: number,\n\t\texpressions: string[],\n\t\tbytecodes: BytecodeProgram[],\n\t\treads: string[],\n\t\twrites: string[],\n\t\tisVariableDef: boolean,\n\t\tinlineSolveCount: number = 0,\n\t): void {\n\t\tconst state = this.lines.get(lineId);\n\t\tif (!state) return;\n\t\tstate.expressions = expressions;\n\t\tstate.bytecodes = bytecodes;\n\t\tstate.reads = reads;\n\t\tstate.writes = writes;\n\t\tstate.isVariableDef = isVariableDef;\n\t\tstate.inlineSolveCount = inlineSolveCount;\n\t\t// NOTE: dirty remains unchanged, line still needs execution\n\t}\n\n\t// ── Properties ──────────────────────────────────────────────────────\n\n\tget lineCount(): number {\n\t\treturn this.orderTree.length;\n\t}\n\n\tget isEmpty(): boolean {\n\t\treturn this.orderTree.isEmpty;\n\t}\n\n\t/**\n\t * Iterator over LineState in document order.\n\t */\n\t*[Symbol.iterator](): IterableIterator<LineState> {\n\t\tfor (const lineId of this.orderTree) {\n\t\t\tconst state = this.lines.get(lineId);\n\t\t\tif (state) yield state;\n\t\t}\n\t}\n\n\t// ── Lifecycle ───────────────────────────────────────────────────────\n\n\tclear(): void {\n\t\tthis.lines.clear();\n\t\tthis.orderTree.clear();\n\t\tthis._positionCache = null;\n\t\tthis.nextLineId = 1;\n\t\tthis.tagIndex = null;\n\t}\n\n\t/**\n\t * Serialize the document model to a plain object for debugging.\n\t */\n\ttoJSON(): object {\n\t\treturn {\n\t\t\tlineCount: this.lineCount,\n\t\t\tlines: this.getAllLines().map((s) => ({\n\t\t\t\tlineId: s.lineId,\n\t\t\t\ttext: s.text.substring(0, 80), // truncate for readability\n\t\t\t\ttextHash: s.textHash,\n\t\t\t\tdirty: s.dirty,\n\t\t\t\tisVariableDef: s.isVariableDef,\n\t\t\t\tisEmpty: s.isEmpty,\n\t\t\t\thasBytecode: s.bytecodes.length > 0,\n\t\t\t\thasResult: s.results.length > 0,\n\t\t\t\tinlineSolveCount: s.inlineSolveCount,\n\t\t\t\treads: s.reads,\n\t\t\t\twrites: s.writes,\n\t\t\t})),\n\t\t};\n\t}\n}\n","/**\n * CompilationWorkerManager, main-thread bridge to the compilation worker.\n *\n * Manages the worker lifecycle and provides a batch compilation API.\n * Receives transferred ArrayBuffers from the worker and reconstructs\n * BytecodeProgram objects with zero-copy TypedArray views.\n *\n * Usage:\n * const manager = new CompilationWorkerManager();\n * const bytecodeByLineId = await manager.compileBatch(items, docModel);\n *\n * Thread safety:\n * Each result includes `compiledAgainstHash`. Before storing bytecode\n * in the DocumentModel, we validate via `docModel.isBytecodeValid()`.\n * If the line was edited between dispatch and response, the bytecode\n * is discarded, the next evaluate() will recompile it synchronously.\n */\n\nimport { DocumentModel } from \"@solve-js/engine/DocumentModel\";\nimport type { BytecodeProgram } from \"@solve-js/parser/BytecodeBuilder\";\nimport { engineWorkerFactory } from \"@solve-js/workers/WorkerFactory\";\n\n// ── Types ─────────────────────────────────────────────────────────────────\n\n/** One line sent to a worker for compilation. */\nexport interface CompileRequestItem {\n\tlineId: number;\n\texpression: string;\n\ttextHash: number;\n}\n\n/** Compiled bytecode returned from a worker, or the error that stopped it. */\nexport interface CompileResponseItem {\n\tlineId: number;\n\t/** Hash of the expression text when the compile was dispatched (for safety validation). */\n\tcompiledAgainstHash: number;\n\tprogram: BytecodeProgram;\n\treads: string[];\n\twrites: string[];\n\tisVariableDef: boolean;\n\terror: string | null;\n}\n\n// Raw result from worker (before BytecodeProgram reconstruction)\ninterface WorkerCompileResult {\n\tlineId: number;\n\tcompiledAgainstHash: number;\n\topcodesBuffer: ArrayBuffer;\n\tnumbersBuffer: ArrayBuffer;\n\topcodesLength: number;\n\tnumbersLength: number;\n\tstrings: string[];\n\treads: string[];\n\twrites: string[];\n\tisVariableDef: boolean;\n\terror: string | null;\n}\n\n// ── CompilationWorkerManager ──────────────────────────────────────────────\n\n/**\n * Compiles lines on a background worker.\n *\n * A response is discarded when the line changed between dispatch and return,\n * because bytecode compiled against stale text would produce a result for an\n * expression the user has already edited. The next evaluation recompiles it\n * synchronously.\n */\nexport class CompilationWorkerManager {\n\tprivate worker: Worker | null = null;\n\tprivate nextId = 1;\n\tprivate pending = new Map<\n\t\tnumber,\n\t\t{\n\t\t\tresolve: (results: CompileResponseItem[]) => void;\n\t\t\treject: (error: Error) => void;\n\t\t}\n\t>();\n\n\tconstructor() {}\n\n\t/**\n\t * Ensure the worker is started (lazy initialization).\n\t *\n\t * Starts it from the factory a host registered with\n\t * `setEngineWorkerFactory`. With no factory there is no worker, which is\n\t * the ordinary case: the throw travels as a rejected promise out of\n\t * `compileBatch` (it is `async`), and its callers already treat that as\n\t * \"compile this line on the main thread instead\". This is the same contract\n\t * the throwing worker stub had before the factory replaced it.\n\t */\n\tprivate ensureWorker(): Worker {\n\t\tif (this.worker) return this.worker;\n\n\t\tconst factory = engineWorkerFactory();\n\t\tif (factory === null) {\n\t\t\tthrow new Error(\"no engine worker factory registered, so compilation stays on the main thread\");\n\t\t}\n\t\tthis.worker = factory();\n\n\t\tthis.worker.onmessage = (event: MessageEvent) => {\n\t\t\tconst data = event.data;\n\t\t\tif (!data || data.type !== \"COMPILE_RESULT\") return;\n\n\t\t\tconst pending = this.pending.get(data.id);\n\t\t\tif (!pending) return;\n\n\t\t\tthis.pending.delete(data.id);\n\n\t\t\tconst results: CompileResponseItem[] = [];\n\t\t\tfor (const raw of data.results as WorkerCompileResult[]) {\n\t\t\t\tresults.push(this.reconstructResult(raw));\n\t\t\t}\n\t\t\tpending.resolve(results);\n\t\t};\n\n\t\tthis.worker.onerror = (err) => {\n\t\t\t// Reject all pending requests on worker error\n\t\t\tfor (const [id, pending] of this.pending) {\n\t\t\t\tpending.reject(\n\t\t\t\t\tnew Error(`Compilation worker error: ${err.message}`)\n\t\t\t\t);\n\t\t\t\tthis.pending.delete(id);\n\t\t\t}\n\t\t};\n\n\t\treturn this.worker;\n\t}\n\n\t/**\n\t * Batch-compile expressions in the worker.\n\t *\n\t * Sends a batch of { lineId, expression, textHash } to the worker,\n\t * which compiles each and transfers bytecode ArrayBuffers back.\n\t * Bytecode is reconstructed into BytecodeProgram objects.\n\t *\n\t * Results are returned in the same order as the input items.\n\t *\n\t * @param items Expressions to compile.\n\t * @returns Compiled bytecode for each item (or error).\n\t */\n\tasync compileBatch(items: CompileRequestItem[]): Promise<CompileResponseItem[]> {\n\t\tif (items.length === 0) return [];\n\n\t\tconst worker = this.ensureWorker();\n\t\tconst id = this.nextId++;\n\n\t\treturn new Promise<CompileResponseItem[]>((resolve, reject) => {\n\t\t\tthis.pending.set(id, { resolve, reject });\n\n\t\t\tworker.postMessage({\n\t\t\t\ttype: \"COMPILE_BATCH\",\n\t\t\t\tid,\n\t\t\t\titems,\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Store worker-compiled bytecode into the DocumentModel, with safety\n\t * validation. Only stores bytecode for lines whose text has not changed\n\t * since the compilation request was dispatched.\n\t *\n\t * Results for the same lineId are batched, all bytecodes from\n\t * successful compilations are passed to updateLineCompiled in a single\n\t * call. This supports multi-expression lines (inline solves).\n\t *\n\t * @param results Compiled results from the worker.\n\t * @param doc The target document model.\n\t * @returns Number of results successfully stored (passed safety check).\n\t */\n\tstoreResults(\n\t\tresults: CompileResponseItem[],\n\t\tdoc: DocumentModel\n\t): number {\n\t\t// Batch results by lineId (multiple inline solves share the same lineId)\n\t\tconst byLineId = new Map<\n\t\t\tnumber,\n\t\t\t{\n\t\t\t\tbytecodes: BytecodeProgram[];\n\t\t\t\treads: Set<string>;\n\t\t\t\twrites: Set<string>;\n\t\t\t\tisVariableDef: boolean;\n\t\t\t\texpressions: string[];\n\t\t\t\ttextHash: number;\n\t\t\t}\n\t\t>();\n\t\tlet validCount = 0;\n\n\t\tfor (const result of results) {\n\t\t\tif (result.error) continue;\n\n\t\t\t// Safety check: has the line been edited since dispatch?\n\t\t\tconst state = doc.getLineById(result.lineId);\n\t\t\tif (!state) continue;\n\n\t\t\t// Validate that the line text hasn't changed since dispatch.\n\t\t\tif (state.textHash !== result.compiledAgainstHash) continue;\n\n\t\t\t// Batch by lineId\n\t\t\tlet batch = byLineId.get(result.lineId);\n\t\t\tif (!batch) {\n\t\t\t\tbatch = {\n\t\t\t\t\tbytecodes: [],\n\t\t\t\t\treads: new Set(),\n\t\t\t\t\twrites: new Set(),\n\t\t\t\t\tisVariableDef: false,\n\t\t\t\t\texpressions: state.expressions.length > 0 ? [...state.expressions] : [],\n\t\t\t\t\ttextHash: result.compiledAgainstHash,\n\t\t\t\t};\n\t\t\t\tbyLineId.set(result.lineId, batch);\n\t\t\t}\n\n\t\t\tbatch.bytecodes.push(result.program);\n\t\t\tfor (const r of result.reads) batch.reads.add(r);\n\t\t\tfor (const w of result.writes) batch.writes.add(w);\n\t\t\tif (result.isVariableDef) batch.isVariableDef = true;\n\t\t\tvalidCount++;\n\t\t}\n\n\t\t// Store batched results\n\t\tlet stored = 0;\n\t\tfor (const [lineId, batch] of byLineId) {\n\t\t\tif (batch.bytecodes.length === 0) continue;\n\t\t\t// Preserve inlineSolveCount from the LineState so the evaluator\n\t\t\t// knows this line has multiple expressions (avoids re-extraction).\n\t\t\tconst state = doc.getLineById(lineId);\n\t\t\tconst inlineSolveCount = state?.inlineSolveCount ?? 0;\n\t\t\tdoc.updateLineCompiled(\n\t\t\t\tlineId,\n\t\t\t\tbatch.expressions,\n\t\t\t\tbatch.bytecodes,\n\t\t\t\t[...batch.reads],\n\t\t\t\t[...batch.writes],\n\t\t\t\tbatch.isVariableDef,\n\t\t\t\tinlineSolveCount,\n\t\t\t);\n\t\t\tstored += batch.bytecodes.length;\n\t\t}\n\n\t\treturn stored;\n\t}\n\n\t/**\n\t * Terminate the worker and clean up.\n\t */\n\tterminate(): void {\n\t\tif (this.worker) {\n\t\t\tthis.worker.terminate();\n\t\t\tthis.worker = null;\n\t\t\tthis.pending.clear();\n\t\t}\n\t}\n\n\t/**\n\t * Whether the worker is currently active.\n\t */\n\tget isActive(): boolean {\n\t\treturn this.worker !== null;\n\t}\n\n\t// ── Private helpers ────────────────────────────────────────────────\n\n\t/**\n\t * Reconstruct a BytecodeProgram from transferred ArrayBuffers.\n\t *\n\t * The transferred ArrayBuffers are zero-copy, we create TypedArray\n\t * views directly over them. No data is copied or serialized.\n\t */\n\tprivate reconstructResult(raw: WorkerCompileResult): CompileResponseItem {\n\t\tif (raw.error) {\n\t\t\treturn {\n\t\t\t\tlineId: raw.lineId,\n\t\t\t\tcompiledAgainstHash: raw.compiledAgainstHash,\n\t\t\t\tprogram: { opcodes: new Uint8Array(0), numbers: new Float64Array(0), strings: [], hasAsync: false },\n\t\t\t\treads: [],\n\t\t\t\twrites: [],\n\t\t\t\tisVariableDef: false,\n\t\t\t\terror: raw.error,\n\t\t\t};\n\t\t}\n\n\t\t// Reconstruct TypedArrays from transferred ArrayBuffers.\n\t\t// These are views over the same memory, zero-copy from worker.\n\t\tconst opcodes =\n\t\t\traw.opcodesLength > 0\n\t\t\t\t? new Uint8Array(raw.opcodesBuffer, 0, raw.opcodesLength)\n\t\t\t\t: new Uint8Array(0);\n\t\tconst numbers =\n\t\t\traw.numbersLength > 0\n\t\t\t\t? new Float64Array(raw.numbersBuffer, 0, raw.numbersLength)\n\t\t\t\t: new Float64Array(0);\n\n\t\tconst program: BytecodeProgram = {\n\t\t\topcodes,\n\t\t\tnumbers,\n\t\t\tstrings: raw.strings,\n\t\t\thasAsync: false,\n\t\t};\n\n\t\treturn {\n\t\t\tlineId: raw.lineId,\n\t\t\tcompiledAgainstHash: raw.compiledAgainstHash,\n\t\t\tprogram,\n\t\t\treads: raw.reads,\n\t\t\twrites: raw.writes,\n\t\t\tisVariableDef: raw.isVariableDef,\n\t\t\terror: null,\n\t\t};\n\t}\n}\n","/**\n * PageManager, page-based LRU eviction + directional preloading.\n *\n * Groups document lines into fixed-size pages (128 lines each) and manages\n * three temperature tiers:\n * • Hot (viewport ± 3 pages): Keep bytecode + results\n * • Warm (viewport ± 6 pages): Keep bytecode, evict results\n * • Cold (beyond ± 6 pages): Evict bytecode + results (except variable defs)\n *\n * Variable definition bytecode is **never evicted** because it forms the\n * backbone of the dependency graph and VM checkpoints.\n *\n * Preloading: When the user scrolls in a consistent direction, the next\n * 1–2 pages are pre-compiled via the background worker so bytecode is\n * cache-hot by the time those lines scroll into view.\n *\n * Usage:\n * const pm = new PageManager();\n * // After evaluation:\n * pm.maintainAfterEval(viewport, doc);\n * // After scroll (viewport-only change):\n * pm.maintainAfterEval(viewport, doc);\n * const targets = pm.getPreloadTargets(viewport, doc);\n * // dispatch targets to compilation worker\n */\n\nimport { DocumentModel } from \"@solve-js/engine/DocumentModel\";\n// Deliberately the shared lexer, not an engine's own.\n//\n// `classifyLine` and `findInlineSolves` are character-level scans for headings,\n// comment markers, code fences and backtick spans. Neither consults the\n// keyword, unit or operator tables, so every lexer returns the same answer and\n// there is nothing per-engine to respect. Pinned by\n// __tests__/lexer/LineClassificationIsVocabularyIndependent.spec.ts, which\n// compares a bare lexer against one carrying extra vocabulary.\n//\n// This matters because these are free functions with no engine to ask. Making\n// them engine-aware would mean changing their signatures and every caller for\n// no behavioural difference.\nimport { sharedLexer } from \"@solve-js/lexer/Lexer\";\n\n// ── Constants ────────────────────────────────────────────────────────────\n\n/** Number of lines per page. Tunable, 128 balances granularity with overhead. */\nexport const PAGE_SIZE = 128;\n\n/** Number of pages on each side of the viewport considered \"hot.\" */\nconst HOT_PAGE_RADIUS = 3;\n\n/** Number of pages on each side of the viewport considered \"warm\" (includes hot). */\nconst WARM_PAGE_RADIUS = 6;\n\n/** Number of pages to preload ahead of the scroll direction. */\nconst PRELOAD_PAGE_COUNT = 2;\n\n/**\n * Pages just beyond the warm range that are \"newly cold\", they were warm\n * on the previous scroll and now need bytecode eviction. Once evicted, they\n * stay cold and don't need re-eviction on subsequent frames (the inner\n * `evictPageBytecode` checks for non-null bytecode and short-circuits).\n *\n * This buffer lets us skip iterating the vast majority of cold pages on\n * every scroll, reducing `maintainAfterEval` from O(total pages) to\n * O(hot + warm + buffer) ≈ O(1) for any document size.\n */\nconst COLD_EVICT_BUFFER = HOT_PAGE_RADIUS;\n\n// ── PageManager ──────────────────────────────────────────────────────────\n\n/**\n * Splits a long document into pages so only what is near the viewport is held\n * in full.\n *\n * A document is edited constantly and most of it is off screen. Paging keeps\n * the cost of a keystroke proportional to what is visible rather than to the\n * length of the document.\n */\nexport class PageManager {\n\t/** Previous viewport for scroll direction detection. null = no previous viewport. */\n\tprivate lastViewportStart: number | null = null;\n\n\t/** Saved scroll direction from the most recent maintainAfterEval call. */\n\tprivate savedDirection: \"down\" | \"up\" | null = null;\n\n\t/** Per-page access counter for LRU tracking. */\n\tprivate pageAccess: Map<number, number> = new Map();\n\n\t/** Monotonically increasing access sequence number. */\n\tprivate accessSeq = 0;\n\n\t// ── Static helpers ─────────────────────────────────────────────────\n\n\t/** Get the page number for a 1-based line number (0-based page index). */\n\tstatic pageForLine(lineNumber: number): number {\n\t\treturn Math.floor((lineNumber - 1) / PAGE_SIZE);\n\t}\n\n\t/** Get the 1-based inclusive line range for a page. */\n\tstatic pageRange(\n\t\tpageNum: number,\n\t\tdocLineCount: number\n\t): { startLine: number; endLine: number } {\n\t\treturn {\n\t\t\tstartLine: pageNum * PAGE_SIZE + 1,\n\t\t\tendLine: Math.min((pageNum + 1) * PAGE_SIZE, docLineCount),\n\t\t};\n\t}\n\n\t// ── Public API ─────────────────────────────────────────────────────\n\n\t/**\n\t * Detect scroll direction from viewport movement.\n\t * Returns null on first call (no previous viewport) or no movement.\n\t */\n\tdetectDirection(newViewport: { startLine: number }): \"down\" | \"up\" | null {\n\t\tif (this.lastViewportStart === null) return null;\n\t\tif (newViewport.startLine > this.lastViewportStart) return \"down\";\n\t\tif (newViewport.startLine < this.lastViewportStart) return \"up\";\n\t\treturn null;\n\t}\n\n\t/**\n\t * Maintain page tiers after evaluation.\n\t *\n\t * 1. Records the new viewport position\n\t * 2. Touches hot pages (viewport ± HOT_PAGE_RADIUS) for LRU\n\t * 3. Evicts cold/warm pages based on distance from viewport\n\t *\n\t * Call this after every `evaluate()`, `setViewport()`, or `evaluateAll()`.\n\t */\n\tmaintainAfterEval(\n\t\tviewport: { startLine: number; endLine: number },\n\t\tdoc: DocumentModel\n\t): void {\n\t\t// ── Capture direction BEFORE updating lastViewportStart ───\n\t\tthis.savedDirection = this.detectDirection(viewport);\n\t\tthis.lastViewportStart = viewport.startLine;\n\n\t\tconst docLineCount = doc.lineCount;\n\t\tconst viewportStartPage = PageManager.pageForLine(viewport.startLine);\n\t\tconst viewportEndPage = PageManager.pageForLine(viewport.endLine);\n\t\tconst lastPage = PageManager.pageForLine(docLineCount);\n\n\t\t// Compute hot/warm page ranges in O(1) using range math:\n\t\t// A page is within RADIUS of the viewport span iff it falls in\n\t\t// [viewportStartPage - RADIUS, viewportEndPage + RADIUS].\n\t\tconst hotStart = Math.max(0, viewportStartPage - HOT_PAGE_RADIUS);\n\t\tconst hotEnd = Math.min(lastPage, viewportEndPage + HOT_PAGE_RADIUS);\n\t\tconst warmStart = Math.max(0, viewportStartPage - WARM_PAGE_RADIUS);\n\t\tconst warmEnd = Math.min(lastPage, viewportEndPage + WARM_PAGE_RADIUS);\n\n\t\t// Touch hot pages to keep them fresh in LRU\n\t\tfor (let p = hotStart; p <= hotEnd; p++) {\n\t\t\tthis.touchPage(p);\n\t\t}\n\n\t\t// Evict warm-but-not-hot pages: keep bytecode, evict results only\n\t\tfor (let p = warmStart; p <= warmEnd; p++) {\n\t\t\tif (p >= hotStart && p <= hotEnd) continue;\n\t\t\tthis.evictPageResults(p, doc, docLineCount);\n\t\t}\n\n\t\t// Evict \"newly cold\" pages: a buffer zone just outside the warm\n\t\t// range. Pages beyond this buffer were already evicted on prior\n\t\t// scrolls and don't need re-eviction.\n\t\t// ── Left side (just above page 0, just below warm range) ──\n\t\tconst coldLeftEnd = warmStart - 1;\n\t\tconst coldLeftStart = Math.max(0, coldLeftEnd - COLD_EVICT_BUFFER + 1);\n\t\tfor (let p = coldLeftStart; p <= coldLeftEnd; p++) {\n\t\t\tthis.evictPageBytecode(p, doc, docLineCount);\n\t\t}\n\t\t// ── Right side (just above warm range, just below lastPage) ──\n\t\tconst coldRightStart = warmEnd + 1;\n\t\tconst coldRightEnd = Math.min(lastPage, coldRightStart + COLD_EVICT_BUFFER - 1);\n\t\tfor (let p = coldRightStart; p <= coldRightEnd; p++) {\n\t\t\tthis.evictPageBytecode(p, doc, docLineCount);\n\t\t}\n\t}\n\n\t/**\n\t * Get lines ahead of the viewport that should be pre-compiled.\n\t *\n\t * Looks PRELOAD_PAGE_COUNT pages ahead of the current scroll direction.\n\t * Only returns lines that are dirty, don't already have bytecode,\n\t * and are not empty/markdown-only.\n\t *\n\t * @param viewport The current viewport.\n\t * @param doc The document model.\n\t * @returns Items ready to send to the compilation worker.\n\t */\n\tgetPreloadTargets(\n\t\tviewport: { startLine: number; endLine: number },\n\t\tdoc: DocumentModel\n\t): Array<{ lineId: number; expression: string; textHash: number }> {\n\t\t// Use the direction saved by the last maintainAfterEval call.\n\t\t// We cannot re-detect because maintainAfterEval already updated\n\t\t// lastViewportStart to the current viewport position.\n\t\tconst direction = this.savedDirection;\n\t\tif (!direction) return [];\n\n\t\tconst items: Array<{ lineId: number; expression: string; textHash: number }> = [];\n\t\t// Use viewport end page for downward preload start, start page for upward.\n\t\t// This ensures we preload *beyond* the full viewport span, not just its start.\n\t\tconst viewportEndPage = PageManager.pageForLine(viewport.endLine);\n\t\tconst viewportStartPage = PageManager.pageForLine(viewport.startLine);\n\t\tconst lastPage = PageManager.pageForLine(doc.lineCount);\n\n\t\tconst pages: number[] = [];\n\t\tif (direction === \"down\") {\n\t\t\t// Preload pages immediately after the viewport's hot radius beyond its end\n\t\t\tconst startPage = viewportEndPage + HOT_PAGE_RADIUS + 1;\n\t\t\tconst endPage = Math.min(startPage + PRELOAD_PAGE_COUNT - 1, lastPage);\n\t\t\tfor (let p = startPage; p <= endPage && p <= lastPage; p++) {\n\t\t\t\tpages.push(p);\n\t\t\t}\n\t\t} else {\n\t\t\t// Preload pages immediately before the viewport's hot radius before its start\n\t\t\tconst firstPreloadPage = viewportStartPage - HOT_PAGE_RADIUS - 1;\n\t\t\tfor (let p = firstPreloadPage; p > firstPreloadPage - PRELOAD_PAGE_COUNT && p >= 0; p--) {\n\t\t\t\tpages.push(p);\n\t\t\t}\n\t\t}\n\n\t\tfor (const pageNum of pages) {\n\t\t\tconst range = PageManager.pageRange(pageNum, doc.lineCount);\n\t\t\tfor (let pos = range.startLine; pos <= range.endLine; pos++) {\n\t\t\t\tconst state = doc.getLineAt(pos);\n\t\t\t\tif (!state) continue;\n\t\t\t\tif (!state.dirty) continue;\n\t\t\t\tif (state.bytecodes.length > 0 && !state.isVariableDef) continue;\n\t\t\t\tif (state.isEmpty) continue;\n\n\t\t\t\t// Extract expressions using the shared lexer to handle inline\n\t\t\t\t// solves (s`...`). For full-line expressions, this returns [text].\n\t\t\t\t// For inline solve lines, this returns each extracted expression.\n\t\t\t\tif (state.expressions.length > 0) {\n\t\t\t\t\t// Use pre-extracted expressions (from prior evaluation)\n\t\t\t\t\tfor (const expression of state.expressions) {\n\t\t\t\t\t\tif (!expression.trim()) continue;\n\t\t\t\t\t\titems.push({\n\t\t\t\t\t\t\tlineId: state.lineId,\n\t\t\t\t\t\t\texpression,\n\t\t\t\t\t\t\ttextHash: state.textHash,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Expressions not yet extracted, use lexer to find inline solves\n\t\t\t\t\tconst inlineSpans = sharedLexer.findInlineSolves(state.text);\n\t\t\t\t\tif (inlineSpans.length > 0) {\n\t\t\t\t\t\tfor (const span of inlineSpans) {\n\t\t\t\t\t\t\tif (!span.expression.trim()) continue;\n\t\t\t\t\t\t\titems.push({\n\t\t\t\t\t\t\t\tlineId: state.lineId,\n\t\t\t\t\t\t\t\texpression: span.expression,\n\t\t\t\t\t\t\t\ttextHash: state.textHash,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Full-line expression\n\t\t\t\t\t\tconst expression = state.text.trim();\n\t\t\t\t\t\tif (expression) {\n\t\t\t\t\t\t\titems.push({\n\t\t\t\t\t\t\t\tlineId: state.lineId,\n\t\t\t\t\t\t\t\texpression,\n\t\t\t\t\t\t\t\ttextHash: state.textHash,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn items;\n\t}\n\n\t/**\n\t * Reset internal state (e.g., after document switch).\n\t */\n\tclear(): void {\n\t\tthis.pageAccess.clear();\n\t\tthis.accessSeq = 0;\n\t\tthis.lastViewportStart = null;\n\t\tthis.savedDirection = null;\n\t}\n\n\t// ── Private helpers ─────────────────────────────────────────────────\n\n\t/** Record a page access with a monotonic sequence number. */\n\tprivate touchPage(pageNum: number): void {\n\t\tthis.pageAccess.set(pageNum, ++this.accessSeq);\n\t}\n\n\t/**\n\t * Evict results from all non-variable-def lines in a page.\n\t * Bytecode is preserved so Tier 2 execution works on scroll-back.\n\t */\n\tprivate evictPageResults(\n\t\tpageNum: number,\n\t\tdoc: DocumentModel,\n\t\tdocLineCount: number\n\t): void {\n\t\tconst range = PageManager.pageRange(pageNum, docLineCount);\n\t\tfor (let pos = range.startLine; pos <= range.endLine; pos++) {\n\t\t\tconst state = doc.getLineAt(pos);\n\t\t\tif (state && !state.isVariableDef && state.results.length > 0) {\n\t\t\t\tstate.results = [];\n\t\t\t\tstate.result = null;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Evict bytecode + results from all non-variable-def lines in a page.\n\t * Variable definition bytecode is **never** evicted, it forms the\n\t * backbone of the DAG and VM checkpoints.\n\t *\n\t * Evicted lines are marked dirty so they get Tier 1 re-evaluation\n\t * when scrolled back into view.\n\t */\n\tprivate evictPageBytecode(\n\t\tpageNum: number,\n\t\tdoc: DocumentModel,\n\t\tdocLineCount: number\n\t): void {\n\t\tconst range = PageManager.pageRange(pageNum, docLineCount);\n\t\tfor (let pos = range.startLine; pos <= range.endLine; pos++) {\n\t\t\tconst state = doc.getLineAt(pos);\n\t\t\tif (!state || state.isVariableDef) continue;\n\n\t\t\tif (state.bytecodes.length > 0 || state.results.length > 0) {\n\t\t\t\tstate.bytecodes = [];\n\t\t\t\tstate.results = [];\n\t\t\t\tstate.result = null;\n\t\t\t\tdoc.markDirty(state.lineId);\n\t\t\t}\n\t\t}\n\t}\n}\n","import { ExpressionEngine } from \"@solve-js/engine/ExpressionEngine\";\nimport {\n\tDocumentModel,\n\tLineChange,\n\tLineState,\n\tViewportRange,\n} from \"@solve-js/engine/DocumentModel\";\nimport { Value, ValueType, enableValueArena, disableValueArena, errorValue, persistentValue, isArenaActive } from \"@solve-js/vm/Value\";\nimport { DependencyGraph, isPrefixedEdgeKey } from \"@solve-js/vm/DependencyGraph\";\nimport { VMCheckpointer } from \"@solve-js/vm/VMCheckpoints\";\nimport { isEmptyLine } from \"@solve-js/engine/ExpressionEngineSafety\";\n// Deliberately the shared lexer, not an engine's own.\n//\n// `classifyLine` and `findInlineSolves` are character-level scans for headings,\n// comment markers, code fences and backtick spans. Neither consults the\n// keyword, unit or operator tables, so every lexer returns the same answer and\n// there is nothing per-engine to respect. Pinned by\n// __tests__/lexer/LineClassificationIsVocabularyIndependent.spec.ts, which\n// compares a bare lexer against one carrying extra vocabulary.\n//\n// This matters because these are free functions with no engine to ask. Making\n// them engine-aware would mean changing their signatures and every caller for\n// no behavioural difference.\nimport { sharedLexer } from \"@solve-js/lexer/Lexer\";\nimport { CompilationWorkerManager, type CompileRequestItem } from \"@solve-js/engine/CompilationWorkerManager\";\nimport { PageManager } from \"@solve-js/engine/PageManager\";\nimport type { BytecodeProgram } from \"@solve-js/parser/BytecodeBuilder\";\nimport { EngineError } from \"@solve-js/errors/UnifiedErrorFramework\";\nimport { withTagEdges } from \"@solve-js/packages/tags/TagScanner\";\nimport { sharedGlobalVariableStore, globalDagKey } from \"@solve-js/vm/GlobalVariableStore\";\n\n// ── EvalTier (diagnostic enum) ──────────────────────────────────────────\n\n/**\n * How much work a line's evaluation required.\n *\n * The tiers exist because most lines in a document have not changed. A line\n * that is clean and cached costs far less than one being compiled fresh, and\n * knowing which happened is how a slow document gets diagnosed.\n */\nexport enum EvalTier {\n\t/** Full pipeline: Lex → Parse → Compile → Execute (visible + dirty). */\n\tTier1 = 1,\n\t/** Execute-only from cached bytecode (visible + cached). */\n\tTier2 = 2,\n\t/** Compile-only for dependency tracking (invisible). Executes only variable assignments. */\n\tTier3 = 3,\n\t/** Skipped, already clean or non-evaluable. */\n\tSkipped = 0,\n}\n\n// ── EvalLineResult ──────────────────────────────────────────────────────\n\n/** Outcome for one line, including which tier handled it. */\nexport interface EvalLineResult {\n\t/** The line's persistent ID from DocumentModel. */\n\tlineId: number;\n\t/** 1-based line position. */\n\tlineNumber: number;\n\t/** Which tier was used. */\n\ttier: EvalTier;\n\t/** The first evaluation result, or null on error / non-evaluable. */\n\tresult: Value | null;\n\t/** All result groups (one per expression/inline-solve), or undefined if skipped. */\n\tresults?: Value[][];\n\t/** Error message, or null. */\n\terror: string | null;\n}\n\n// ── EvalResult ──────────────────────────────────────────────────────────\n\n/** Outcome for a whole evaluation pass, with per-tier counts. */\nexport interface EvalResult {\n\t/** Per-line evaluation results. */\n\tlines: EvalLineResult[];\n\t/** Map of line numbers → flattened results for quick lookup. */\n\tresultMap: Map<number, Value[]>;\n\t/** Number of lines processed at each tier. */\n\ttierCounts: { tier1: number; tier2: number; tier3: number; skipped: number };\n}\n\n// ── ThreeTierEvaluator ──────────────────────────────────────────────────\n\n/**\n * Whether a definition's right-hand side reads the name it defines.\n *\n * `extractReadsAndWrites` records every `:name = ...` as reading `name` once,\n * a convention the graph relies on, so one occurrence says nothing. A second\n * is the right-hand side.\n */\n/**\n * The names these programs define as functions, from the bodies compiled\n * alongside them.\n *\n * The checkpoint chain keeps a variable and a function of the same name in\n * separate bags and asks which bag a written name goes in. The VM cannot\n * say: once `f(x) = x + 1` is defined above, it holds a function named `f`\n * whether the line that just ran defined it or set the variable `f`. The\n * bytecode can.\n */\nfunction functionsDefinedBy(bytecodes: readonly BytecodeProgram[]): Set<string> {\n\tconst names = new Set<string>();\n\tfor (const program of bytecodes) {\n\t\tif (program.userFunctionBodies === undefined) continue;\n\t\tfor (const body of program.userFunctionBodies) names.add(body.name);\n\t}\n\treturn names;\n}\n\nfunction readsItself(reads: readonly string[], name: string): boolean {\n\tlet seen = 0;\n\tfor (const read of reads) if (read === name && ++seen === 2) return true;\n\treturn false;\n}\n\n/**\n * Orchestrates three-tier evaluation over a persistent DocumentModel.\n *\n * ── Tier assignment ─────────────────────────────────────────────────\n * | Tier | Condition | Action |\n * |───────|────────────────────────────────────|───────────────────────────────|\n * | **1** | Visible + Dirty (new/changed) | Full pipeline: lex→parse→compile→execute |\n * | **2** | Visible + Cached (scroll into view)| Execute from cached bytecode |\n * | **3** | Invisible + Dirty | Compile-only; execute only variable defs |\n * | Skip | Clean, empty, or non-evaluable | No action |\n *\n * ── Evaluation order ─────────────────────────────────────────────────\n * Lines are always processed in ascending document order (line 1 → end)\n * so that variable assignments flow correctly through the shared VM.\n * Tier 2 relies on this: by the time a clean cached line is reached,\n * the VM already contains all variables from preceding Tier-1 lines.\n *\n * ── Thread safety ────────────────────────────────────────────────────\n * Tier 1 (visible+dirty) compilation runs synchronously on the main thread\n * for immediate rendering. Tier 3 (invisible+dirty) compilation can be\n * dispatched to a Web Worker via `dispatchBackgroundCompiles()`. Worker-\n * compiled bytecode is stored in the DocumentModel and validated via\n * `isBytecodeValid()` to ensure the line text hasn't changed between\n * dispatch and response.\n */\nexport class ThreeTierEvaluator {\n\tprivate doc: DocumentModel;\n\tprivate engine: ExpressionEngine;\n\tprivate dag: DependencyGraph;\n\tprivate checkpointer: VMCheckpointer | null;\n\tprivate compilationWorker: CompilationWorkerManager | null = null;\n\tprivate pageManager: PageManager;\n\n\t/**\n\t * The text hash each line had when the graph last recorded what it reads,\n\t * by line id.\n\t *\n\t * A positional edge is recorded at run time and pinned, so it outlives the\n\t * text that produced it until the line runs again. Between an edit and\n\t * that run the graph still describes the old text, and a line edited out\n\t * of the viewport is compiled there without running, so the window can be\n\t * as long as the reader likes. The document model records the edit as a\n\t * new hash, and this is the hash the graph's edges for the line describe:\n\t * a line arriving at the pass with a different one has its positions\n\t * forgotten before anything else happens to it. See\n\t * {@link forgetPositionsOfEditedText}.\n\t */\n\tprivate textHashOfRecordedEdges: Map<number, number> = new Map();\n\n\t/**\n\t * The names each line genuinely reads among those it writes, by line id.\n\t *\n\t * `:v3 = v3 + 3` reads `v3` on its right-hand side; `:v3 = 44` does not,\n\t * although the graph records both as readers of `v3` by convention. The\n\t * difference is visible only in the per-expression extraction, where the\n\t * name appears twice, and only while Tier 1 has that in hand; the stored\n\t * read set is deduplicated. Recorded there, read by Tier 2, which puts such\n\t * a name back to the prefix before the line runs. Keyed by id so a\n\t * structural edit does not lose it.\n\t */\n\tprivate selfReadingWrites: Map<number, string[]> = new Map();\n\n\t/**\n\t * The lines that sit on a cycle, by line id. See {@link settleCycles}.\n\t */\n\tprivate cycleMemberIds: Set<number> = new Set();\n\t/**\n\t * The lowest position a structural edit moved, awaiting the next pass, or\n\t * `MAX_SAFE_INTEGER` when nothing is pending.\n\t *\n\t * A structural edit shifts every line from here down to a new position, and\n\t * the answer such a line still holds was computed for where it used to be.\n\t * The next pass knows the viewport, so it can clear exactly the stale ones,\n\t * the moved lines below the range it runs; see\n\t * {@link forgetMovedBelowViewport}. Recorded rather than acted on at edit\n\t * time because a moved line ABOVE the range is one a fresh pass still shows.\n\t */\n\tprivate movedFloor = Number.MAX_SAFE_INTEGER;\n\n\t/**\n\t * Unsubscribe from sharedGlobalVariableStore, set in the constructor\n\t * called from terminateWorker(). See the subscription itself below for\n\t * why this only marks lines dirty and never re-evaluates synchronously.\n\t */\n\tprivate globalUnsubscribe: (() => void) | null = null;\n\n\t/**\n\t * @param doc The persistent document model.\n\t * @param engine The expression engine (shared VM is accessed via engine.getVM()).\n\t * @param checkpointer Optional VM state checkpointer. If provided, the evaluator\n\t * will create checkpoints after variable-definition lines and support fast VM\n\t * restoration via `restoreTo()`. If omitted, checkpointing is disabled.\n\t */\n\tconstructor(\n\t\tdoc: DocumentModel,\n\t\tengine: ExpressionEngine,\n\t\tcheckpointer?: VMCheckpointer\n\t) {\n\t\tthis.doc = doc;\n\t\tthis.engine = engine;\n\t\tthis.dag = engine.getDag();\n\t\t// Built by default, because without one the engine answers wrong.\n\t\t//\n\t\t// A name written on more than one line has a value per position, and the\n\t\t// VM holds whichever write ran last. Anything re-running part of a\n\t\t// document therefore reads the state the document ENDS in unless this\n\t\t// rebuilds the state that position actually has. `setViewport` has always\n\t\t// asked for that (see {@link restoreTo}) and got nothing, so scrolling to\n\t\t// a line above a redefinition changed its answer: `:x = 1` / `x + 100`\n\t\t// with `:x = 99` below it showed 101, and 199 once scrolled to.\n\t\t//\n\t\t// It was an optional argument, which meant every host that did not know\n\t\t// to pass one got that. A caller may still supply its own, to share a\n\t\t// chain or to inspect it.\n\t\tthis.checkpointer = checkpointer ?? new VMCheckpointer(engine.getVM());\n\t\tthis.pageManager = new PageManager();\n\n\t\t// The async batcher re-runs a handful of lines out of the document when\n\t\t// a value arrives, against the same VM. Without the chain it reads what\n\t\t// the last full pass left, which is the state at the END of the\n\t\t// document, and a name written on more than one line is then wrong\n\t\t// everywhere below its second definition. Sharing the chain lets it\n\t\t// rebuild the state each line actually sits in. See\n\t\t// `AsyncResolutionBatcher.checkpointer`.\n\t\tthis.engine.getBatcher().checkpointer = this.checkpointer;\n\n\t\t// Lets the engine answer \"what's line N's cached result\" for\n\t\t// cross-line features (prev/line<N>/aggregation) without owning\n\t\t// document lifecycle itself. See ExpressionEngine.makeLineContext().\n\t\tthis.engine.setDocumentModel(this.doc);\n\n\t\t// ── Cross-document global-variable propagation ──────────────────\n\t\t// GlobalVariableAsyncResolver (via preflight) handles a line's FIRST\n\t\t// resolution when a global it reads wasn't known yet. This handles\n\t\t// the ONGOING case: a line that already has a real (non-pending)\n\t\t// value for `global :x` needs to go dirty again when some OTHER\n\t\t// document writes a NEW value to x, so this document's next\n\t\t// evaluate() picks up the change, \"dealing with the DAG across\n\t\t// pages\", not just first-resolution.\n\t\tthis.globalUnsubscribe = sharedGlobalVariableStore.subscribe((name) => {\n\t\t\t// Mark-dirty ONLY, never synchronously re-evaluate here.\n\t\t\t// enableValueArena/disableValueArena (Value.ts) is a single\n\t\t\t// non-reentrant module-level flag; evaluate()/setViewport() both\n\t\t\t// wrap their body in it, so a re-entrant evaluate() call from\n\t\t\t// inside this callback (itself possibly firing from INSIDE\n\t\t\t// another evaluate() call, via a STORE_GLOBAL_VAR opcode in some\n\t\t\t// other document being evaluated concurrently) would disable the\n\t\t\t// arena out from under the still-running outer call. This\n\t\t\t// mirrors applyTransaction()'s existing contract exactly: mark\n\t\t\t// dirty, let the caller's own evaluate() cadence pick it up.\n\t\t\tfor (const lineNumber of this.dag.getAffectedLines(globalDagKey(name))) {\n\t\t\t\tthis.doc.markDirtyByLineNumber(lineNumber);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Evaluate all lines needed to render the given viewport.\n\t *\n\t * Processes lines from 1 to `viewport.endLine` in document order.\n\t * Dirty lines in the viewport get Tier-1 full pipeline; clean cached\n\t * lines get Tier-2 bytecode execution. Lines after the viewport\n\t * get Tier-3 compile-only (with variable-def execution).\n\t *\n\t * @returns Results for all processed lines, including tier metadata.\n\t */\n\tevaluate(viewport: ViewportRange, signal?: AbortSignal): EvalResult {\n\t\t// ── One AbortController Per Keystroke ────────────────────────\n\t\t// Link the UI layer's keystroke signal to the engine so that\n\t\t// all per-evaluation AbortControllers created during this call\n\t\t// are canceled when the user types a new keystroke.\n\t\tthis.engine.setKeystrokeSignal(signal ?? null);\n\n\t\t// ── Phase 5.3: Enable arena for zero-allocation Value reuse ──\n\t\tenableValueArena();\n\t\ttry {\n\t\t\t// Re-seed running-total accumulators before the from-line-1 pass. A\n\t\t\t// `total += 5` line reads its own running value, so re-running it in\n\t\t\t// place would read the previous evaluation's total and double-count.\n\t\t\t// Reset each accumulator variable and mark every line that touches\n\t\t\t// it dirty, so the loop below recomputes the total from its seed,\n\t\t\t// top to bottom. (No-op when the document has no accumulators.)\n\t\t\tthis.reseedAccumulators();\n\n\t\t\t// The units in scope before the pass, so one that disappears during\n\t\t\t// it can be noticed.\n\t\t\tconst unitsBefore = this.engine.userUnitNames();\n\n\t\t\tconst lines: EvalLineResult[] = [];\n\t\t\tconst resultMap = new Map<number, Value[]>();\n\t\t\tconst tierCounts = { tier1: 0, tier2: 0, tier3: 0, skipped: 0 };\n\n\t\t\t// Process from line 1 to the end of the viewport for correct VM state.\n\t\t\t// We go to viewport.endLine because Tier 3 for invisible lines can be\n\t\t\t// done separately via backgroundCompile().\n\t\t\tconst docEnd = this.doc.lineCount;\n\t\t\tconst evalEnd = Math.min(viewport.endLine, docEnd);\n\t\t\tthis.forgetMovedBelowViewport(evalEnd);\n\n\t\t\t// Walked once, rather than descended into per position. See\n\t\t\t// `DocumentModel.getLineStatesInRange`.\n\t\t\tconst states = this.doc.getLineStatesInRange(1, evalEnd);\n\n\t\t\tfor (let pos = 1; pos <= evalEnd; pos++) {\n\t\t\t\tconst state = states[pos - 1];\n\t\t\t\tif (!state) {\n\t\t\t\t\ttierCounts.skipped++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst inViewport = pos >= viewport.startLine && pos <= viewport.endLine;\n\n\t\t\t\tconst lineResult = this.evaluateSingleLine(state, pos, inViewport);\n\t\t\t\tlines.push(lineResult);\n\n\t\t\t\tif (lineResult.tier === EvalTier.Tier1) tierCounts.tier1++;\n\t\t\t\telse if (lineResult.tier === EvalTier.Tier2) tierCounts.tier2++;\n\t\t\t\telse if (lineResult.tier === EvalTier.Tier3) tierCounts.tier3++;\n\t\t\t\telse tierCounts.skipped++;\n\n\t\t\t\tif (lineResult.results && inViewport) {\n\t\t\t\t\tresultMap.set(pos, lineResult.results.flat());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// A line that read a position it had not read before may have\n\t\t\t// closed a cycle, and the end of the pass is when every edge the\n\t\t\t// walk would follow describes a run of the text that holds it.\n\t\t\tthis.settleCycles();\n\n\t\t\t// ── Phase 5.2g: Page-based LRU eviction ──────────────────────\n\t\t\t// Evict bytecode/results from cold/warm pages to bound memory.\n\t\t\tthis.pageManager.maintainAfterEval(viewport, this.doc);\n\n\t\t\t// Every line has registered by now and the document is current, so a\n\t\t\t// name the pass reported as possibly undefined can be decided. See\n\t\t\t// `ExpressionEngine.settleOrphanedNames`.\n\t\t\tthis.engine.settleOrphanedNames();\n\n\t\t\t// A user unit that was in scope when the pass began and is not now\n\t\t\t// has to reach the lines that used it, since a unit is expanded\n\t\t\t// while a line is compiled and those lines hold bytecode built\n\t\t\t// around it. Compared rather than counted, so a line that redefines\n\t\t\t// the same unit every pass does not invalidate for ever.\n\t\t\tconst unitsAfter = new Set(this.engine.userUnitNames());\n\t\t\tif (unitsBefore.some((name) => !unitsAfter.has(name))) {\n\t\t\t\tthis.engine.invalidateForRemovedUserUnits();\n\t\t\t}\n\n\t\t\treturn { lines, resultMap, tierCounts };\n\t\t} finally {\n\t\t\t// Clear keystroke signal to prevent stale signal references\n\t\t\t// from being used by subsequent evaluations from other code paths.\n\t\t\tthis.engine.setKeystrokeSignal(null);\n\n\t\t\t// Phase 5.3: Always disable arena, even on exception.\n\t\t\t// Prevents arena Values from leaking into subsequent evaluations or tests.\n\t\t\tdisableValueArena();\n\t\t}\n\t}\n\n\t/**\n\t * Background-compile invisible dirty lines beyond the viewport (Tier 3 only).\n\t *\n\t * Compiles expressions to discover reads/writes for the dependency graph\n\t * without executing display-only expressions. Variable definitions are\n\t * executed to maintain VM state for future Tier-2 executions.\n\t *\n\t * This is intended to be called after evaluate() so visible lines are\n\t * rendered first, then background work fills in the dependency graph.\n\t *\n\t * **Phase 5.2h:** This synchronous method is retained for environments\n\t * without Worker support. Prefer `dispatchBackgroundCompiles()` which\n\t * offloads compilation to a Web Worker with Transferable bytecode.\n\t */\n\tbackgroundCompile(viewport: ViewportRange): EvalLineResult[] {\n\t\tconst results: EvalLineResult[] = [];\n\t\tconst docEnd = this.doc.lineCount;\n\t\tconst startPos = viewport.endLine + 1;\n\n\t\tfor (let pos = startPos; pos <= docEnd; pos++) {\n\t\t\tconst state = this.doc.getLineAt(pos);\n\t\t\tif (!state) continue;\n\n\t\t\t// Skip clean lines, already compiled + executed\n\t\t\tif (!state.dirty) continue;\n\n\t\t\t// Skip already-compiled Tier 3 lines, they have bytecode\n\t\t\t// but were compiled without execution (non-variable-def).\n\t\t\t// Recompiling is wasteful since the text hasn't changed\n\t\t\t// (text change clears bytecodes via editLine).\n\t\t\tif (state.bytecodes.length > 0 && !state.isVariableDef) continue;\n\n\t\t\tconst lineResult = this.evaluateSingleLine(state, pos, false);\n\t\t\tresults.push(lineResult);\n\t\t}\n\n\t\treturn results;\n\t}\n\n\t/**\n\t * Dispatch background compilation to a Web Worker (Phase 5.2h).\n\t *\n\t * Collects invisible dirty lines beyond the viewport that need compilation,\n\t * sends them to the compilation worker, and asynchronously stores the\n\t * transferred bytecode in the DocumentModel when the worker responds.\n\t *\n\t * This is the non-blocking alternative to `backgroundCompile()`. The worker\n\t * compiles expressions with Transferable ArrayBuffers (zero-copy postMessage),\n\t * so bytecode appears on the main thread without serialization overhead.\n\t *\n\t * Lines that already have cached bytecode (from a previous worker pass or\n\t * synchronous compile) are skipped, only truly uncompiled dirty lines are\n\t * sent to the worker.\n\t *\n\t * **Usage:** Call after `evaluate()` so visible lines render first, then\n\t * this fills the bytecode cache for future Tier-2 scrolls.\n\t *\n\t * @param viewport The current visible range. Lines beyond viewport.endLine\n\t * that are dirty and don't have bytecode are dispatched.\n\t */\n\tdispatchBackgroundCompiles(viewport: ViewportRange): void {\n\t\t// Collect invisible dirty lines that need compilation\n\t\tconst items = this.collectInvisibleCompileTargets(viewport);\n\t\tif (items.length === 0) return;\n\n\t\t// Lazy-init the worker (only if there are items to compile)\n\t\tif (!this.compilationWorker) {\n\t\t\tthis.compilationWorker = new CompilationWorkerManager();\n\t\t}\n\n\t\t// Fire-and-forget: send to worker, store results when they arrive\n\t\tthis.compilationWorker.compileBatch(items).then((results) => {\n\t\t\tthis.compilationWorker!.storeResults(results, this.doc);\n\t\t}).catch((_err) => {\n\t\t\t// Worker failure is non-fatal, next evaluate() will compile\n\t\t\t// these expressions synchronously.\n\t\t});\n\t}\n\n\t/**\n\t * Terminate the compilation worker if active, and unsubscribe from\n\t * sharedGlobalVariableStore. Call this when the evaluator is no longer\n\t * needed to clean up resources, every call site that retires a\n\t * ThreeTierEvaluator (document switch, pane destroy()) already calls\n\t * this unconditionally, so folding the global-store unsubscribe in here\n\t * needs no new call sites anywhere.\n\t */\n\tterminateWorker(): void {\n\t\tif (this.compilationWorker) {\n\t\t\tthis.compilationWorker.terminate();\n\t\t\tthis.compilationWorker = null;\n\t\t}\n\t\tif (this.globalUnsubscribe) {\n\t\t\tthis.globalUnsubscribe();\n\t\t\tthis.globalUnsubscribe = null;\n\t\t}\n\t}\n\n\t/**\n\t * Get the DocumentModel (read-only access for decoration building).\n\t */\n\tgetDoc(): DocumentModel {\n\t\treturn this.doc;\n\t}\n\n\t/**\n\t * Evaluate all dirty lines in the document, regardless of viewport.\n\t * Used for full re-evaluation after plugin register/unregister.\n\t */\n\tevaluateAll(signal?: AbortSignal): EvalResult {\n\t\tconst viewport = { startLine: 1, endLine: this.doc.lineCount };\n\t\tconst result = this.evaluate(viewport, signal);\n\t\t// evaluate() already calls maintainAfterEval internally\n\t\treturn result;\n\t}\n\n\t/**\n\t * Zero-allocation viewport evaluation, the Phase 5.2e \"holy grail.\"\n\t *\n\t * **Key insight:** When the user scrolls (viewport-only change, no edits),\n\t * we don't need to re-evaluate from line 1. Instead:\n\t *\n\t * 1. Restore the VM to just before the viewport via the nearest checkpoint.\n\t * 2. Evaluate ONLY the visible lines (Tier 2 for clean cached, Tier 1 for dirty).\n\t * 3. Lines before the viewport are completely skipped, their state lives in\n\t * the VM checkpointer's prototypal chain.\n\t *\n\t * **Correctness guard:** If any variable-definition line before the viewport\n\t * is dirty (e.g., the user edited a variable def that hasn't been\n\t * re-evaluated yet), we clear stale checkpoints and fall back to `evaluate()`\n\t * which processes from line 1 and rebuilds fresh checkpoints. This\n\t * guarantees that stale checkpoints are never used as restoration targets.\n\t * Only variable-def lines matter here, `VMCheckpointer.snapshot()` only\n\t * records state for lines that write a variable, so a dirty plain-expression\n\t * line before the viewport has no checkpoint to invalidate (see\n\t * `DocumentModel.hasAnyDirtyVariableDefLineBefore()`).\n\t *\n\t * **Performance:** O(visible lines) instead of O(document length). Target:\n\t * < 1ms for a typical ~30-line viewport, independent of document size.\n\t *\n\t * @param viewport The visible line range.\n\t * @returns Results for visible lines only. Lines before the viewport are\n\t * not included in `lines[]` or `resultMap`.\n\t */\n\tsetViewport(viewport: ViewportRange, signal?: AbortSignal): EvalResult {\n\t\t// ── One AbortController Per Keystroke ────────────────────────\n\t\t// Link the UI layer's keystroke signal to the engine.\n\t\tthis.engine.setKeystrokeSignal(signal ?? null);\n\n\t\t// ── Correctness guard: dirty lines before the viewport run first ──\n\t\t// A dirty line above the viewport may define something the viewport\n\t\t// reads, so the pass runs from line 1 and reaches it. The chain is\n\t\t// kept: a dirty line replaces its own entry when it runs, and a clean\n\t\t// line's entry, which nothing here runs again, is the one the lines\n\t\t// below need. Clearing it left them reading an empty prefix.\n\t\tif (viewport.startLine > 1 && this.hasDirtyLinesBefore(viewport.startLine)) {\n\t\t\t// evaluate() handles its own arena enable/disable and signal cleanup.\n\t\t\treturn this.evaluate(viewport, signal);\n\t\t}\n\n\t\t// ── Running totals are re-seeded here too ──\n\t\t// A `total += 5` line compiles to no bytecode, so a clean one cannot be\n\t\t// re-run from cache: it has to go back through the full pipeline. That\n\t\t// was already true, and harmless while the VM was never rewound, because\n\t\t// the total simply stayed where the last pass left it. Restoring to the\n\t\t// line before the viewport rewinds it, and a viewport containing the\n\t\t// accumulator lines then found nothing to rebuild them from:\n\t\t// `spent += 10` / `spent += 20` / `spent` answered\n\t\t// `Undefined variable: spent` on a scroll. Marking them dirty sends them\n\t\t// to Tier 1, which recomputes each total from its seed.\n\t\tthis.reseedAccumulators();\n\n\t\t// ── Phase 5.2g: Page-based LRU eviction (MUST run before preload) ──\n\t\t// maintainAfterEval captures the scroll direction and updates lastViewportStart\n\t\t// BEFORE preloadNextPages reads the direction for preloading.\n\t\tthis.pageManager.maintainAfterEval(viewport, this.doc);\n\n\t\t// ── Phase 5.2g: Detect scroll direction & preload ───────────\n\t\tthis.preloadNextPages(viewport);\n\n\t\t// ── Restore VM state from nearest checkpoint before the viewport ──\n\t\t// This sets all variables that were defined at or before startLine-1.\n\t\tthis.restoreTo(viewport.startLine - 1);\n\n\t\t// ── Phase 5.3: Enable arena for zero-allocation Tier 2 execution ──\n\t\tenableValueArena();\n\t\ttry {\n\t\t\t// ── Evaluate only visible lines ──\n\t\t\tconst result = this.collectEvalResults(viewport.startLine, viewport.endLine);\n\t\t\treturn result;\n\t\t} finally {\n\t\t\t// Clear keystroke signal to prevent stale signal references.\n\t\t\tthis.engine.setKeystrokeSignal(null);\n\n\t\t\t// Phase 5.3: Always disable arena, even on exception.\n\t\t\t// Prevents cross-test contamination from arena leaks.\n\t\t\tdisableValueArena();\n\t\t}\n\t}\n\n\t/**\n\t * Apply incremental line-level changes to the document model.\n\t *\n\t * **Phase 5.2f:** Replaces the O(N) `setDocument()` + full re-evaluation\n\t * with O(changed) incremental updates. Key benefits:\n\t *\n\t * 1. Unchanged lines retain their persistent lineIds → bytecode survives\n\t * 2. Only changed + DAG-downstream lines are marked dirty → Tier 1 re-evaluation\n\t * 3. Clean lines in viewport use Tier 2 (cached bytecode execution)\n\t * 4. Clean lines outside viewport are skipped entirely\n\t *\n\t * The DAG is fully cleared after propagation: shifted lines would have\n\t * stale entries keyed by old line numbers, so the DAG is rebuilt from\n\t * scratch during the subsequent `evaluate()` call.\n\t *\n\t * **Caller should follow up with `evaluate(viewport)`** to re-evaluate\n\t * dirty lines from line 1 and rebuild the DAG + checkpoints.\n\t *\n\t * @param changes Line-level changes to apply. Must be non-overlapping.\n\t * @returns Metadata about the applied changes.\n\t */\n\tapplyTransaction(changes: LineChange[]): {\n\t\tinserted: number[];\n\t\tremoved: number[];\n\t} {\n\t\t// ── Phase 1: Collect DAG writes + downstream lineIds ───────────\n\t\t// Must happen BEFORE applyChanges() because line numbers are still\n\t\t// valid at this point. We collect writes from deleted lines and\n\t\t// resolve downstream consumers to lineIds (not line numbers) so\n\t\t// they survive the position shifts that applyChanges() causes.\n\t\tconst allWrites = new Set<string>();\n\n\t\tfor (const change of changes) {\n\t\t\tfor (let i = 0; i < change.deleteCount; i++) {\n\t\t\t\tconst lineNum = change.startLine + i;\n\t\t\t\tconst writes = this.dag.getWrites(lineNum);\n\t\t\t\tfor (const w of writes) {\n\t\t\t\t\tallWrites.add(w);\n\t\t\t\t}\n\t\t\t\t// Clean up DAG references for this line, and forget any name the\n\t\t\t\t// deleted line was the last to define.\n\t\t\t\tthis.undefineOrphans(this.dag.removeLine(lineNum));\n\t\t\t\t// A deleted line will never be compiled again, so it cannot drop\n\t\t\t\t// its own definitions on the way through. Losing one reaches the\n\t\t\t\t// lines that used it here rather than at the end of a pass,\n\t\t\t\t// because this runs before the next pass begins and that\n\t\t\t\t// comparison would see the unit already gone.\n\t\t\t\tif (this.engine.undefineUserUnitsFrom(this.doc.getLineAt(lineNum)?.lineId ?? -1)) {\n\t\t\t\t\tthis.engine.invalidateForRemovedUserUnits();\n\t\t\t\t}\n\t\t\t\t// And its cached bytecode. The dependency graph was already\n\t\t\t\t// pruned here; the LineCache was not, so a deleted line kept its\n\t\t\t\t// entry until the whole cache was dropped on a document switch.\n\t\t\t\t// Editing a long document over a session accumulated entries for\n\t\t\t\t// line numbers that no longer existed.\n\t\t\t\tthis.engine.getLineCache().removeAllForLine(lineNum);\n\t\t\t}\n\t\t}\n\n\t\t// Resolve downstream consumers to persistent lineIds BEFORE the\n\t\t// structural change shifts line numbers. After applyChanges(),\n\t\t// we mark these lineIds dirty, their positions don't matter.\n\t\tconst downstreamLineIds = new Set<number>();\n\t\tconst positionalReaderLineIds = new Set<number>();\n\n\t\t// Every line that reads a position joins them, because a structural\n\t\t// edit changes what a position means without changing a character of\n\t\t// the line that reads it. `line 5` names different text once a line is\n\t\t// inserted above it, `prev` names a different neighbour, and an `above`\n\t\t// aggregate covers a different block. None of that reaches the loop\n\t\t// above, which follows the names a deleted line wrote.\n\t\t//\n\t\t// A reader that moved counts too, not only one whose target moved:\n\t\t// `line 5 + 4` sitting at position 4 is a reference, and after an\n\t\t// insert above it, sitting at position 5, it is a self-reference. A\n\t\t// settled pass reports that; the edited document answered from the\n\t\t// value it had.\n\t\t//\n\t\t// Their answers go as well as their dirty flag, which is the half that\n\t\t// was missing. Marking a line dirty says it must run again; it does not\n\t\t// stop another line reading what it said in the meantime, and after a\n\t\t// structural edit what it said was about a document that no longer\n\t\t// exists. Two positional readers that end up reading each other then\n\t\t// chase those old answers instead of reporting the cycle: `line 3 + 5`\n\t\t// above an `average above` that covers it took the average's previous\n\t\t// value, computed a new one, and the average recomputed from that, by a\n\t\t// smaller amount each pass, landing wherever the passes ran out. With\n\t\t// nothing to chase, both report the cycle, which is what a pass over the\n\t\t// same text reports.\n\t\t//\n\t\t// Only on a structural edit, and only these lines. An ordinary edit\n\t\t// leaves every position meaning what it meant, so a reader's answer is\n\t\t// still about this document and taking it away would show an error to\n\t\t// whoever asked before it ran again.\n\t\tfor (const reader of this.dag.linesReadingAPosition()) {\n\t\t\tconst state = this.doc.getLineAt(reader);\n\t\t\tif (!state) continue;\n\t\t\tdownstreamLineIds.add(state.lineId);\n\t\t\tpositionalReaderLineIds.add(state.lineId);\n\t\t}\n\t\tfor (const writeVar of allWrites) {\n\t\t\tconst affected = this.dag.getAffectedLines(writeVar);\n\t\t\tfor (const lineNum of affected) {\n\t\t\t\tconst state = this.doc.getLineAt(lineNum);\n\t\t\t\tif (state) {\n\t\t\t\t\tdownstreamLineIds.add(state.lineId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// ── Phase 2: Apply structural changes to DocumentModel ─────────\n\t\tconst result = this.doc.applyChanges(changes);\n\n\t\t// ── Phase 3: The chain follows the lines to their new positions ──\n\t\t// It used to be cleared here and rebuilt as lines ran, but a clean\n\t\t// line above the viewport never runs, so its entry never came back\n\t\t// and a line below asking what the prefix held was told nothing.\n\t\tthis.checkpointer?.renumber((lineId) => this.doc.getLinePosition(lineId));\n\t\t// A deleted line's names hold what the rest of the document leaves\n\t\t// them, which is where a pass over the remaining text would leave them;\n\t\t// until now they held what the deleted line had written.\n\t\tfor (const written of allWrites) this.engine.restoreToPrefix(written, Number.MAX_SAFE_INTEGER);\n\n\t\t// ── Phase 4: Mark DAG-downstream lines dirty by lineId ─────────\n\t\t// Using lineId instead of line number is position-agnostic:\n\t\t// lines that shifted due to insertions/deletions above them are\n\t\t// still correctly targeted. Lines that were deleted (lineId no\n\t\t// longer in the doc) are silently ignored by markDirty().\n\t\tfor (const lineId of downstreamLineIds) {\n\t\t\tthis.doc.markDirty(lineId);\n\t\t}\n\t\tfor (const lineId of positionalReaderLineIds) {\n\t\t\tthis.doc.forgetResult(lineId);\n\t\t}\n\n\t\t// Every line from the earliest change downward now sits at a different\n\t\t// position, so a result it still holds was computed for where it used to\n\t\t// be. The stale one must not outlive the edit below the viewport, where a\n\t\t// pass never reaches it and a fresh pass shows nothing: a line reading\n\t\t// that position read the stale answer back as a real one (`line 4`\n\t\t// reading a line that had moved to position 4 reported its error rather\n\t\t// than \"not evaluated yet\"). It is cleared at the next evaluate, once the\n\t\t// viewport is known, since a moved line ABOVE the viewport is one a fresh\n\t\t// pass still shows and must be kept. See {@link forgetMovedBelowViewport}.\n\t\tconst earliestChange = changes.reduce((lowest, change) => Math.min(lowest, change.startLine), Number.MAX_SAFE_INTEGER);\n\t\tthis.movedFloor = Math.min(this.movedFloor, earliestChange);\n\n\t\t// ── Phase 5: Clear DAG to avoid phantom entries ────────────────\n\t\t// Entries keyed by old line numbers are stale after structural\n\t\t// changes. Rather than updating shifted entries, we clear the DAG\n\t\t// and let the subsequent evaluate() call rebuild it from scratch.\n\t\tthis.dag.clear();\n\n\t\t// A deleted line's id is never seen again, so its entries are dead weight.\n\t\tfor (const lineId of result.removed) {\n\t\t\tthis.textHashOfRecordedEdges.delete(lineId);\n\t\t\tthis.selfReadingWrites.delete(lineId);\n\t\t\tthis.cycleMemberIds.delete(lineId);\n\t\t}\n\n\t\treturn {\n\t\t\tinserted: result.inserted,\n\t\t\tremoved: result.removed,\n\t\t};\n\t}\n\n\t/**\n\t * Forget the positions the graph says a line reads, once its text changed.\n\t *\n\t * A positional edge is discovered while the line runs and pinned, because\n\t * the text cannot put it back when the line re-registers. That is right\n\t * while the text is the same and wrong the moment it is not: `prev + 1`\n\t * edited to `7` went on reading line 1 in the graph until it ran, and a\n\t * line edited out of the viewport is compiled there without running, so\n\t * it could go on for as long as the reader liked. Three of the attempts\n\t * recorded on #444 found cycles that had already been edited away for\n\t * exactly that reason.\n\t *\n\t * Keyed on the edit itself, which the document model records as a new\n\t * text hash, rather than on anything the graph knows. Done at the line's\n\t * first visit after the edit, whatever tier that visit takes, and only\n\t * when the line is dirty, since an edit always dirties, so a clean line's\n\t * text is the text its edges describe. The run that follows records what\n\t * the new text reads, and {@link DependencyGraph.reconcilePositionReads}\n\t * keeps that exact from then on.\n\t */\n\tprivate forgetPositionsOfEditedText(state: LineState, lineNumber: number): void {\n\t\tconst recordedFor = this.textHashOfRecordedEdges.get(state.lineId);\n\t\tif (recordedFor === state.textHash) return;\n\t\tif (recordedFor !== undefined) this.dag.forgetPositionReads(lineNumber);\n\t\tthis.textHashOfRecordedEdges.set(state.lineId, state.textHash);\n\t}\n\n\t/**\n\t * Keep the record of which lines sit on a cycle current.\n\t *\n\t * A cycle can only appear or vanish with an edge, so the record is\n\t * recomputed only when the graph changed this pass, by a walk from what\n\t * changed: readers that gained a position, lines whose edge set changed\n\t * (a dropped positional edge included, which is how a cycle is broken), and\n\t * the readers of a name whose producers changed, which is how a name that\n\t * pinned a cycle is withdrawn. A line whose status flips is marked to run\n\t * again, so it runs the other way on the next pass. A settled pass changes\n\t * nothing and walks nothing.\n\t *\n\t * What membership does is decided where a line runs: see\n\t * {@link evaluateSingleLine} and `ExpressionEngine.setLineOnCycle`. Earlier\n\t * versions reset a member's answer here instead, at the end of the pass,\n\t * and that was one line too late: the number the member computed had\n\t * already been read by the lines below it during the pass. Deciding how the\n\t * member runs means no number ever exists to be read.\n\t */\n\tprivate settleCycles(): void {\n\t\tconst gained = this.dag.takeReadersThatGainedAPosition();\n\t\tconst changes = this.dag.takeEdgeChanges();\n\t\tconst roots = new Set<number>(gained);\n\t\tfor (const line of changes.lines) roots.add(line);\n\t\tfor (const key of changes.keys) {\n\t\t\tif (isPrefixedEdgeKey(key)) continue;\n\t\t\tfor (const reader of this.dag.directConsumersOf(key)) roots.add(reader);\n\t\t}\n\t\tif (roots.size === 0) return;\n\t\t// Every current member is re-examined as well. A cycle broken by an edit\n\t\t// to a line that merely fed it is no longer reachable from that line, so\n\t\t// a walk from the change alone left the members marked for ever.\n\t\tif (this.cycleMemberIds.size > 0) {\n\t\t\tconst lineCount = this.doc.lineCount;\n\t\t\tfor (let position = 1; position <= lineCount; position++) {\n\t\t\t\tconst state = this.doc.getLineAt(position);\n\t\t\t\tif (state !== undefined && this.cycleMemberIds.has(state.lineId)) roots.add(position);\n\t\t\t}\n\t\t}\n\t\tthis.recomputeCycleMembership(roots);\n\t}\n\n\t/**\n\t * Recompute which lines sit on a cycle, for every line the change reaches.\n\t *\n\t * Tarjan's components, iteratively: a `prev` chain can be thousands of\n\t * lines deep, which is deeper than a recursive walk should go. A line the\n\t * walk visits is on a cycle if its component has more than one member, and\n\t * off one otherwise; a line the walk does not reach kept its edges and its\n\t * status. Membership is by line id, so a structural edit does not lose it;\n\t * such an edit clears the graph and every line re-registers on the next\n\t * pass, so the walk then reaches everything.\n\t */\n\tprivate recomputeCycleMembership(roots: ReadonlySet<number>): void {\n\t\tconst index = new Map<number, number>();\n\t\tconst low = new Map<number, number>();\n\t\tconst onStack = new Set<number>();\n\t\tconst stack: number[] = [];\n\t\tconst frames: { line: number; targets: number[]; next: number }[] = [];\n\t\tlet visited = 0;\n\t\tconst enter = (line: number): void => {\n\t\t\tindex.set(line, visited);\n\t\t\tlow.set(line, visited);\n\t\t\tvisited++;\n\t\t\tstack.push(line);\n\t\t\tonStack.add(line);\n\t\t\tframes.push({ line, targets: this.dependantsOf(line), next: 0 });\n\t\t};\n\t\tconst setMembership = (line: number, onCycle: boolean): void => {\n\t\t\tconst state = this.doc.getLineAt(line);\n\t\t\tif (state === undefined) return;\n\t\t\tif (onCycle === this.cycleMemberIds.has(state.lineId)) return;\n\t\t\tif (onCycle) this.cycleMemberIds.add(state.lineId);\n\t\t\telse this.cycleMemberIds.delete(state.lineId);\n\t\t\t// It runs the other way from now on, so it has to run.\n\t\t\tthis.doc.markDirty(state.lineId);\n\t\t};\n\n\t\tfor (const root of roots) {\n\t\t\tif (index.has(root)) continue;\n\t\t\tenter(root);\n\t\t\twhile (frames.length > 0) {\n\t\t\t\tconst frame = frames[frames.length - 1];\n\t\t\t\tif (frame.next < frame.targets.length) {\n\t\t\t\t\tconst target = frame.targets[frame.next++];\n\t\t\t\t\tif (!index.has(target)) enter(target);\n\t\t\t\t\telse if (onStack.has(target)) low.set(frame.line, Math.min(low.get(frame.line)!, index.get(target)!));\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tframes.pop();\n\t\t\t\tif (frames.length > 0) {\n\t\t\t\t\tconst parent = frames[frames.length - 1].line;\n\t\t\t\t\tlow.set(parent, Math.min(low.get(parent)!, low.get(frame.line)!));\n\t\t\t\t}\n\t\t\t\tif (low.get(frame.line) !== index.get(frame.line)) continue;\n\t\t\t\tconst members: number[] = [];\n\t\t\t\tlet member: number;\n\t\t\t\tdo {\n\t\t\t\t\tmember = stack.pop()!;\n\t\t\t\t\tonStack.delete(member);\n\t\t\t\t\tmembers.push(member);\n\t\t\t\t} while (member !== frame.line);\n\t\t\t\tfor (const line of members) setMembership(line, members.length > 1);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * The lines whose answers are computed from this line's: the readers of its\n\t * position, and the readers of each name it writes.\n\t *\n\t * Both kinds of edge, because a cycle can run through either. `line 2 + 5`\n\t * above `prev + 5` is a cycle of positions; `:v1 = v2 + 5` above\n\t * `:v2 = v1 + 8` is a cycle of names; `spent += line 3` above `spent` is\n\t * one of each. A walk that followed positions alone found the first and\n\t * declared the other two out of scope.\n\t *\n\t * Edges point the way the graph's consumer index points, from a line to\n\t * what reads it, which is the transpose of \"what this line reads\". The\n\t * strongly connected components of a graph and its transpose are the same\n\t * sets, so the walk finds the same cycles, and the consumer index is the one\n\t * the graph keeps clear of a definition's conventional read of its own name\n\t * and of a running total's read of its own total. Followed as raw reads,\n\t * every definition was a self-loop and every twice-defined name a cycle.\n\t *\n\t * Only variable names among the keys: a tag, a global or a data source is\n\t * not a line's answer, and a cycle cannot run through one.\n\t */\n\tprivate dependantsOf(line: number): number[] {\n\t\tconst out = [...this.dag.getAffectedLinesByPosition(line)];\n\t\tfor (const key of this.dag.getWrites(line)) {\n\t\t\tif (isPrefixedEdgeKey(key)) continue;\n\t\t\tfor (const reader of this.dag.directConsumersOf(key)) out.push(reader);\n\t\t\t// A running total's other steppers depend on this one through the\n\t\t\t// total, and the graph keeps a writer out of its own name's consumers,\n\t\t\t// so that dependency is recovered here for an accumulator name alone.\n\t\t\t// `spent += line 2` above `spent += 9` is a cycle: the step below\n\t\t\t// folds in the step above, and the step above reads the line below.\n\t\t\t// Ordered, because the fold is: a step depends on the steps above it,\n\t\t\t// never on those below. Without the order a plain column of steps was\n\t\t\t// a cycle.\n\t\t\tif (this.engine.isAccumulatorName(key)) {\n\t\t\t\tfor (const stepper of this.dag.getProducers(key)) if (stepper > line) out.push(stepper);\n\t\t\t}\n\t\t}\n\t\treturn out;\n\t}\n\n\t// ── Private helpers ─────────────────────────────────────────────────\n\n\t/**\n\t * Reset running-total accumulators and mark the lines that touch them\n\t * dirty, so the from-line-1 pass in {@link evaluate} recomputes each total\n\t * from its seed instead of reading its own previous evaluation's value.\n\t *\n\t * A `total += 5` line compiles to no bytecode (it rides the symbolic\n\t * channel), so a clean one would be skipped by the tier dispatch rather\n\t * than re-run. It must re-run to re-apply its delta over the freshly-reset\n\t * base, so every line that WRITES an accumulator name is forced dirty (the\n\t * DAG drops a self-writing line from its own variable's consumers, so\n\t * getAffectedLines can't surface them). Pure readers of the total keep\n\t * their real bytecode and re-run via Tier 2 once the total is correct, so\n\t * they need no special handling. Cheap: a no-op unless the document\n\t * actually uses `+=`/`-=`, and accumulators are rare.\n\t */\n\tprivate reseedAccumulators(): void {\n\t\tconst names = this.engine.resetAccumulators();\n\t\tif (names.size === 0) return;\n\t\t// Read from the lines themselves, not from the dependency graph.\n\t\t//\n\t\t// A structural edit clears the graph (see {@link applyTransaction}) and\n\t\t// lets the next pass rebuild it, so asking the graph what each line\n\t\t// writes answers nothing at all on the pass that follows an insert or a\n\t\t// delete. No accumulator line was marked, so none re-ran, and the totals\n\t\t// below the edit kept the previous pass's sum: inserting `spent += 2`\n\t\t// above `spent += 7` left the second line reading 7 rather than 9. The\n\t\t// line's own write set is recorded on the line and survives the edit.\n\t\tconst docEnd = this.doc.lineCount;\n\t\tfor (let pos = 1; pos <= docEnd; pos++) {\n\t\t\tconst state = this.doc.getLineAt(pos);\n\t\t\tif (state === undefined || state.writes.length === 0) continue;\n\t\t\tfor (const written of state.writes) {\n\t\t\t\tif (names.has(written)) {\n\t\t\t\t\tthis.doc.markDirtyByLineNumber(pos);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Collect evaluation results for a contiguous range of lines.\n\t *\n\t * Used by both `evaluate()` (startLine=1) and `setViewport()` (any start).\n\t * All lines in the range are treated as in-viewport (visible), callers that\n\t * need the invisible/dirty → Tier 3 handling should use `evaluate()` instead.\n\t *\n\t * @param startLine First line to evaluate (1-based, inclusive).\n\t * @param endLine Last line to evaluate (1-based, inclusive). Clamped to docEnd.\n\t */\n\t/**\n\t * Clear the stale answers a structural edit left below the viewport.\n\t *\n\t * An insert or a delete moves every line from {@link movedFloor} down to a\n\t * new position; the answer each still holds was computed for its old one. A\n\t * moved line inside the range about to run is re-run and gets a fresh answer,\n\t * and a moved line above it is one a fresh pass driven to this viewport still\n\t * shows, so both are left alone. A moved line BELOW the range is neither: the\n\t * pass never reaches it, nothing re-runs it, and a fresh pass shows nothing\n\t * there, yet the incremental path kept its old answer, which a line reading\n\t * that position then read back as a real one (#458). Those are cleared here,\n\t * from just past `evalEnd` (or the earliest move, whichever is lower down) to\n\t * the end of the document. Consumed once: a later pass over a wider viewport\n\t * re-runs whichever of them it now covers.\n\t *\n\t * @param evalEnd - The last line this pass will run.\n\t */\n\tprivate forgetMovedBelowViewport(evalEnd: number): void {\n\t\tif (this.movedFloor === Number.MAX_SAFE_INTEGER) return;\n\t\tconst from = Math.max(evalEnd + 1, this.movedFloor);\n\t\tconst docEnd = this.doc.lineCount;\n\t\tfor (let position = from; position <= docEnd; position++) {\n\t\t\tconst moved = this.doc.getLineAt(position);\n\t\t\tif (moved) this.doc.forgetResult(moved.lineId);\n\t\t}\n\t\tthis.movedFloor = Number.MAX_SAFE_INTEGER;\n\t}\n\n\tprivate collectEvalResults(startLine: number, endLine: number): EvalResult {\n\t\tconst lines: EvalLineResult[] = [];\n\t\tconst resultMap = new Map<number, Value[]>();\n\t\tconst tierCounts = { tier1: 0, tier2: 0, tier3: 0, skipped: 0 };\n\n\t\tconst docEnd = this.doc.lineCount;\n\t\tconst evalEnd = Math.min(endLine, docEnd);\n\t\tthis.forgetMovedBelowViewport(evalEnd);\n\n\t\tfor (let pos = startLine; pos <= evalEnd; pos++) {\n\t\t\tconst state = this.doc.getLineAt(pos);\n\t\t\tif (!state) continue;\n\n\t\t\t// All processed lines are in-viewport for setViewport, or conditionally\n\t\t\t// in-viewport for evaluate (handled by caller). We pass `true` here\n\t\t\t// because evaluateSingleLine's `inViewport` param controls Tier 1 vs\n\t\t\t// Tier 3 dispatch; callers must manage this distinction externally.\n\t\t\t//\n\t\t\t// evaluate() handles this by passing `inViewport` per-line; it loops\n\t\t\t// directly rather than using this helper for that reason.\n\t\t\tconst lineResult = this.evaluateSingleLine(state, pos, true);\n\t\t\tlines.push(lineResult);\n\n\t\t\tif (lineResult.tier === EvalTier.Tier1) tierCounts.tier1++;\n\t\t\telse if (lineResult.tier === EvalTier.Tier2) tierCounts.tier2++;\n\t\t\telse if (lineResult.tier === EvalTier.Tier3) tierCounts.tier3++;\n\t\t\telse tierCounts.skipped++;\n\n\t\t\tif (lineResult.results) {\n\t\t\t\tresultMap.set(pos, lineResult.results.flat());\n\t\t\t}\n\t\t}\n\n\t\t// The same end-of-pass check `evaluate` makes; a viewport pass runs\n\t\t// lines and records what they read just as a full one does.\n\t\tthis.settleCycles();\n\n\t\treturn { lines, resultMap, tierCounts };\n\t}\n\n\t/**\n\t * Check whether any **variable-definition** line before `position`\n\t * (1-based, exclusive) is dirty.\n\t *\n\t * Used by `setViewport()` to decide whether to fall back to `evaluate()`:\n\t * if a variable-def before the viewport is dirty, the checkpoint state\n\t * `restoreTo()` would use may be stale and we need to reprocess from\n\t * line 1 to rebuild checkpoints correctly.\n\t *\n\t * Deliberately narrower than `DocumentModel.hasAnyDirtyLineBefore()`:\n\t * checkpoints only snapshot variable-def lines (see VMCheckpointer), so a\n\t * dirty plain-expression line before the viewport can't have invalidated\n\t * one, there's nothing checkpointed for it to invalidate. Using the\n\t * broader check here previously caused a real perf bug: `PageManager`'s\n\t * cold-page eviction marks evicted non-variable-def lines dirty, so\n\t * scrolling far into a large, variable-def-free document would trip this\n\t * guard, fall back to `evaluate()`, which recompiles those lines via\n\t * Tier 3 (never clearing their dirty flag by design), causing the very\n\t * next `maintainAfterEval()` to re-evict and re-dirty them, a\n\t * self-sustaining loop that pinned every subsequent `setViewport()` call\n\t * to the cost of a full re-evaluation instead of O(visible lines).\n\t *\n\t * Delegates to DocumentModel.hasAnyDirtyVariableDefLineBefore(), which\n\t * tracks dirty lineIds incrementally instead of scanning every line up to\n\t * `position` on every call. This used to be a real per-scroll cost\n\t * (benchmarked at ~10ms scrolled near the bottom of a 20k-line document)\n\t * since it fired on every viewport change, not just edits.\n\t */\n\tprivate hasDirtyLinesBefore(position: number): boolean {\n\t\treturn this.doc.hasAnyDirtyVariableDefLineBefore(position);\n\t}\n\n\t/**\n\t * Evaluate a single line using the appropriate tier.\n\t *\n\t * Tier assignment logic:\n\t * - Empty/markdown-only lines → skipped\n\t * - Dirty + in-viewport → Tier 1 (full pipeline)\n\t * - Dirty + not in viewport → Tier 3 (compile-only, execute variable defs)\n\t * - Clean + has bytecode + in viewport → Tier 2 (execute from cache)\n\t * - Clean + no bytecode → skipped (non-evaluable)\n\t *\n\t * Around the dispatch, the graph is kept honest about what the line reads\n\t * by position: an edited line's old positions go before it runs, and a\n\t * line that executed is cut back to the positions that run read. Only an\n\t * executing tier is reconciled, since a line compiled without running, or\n\t * skipped, read nothing for a reason that says nothing about its text.\n\t */\n\tprivate evaluateSingleLine(\n\t\tstate: LineState,\n\t\tlineNumber: number,\n\t\tinViewport: boolean\n\t): EvalLineResult {\n\t\t// An edit always dirties, so a clean line's edges describe its text and\n\t\t// the map is not consulted for it: the check costs the pass nothing on\n\t\t// the lines that are most of it.\n\t\tif (state.dirty) this.forgetPositionsOfEditedText(state, lineNumber);\n\t\t// A line on a cycle runs the way a single fresh pass runs it: the names\n\t\t// it reads hold what the lines above left, and a line below it is not\n\t\t// yet evaluated. That is the answer `parseDocument` gives such a line,\n\t\t// and it is stable, where a number found in the VM from the previous\n\t\t// pass is the start of a chase. See `ExpressionEngine.setLineOnCycle`.\n\t\tconst onCycle = this.cycleMemberIds.has(state.lineId);\n\t\tthis.engine.setLineOnCycle(onCycle);\n\t\tif (onCycle) {\n\t\t\tfor (const read of state.reads) {\n\t\t\t\tif (!isPrefixedEdgeKey(read)) this.engine.restoreToPrefix(read, lineNumber);\n\t\t\t}\n\t\t}\n\t\tconst lineResult = this.dispatchLine(state, lineNumber, inViewport);\n\t\t// A member's edges follow its run like any other line's. Its run is cut\n\t\t// short on purpose (a forward read stops a range early), which is why\n\t\t// every aggregate declares its whole span before reading any of it;\n\t\t// keeping a member's forward edges regardless kept an edge to a line\n\t\t// the member had stopped reading, and that phantom cycle outlived the\n\t\t// real one.\n\t\tif (lineResult.tier === EvalTier.Tier1 || lineResult.tier === EvalTier.Tier2) {\n\t\t\tthis.dag.reconcilePositionReads(lineNumber);\n\t\t}\n\t\treturn lineResult;\n\t}\n\n\t/** The tier dispatch itself; see {@link evaluateSingleLine} for what wraps it. */\n\tprivate dispatchLine(\n\t\tstate: LineState,\n\t\tlineNumber: number,\n\t\tinViewport: boolean\n\t): EvalLineResult {\n\t\tconst baseResult: Omit<EvalLineResult, \"tier\" | \"result\" | \"error\"> = {\n\t\t\tlineId: state.lineId,\n\t\t\tlineNumber,\n\t\t};\n\n\t\t// A clean line outside the viewport has nothing to do, and nothing has\n\t\t// to be worked out in order to know that.\n\t\t//\n\t\t// It is the last case the dispatch below reaches, so it used to arrive\n\t\t// there having paid for an emptiness scan of its text and an expression\n\t\t// extraction, neither of which can change the answer: the line is not\n\t\t// dirty, so it is not compiled, and it is not visible, so it is not\n\t\t// executed. A pass runs from line 1 to the end of the viewport, so on a\n\t\t// long document scrolled to the bottom almost every line took that\n\t\t// route, and an edit cost the distance from line 1 rather than the size\n\t\t// of the viewport.\n\t\t//\n\t\t// The two paths below this also mark an empty line clean and record\n\t\t// that it is empty. Both are already true of a line that is clean: it\n\t\t// has been through here before, on the pass that cleaned it.\n\t\tif (!state.dirty && !inViewport) {\n\t\t\treturn { ...baseResult, tier: EvalTier.Skipped, result: null, error: null };\n\t\t}\n\n\t\t// Skip empty/markdown-only lines\n\t\tif (state.isEmpty || isEmptyLine(state.text)) {\n\t\t\tthis.deregisterIfDirty(state, lineNumber);\n\t\t\tstate.isEmpty = true;\n\t\t\tthis.doc.markClean(state.lineId);\n\t\t\treturn { ...baseResult, tier: EvalTier.Skipped, result: null, error: null };\n\t\t}\n\n\t\t// Extract all evaluable expressions (may be multiple inline solves)\n\t\tconst { expressions, inlineSolveCount } = this.extractExpressions(state);\n\t\tif (expressions.length === 0) {\n\t\t\tthis.deregisterIfDirty(state, lineNumber);\n\t\t\tstate.isEmpty = true;\n\t\t\tthis.doc.markClean(state.lineId);\n\t\t\treturn { ...baseResult, tier: EvalTier.Skipped, result: null, error: null };\n\t\t}\n\n\t\t// Determine the expression to evaluate (only needed for dirty lines)\n\t\tif (state.dirty) {\n\t\t\tif (inViewport) {\n\t\t\t\t// ── Tier 1: Visible + Dirty → Full Pipeline ──────────\n\t\t\t\treturn this.evaluateTier1(state, lineNumber, expressions, inlineSolveCount, baseResult);\n\t\t\t} else {\n\t\t\t\t// ── Tier 3: Invisible + Dirty → Compile-only ─────────\n\t\t\t\t// Skip recompilation if already compiled by a previous Tier 3 pass.\n\t\t\t\t// Non-variable-def lines keep dirty=true after Tier 3 (so they get\n\t\t\t\t// Tier 1 when scrolled into view), but recompiling identical text\n\t\t\t\t// produces the same bytecode and DAG entries. Text changes clear\n\t\t\t\t// bytecodes via DocumentModel.editLine(), so a length check is safe.\n\t\t\t\tif (state.bytecodes.length > 0 && state.bytecodes.length === expressions.length && !state.isVariableDef) {\n\t\t\t\t\treturn { ...baseResult, tier: EvalTier.Skipped, result: null, error: null };\n\t\t\t\t}\n\t\t\t\treturn this.evaluateTier3(state, lineNumber, expressions, inlineSolveCount, baseResult);\n\t\t\t}\n\t\t}\n\n\t\t// Line is clean\n\t\tif (inViewport && state.bytecodes.length > 0) {\n\t\t\t// ── Tier 2: Visible + Cached → Execute from bytecode ────\n\t\t\treturn this.evaluateTier2(state, lineNumber, baseResult);\n\t\t}\n\n\t\t// Clean, not in viewport, or no bytecode → skip\n\t\treturn { ...baseResult, tier: EvalTier.Skipped, result: null, error: null };\n\t}\n\n\t/**\n\t * A line edited into something with nothing to evaluate stops defining.\n\t *\n\t * A heading, a comment or a blank is skipped before anything is compiled, so\n\t * it never reached the registration that tells the graph what it writes, and\n\t * its old edges stood. Turning `:v3 = 19` into `# a heading` therefore left\n\t * `v3` defined for the rest of the session, and a line reading it went on\n\t * answering, or blamed the wrong name: `v3 * v0` reported `v0` as the\n\t * undefined one where a fresh pass reports `v3`.\n\t *\n\t * Registering it with no edges is what says so. Only when the line is dirty,\n\t * since a line that was already empty has nothing to withdraw, and its own\n\t * write set is dropped as part of registering nothing. What it wrote goes\n\t * back to the prefix first, as it does when a line is compiled again: the\n\t * graph unbinds only a name no other line produces, and `:f = 5` under\n\t * `f(x) = x + 4` produces `f` too, so emptying it left the variable and\n\t * emptying the function's line left the function.\n\t *\n\t * Any unit it defined goes the same way, and for the same reason: a line\n\t * drops its own definitions as it is compiled again, which a line nothing\n\t * compiles never reaches. Whether that has to reach the lines that used the\n\t * unit is decided once at the end of the pass, by comparing the units in\n\t * scope before and after it.\n\t */\n\tprivate deregisterIfDirty(state: LineState, lineNumber: number): void {\n\t\tif (!state.dirty) return;\n\t\tfor (const written of state.writes) this.engine.restoreToPrefix(written, lineNumber);\n\t\tstate.reads = [];\n\t\tstate.writes = [];\n\t\tthis.registerWithTags(lineNumber, [], []);\n\t\tthis.engine.undefineUserUnitsFrom(state.lineId);\n\t\t// And its checkpoint, for the same reason Tier 1 drops one for a line\n\t\t// that wrote nothing: a heading defines nothing.\n\t\tthis.checkpointer?.dropCheckpointAt(lineNumber);\n\t}\n\n\t/**\n\t * Register a line's edges, with the groups its text joins and asks about.\n\t *\n\t * The evaluator registers from three places and the engine from two, and a\n\t * line registered twice with different edge sets loses whichever went first.\n\t * They all route through the same helper so the sets cannot disagree.\n\t */\n\tprivate registerWithTags(lineNumber: number, reads: string[], writes: string[]): void {\n\t\tconst text = this.doc.getLineAt(lineNumber)?.text ?? \"\";\n\t\tconst edges = withTagEdges(text, reads, writes);\n\t\tthis.undefineOrphans(this.dag.registerLine(lineNumber, edges.reads, edges.writes));\n\t}\n\n\t/**\n\t * Forget names that no line defines any more.\n\t *\n\t * The graph reports a key when the line that wrote it stops doing so and no\n\t * other line writes it. The VM's variable store only ever accumulated, so\n\t * the value outlived the line: deleting `:x = 12` left `x + 4` answering\n\t * `16` for the rest of the session, and editing away `1 sprint = 2 weeks`\n\t * left the conversions below it working.\n\t *\n\t * The checkpoint chain is told as well, since it records what each line\n\t * wrote and would otherwise put the name back on the next restore.\n\t *\n\t * Only variable names: the key space also holds category tags, globals, data\n\t * sources and line positions, and none of those is a VM binding. A prefix is\n\t * what tells them apart, and a variable's key is the bare name.\n\t */\n\tprivate undefineOrphans(orphaned: readonly string[]): void {\n\t\tthis.engine.forgetOrphanedNames(orphaned);\n\t}\n\n\t/**\n\t * Tier 1: Full pipeline, lex, parse, compile, execute.\n\t * Uses the engine's existing evaluateLine() which handles all pipeline\n\t * stages including DAG updates and LineCache population.\n\t *\n\t * Supports multiple expressions per line (inline solves). Evaluates each\n\t * expression left-to-right through the engine so variable definitions in\n\t * earlier solves update the VM state before later solves are evaluated.\n\t * Reads/writes are aggregated across all expressions for the DAG.\n\t */\n\tprivate evaluateTier1(\n\t\tstate: LineState,\n\t\tlineNumber: number,\n\t\texpressions: string[],\n\t\tinlineSolveCount: number,\n\t\tbaseResult: Omit<EvalLineResult, \"tier\" | \"result\" | \"error\">\n\t): EvalLineResult {\n\t\t// Whatever this line used to define, it does not any more until it says\n\t\t// so again while being compiled below.\n\t\t//\n\t\t// Done here rather than while compiling, because compiling can be\n\t\t// skipped: the bytecode cache is keyed by the expression's TEXT, so a\n\t\t// line edited into something another line already says is a cache hit\n\t\t// and never reaches the compiler. Editing the definition in\n\t\t// `5 sprints in weeks` / `spent` / `1 sprint = 4 weeks` to `spent` is\n\t\t// exactly that, and the unit survived the line that declared it.\n\t\t//\n\t\t// A line that still is a definition re-registers it as it compiles, and\n\t\t// the pass compares the units in scope before and after itself, so\n\t\t// dropping and re-adding the same one invalidates nothing.\n\t\tthis.engine.undefineUserUnitsFrom(state.lineId);\n\n\t\tconst allResults: Value[][] = [];\n\t\tconst allBytecodes: BytecodeProgram[] = [];\n\t\tconst allReads = new Set<string>();\n\t\tconst allWrites = new Set<string>();\n\t\tlet hasVariableDef = false;\n\t\tlet lastValue: Value | null = null;\n\t\tlet firstError: string | null = null;\n\t\tlet anyFailed = false;\n\t\t/** Set when any expression returned a value still waiting on a resolver. */\n\t\tlet anyPending = false;\n\n\t\t// Evaluate each expression independently, a failure in one expression\n\t\t// (e.g., parse error in s`bad syntax`) must not prevent other expressions\n\t\t// on the same line from being evaluated and having their results stored.\n\t\t// Without per-expression error handling, same-line cross-reference inline\n\t\t// solves (s`:a = 5` s`:b = a + 3` s`a + b`) would lose ALL results when\n\t\t// the third expression throws because 'b' references cross a VM state\n\t\t// boundary or the engine encounters a transient error.\n\t\t// Before this line runs, each name it wrote holds what the lines above\n\t\t// left, as it does when a pass from scratch reaches the line. A Tier 1\n\t\t// line is dirty, so its recorded writes may be about text that has\n\t\t// changed; that is fine either way, since a name the new text no\n\t\t// longer writes is one the line no longer defines. See\n\t\t// `ExpressionEngine.restoreToPrefix` for what this closes.\n\t\tfor (const written of state.writes) this.engine.restoreToPrefix(written, lineNumber);\n\n\t\t// Names an earlier expression on this line has already set, so a later\n\t\t// one on the same line that fails does not undo it.\n\t\tconst definedEarlierOnThisLine = new Set<string>();\n\t\t// And the names this line writes that its right-hand side reads, for\n\t\t// Tier 2; see {@link selfReadingWrites}.\n\t\tconst selfReading: string[] = [];\n\t\tfor (const expression of expressions) {\n\t\t\tif (!expression.trim()) continue;\n\n\t\t\tlet value: Value[] | null = null;\n\t\t\tlet entry: { bytecode: BytecodeProgram; readVariables: string[]; writeVariable: string | null } | undefined;\n\n\t\t\ttry {\n\t\t\t\t// evaluateLine returns the single evaluated Value; this evaluator\n\t\t\t\t// keeps its results grouped as Value[] per expression (allResults\n\t\t\t\t// is Value[][]), so wrap the one value into its group here.\n\t\t\t\tconst evaluated = this.engine.evaluateLine(lineNumber, expression);\n\t\t\t\tvalue = [evaluated];\n\t\t\t\tlastValue = evaluated;\n\t\t\t\t// Sync the DocumentModel from the LineCache.\n\t\t\t\t// Use get(lineNumber, expression) instead of getEntryForLine(lineNumber)\n\t\t\t\t// because multiple expressions on the same line share the same lineNumber\n\t\t\t\t// and getEntryForLine always returns the FIRST entry (Map insertion order).\n\t\t\t\tentry = this.engine.getLineCache().get(lineNumber, expression) as typeof entry;\n\t\t\t} catch (e) {\n\t\t\t\tconst errorMessage = e instanceof Error ? e.message : String(e);\n\t\t\t\tif (!firstError) firstError = errorMessage;\n\t\t\t\tanyFailed = true;\n\t\t\t\tvalue = null;\n\t\t\t}\n\n\t\t\t// An unresolved async value does not throw, so `anyFailed` alone would\n\t\t\t// let this line be marked clean while its data has not arrived. Once\n\t\t\t// clean, nothing re-runs the resolver preflight and the value stays\n\t\t\t// pending for good. Treat it as not-yet-complete instead.\n\t\t\tif (value && value.some((v) => v.type === ValueType.Pending)) {\n\t\t\t\tanyPending = true;\n\t\t\t}\n\n\t\t\tif (value) {\n\t\t\t\tallResults.push(value);\n\t\t\t} else {\n\t\t\t\t// Expression failed, push an ErrorValue sentinel so results[] stays\n\t\t\t\t// aligned with expressions[] and bytecodes[] indices. Downstream code\n\t\t\t\t// checking result.type === Error will find it, vs a raw null that NPEs.\n\t\t\t\tallResults.push([errorValue(\"eval_failed\", firstError ?? \"unknown error\")]);\n\t\t\t}\n\n\t\t\t// Failed means the store never happened, which is a throw (or, below, a\n\t\t\t// right-hand side that did not compile). An error that comes back as a\n\t\t\t// value is stored like any other value: errors are values in this\n\t\t\t// engine, `:a = line 9 + 1` leaves `a` holding one, and a reader of `a`\n\t\t\t// reports it. Treating that as a failure too made both paths delete the\n\t\t\t// name instead, which the two of them agreed on and a pass through\n\t\t\t// parseDocument does not.\n\t\t\tconst failed = value === null;\n\t\t\tif (entry) {\n\t\t\t\tallBytecodes.push(entry.bytecode);\n\t\t\t\tfor (const r of entry.readVariables) allReads.add(r);\n\t\t\t\tif (entry.writeVariable) {\n\t\t\t\t\tallWrites.add(entry.writeVariable);\n\t\t\t\t\thasVariableDef = true;\n\t\t\t\t\tif (readsItself(entry.readVariables, entry.writeVariable)) selfReading.push(entry.writeVariable);\n\t\t\t\t\t// The write stays declared whatever the answer was: the line is\n\t\t\t\t\t// still the one that defines the name, and a running total that\n\t\t\t\t\t// dropped its write when its step failed was never re-seeded\n\t\t\t\t\t// again, stuck on the error after the line it read was fixed.\n\t\t\t\t\t//\n\t\t\t\t\t// What a failed definition may not do is keep its own old value.\n\t\t\t\t\t// A pass from scratch skips the store, leaving the name as the\n\t\t\t\t\t// lines above had it, and this puts it back there. Unless an\n\t\t\t\t\t// earlier expression on this same line set it, in which case that\n\t\t\t\t\t// is what the lines above left and it is already in place. A\n\t\t\t\t\t// pending value is not a failure: it has not arrived yet.\n\t\t\t\t\tif (failed && !definedEarlierOnThisLine.has(entry.writeVariable)) {\n\t\t\t\t\t\tthis.engine.restoreToPrefix(entry.writeVariable, lineNumber);\n\t\t\t\t\t} else if (!failed) {\n\t\t\t\t\t\tdefinedEarlierOnThisLine.add(entry.writeVariable);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Fallback: LineCache missed, compile expression ourselves\n\t\t\t\ttry {\n\t\t\t\t\tconst { program, reads, writes } = this.engine.compileExpression(expression);\n\t\t\t\t\tallBytecodes.push(program);\n\t\t\t\t\tfor (const r of reads) allReads.add(r);\n\t\t\t\t\tfor (const w of writes) allWrites.add(w);\n\t\t\t\t\tif (writes.length > 0) hasVariableDef = true;\n\t\t\t\t\tfor (const w of writes) if (readsItself(reads, w)) selfReading.push(w);\n\t\t\t\t\t// The same restore as the cached branch above; this is the branch\n\t\t\t\t\t// a throwing right-hand side takes, since a throw leaves no cache\n\t\t\t\t\t// entry behind, and it was the one path that kept its old value.\n\t\t\t\t\tfor (const w of writes) {\n\t\t\t\t\t\tif (failed && !definedEarlierOnThisLine.has(w)) this.engine.restoreToPrefix(w, lineNumber);\n\t\t\t\t\t\telse if (!failed) definedEarlierOnThisLine.add(w);\n\t\t\t\t\t}\n\t\t\t\t} catch (compileErr) {\n\t\t\t\t\t// Push empty bytecode, expression will recompile on next pass.\n\t\t\t\t\t// The expression still failed to compile, but reads/writes were\n\t\t\t\t\t// already extracted from its tokens before the parse attempt\n\t\t\t\t\t// compileExpression() surfaces them via the thrown error's\n\t\t\t\t\t// context. Register them anyway so the DAG knows this line\n\t\t\t\t\t// depends on those variables and re-evaluates it once they\n\t\t\t\t\t// become defined, instead of losing the dependency entirely.\n\t\t\t\t\tallBytecodes.push({ opcodes: new Uint8Array(0), numbers: new Float64Array(0), strings: [], hasAsync: false });\n\t\t\t\t\tif (compileErr instanceof EngineError && compileErr.context) {\n\t\t\t\t\t\tconst errReads = compileErr.context.reads;\n\t\t\t\t\t\tconst errWrites = compileErr.context.writes;\n\t\t\t\t\t\tif (Array.isArray(errReads)) for (const r of errReads) allReads.add(r);\n\t\t\t\t\t\tif (Array.isArray(errWrites)) {\n\t\t\t\t\t\t\tfor (const w of errWrites) {\n\t\t\t\t\t\t\t\tallWrites.add(w);\n\t\t\t\t\t\t\t\t// A definition that does not even compile set nothing.\n\t\t\t\t\t\t\t\tif (!definedEarlierOnThisLine.has(w)) this.engine.restoreToPrefix(w, lineNumber);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst reads = [...allReads];\n\t\tconst writes = [...allWrites];\n\n\t\t// Always store results, even partial ones. If any expression failed\n\t\t// the line stays dirty so the failed expression(s) get retried on the\n\t\t// next evaluation pass. But successfully-evaluated expressions' results\n\t\t// are preserved so the DAG, UI decorations, and downstream consumers\n\t\t// can use them immediately.\n\t\tif (anyFailed || anyPending) {\n\t\t\t// Store partial results via updateLineCompiled (doesn't clear dirty).\n\t\t\t// The successful expressions' bytecodes are cached so Tier 2 works\n\t\t\t// for them on the next scroll pass.\n\t\t\tthis.doc.updateLineCompiled(\n\t\t\t\tstate.lineId,\n\t\t\t\texpressions,\n\t\t\t\tallBytecodes,\n\t\t\t\treads,\n\t\t\t\twrites,\n\t\t\t\thasVariableDef,\n\t\t\t\tinlineSolveCount,\n\t\t\t);\n\t\t\t// Also set results for the successful expressions\n\t\t\tstate.results = allResults;\n\t\t\tstate.result = allResults[0]?.[0] ?? null;\n\t\t\tstate.inlineSolveCount = inlineSolveCount;\n\t\t\tstate.expressions = expressions;\n\t\t} else {\n\t\t\t// All expressions succeeded, store full results and mark clean\n\t\t\tthis.doc.updateLineResult(\n\t\t\t\tstate.lineId,\n\t\t\t\tallResults,\n\t\t\t\tallBytecodes,\n\t\t\t\texpressions,\n\t\t\t\treads,\n\t\t\t\twrites,\n\t\t\t\thasVariableDef,\n\t\t\t\tinlineSolveCount,\n\t\t\t);\n\t\t}\n\n\t\tif (selfReading.length > 0) this.selfReadingWrites.set(state.lineId, selfReading);\n\t\telse this.selfReadingWrites.delete(state.lineId);\n\n\t\t// Register reads/writes in DAG (aggregated across all expressions).\n\t\t// Always register, even lines with no reads/writes (pure expressions\n\t\t// like \"2+2\") need DAG entries so downstream queries for line presence work.\n\t\tthis.registerWithTags(lineNumber, reads, writes);\n\n\t\t// ── Checkpoint after variable definition ──\n\t\t// A pending value is not a value worth checkpointing: restoring it would\n\t\t// reinstate the unresolved placeholder rather than the eventual result.\n\t\t// A line that failed is snapshotted too. Its names were put back to\n\t\t// the prefix as it failed, so what the VM holds for them now is right,\n\t\t// and skipping the snapshot left the entry from before the edit in the\n\t\t// chain: `:v0 = 49` edited into a line that throws went on telling the\n\t\t// lines below that the prefix held 49. A pending value is still left\n\t\t// out, since it has not arrived.\n\t\tif (this.checkpointer && writes.length > 0 && !anyPending) {\n\t\t\tthis.checkpointer.snapshot(lineNumber, state.lineId, writes, functionsDefinedBy(allBytecodes));\n\t\t} else if (this.checkpointer && writes.length === 0) {\n\t\t\t// A line that defines nothing any more has nothing for the chain to\n\t\t\t// say about it, and what it used to say is what the lines below would\n\t\t\t// otherwise be told the prefix holds.\n\t\t\tthis.checkpointer.dropCheckpointAt(lineNumber);\n\t\t}\n\n\t\treturn {\n\t\t\t...baseResult,\n\t\t\ttier: EvalTier.Tier1,\n\t\t\tresult: lastValue,\n\t\t\tresults: allResults,\n\t\t\terror: firstError,\n\t\t};\n\t}\n\n\t/**\n\t * Tier 2: Execute from cached bytecode only.\n\t * Skips lexing, parsing, and compiling, runs the pre-compiled bytecode\n\t * against the engine's shared VM. Supports multiple bytecodes per line\n\t * (inline solves), each is executed left-to-right so variable definitions\n\t * in earlier bytecodes update the VM before later ones run.\n\t * Assumes the VM already has correct variable state from preceding\n\t * Tier-1 evaluations.\n\t */\n\tprivate evaluateTier2(\n\t\tstate: LineState,\n\t\tlineNumber: number,\n\t\tbaseResult: Omit<EvalLineResult, \"tier\" | \"result\" | \"error\">\n\t): EvalLineResult {\n\t\tif (state.bytecodes.length === 0) {\n\t\t\treturn { ...baseResult, tier: EvalTier.Skipped, result: null, error: null };\n\t\t}\n\n\t\tconst results: Value[][] = [];\n\t\tlet lastValue: Value | null = null;\n\t\tlet firstError: string | null = null;\n\t\tlet anyFailed = false;\n\n\t\t// Execute each bytecode independently, a failure in one should not\n\t\t// prevent other bytecodes on the same line from executing. Same-line\n\t\t// inline solves with variable definitions (s`:a = 5` s`a + 3`) rely\n\t\t// on earlier bytecodes updating the VM before later ones execute.\n\t\t// A clean line's VM state is the prefix, with one exception a pass from\n\t\t// scratch never has: a definition that reads the name it writes finds\n\t\t// its own previous answer there. `:v3 = v3 + 3` climbed by three a pass\n\t\t// once the `:v3 = 44` above it was edited away, where from scratch\n\t\t// nothing defines `v3` and the line reports so. The prefix is put back\n\t\t// first, per expression, and again after one that fails, since a failed\n\t\t// store leaves the name holding this line's old answer too.\n\t\t// The line's own record of what it reads and writes, which moves with the\n\t\t// line. The line cache is keyed by position and is not renumbered by a\n\t\t// structural edit, so a line that moved found no entry there and its\n\t\t// self-read went unrestored: `:v3 = v3 + 3` climbed by three a pass once\n\t\t// the `:v3 = 44` above it was deleted rather than edited.\n\t\t//\n\t\t// Genuinely reads it: the graph's convention records every definition as\n\t\t// a reader of its own name once, so the name has to appear among the\n\t\t// reads a second time, from the right-hand side, before this fires. That\n\t\t// keeps the prefix lookup off the ordinary `:x = 5`, which is most\n\t\t// definitions and would otherwise pay for it on every pass.\n\t\tfor (const written of this.selfReadingWrites.get(state.lineId) ?? []) {\n\t\t\tthis.engine.restoreToPrefix(written, lineNumber);\n\t\t}\n\t\tconst cache = this.engine.getLineCache();\n\t\tconst definedEarlierOnThisLine = new Set<string>();\n\t\tfor (let i = 0; i < state.bytecodes.length; i++) {\n\t\t\tconst bytecode = state.bytecodes[i];\n\t\t\tif (bytecode.opcodes.length === 0) continue;\n\t\t\t// Per expression where the cache still has the entry, so a later\n\t\t\t// expression that fails does not undo an earlier one on the same line;\n\t\t\t// the line's whole write set otherwise, which a moved line falls back to.\n\t\t\tconst entry = cache.get(lineNumber, state.expressions[i] ?? \"\") as\n\t\t\t\t{ readVariables: string[]; writeVariable: string | null } | undefined;\n\t\t\tconst writtenHere: readonly string[] = entry !== undefined\n\t\t\t\t? (entry.writeVariable === null ? [] : [entry.writeVariable])\n\t\t\t\t: state.writes;\n\t\t\tlet failed = false;\n\t\t\ttry {\n\t\t\t\tconst value = this.engine.executeCached(bytecode, lineNumber);\n\t\t\t\tlastValue = value;\n\t\t\t\tresults.push([value]);\n\t\t\t} catch (e) {\n\t\t\t\tconst errorMessage = e instanceof Error ? e.message : String(e);\n\t\t\t\tif (!firstError) firstError = errorMessage;\n\t\t\t\tanyFailed = true;\n\t\t\t\tfailed = true;\n\t\t\t\t// Push error sentinel to maintain results[i] ↔ bytecodes[i] alignment\n\t\t\t\tresults.push([errorValue(\"exec_failed\", errorMessage)]);\n\t\t\t}\n\t\t\tfor (const written of writtenHere) {\n\t\t\t\tif (failed && !definedEarlierOnThisLine.has(written)) this.engine.restoreToPrefix(written, lineNumber);\n\t\t\t\telse if (!failed) definedEarlierOnThisLine.add(written);\n\t\t\t}\n\t\t}\n\n\t\t// Update DAG: re-register reads/writes from the cached metadata.\n\t\t// Always register, even empty reads/writes so DAG line-presence queries work.\n\t\tthis.registerWithTags(lineNumber, state.reads, state.writes);\n\n\t\t// A line that ran nothing keeps the result it already had.\n\t\t//\n\t\t// Some expressions compile to a program with no opcodes: a unit\n\t\t// definition (`1 sprint = 2 weeks`) and an equation stored for later\n\t\t// (`y + 3 = 10`) both do their work while being compiled, and have\n\t\t// nothing left to execute. The loop above skips those programs, so\n\t\t// `results` comes back empty, and assigning it unconditionally replaced\n\t\t// the answer Tier 1 had computed with nothing. The line showed\n\t\t// `sprint defined` on the pass that compiled it and went blank on the\n\t\t// next one, with the document untouched, because a live document is\n\t\t// evaluated again and again rather than once.\n\t\t//\n\t\t// Executing nothing produces no new result, which is not the same as\n\t\t// producing an empty one.\n\t\tif (results.length > 0) {\n\t\t\tstate.results = results;\n\t\t\tstate.result = results[0][0] ?? null;\n\t\t} else if (state.result !== null && isArenaActive()) {\n\t\t\t// Kept by value, not by reference.\n\t\t\t//\n\t\t\t// A pass runs with the Value arena on, and a result that never came\n\t\t\t// back through the VM's HALT is arena-owned: nothing cloned it on\n\t\t\t// the way out, because the program had no opcodes to return from.\n\t\t\t// Holding that object means holding a slot the arena hands to a\n\t\t\t// later line, and the line's own answer is then overwritten in\n\t\t\t// place. `1 sprint = 2 weeks` read `sprint defined` and then, once\n\t\t\t// the line below it had run, read that line's number instead.\n\t\t\t// Every stored result, keeping the line's shape: a line can hold\n\t\t\t// several expressions, and all of them can be definitions.\n\t\t\tstate.results = state.results.map((group) => group.map(persistentValue));\n\t\t\tstate.result = state.results[0]?.[0] ?? persistentValue(state.result);\n\t\t}\n\t\tif (anyFailed) {\n\t\t\t// Mark dirty so failed bytecodes are re-compiled (Tier 1) next pass\n\t\t\tthis.doc.markDirty(state.lineId);\n\t\t}\n\n\t\t// ── Checkpoint after a line that writes ──\n\t\t// Tier 1 does this for a line it compiled; a line running from cache\n\t\t// writes the same names to the VM and has to record them the same way.\n\t\t// Without it the chain has a hole wherever a clean line sits between\n\t\t// two dirty ones, and `snapshot`'s re-run truncation would drop\n\t\t// entries that nothing ever puts back.\n\t\tif (this.checkpointer && state.writes.length > 0 && !anyFailed) {\n\t\t\tthis.checkpointer.snapshot(lineNumber, state.lineId, state.writes, functionsDefinedBy(state.bytecodes));\n\t\t}\n\n\t\treturn {\n\t\t\t...baseResult,\n\t\t\ttier: EvalTier.Tier2,\n\t\t\t// The stored result, for the same reason: a caller reading this pass's\n\t\t\t// answer for the line must see what the line says, not the nothing\n\t\t\t// that running no opcodes returned.\n\t\t\tresult: results.length > 0 ? lastValue : state.result,\n\t\t\tresults: results.length > 0 ? results : state.results,\n\t\t\terror: firstError,\n\t\t};\n\t}\n\n\t/**\n\t * Tier 3: Compile-only for invisible lines.\n\t * Lex → Parse → Compile to discover reads/writes for the dependency graph.\n\t * Executes the bytecode ONLY if the line defines a variable (isVariableDef\n\t * or writes.length > 0), because variable assignments affect VM state\n\t * that other lines depend on. Pure expression lines are compiled but NOT\n\t * executed, saving CPU for large documents.\n\t *\n\t * Supports multiple expressions per line (inline solves). Each is compiled\n\t * separately; variable-def expressions are also executed.\n\t */\n\tprivate evaluateTier3(\n\t\tstate: LineState,\n\t\tlineNumber: number,\n\t\texpressions: string[],\n\t\tinlineSolveCount: number,\n\t\tbaseResult: Omit<EvalLineResult, \"tier\" | \"result\" | \"error\">\n\t): EvalLineResult {\n\t\tconst allBytecodes: BytecodeProgram[] = [];\n\t\tconst allReads = new Set<string>();\n\t\tconst allWrites = new Set<string>();\n\t\tlet hasVariableDef = false;\n\t\tlet lastResult: Value | null = null;\n\t\tlet firstError: string | null = null;\n\t\tlet anyFailed = false;\n\t\t/** Set when a result is still waiting on a resolver. */\n\t\tlet anyPending = false;\n\n\t\t// The same discipline as Tier 1, because a definition runs here too:\n\t\t// before it does, each name it wrote holds what the lines above left,\n\t\t// and a definition that fails is put back there. Without it a line\n\t\t// scrolled out of view kept the value it had before the edit, and one\n\t\t// that read its own name climbed by its step on every pass, for as long\n\t\t// as it stayed out of view. See `ExpressionEngine.restoreToPrefix`.\n\t\tfor (const written of state.writes) this.engine.restoreToPrefix(written, lineNumber);\n\t\tconst definedEarlierOnThisLine = new Set<string>();\n\t\tconst selfReading: string[] = [];\n\n\t\t// Compile each expression independently, a parse error in one\n\t\t// should not prevent other expressions from being compiled and\n\t\t// having their reads/writes registered in the DAG.\n\t\tfor (const expression of expressions) {\n\t\t\tif (!expression.trim()) continue;\n\n\t\t\tlet compiled: { program: BytecodeProgram; reads: string[]; writes: string[] };\n\t\t\ttry {\n\t\t\t\tcompiled = this.engine.compileExpression(expression);\n\t\t\t} catch (e) {\n\t\t\t\tconst errorMessage = e instanceof Error ? e.message : String(e);\n\t\t\t\tif (!firstError) firstError = errorMessage;\n\t\t\t\tanyFailed = true;\n\t\t\t\t// Push empty bytecode placeholder for alignment\n\t\t\t\tallBytecodes.push({ opcodes: new Uint8Array(0), numbers: new Float64Array(0), strings: [], hasAsync: false });\n\t\t\t\t// A definition that does not compile set nothing. Its names are\n\t\t\t\t// still registered, as Tier 1 registers them, so the line runs\n\t\t\t\t// again once they are defined.\n\t\t\t\tif (e instanceof EngineError && e.context) {\n\t\t\t\t\tconst errReads = e.context.reads;\n\t\t\t\t\tconst errWrites = e.context.writes;\n\t\t\t\t\tif (Array.isArray(errReads)) for (const r of errReads) allReads.add(r);\n\t\t\t\t\tif (Array.isArray(errWrites)) {\n\t\t\t\t\t\tfor (const w of errWrites) {\n\t\t\t\t\t\t\tallWrites.add(w);\n\t\t\t\t\t\t\tif (!definedEarlierOnThisLine.has(w)) this.engine.restoreToPrefix(w, lineNumber);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst { program, reads, writes } = compiled;\n\t\t\tallBytecodes.push(program);\n\t\t\tfor (const r of reads) allReads.add(r);\n\t\t\tfor (const w of writes) allWrites.add(w);\n\t\t\tif (writes.length > 0) hasVariableDef = true;\n\t\t\tfor (const w of writes) if (readsItself(reads, w)) selfReading.push(w);\n\n\t\t\tif (writes.length > 0 && program.opcodes.length > 0) {\n\t\t\t\t// Variable definitions MUST execute to maintain VM state\n\t\t\t\ttry {\n\t\t\t\t\tlastResult = this.engine.executeCached(program, lineNumber);\n\t\t\t\t\t// Same reasoning as Tier 1: a pending result does not throw,\n\t\t\t\t\t// and marking the line clean would strand it unresolved.\n\t\t\t\t\tif (lastResult && lastResult.type === ValueType.Pending) {\n\t\t\t\t\t\tanyPending = true;\n\t\t\t\t\t}\n\t\t\t\t\tfor (const w of writes) definedEarlierOnThisLine.add(w);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconst errorMessage = e instanceof Error ? e.message : String(e);\n\t\t\t\t\tif (!firstError) firstError = errorMessage;\n\t\t\t\t\tanyFailed = true;\n\t\t\t\t\t// A definition that threw stored nothing; see Tier 1.\n\t\t\t\t\tfor (const w of writes) {\n\t\t\t\t\t\tif (!definedEarlierOnThisLine.has(w)) this.engine.restoreToPrefix(w, lineNumber);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst reads = [...allReads];\n\t\tconst writes = [...allWrites];\n\n\t\t// Store compile-only state in DocumentModel, even partial results\n\t\t// preserve successful expressions' bytecodes and DAG data.\n\t\tthis.doc.updateLineCompiled(\n\t\t\tstate.lineId,\n\t\t\texpressions,\n\t\t\tallBytecodes,\n\t\t\treads,\n\t\t\twrites,\n\t\t\thasVariableDef,\n\t\t\tinlineSolveCount,\n\t\t);\n\n\t\t// Register reads/writes in DAG regardless, partial data is valid.\n\t\t// Always register, even empty reads/writes for DAG line-presence queries.\n\t\tthis.registerWithTags(lineNumber, reads, writes);\n\n\t\tif (selfReading.length > 0) this.selfReadingWrites.set(state.lineId, selfReading);\n\t\telse this.selfReadingWrites.delete(state.lineId);\n\n\t\tif (hasVariableDef && lastResult && !anyFailed && !anyPending) {\n\t\t\tstate.results = [[lastResult]];\n\t\t\tstate.result = lastResult;\n\t\t\tthis.doc.markClean(state.lineId);\n\t\t}\n\n\t\t// ── Checkpoint after variable definition ────────────\n\t\t// Taken whether or not the line failed, as in Tier 1: its names were\n\t\t// put back to the prefix as it failed, so what the VM holds for them\n\t\t// now is right, and the entry from before the edit is the wrong thing\n\t\t// to keep. A pending value is left out, since it has not arrived. A\n\t\t// line that defines nothing any more has nothing for the chain to say.\n\t\tif (this.checkpointer && writes.length > 0 && !anyPending) {\n\t\t\tthis.checkpointer.snapshot(lineNumber, state.lineId, writes, functionsDefinedBy(allBytecodes));\n\t\t} else if (this.checkpointer && writes.length === 0) {\n\t\t\tthis.checkpointer.dropCheckpointAt(lineNumber);\n\t\t}\n\n\t\treturn { ...baseResult, tier: EvalTier.Tier3, result: lastResult, results: hasVariableDef && lastResult && !anyFailed && !anyPending ? [[lastResult]] : undefined, error: firstError };\n\t}\n\n\t// ── Public checkpoint API (used by Phase 5.2e setViewport) ──────\n\n\t/**\n\t * Restore the VM to the state at or just after the given line number.\n\t *\n\t * Finds the nearest checkpoint at or before `lineNumber` and replays\n\t * all variable definitions from the checkpoint chain into the VM.\n\t * After calling this, the VM is ready to evaluate lines starting at\n\t * `lineNumber + 1` without re-evaluating all preceding lines.\n\t *\n\t * **Usage:** Phase 5.2e's `setViewport()` calls `restoreTo(viewport.startLine - 1)`\n\t * before evaluating only the newly visible lines. This is the key to\n\t * O(visible lines) scrolling.\n\t *\n\t * @param lineNumber The line number to restore to. Variables defined\n\t * at lines ≤ this number will be available in the VM.\n\t */\n\trestoreTo(lineNumber: number): void {\n\t\tif (this.checkpointer) {\n\t\t\tthis.checkpointer.restoreTo(lineNumber);\n\t\t}\n\t}\n\n\t/**\n\t * Get the VM checkpointer, or null if checkpointing is disabled.\n\t */\n\tgetCheckpointer(): VMCheckpointer | null {\n\t\treturn this.checkpointer;\n\t}\n\n\t/**\n\t * Get the PageManager (Phase 5.2g).\n\t * Exposed for testing.\n\t */\n\tgetPageManager(): PageManager {\n\t\treturn this.pageManager;\n\t}\n\n\t// ── Phase 5.2g: Directional preloading ──────────────────────────\n\n\t/**\n\t * Preload the next 1–2 pages in the current scroll direction.\n\t *\n\t * Called during `setViewport()` (scroll-only path). Collects dirty\n\t * uncompiled lines in pages just beyond the viewport and dispatches\n\t * them to the background compilation worker so bytecode is ready\n\t * before the user scrolls those lines into view.\n\t */\n\tprivate preloadNextPages(viewport: ViewportRange): void {\n\t\tconst targets = this.pageManager.getPreloadTargets(viewport, this.doc);\n\t\tif (targets.length === 0) return;\n\n\t\t// Lazy-init worker if needed\n\t\tif (!this.compilationWorker) {\n\t\t\tthis.compilationWorker = new CompilationWorkerManager();\n\t\t}\n\n\t\t// Fire-and-forget: worker compiles, stores bytecode on response\n\t\tthis.compilationWorker.compileBatch(targets).then((results) => {\n\t\t\tthis.compilationWorker!.storeResults(results, this.doc);\n\t\t}).catch((_err) => {\n\t\t\t// Non-fatal, next evaluate() will compile synchronously\n\t\t});\n\t}\n\n\t/**\n\t * Collect invisible dirty lines that need background compilation.\n\t *\n\t * Iterates lines beyond `viewport.endLine`, filtering for:\n\t * - Dirty lines (need re-compilation)\n\t * - Non-empty, non-markdown lines\n\t * - No existing bytecode (skip already-compiled Tier 3 lines)\n\t *\n\t * Returns CompileRequestItem[] suitable for CompilationWorkerManager.\n\t */\n\tprivate collectInvisibleCompileTargets(viewport: ViewportRange): CompileRequestItem[] {\n\t\tconst items: CompileRequestItem[] = [];\n\t\tconst docEnd = this.doc.lineCount;\n\t\tconst startPos = viewport.endLine + 1;\n\n\t\tfor (let pos = startPos; pos <= docEnd; pos++) {\n\t\t\tconst state = this.doc.getLineAt(pos);\n\t\t\tif (!state) continue;\n\n\t\t\t// Skip clean lines\n\t\t\tif (!state.dirty) continue;\n\n\t\t\t// Skip already-compiled lines\n\t\t\tif (state.bytecodes.length > 0 && !state.isVariableDef) continue;\n\n\t\t\t// Skip empty/markdown-only lines\n\t\t\tif (state.isEmpty || isEmptyLine(state.text)) continue;\n\n\t\t\tconst { expressions } = this.extractExpressions(state);\n\t\t\tif (expressions.length === 0) continue;\n\n\t\t\t// Create one CompileRequestItem per expression (inline solves\n\t\t\t// on the same line share the same lineId + textHash). The worker\n\t\t\t// compiles each independently; storeResults batches by lineId.\n\t\t\tfor (const expression of expressions) {\n\t\t\t\tif (!expression.trim()) continue;\n\t\t\t\titems.push({\n\t\t\t\t\tlineId: state.lineId,\n\t\t\t\t\texpression,\n\t\t\t\t\ttextHash: state.textHash,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn items;\n\t}\n\n\t/**\n\t * Extract all evaluable expressions from a LineState.\n\t *\n\t * For full-line expressions: returns `{ expressions: [trimmedText], inlineSolveCount: 0 }`.\n\t * For inline solve lines: returns `{ expressions: [...allSolves], inlineSolveCount: N }`.\n\t * For pre-extracted (cached) expressions: returns the cached array.\n\t *\n\t * Inline solves are extracted left-to-right via the sharedLexer, so variable\n\t * definitions in earlier solves (e.g., `s\\`x = 5\\` more text s\\`x + 1\\``)\n\t * correctly update the VM state before later solves are evaluated.\n\t */\n\tprivate extractExpressions(state: LineState): { expressions: string[]; inlineSolveCount: number } {\n\t\t// Use pre-extracted expressions if available (from cache / previous evaluation)\n\t\tif (state.expressions.length > 0) {\n\t\t\treturn { expressions: state.expressions, inlineSolveCount: state.inlineSolveCount };\n\t\t}\n\n\t\tconst trimmed = state.text.trim();\n\t\tif (trimmed.length === 0) return { expressions: [], inlineSolveCount: 0 };\n\n\t\t// Check for inline solve syntax: s`expression`\n\t\tconst inlineSpans = sharedLexer.findInlineSolves(state.text);\n\t\tif (inlineSpans.length > 0) {\n\t\t\treturn {\n\t\t\t\texpressions: inlineSpans.map(s => s.expression),\n\t\t\t\tinlineSolveCount: inlineSpans.length,\n\t\t\t};\n\t\t}\n\n\t\t// Full-line expression\n\t\treturn { expressions: [trimmed], inlineSolveCount: 0 };\n\t}\n}\n","/**\n * Whole-document evaluation through the incremental engine.\n *\n * The engine has two ways to read a document, and they are not interchangeable.\n * {@link ExpressionEngine.parseDocument} is the light batch pass: it scans the\n * text, reads earlier lines' results, and skips markdown, which is everything\n * line references, category tags and table columns need. What it has no way to\n * do is re-run an earlier line with a variable bound to a trial value, which is\n * exactly what goal seek (`solve line N for x = target`) is, so goal seek comes\n * back as a `GOAL_SEEK_NO_DOCUMENT` error through `parseDocument`.\n *\n * The re-run primitive lives on the incremental `DocumentModel` +\n * `ThreeTierEvaluator` path (a live editor drives it on every keystroke). This\n * helper stands that path up for a single pass and hands back the familiar\n * {@link ParsingResult}, so a caller reads the answers exactly as it would from\n * `parseDocument`, goal seek included. A documentation notepad that wants goal\n * seek to resolve, and the tests that prove those examples, both go through\n * here.\n *\n * Two boundaries, each deliberate:\n *\n * - It builds a fresh model and evaluator per call and disposes the evaluator\n * before returning, which suits occasional evaluation, not the keystroke loop\n * a real editor runs against one long-lived `ThreeTierEvaluator`.\n * - Unlike `parseDocument`, it does not classify a markdown table's own rows as\n * skippable: they reach the evaluator as expressions and error. A document\n * that mixes a raw table with goal seek is the one case that wants both paths,\n * the table read through `parseDocument` and the goal seek through here.\n */\nimport type { ExpressionEngine } from \"@solve-js/engine/ExpressionEngine\";\nimport { DocumentModel } from \"@solve-js/engine/DocumentModel\";\nimport { ThreeTierEvaluator } from \"@solve-js/engine/ThreeTierEvaluator\";\nimport { VMCheckpointer } from \"@solve-js/vm/VMCheckpoints\";\nimport { findInlineSolvesInLine } from \"@solve-js/engine/ExpressionEngineSafety\";\nimport { Value, ValueType } from \"@solve-js/vm/Value\";\nimport type {\n\tParsedLine,\n\tParsingResult,\n\tUnifiedParsingOptions,\n\tInlineSolvePosition,\n} from \"@solve-js/types/ParsingResult\";\n\n/** Read the human-readable message off an error Value (`unit` holds it, `value` holds the code). */\nfunction errorMessageOf(value: Value): string {\n\treturn typeof value.unit === \"string\" ? value.unit : String(value.value);\n}\n\n/**\n * Evaluate a whole document with full cross-line context, including the line\n * re-runs goal seek needs, and return per-line results in the same shape\n * {@link ExpressionEngine.parseDocument} produces.\n *\n * @param engine - The engine to borrow for the pass. Its current document model\n * (if any) is restored before returning.\n * @param input - The document text, newline-separated.\n * @param _options - Accepted so a call reads the same as `parseDocument`'s, but\n * not consulted: the incremental pass reads the document as markdown regardless.\n */\nexport function evaluateDocument(\n\tengine: ExpressionEngine,\n\tinput: string,\n\t_options: UnifiedParsingOptions = { inputType: \"markdown\" },\n): ParsingResult {\n\tconst previousDocumentModel = engine.getDocumentModel();\n\n\tconst doc = new DocumentModel();\n\tdoc.setDocument(input);\n\t// The constructor wires the model onto the engine (engine.setDocumentModel),\n\t// which is what makeLineContext reads to expose the re-run primitive.\n\t// The chain the evaluator records as it goes, and the async batcher reads\n\t// when a value arrives later. Without it a re-run of a few lines reads the\n\t// variable state the document ENDS in rather than the state each line sits\n\t// in, which is wrong for any name defined more than once.\n\tconst evaluator = new ThreeTierEvaluator(doc, engine, new VMCheckpointer(engine.getVM()));\n\n\ttry {\n\t\tconst lineCount = doc.lineCount;\n\t\tevaluator.evaluate({ startLine: 1, endLine: lineCount });\n\n\t\tconst lines: ParsedLine[] = [];\n\t\tconst errors: string[] = [];\n\t\t// Document character offsets are not carried on the line model, so they\n\t\t// are accumulated here to keep startPosition/endPosition faithful to\n\t\t// parseDocument's, one newline between lines.\n\t\tlet offset = 0;\n\n\t\tfor (let n = 1; n <= lineCount; n++) {\n\t\t\tconst state = doc.getLineAt(n)!;\n\t\t\tconst text = state.text;\n\t\t\tconst startPosition = offset;\n\t\t\tconst endPosition = offset + text.length;\n\t\t\toffset = endPosition + 1; // + the newline that separated this line from the next\n\n\t\t\tconst hasInlineSolves = state.inlineSolveCount > 0;\n\t\t\tlet inlineSolves: InlineSolvePosition[] = [];\n\t\t\tlet expression: string | null = null;\n\t\t\tlet result: Value | null = null;\n\n\t\t\tif (state.isEmpty) {\n\t\t\t\t// Prose, a heading, or a blank line: nothing to report.\n\t\t\t} else if (hasInlineSolves) {\n\t\t\t\t// Zip the located spans against the line's per-expression results,\n\t\t\t\t// which the model holds in the same left-to-right order.\n\t\t\t\tinlineSolves = findInlineSolvesInLine(text, n).map((span, i) => {\n\t\t\t\t\tconst value = state.results[i]?.[0] ?? null;\n\t\t\t\t\tif (value && value.type === ValueType.Error) {\n\t\t\t\t\t\tconst message = errorMessageOf(value);\n\t\t\t\t\t\terrors.push(`Line ${n}: ${message}`);\n\t\t\t\t\t\treturn { ...span, result: null, error: message };\n\t\t\t\t\t}\n\t\t\t\t\treturn { ...span, result: value, error: null };\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\texpression = state.expressions[0] ?? null;\n\t\t\t\t// A returned error (goal seek with no document, a faulted\n\t\t\t\t// conversion) stays in `result` as an error-typed Value, exactly\n\t\t\t\t// as parseDocument leaves it, so both document passes report a\n\t\t\t\t// line the same way and a caller can compare them value for\n\t\t\t\t// value. It is also gathered into `errors` for a flat list.\n\t\t\t\tresult = state.result ?? null;\n\t\t\t\tif (result && result.type === ValueType.Error) {\n\t\t\t\t\terrors.push(`Line ${n}: ${errorMessageOf(result)}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlines.push({\n\t\t\t\tlineNumber: n,\n\t\t\t\ttext,\n\t\t\t\tstartPosition,\n\t\t\t\tendPosition,\n\t\t\t\tisEmpty: state.isEmpty,\n\t\t\t\thasInlineSolves,\n\t\t\t\tinlineSolves,\n\t\t\t\texpression,\n\t\t\t\t// Always null: the incremental pass stores a line's failure as an\n\t\t\t\t// error-typed Value in `result` (above), never as a thrown error\n\t\t\t\t// the way parseDocument sets this field. Kept for shape parity.\n\t\t\t\terror: null,\n\t\t\t\tresult,\n\t\t\t});\n\t\t}\n\n\t\treturn { lines, totalLines: lineCount, errors };\n\t} finally {\n\t\t// Drop this pass's evaluator (unsubscribes it from the shared global\n\t\t// store) and put back whatever document the host had wired, so borrowing\n\t\t// the engine for one pass leaves nothing behind.\n\t\tevaluator.terminateWorker();\n\t\tengine.setDocumentModel(previousDocumentModel);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/engine/SegmentTree.ts","../src/engine/DocumentModel.ts","../src/engine/CompilationWorkerManager.ts","../src/engine/PageManager.ts","../src/engine/ThreeTierEvaluator.ts","../src/engine/evaluateDocument.ts"],"names":["SegmentTree","index","lineId","left","right","split","merge","createNode","midRight","mid","removed","startIndex","deleteCount","newIds","clampedStart","clampedCount","collectInOrder","newTree","buildTreap","lineIds","endIndex","clampedEnd","result","node","leftSize","targetStart","targetEnd","offset","nodeIndex","updateSize","collect","start","end","priority","pseudoRandom","seed","x","DocumentModel","maxLines","DEFAULT_CONFIG","text","countLines","ErrorFactory","rawLines","i","djb2Hash","id","changes","inserted","sorted","a","b","change","startIdx","removedIds","atLine","texts","startLine","endLine","lineNumber","newText","state","newHash","tag","ids","positions","position","memberTagsOf","idx","pos","compiledAgainstHash","results","bytecodes","expressions","reads","writes","isVariableDef","inlineSolveCount","s","CompilationWorkerManager","factory","engineWorkerFactory","event","data","pending","raw","err","items","worker","resolve","reject","doc","byLineId","batch","r","w","stored","opcodes","numbers","program","PAGE_SIZE","HOT_PAGE_RADIUS","WARM_PAGE_RADIUS","PRELOAD_PAGE_COUNT","COLD_EVICT_BUFFER","PageManager","_PageManager","pageNum","docLineCount","newViewport","viewport","viewportStartPage","viewportEndPage","lastPage","hotStart","hotEnd","warmStart","warmEnd","p","coldLeftEnd","coldLeftStart","coldRightStart","coldRightEnd","direction","pages","startPage","endPage","firstPreloadPage","range","expression","inlineSpans","sharedLexer","span","EvalTier","functionsDefinedBy","names","body","readsItself","name","seen","read","ThreeTierEvaluator","engine","checkpointer","VMCheckpointer","sharedGlobalVariableStore","globalDagKey","signal","enableValueArena","unitsBefore","lines","resultMap","tierCounts","docEnd","evalEnd","states","inViewport","lineResult","unitsAfter","disableValueArena","startPos","_err","allWrites","lineNum","downstreamLineIds","positionalReaderLineIds","reader","writeVar","affected","written","earliestChange","lowest","recordedFor","gained","roots","line","key","isPrefixedEdgeKey","lineCount","low","onStack","stack","frames","visited","enter","setMembership","onCycle","root","frame","target","parent","members","member","out","stepper","from","moved","baseResult","isEmptyLine","edges","withTagEdges","orphaned","allResults","allBytecodes","allReads","hasVariableDef","lastValue","firstError","anyFailed","anyPending","definedEarlierOnThisLine","selfReading","value","entry","evaluated","e","errorMessage","v","errorValue","failed","compileErr","EngineError","errReads","errWrites","cache","bytecode","writtenHere","isArenaActive","group","persistentValue","lastResult","compiled","targets","trimmed","errorMessageOf","evaluateDocument","input","_options","previousDocumentModel","evaluator","errors","n","startPosition","endPosition","hasInlineSolves","inlineSolves","findInlineSolvesInLine","message"],"mappings":"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","file":"chunk-44E7U4O5.js","sourcesContent":["/**\n * SegmentTree, an order-statistic Treap (randomized BST).\n *\n * Replaces the flat `lineOrder: number[]` array in DocumentModel with a\n * balanced tree that supports O(log N) insert, delete, and get-at-index\n * operations. Each node stores a `lineId` and its subtree size, enabling\n * order-statistic queries (find the k-th element by position).\n *\n * Key operations:\n * getAt(index), O(log N) expected\n * insertAt(index), O(log N) expected\n * deleteAt(index), O(log N) expected\n * spliceAt(index, deleteCount, newIds), O(log N + deleteCount + insertCount)\n * indexOf(lineId), O(N) without reverse map; cached lazily in DocumentModel\n *\n * Implementation: Treap (Tree + Heap) with implicit keys.\n * Nodes are ordered by position (in-order traversal), not by lineId value.\n * The `size` field enables O(log N) order statistics.\n * Random priorities maintain balance with high probability (O(log N) depth).\n *\n * Memory: ~40 bytes per node (lineId + size + priority + left + right).\n * For 100K lines: ~4MB for tree structure (acceptable).\n */\n\n// ── Types ──────────────────────────────────────────────────────────────────\n\ninterface Node {\n\tlineId: number;\n\t/** Total number of nodes in this subtree (including self). */\n\tsize: number;\n\t/** Random priority for heap property (maintains balance). */\n\tpriority: number;\n\tleft: Node | null;\n\tright: Node | null;\n}\n\n// ── SegmentTree ────────────────────────────────────────────────────────────\n\n/**\n * Maps document offsets to line numbers in logarithmic time.\n *\n * A document is edited constantly, and every edit shifts the offsets of every\n * line after it. Recomputing them linearly on each keystroke is what this\n * avoids.\n */\nexport class SegmentTree {\n\tprivate root: Node | null = null;\n\n\t// ── Public API ───────────────────────────────────────────────────────\n\n\t/** Total number of elements in the tree. */\n\tget length(): number {\n\t\treturn this.root ? this.root.size : 0;\n\t}\n\n\t/** Whether the tree is empty. */\n\tget isEmpty(): boolean {\n\t\treturn this.root === null;\n\t}\n\n\t/**\n\t * Get the lineId at the given 0-based index.\n\t * Returns undefined if index is out of bounds.\n\t * O(log N) expected.\n\t */\n\tgetAt(index: number): number | undefined {\n\t\tif (index < 0 || index >= (this.root?.size ?? 0)) return undefined;\n\t\treturn this.nodeAt(this.root, index).lineId;\n\t}\n\n\t/**\n\t * Insert a single lineId at the given 0-based index.\n\t * O(log N) expected.\n\t */\n\tinsertAt(index: number, lineId: number): void {\n\t\tconst [left, right] = split(this.root, index);\n\t\tthis.root = merge(merge(left, createNode(lineId)), right);\n\t}\n\n\t/**\n\t * Delete a single element at the given 0-based index.\n\t * Returns the removed lineId, or undefined if out of bounds.\n\t * O(log N) expected.\n\t */\n\tdeleteAt(index: number): number | undefined {\n\t\tif (index < 0 || index >= (this.root?.size ?? 0)) return undefined;\n\n\t\tconst [left, midRight] = split(this.root, index);\n\t\tconst [mid, right] = split(midRight, 1);\n\n\t\tconst removed = mid ? mid.lineId : undefined;\n\t\tthis.root = merge(left, right);\n\t\treturn removed;\n\t}\n\n\t/**\n\t * Bulk splice: delete `deleteCount` elements at `startIndex` (0-based),\n\t * then insert `newIds` in their place.\n\t *\n\t * Returns the removed lineId array.\n\t * O(log N + deleteCount + insertCount) expected.\n\t */\n\tspliceAt(\n\t\tstartIndex: number,\n\t\tdeleteCount: number,\n\t\tnewIds: number[]\n\t): number[] {\n\t\tconst clampedStart = Math.max(0, startIndex);\n\t\tconst clampedCount = Math.min(\n\t\t\tMath.max(0, deleteCount),\n\t\t\t(this.root?.size ?? 0) - clampedStart\n\t\t);\n\n\t\t// Split into three parts: [0, start) [start, start+count) [start+count, end)\n\t\tconst [left, midRight] = split(this.root, clampedStart);\n\t\tconst [mid, right] = split(midRight, clampedCount);\n\n\t\t// Collect removed IDs\n\t\tconst removed = collectInOrder(mid);\n\n\t\t// Build a treap from the new IDs and merge\n\t\tconst newTree = buildTreap(newIds, 0, newIds.length);\n\t\tthis.root = merge(merge(left, newTree), right);\n\n\t\treturn removed;\n\t}\n\n\t/**\n\t * Replace the entire tree with a new set of lineIds.\n\t * O(N), builds a balanced treap from a flat array.\n\t */\n\treplaceAll(lineIds: number[]): void {\n\t\tthis.root = buildTreap(lineIds, 0, lineIds.length);\n\t}\n\n\t/**\n\t * Get a contiguous range of lineIds by index.\n\t * O(rangeSize + log N), single in-order walk instead of N × O(log N).\n\t *\n\t * This is the hot path for viewport rendering in DocumentModel.getVisibleLines().\n\t */\n\tgetRange(startIndex: number, endIndex: number): number[] {\n\t\tconst clampedStart = Math.max(0, startIndex);\n\t\tconst clampedEnd = Math.min((this.root?.size ?? 0) - 1, endIndex);\n\t\tif (clampedStart > clampedEnd) return [];\n\n\t\tconst result: number[] = [];\n\t\tthis.collectRange(this.root, clampedStart, clampedEnd, 0, result);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Clear the tree.\n\t */\n\tclear(): void {\n\t\tthis.root = null;\n\t}\n\n\t// ── Iteration ─────────────────────────────────────────────────────────\n\n\t/**\n\t * In-order iterator yielding all lineIds.\n\t */\n\t*[Symbol.iterator](): IterableIterator<number> {\n\t\tyield* this.inOrder(this.root);\n\t}\n\n\tprivate *inOrder(node: Node | null): IterableIterator<number> {\n\t\tif (!node) return;\n\t\tyield* this.inOrder(node.left);\n\t\tyield node.lineId;\n\t\tyield* this.inOrder(node.right);\n\t}\n\n\t// ── Private helpers ───────────────────────────────────────────────────\n\n\tprivate nodeAt(node: Node | null, index: number): Node {\n\t\t// This is only called with valid indices, so node is non-null\n\t\tconst leftSize = node!.left ? node!.left.size : 0;\n\n\t\tif (index < leftSize) {\n\t\t\treturn this.nodeAt(node!.left, index);\n\t\t} else if (index === leftSize) {\n\t\t\treturn node!;\n\t\t} else {\n\t\t\treturn this.nodeAt(node!.right, index - leftSize - 1);\n\t\t}\n\t}\n\n\t/**\n\t * Collect all node values in the range [targetStart, targetEnd] into result.\n\t * `offset` is the 0-based start of the current subtree within the full tree.\n\t * O(rangeSize + log N), skips entire subtrees outside the target range.\n\t */\n\tprivate collectRange(\n\t\tnode: Node | null,\n\t\ttargetStart: number,\n\t\ttargetEnd: number,\n\t\toffset: number,\n\t\tresult: number[]\n\t): void {\n\t\tif (!node) return;\n\n\t\tconst leftSize = node.left ? node.left.size : 0;\n\t\tconst nodeIndex = offset + leftSize;\n\n\t\t// Traverse left subtree if it overlaps the target range\n\t\tif (nodeIndex > targetStart && node.left) {\n\t\t\tthis.collectRange(node.left, targetStart, targetEnd, offset, result);\n\t\t}\n\n\t\t// Include this node if within range\n\t\tif (nodeIndex >= targetStart && nodeIndex <= targetEnd) {\n\t\t\tresult.push(node.lineId);\n\t\t}\n\n\t\t// Traverse right subtree if it overlaps the target range\n\t\tif (nodeIndex < targetEnd && node.right) {\n\t\t\tthis.collectRange(node.right, targetStart, targetEnd, nodeIndex + 1, result);\n\t\t}\n\t}\n}\n\n// ── Free functions ──────────────────────────────────────────────────────────\n\n/** Create a new treap node. */\nfunction createNode(lineId: number): Node {\n\treturn {\n\t\tlineId,\n\t\tsize: 1,\n\t\tpriority: Math.random(),\n\t\tleft: null,\n\t\tright: null,\n\t};\n}\n\n/** Update a node's size from its children. */\nfunction updateSize(node: Node): void {\n\tnode.size = 1 + (node.left ? node.left.size : 0) + (node.right ? node.right.size : 0);\n}\n\n/**\n * Merge two treaps. All keys in `left` must be ≤ all keys in `right`.\n * O(log N) expected.\n */\nfunction merge(left: Node | null, right: Node | null): Node | null {\n\tif (!left) return right;\n\tif (!right) return left;\n\n\tif (left.priority > right.priority) {\n\t\tleft.right = merge(left.right, right);\n\t\tupdateSize(left);\n\t\treturn left;\n\t} else {\n\t\tright.left = merge(left, right.left);\n\t\tupdateSize(right);\n\t\treturn right;\n\t}\n}\n\n/**\n * Split a treap at `index` (0-based).\n * Returns [left, right] where `left` contains the first `index` elements\n * and `right` contains the rest.\n * O(log N) expected.\n */\nfunction split(\n\tnode: Node | null,\n\tindex: number\n): [Node | null, Node | null] {\n\tif (!node) return [null, null];\n\n\tconst leftSize = node.left ? node.left.size : 0;\n\n\tif (index <= leftSize) {\n\t\tconst [left, right] = split(node.left, index);\n\t\tnode.left = right;\n\t\tupdateSize(node);\n\t\treturn [left, node];\n\t} else {\n\t\tconst [left, right] = split(node.right, index - leftSize - 1);\n\t\tnode.right = left;\n\t\tupdateSize(node);\n\t\treturn [node, right];\n\t}\n}\n\n/**\n * Collect all lineIds from a treap in-order.\n * O(N).\n */\nfunction collectInOrder(node: Node | null): number[] {\n\tconst result: number[] = [];\n\tcollect(node, result);\n\treturn result;\n}\n\nfunction collect(node: Node | null, result: number[]): void {\n\tif (!node) return;\n\tcollect(node.left, result);\n\tresult.push(node.lineId);\n\tcollect(node.right, result);\n}\n\n/**\n * Build a balanced treap from a flat array of lineIds.\n * Uses divide-and-conquer with pre-sorted priorities to produce\n * a heap-ordered tree in O(N) time.\n * O(N).\n */\nfunction buildTreap(lineIds: number[], start: number, end: number): Node | null {\n\tif (start >= end) return null;\n\n\tconst mid = Math.floor((start + end) / 2);\n\n\t// The priority comes from the line's own id, not from its position in this\n\t// build. Position carries no entropy across separate builds: an insert of a\n\t// single line is always `buildTreap(ids, 0, 1)`, so `mid` is always 0, so\n\t// every singly-inserted node got the same priority and they chained into a\n\t// list instead of a tree. The id is unique and stable, so the same set of\n\t// lines still produces the same shape, which is what the determinism here\n\t// was for.\n\tconst priority = pseudoRandom(lineIds[mid]);\n\n\tconst node: Node = {\n\t\tlineId: lineIds[mid],\n\t\tsize: end - start,\n\t\tpriority,\n\t\tleft: buildTreap(lineIds, start, mid),\n\t\tright: buildTreap(lineIds, mid + 1, end),\n\t};\n\n\treturn node;\n}\n\n/**\n * Simple pseudo-random number generator for deterministic treap building.\n * Uses a multiplicative hash of the input.\n *\n * Zero used to be a fixed point: it survives the shift, the xor and the\n * multiply unchanged, so `pseudoRandom(0)` was exactly `0`, the lowest priority\n * a node can have. The seed is offset by a large odd constant first, so no input\n * maps to itself, and the result is read as unsigned so the range really is\n * `[0, 1)` rather than straddling zero.\n */\nfunction pseudoRandom(seed: number): number {\n\tlet x = (seed + 0x9e3779b9) | 0;\n\tx = Math.imul((x >>> 16) ^ x, 0x45d9f3b);\n\tx = Math.imul((x >>> 16) ^ x, 0x45d9f3b);\n\tx = ((x >>> 16) ^ x) >>> 0;\n\treturn x / 0x100000000; // normalize to [0, 1)\n}\n","import { Value } from \"@solve-js/vm/Value\";\nimport { BytecodeProgram } from \"@solve-js/parser/BytecodeBuilder\";\nimport { djb2Hash } from \"@solve-js/utilities/Hash\";\nimport { SegmentTree } from \"@solve-js/engine/SegmentTree\";\nimport { DEFAULT_CONFIG } from \"@solve-js/constants/Configuration\";\nimport { countLines } from \"@solve-js/utilities/Strings\";\nimport { memberTagsOf } from \"@solve-js/packages/tags/TagScanner\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n// ── LineState ──────────────────────────────────────────────────────────────\n\n/**\n * Persistent per-line state tracked by the DocumentModel.\n *\n * Each line receives an immutable `lineId` that survives structural edits\n * (insertions, deletions, line shifts). This allows caches, the dependency\n * graph, and VM checkpoints to reference lines by ID instead of by volatile\n * line numbers.\n *\n * ── Multi-expression support ─────────────────────────────────────────\n * A single document line may contain multiple inline solves (`s\\`...\\``).\n * To support this without breaking the 1:1 line-to-DAG-node contract:\n * - `expressions[]` holds all extracted expression strings (1 entry for\n * full-line expressions, N entries for N inline solves).\n * - `bytecodes[]` holds compiled bytecode in corresponding order.\n * - `results[]` holds evaluation **result groups** in corresponding order.\n * Each element is a `Value[]`, a group of Values produced by that expression.\n * For variable definitions, the group always has exactly 1 element.\n * For multi-output expressions (e.g., currency conversion), the group\n * may have multiple Values (one per target unit).\n * - `reads[]` and `writes[]` are aggregated across ALL expressions on\n * the line, the DAG treats the line as a single dependency node.\n * - `inlineSolveCount` is 0 for full-line expressions, >0 for inline solves.\n */\nexport interface LineState {\n\t/** Immutable unique identifier, survives all structural edits. */\n\treadonly lineId: number;\n\n\t/** djb2 hash of the line text, used for O(1) change detection. */\n\ttextHash: number;\n\n\t/** The full line text (may include markdown). */\n\ttext: string;\n\n\t/**\n\t * Extracted expression texts.\n\t * - Full-line expressions: one entry (the trimmed text).\n\t * - Inline solve lines: one entry per `s\\`...\\`` span, in left-to-right order.\n\t * - Markdown-only lines: empty array.\n\t */\n\texpressions: string[];\n\n\t/**\n\t * Compiled bytecode for each expression, in corresponding order.\n\t * Parallel to {@link expressions}.\n\t */\n\tbytecodes: BytecodeProgram[];\n\n\t/** Variables this line reads (for dependency tracking). Aggregated across all expressions. */\n\treads: string[];\n\n\t/** Variables this line writes (empty if not a variable definition). Aggregated across all expressions. */\n\twrites: string[];\n\n\t/**\n\t * Evaluation result groups for each expression, in corresponding order.\n\t * Each `Value[]` is a group of Values produced by one expression.\n\t * Variable definitions always produce exactly 1 Value per group.\n\t * Multi-output expressions (e.g. currency conversion) may produce N Values.\n\t */\n\tresults: Value[][];\n\n\t/**\n\t * Convenience accessor for the first expression's first result.\n\t * Equals `results[0]?.[0] ?? null`. For single-expression lines,\n\t * this is the primary result. For multi-expression (inline solve) lines,\n\t * prefer accessing `results[i][j]` directly.\n\t */\n\tresult: Value | null;\n\n\t/** True if this line needs re-evaluation. */\n\tdirty: boolean;\n\n\t/** True if any expression on this line defines a variable (never evict bytecode). */\n\tisVariableDef: boolean;\n\n\t/** True if this line contains only markdown (no evaluable expression). */\n\tisEmpty: boolean;\n\n\t/**\n\t * Number of inline solve expressions on this line.\n\t * 0 = full-line expression (or markdown-only).\n\t * >0 = N inline solves embedded in markdown text.\n\t */\n\tinlineSolveCount: number;\n}\n\n// ── ViewportRange ──────────────────────────────────────────────────────────\n\n/** The visible line range, used to decide which lines are evaluated eagerly. */\nexport interface ViewportRange {\n\tstartLine: number; // 1-based, inclusive\n\tendLine: number; // 1-based, inclusive\n}\n\n// ── LineChange ─────────────────────────────────────────────────────────────\n\n/** Describes a structural change to the document's line list. */\nexport interface LineChange {\n\t/** 1-based line number where the change starts. */\n\tstartLine: number;\n\t/** Number of lines deleted (0 for pure insertion). */\n\tdeleteCount: number;\n\t/** New line texts inserted in place of deleted lines. */\n\tinsertLines: string[];\n}\n\n// ── ApplyChangesResult ─────────────────────────────────────────────────────\n\n/** Result of applying changes to the document model. */\nexport interface ApplyChangesResult {\n\t/** Line IDs of newly inserted lines. */\n\tinserted: number[];\n\t/** Line IDs that were removed. */\n\tremoved: number[];\n}\n\n// ── DocumentModel ──────────────────────────────────────────────────────────\n\n/**\n * Persistent document model with O(log N) line lookups and structural edits.\n *\n * Design:\n * - Each line has an immutable `lineId` (monotonically increasing counter).\n * - `LineState` objects are stored in a `Map<lineId, LineState>` for O(1) access.\n * - Line ordering is maintained in a `SegmentTree` (order-statistic Treap) that\n * supports O(log N) insert, delete, and get-at-index operations.\n * - A lazy position cache (`Map<lineId, number>`) provides O(1) position lookups\n * after the first `getLinePosition()` call and is invalidated on structural edits.\n *\n * Key invariant: line IDs never change, only their positions in the order tree.\n * This means cached bytecode, dependency graph entries, and VM checkpoints\n * keyed by lineId remain valid across all structural edits.\n */\nexport class DocumentModel {\n\t/** Persistent line ID → LineState. */\n\tprivate lines: Map<number, LineState> = new Map();\n\n\t/** Order-statistic treap representing the current document line order. */\n\tprivate orderTree: SegmentTree = new SegmentTree();\n\n\t/** Monotonically increasing counter for new line IDs. */\n\tprivate nextLineId: number = 1;\n\n\t/**\n\t * Lazy position cache: lineId → 1-based position.\n\t * Built on first `getLinePosition()` call, invalidated on structural edits.\n\t */\n\tprivate _positionCache: Map<number, number> | null = null;\n\n\t/**\n\t * Line IDs currently marked dirty, maintained alongside every\n\t * `state.dirty` mutation (in this class and in every other module that\n\t * holds a direct `LineState` reference: ThreeTierEvaluator, PageManager).\n\t * Lets {@link hasAnyDirtyLineBefore} answer \"is anything before position\n\t * X dirty\" in O(d log N), d = current dirty count, typically tiny once\n\t * a document has settled after its initial evaluation, instead of\n\t * O(N log N), which used to mean every scroll event re-walked the WHOLE\n\t * document via `getLineAt()` regardless of how little of it was actually\n\t * dirty. Benchmarked: ~10.6ms per setViewport() call scrolled near the\n\t * bottom of a 20k-line document before this fix.\n\t */\n\tprivate dirtyLineIds: Set<number> = new Set();\n\n\t/**\n\t * Lower-cased category tag to the ids of the lines carrying it, or `null`\n\t * while nothing has asked.\n\t *\n\t * `total of #food` used to walk the whole document looking for `#food`, so a\n\t * document of members and totals cost one walk per total: D aggregates over\n\t * N lines is D x N, and a notepad of a few thousand tagged lines took\n\t * minutes. This turns an aggregate into a lookup of its own members.\n\t *\n\t * Built on the first query rather than with the document, so a document with\n\t * no tags in it, which is most of them, never scans for any. Once built it is\n\t * kept in step by the four places that change a line's text: {@link setDocument},\n\t * {@link applyChanges}, {@link editLine} and {@link clear}. Keyed by line id\n\t * rather than position, so inserting or deleting a line above a tagged one\n\t * does not touch it.\n\t */\n\tprivate tagIndex: Map<string, Set<number>> | null = null;\n\n\t/**\n\t * Most lines this model will hold. See `constants/Configuration.ts`'s\n\t * `performance.maxDocumentLines`, which is where the default comes from and\n\t * which a host raises through its engine config; a host that builds a\n\t * DocumentModel directly passes it here instead.\n\t */\n\tprivate readonly maxLines: number;\n\n\t/**\n\t * @param maxLines - Ceiling on the line count, defaulting to the engine's\n\t * configured one. Every line costs a LineState with six arrays in it\n\t * whatever the line says, so the cost of a document is its line count and\n\t * nothing else bounds it: two hundred thousand lines of `1 + 1` exhausted\n\t * the heap here, before a single expression had been looked at.\n\t */\n\tconstructor(maxLines: number = DEFAULT_CONFIG.performance.maxDocumentLines) {\n\t\tthis.maxLines = maxLines;\n\t}\n\n\t// ── Initialization ──────────────────────────────────────────────────\n\n\t/**\n\t * Initialize or replace the entire document from a text blob.\n\t * Clears all existing state and assigns new persistent line IDs.\n\t *\n\t * @throws `DOCUMENT_TOO_LARGE` for a document past {@link maxLines}, before\n\t * any of it is stored. Recoverable: nothing has been replaced yet, so the\n\t * model still holds whatever it held.\n\t */\n\tsetDocument(text: string): void {\n\t\t// Counted before anything is cleared, so a refused document leaves the\n\t\t// previous one intact rather than half-replaced.\n\t\tconst lineCount = countLines(text, this.maxLines);\n\t\tif (lineCount > this.maxLines) {\n\t\t\tthrow ErrorFactory.execution(\n\t\t\t\t\"DOCUMENT_TOO_LARGE\",\n\t\t\t\t`This document has more than ${this.maxLines.toLocaleString(\"en-US\")} lines, which is the most the engine will hold at once`,\n\t\t\t\t{ maxLines: this.maxLines },\n\t\t\t);\n\t\t}\n\t\tthis.lines.clear();\n\t\tthis.orderTree.clear();\n\t\tthis._positionCache = null;\n\t\tthis.dirtyLineIds.clear();\n\t\tthis.nextLineId = 1;\n\t\tthis.tagIndex = null;\n\n\t\tconst rawLines = text.split(\"\\n\");\n\t\tconst lineIds = new Array<number>(rawLines.length);\n\n\t\tfor (let i = 0; i < rawLines.length; i++) {\n\t\t\tconst lineId = this.nextLineId++;\n\t\t\tlineIds[i] = lineId;\n\t\t\tthis.lines.set(lineId, {\n\t\t\t\tlineId,\n\t\t\t\ttextHash: djb2Hash(rawLines[i]),\n\t\t\t\ttext: rawLines[i],\n\t\t\t\texpressions: [],\n\t\t\t\tbytecodes: [],\n\t\t\t\treads: [],\n\t\t\t\twrites: [],\n\t\t\t\tresults: [],\n\t\t\t\tresult: null,\n\t\t\t\tdirty: true,\n\t\t\t\tisVariableDef: false,\n\t\t\t\tisEmpty: rawLines[i].trim().length === 0,\n\t\t\t\tinlineSolveCount: 0,\n\t\t\t});\n\t\t}\n\n\t\t// O(N) balanced treap build from flat array\n\t\tthis.orderTree.replaceAll(lineIds);\n\n\t\t// Every line starts dirty (see the object literal above), seed the\n\t\t// tracking set to match. This is the one place the set is legitimately\n\t\t// O(N): a fresh document needs full evaluation anyway.\n\t\tfor (const id of lineIds) this.dirtyLineIds.add(id);\n\t}\n\n\t// ── Structural edits ────────────────────────────────────────────────\n\n\t/**\n\t * Apply one or more line-level changes to the document.\n\t *\n\t * **Precondition:** Changes must be **non-overlapping** in their line ranges.\n\t * If two changes target the same or adjacent lines, the reverse-order\n\t * processing may produce incorrect results because the first-applied\n\t * change shifts the line numbers that the second change references.\n\t *\n\t * Changes are applied in **reverse order** (highest startLine first) so\n\t * that earlier changes in the document don't shift the indices of later\n\t * changes during processing.\n\t *\n\t * Returns both the newly inserted line IDs and the removed line IDs.\n\t * Callers should use `removed` to clean up the dependency graph and\n\t * other data structures keyed by lineId.\n\t */\n\tapplyChanges(changes: LineChange[]): ApplyChangesResult {\n\t\tconst inserted: number[] = [];\n\t\tconst removed: number[] = [];\n\n\t\t// Sort descending by startLine so earlier changes don't shift later indices\n\t\tconst sorted = [...changes].sort((a, b) => b.startLine - a.startLine);\n\n\t\tfor (const change of sorted) {\n\t\t\tconst startIdx = change.startLine - 1; // convert to 0-based\n\n\t\t\t// Create new LineState entries for inserted lines\n\t\t\tconst newIds: number[] = [];\n\t\t\tfor (const text of change.insertLines) {\n\t\t\t\tconst lineId = this.nextLineId++;\n\t\t\t\tnewIds.push(lineId);\n\t\t\t\tinserted.push(lineId);\n\t\t\t\tthis.lines.set(lineId, {\n\t\t\t\t\tlineId,\n\t\t\t\t\ttextHash: djb2Hash(text),\n\t\t\t\t\ttext,\n\t\t\t\t\texpressions: [],\n\t\t\t\t\tbytecodes: [],\n\t\t\t\t\treads: [],\n\t\t\t\t\twrites: [],\n\t\t\t\t\tresults: [],\n\t\t\t\t\tresult: null,\n\t\t\t\t\tdirty: true,\n\t\t\t\t\tisVariableDef: false,\n\t\t\t\t\tisEmpty: text.trim().length === 0,\n\t\t\t\t\tinlineSolveCount: 0,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// New lines start dirty (see the object literal above), track them.\n\t\t\tfor (const id of newIds) this.dirtyLineIds.add(id);\n\n\n\t\t\t// Only when something has already asked for one: an index nothing has\n\t\t\t// built yet describes no lines, so it has none to learn about either.\n\t\t\tif (this.tagIndex !== null) {\n\t\t\t\tfor (const id of newIds) this.indexTags(id, this.lines.get(id)!.text);\n\t\t\t}\n\n\t\t\t// O(log N) splice: delete old IDs, insert new IDs\n\t\t\tconst removedIds = this.orderTree.spliceAt(\n\t\t\t\tstartIdx,\n\t\t\t\tchange.deleteCount,\n\t\t\t\tnewIds\n\t\t\t);\n\t\t\tfor (const id of removedIds) {\n\t\t\t\tremoved.push(id);\n\t\t\t\tif (this.tagIndex !== null) this.unindexTags(id, this.lines.get(id)?.text ?? \"\");\n\t\t\t\tthis.lines.delete(id);\n\t\t\t\tthis.dirtyLineIds.delete(id);\n\t\t\t}\n\t\t}\n\n\t\t// Invalidate position cache, positions shifted for all lines\n\t\tthis._positionCache = null;\n\n\t\treturn { inserted, removed };\n\t}\n\n\t/**\n\t * Insert new lines at the given 1-based position.\n\t * Convenience wrapper around applyChanges.\n\t */\n\tinsertLines(atLine: number, texts: string[]): number[] {\n\t\tconst change: LineChange = {\n\t\t\tstartLine: atLine,\n\t\t\tdeleteCount: 0,\n\t\t\tinsertLines: texts,\n\t\t};\n\t\tconst result = this.applyChanges([change]);\n\t\treturn result.inserted;\n\t}\n\n\t/**\n\t * Delete lines in the given 1-based range [startLine, endLine] inclusive.\n\t * Convenience wrapper around applyChanges.\n\t */\n\tdeleteLines(startLine: number, endLine: number): number[] {\n\t\tconst change: LineChange = {\n\t\t\tstartLine,\n\t\t\tdeleteCount: endLine - startLine + 1,\n\t\t\tinsertLines: [],\n\t\t};\n\t\tconst result = this.applyChanges([change]);\n\t\treturn result.removed;\n\t}\n\n\t/**\n\t * Update the text of a single line in place.\n\t * If the text hash differs, marks the line dirty and clears its\n\t * bytecode/result so it gets re-evaluated.\n\t *\n\t * Returns true if the text actually changed (hash mismatch).\n\t */\n\teditLine(lineNumber: number, newText: string): boolean {\n\t\tconst state = this.getLineAt(lineNumber);\n\t\tif (!state) return false;\n\n\t\tconst newHash = djb2Hash(newText);\n\t\tif (newHash === state.textHash) return false;\n\n\t\tif (this.tagIndex !== null) {\n\t\t\tthis.unindexTags(state.lineId, state.text);\n\t\t\tthis.indexTags(state.lineId, newText);\n\t\t}\n\t\tstate.text = newText;\n\t\tstate.textHash = newHash;\n\t\tstate.expressions = [];\n\t\tstate.bytecodes = [];\n\t\tstate.results = [];\n\t\tstate.result = null;\n\t\tstate.inlineSolveCount = 0;\n\t\tstate.dirty = true;\n\t\tthis.dirtyLineIds.add(state.lineId);\n\t\tstate.isEmpty = newText.trim().length === 0;\n\t\treturn true;\n\t}\n\n\t// ── Queries ─────────────────────────────────────────────────────────\n\n\t/**\n\t * The 1-based positions of the lines carrying `#tag`, in document order.\n\t *\n\t * What `total of #tag` reads. Case-insensitive, matching how a tag is read\n\t * on a line, and it names membership only: a line that merely asks about the\n\t * group (`total of #tag`) is not in it, and neither is a `#heading`.\n\t *\n\t * Ascending, because an aggregate reports the first line it cannot read and\n\t * the line it names has to be the first one in the document, not whichever\n\t * happened to be indexed first.\n\t *\n\t * @param tag - The tag name without its `#`.\n\t * @returns Its lines' positions, ascending; empty when no line carries it.\n\t */\n\tlinesCarryingTag(tag: string): number[] {\n\t\tconst ids = this.ensureTagIndex().get(tag.toLowerCase());\n\t\tif (ids === undefined || ids.size === 0) return [];\n\t\tconst positions: number[] = [];\n\t\tfor (const id of ids) {\n\t\t\tconst position = this.getLinePosition(id);\n\t\t\t// A line still in the index but no longer in the document cannot\n\t\t\t// happen through the maintained paths; guarded rather than trusted,\n\t\t\t// since the alternative is aggregating over position -1.\n\t\t\tif (position > 0) positions.push(position);\n\t\t}\n\t\tpositions.sort((a, b) => a - b);\n\t\treturn positions;\n\t}\n\n\t/** The tag index, built over every line the first time one is asked for. */\n\tprivate ensureTagIndex(): Map<string, Set<number>> {\n\t\tif (this.tagIndex !== null) return this.tagIndex;\n\t\tthis.tagIndex = new Map();\n\t\tfor (const state of this.lines.values()) this.indexTags(state.lineId, state.text);\n\t\treturn this.tagIndex;\n\t}\n\n\t/** Records `lineId` as a member of every group its text joins. */\n\tprivate indexTags(lineId: number, text: string): void {\n\t\tif (text.indexOf(\"#\") < 0) return; // the overwhelmingly common line\n\t\tfor (const tag of memberTagsOf(text)) {\n\t\t\tconst ids = this.tagIndex!.get(tag);\n\t\t\tif (ids === undefined) this.tagIndex!.set(tag, new Set([lineId]));\n\t\t\telse ids.add(lineId);\n\t\t}\n\t}\n\n\t/** Drops `lineId` from every group the text it used to hold joined. */\n\tprivate unindexTags(lineId: number, text: string): void {\n\t\tif (text.indexOf(\"#\") < 0) return;\n\t\tfor (const tag of memberTagsOf(text)) {\n\t\t\tconst ids = this.tagIndex!.get(tag);\n\t\t\tif (ids === undefined) continue;\n\t\t\tids.delete(lineId);\n\t\t\tif (ids.size === 0) this.tagIndex!.delete(tag);\n\t\t}\n\t}\n\n\t/**\n\t * Get the LineState at the given 1-based line position. O(1).\n\t */\n\tgetLineAt(position: number): LineState | undefined {\n\t\tconst idx = position - 1;\n\t\tconst lineId = this.orderTree.getAt(idx);\n\t\tif (lineId === undefined) return undefined;\n\t\treturn this.lines.get(lineId);\n\t}\n\n\t/**\n\t * Get the 1-based position of a line by its persistent ID.\n\t * Returns -1 if the line ID is not in the document.\n\t *\n\t * Uses a lazy position cache: O(N) on first call after structural edit,\n\t * O(1) on subsequent calls. The cache is invalidated by any structural edit.\n\t */\n\tgetLinePosition(lineId: number): number {\n\t\tif (this._positionCache) {\n\t\t\treturn this._positionCache.get(lineId) ?? -1;\n\t\t}\n\n\t\t// Build position cache on first call after invalidation\n\t\tthis._positionCache = new Map();\n\t\tlet pos = 1;\n\t\tfor (const id of this.orderTree) {\n\t\t\tthis._positionCache.set(id, pos++);\n\t\t}\n\n\t\treturn this._positionCache.get(lineId) ?? -1;\n\t}\n\n\t/**\n\t * Get all LineState entries within the given viewport range (1-based, inclusive).\n\t * Uses SegmentTree.getRange() for O(viewport + log N) collection instead of\n\t * O(viewport × log N) per-line lookups.\n\t */\n\tgetVisibleLines(startLine: number, endLine: number): LineState[] {\n\t\tconst lineIds = this.orderTree.getRange(startLine - 1, endLine - 1);\n\t\tconst result: LineState[] = [];\n\t\tfor (const lineId of lineIds) {\n\t\t\tconst state = this.lines.get(lineId);\n\t\t\tif (state) result.push(state);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * The lines from `startLine` to `endLine`, one entry per position.\n\t *\n\t * The same lines {@link getVisibleLines} returns, except that a position\n\t * whose line is missing keeps its place as `undefined` rather than closing\n\t * the gap, so the caller can index by position.\n\t *\n\t * Resolving the ids here rather than handing them back is deliberate and\n\t * measured: the loop below is monomorphic over this class's own map, and\n\t * asking {@link getLineById} once per position instead was 20% slower on a\n\t * twenty-thousand-line document.\n\t *\n\t * One in-order walk of the order tree, O(span + log N), against the\n\t * O(log N) descent per position that asking {@link getLineAt} in a loop\n\t * costs. The evaluator walks from line 1 to the end of the viewport on\n\t * every pass, so that descent was a third of the cost of an edit on a long\n\t * document scrolled near the bottom.\n\t *\n\t * @param startLine - First position, 1-based, inclusive.\n\t * @param endLine - Last position, 1-based, inclusive.\n\t * @returns One entry per position in the span, in document order.\n\t */\n\tgetLineStatesInRange(startLine: number, endLine: number): (LineState | undefined)[] {\n\t\tconst lineIds = this.orderTree.getRange(startLine - 1, endLine - 1);\n\t\tconst result = new Array<LineState | undefined>(lineIds.length);\n\t\tfor (let i = 0; i < lineIds.length; i++) result[i] = this.lines.get(lineIds[i]);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get all LineState entries in order. Useful for batch processing.\n\t */\n\tgetAllLines(): LineState[] {\n\t\treturn this.getVisibleLines(1, this.lineCount);\n\t}\n\n\t/**\n\t * Get a LineState by its persistent line ID. O(1).\n\t */\n\tgetLineById(lineId: number): LineState | undefined {\n\t\treturn this.lines.get(lineId);\n\t}\n\n\t/**\n\t * Get all lines that are marked dirty.\n\t */\n\tgetDirtyLines(): LineState[] {\n\t\tconst result: LineState[] = [];\n\t\tfor (const state of this.lines.values()) {\n\t\t\tif (state.dirty) result.push(state);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Whether any line before `position` (1-based, exclusive) is dirty.\n\t *\n\t * Used by ThreeTierEvaluator.setViewport() to decide whether cached\n\t * checkpoint state might be stale and a full evaluate() (from line 1) is\n\t * needed instead of the cheap viewport-only path.\n\t *\n\t * O(d log N) where d = current dirty line count via {@link dirtyLineIds},\n\t * not O(N log N), a document that's mostly clean (the steady state after\n\t * initial load) answers this in the cost of resolving a handful of\n\t * lineIds to positions, not walking every line up to `position`.\n\t */\n\thasAnyDirtyLineBefore(position: number): boolean {\n\t\tfor (const lineId of this.dirtyLineIds) {\n\t\t\tconst pos = this.getLinePosition(lineId);\n\t\t\tif (pos >= 1 && pos < position) return true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Whether any **variable-definition** line before `position` (1-based,\n\t * exclusive) is dirty.\n\t *\n\t * Narrower than {@link hasAnyDirtyLineBefore}: `VMCheckpointer.snapshot()`\n\t * only ever records state for lines with `writes.length > 0` (see\n\t * VMCheckpoints.ts), so a dirty plain-expression line before the viewport\n\t * cannot have invalidated any checkpoint, there's no checkpoint entry\n\t * for it to invalidate. Only a dirty variable-def line can mean the VM\n\t * state a checkpoint would restore is stale.\n\t *\n\t * This distinction matters because `PageManager.evictPageBytecode()`\n\t * marks evicted non-variable-def lines dirty (so they get Tier 1 if\n\t * scrolled back into view), and Tier 3's compile-only path never clears\n\t * `dirty` for non-variable-def lines by design. Using the broader\n\t * `hasAnyDirtyLineBefore` here meant scrolling far into a large,\n\t * variable-def-free document would trip `setViewport()`'s fallback to\n\t * `evaluate()` on every single call, evaluate() reprocesses the evicted\n\t * lines via Tier 3, which recompiles their bytecode without clearing\n\t * dirty, so the very next `maintainAfterEval()` re-evicts and re-dirties\n\t * the same lines, forever re-triggering the fallback on an otherwise\n\t * unchanged viewport.\n\t */\n\thasAnyDirtyVariableDefLineBefore(position: number): boolean {\n\t\tfor (const lineId of this.dirtyLineIds) {\n\t\t\tconst state = this.lines.get(lineId);\n\t\t\tif (!state || !state.isVariableDef) continue;\n\t\t\tconst pos = this.getLinePosition(lineId);\n\t\t\tif (pos >= 1 && pos < position) return true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Number of lines currently marked dirty. For diagnostics/tests. */\n\tget dirtyCount(): number {\n\t\treturn this.dirtyLineIds.size;\n\t}\n\n\t// ── Thread-safety validation ────────────────────────────────────────\n\n\t/**\n\t * Verify that bytecode compiled by a worker is still valid for this line.\n\t *\n\t * When Phase 5.2h sends compilation to a worker, the worker posts back\n\t * `{lineId, bytecode, reads, writes, compiledAgainstHash}`. Between dispatch\n\t * and response, the user may have edited the line. This method lets the\n\t * main thread check whether the bytecode is still applicable.\n\t *\n\t * @returns true if the line still exists and its text hash matches.\n\t */\n\tisBytecodeValid(lineId: number, compiledAgainstHash: number): boolean {\n\t\tconst state = this.lines.get(lineId);\n\t\treturn state !== undefined && state.textHash === compiledAgainstHash;\n\t}\n\n\t// ── State mutations ─────────────────────────────────────────────────\n\n\t/**\n\t * Mark a line as clean (re-evaluated successfully).\n\t */\n\t/**\n\t * Forget what a line answered, without forgetting the line.\n\t *\n\t * For a line whose answer was computed about a document that no longer\n\t * exists. Marking it dirty says it must run again; this says that until it\n\t * does, it has nothing to tell anyone who asks, which is the state a pass\n\t * over the text from scratch would be in.\n\t *\n\t * The bytecode is left alone. It is compiled from the line's own text, which\n\t * a structural edit does not change, and dropping it would recompile the\n\t * document for nothing.\n\t *\n\t * @param lineId - Persistent line identifier.\n\t */\n\tforgetResult(lineId: number): void {\n\t\tconst state = this.lines.get(lineId);\n\t\tif (!state) return;\n\t\tstate.result = null;\n\t\tstate.results = [];\n\t}\n\n\tmarkClean(lineId: number): void {\n\t\tconst state = this.lines.get(lineId);\n\t\tif (state) {\n\t\t\tstate.dirty = false;\n\t\t\tthis.dirtyLineIds.delete(lineId);\n\t\t}\n\t}\n\n\t/**\n\t * Mark a line as dirty (needs re-evaluation) by its 1-based position.\n\t * Convenience for callers that have line numbers instead of line IDs.\n\t */\n\tmarkDirtyByLineNumber(lineNumber: number): void {\n\t\tconst state = this.getLineAt(lineNumber);\n\t\tif (state) {\n\t\t\tstate.dirty = true;\n\t\t\tthis.dirtyLineIds.add(state.lineId);\n\t\t}\n\t}\n\n\t/**\n\t * Mark a line as dirty (needs re-evaluation).\n\t */\n\tmarkDirty(lineId: number): void {\n\t\tconst state = this.lines.get(lineId);\n\t\tif (state) {\n\t\t\tstate.dirty = true;\n\t\t\tthis.dirtyLineIds.add(lineId);\n\t\t}\n\t}\n\n\t/**\n\t * Mark all lines as dirty (e.g., after plugin register/unregister).\n\t */\n\tinvalidateAll(): void {\n\t\tfor (const state of this.lines.values()) {\n\t\t\tstate.dirty = true;\n\t\t}\n\t\tthis.dirtyLineIds = new Set(this.lines.keys());\n\t}\n\n\t/**\n\t * Update a line's evaluation state after successful execution (Tier 1 / Tier 2).\n\t *\n\t * Sets results, bytecodes, reads, writes, and marks the line clean.\n\t * Supports multi-expression lines (inline solves) via parallel arrays.\n\t *\n\t * @param lineId - Persistent line identifier.\n\t * @param results - Evaluation result groups for each expression (in order). Each element is a Value[].\n\t * @param bytecodes - Compiled bytecode for each expression (in order).\n\t * @param expressions - Extracted expression strings (in order).\n\t * @param reads - Aggregated read variables across all expressions.\n\t * @param writes - Aggregated write variables across all expressions.\n\t * @param isVariableDef - True if any expression defines a variable.\n\t * @param inlineSolveCount - Number of inline solves (0 for full-line).\n\t */\n\tupdateLineResult(\n\t\tlineId: number,\n\t\tresults: Value[][],\n\t\tbytecodes: BytecodeProgram[],\n\t\texpressions: string[],\n\t\treads: string[],\n\t\twrites: string[],\n\t\tisVariableDef: boolean,\n\t\tinlineSolveCount: number = 0,\n\t): void {\n\t\tconst state = this.lines.get(lineId);\n\t\tif (!state) return;\n\t\tstate.results = results;\n\t\tstate.result = results[0]?.[0] ?? null;\n\t\tstate.bytecodes = bytecodes;\n\t\tstate.expressions = expressions;\n\t\tstate.reads = reads;\n\t\tstate.writes = writes;\n\t\tstate.isVariableDef = isVariableDef;\n\t\tstate.inlineSolveCount = inlineSolveCount;\n\t\tstate.dirty = false;\n\t\tthis.dirtyLineIds.delete(lineId);\n\t}\n\n\t/**\n\t * Update a line's compile-only state (Tier 3: background compilation).\n\t *\n\t * Stores expressions, bytecodes, reads, and writes. Does NOT set results\n\t * and does NOT mark the line clean, it still needs execution (Tier 1 or\n\t * Tier 2) to produce results. This distinction allows the three-tier\n\t * evaluation strategy: compile invisible lines in the background without\n\t * executing them, then execute from cached bytecode when scrolled into view.\n\t *\n\t * @param lineId - Persistent line identifier.\n\t * @param expressions - Extracted expression strings (in order).\n\t * @param bytecodes - Compiled bytecode for each expression (in order).\n\t * @param reads - Aggregated read variables across all expressions.\n\t * @param writes - Aggregated write variables across all expressions.\n\t * @param isVariableDef - True if any expression defines a variable.\n\t * @param inlineSolveCount - Number of inline solves (0 for full-line).\n\t */\n\tupdateLineCompiled(\n\t\tlineId: number,\n\t\texpressions: string[],\n\t\tbytecodes: BytecodeProgram[],\n\t\treads: string[],\n\t\twrites: string[],\n\t\tisVariableDef: boolean,\n\t\tinlineSolveCount: number = 0,\n\t): void {\n\t\tconst state = this.lines.get(lineId);\n\t\tif (!state) return;\n\t\tstate.expressions = expressions;\n\t\tstate.bytecodes = bytecodes;\n\t\tstate.reads = reads;\n\t\tstate.writes = writes;\n\t\tstate.isVariableDef = isVariableDef;\n\t\tstate.inlineSolveCount = inlineSolveCount;\n\t\t// NOTE: dirty remains unchanged, line still needs execution\n\t}\n\n\t// ── Properties ──────────────────────────────────────────────────────\n\n\tget lineCount(): number {\n\t\treturn this.orderTree.length;\n\t}\n\n\tget isEmpty(): boolean {\n\t\treturn this.orderTree.isEmpty;\n\t}\n\n\t/**\n\t * Iterator over LineState in document order.\n\t */\n\t*[Symbol.iterator](): IterableIterator<LineState> {\n\t\tfor (const lineId of this.orderTree) {\n\t\t\tconst state = this.lines.get(lineId);\n\t\t\tif (state) yield state;\n\t\t}\n\t}\n\n\t// ── Lifecycle ───────────────────────────────────────────────────────\n\n\tclear(): void {\n\t\tthis.lines.clear();\n\t\tthis.orderTree.clear();\n\t\tthis._positionCache = null;\n\t\tthis.nextLineId = 1;\n\t\tthis.tagIndex = null;\n\t}\n\n\t/**\n\t * Serialize the document model to a plain object for debugging.\n\t */\n\ttoJSON(): object {\n\t\treturn {\n\t\t\tlineCount: this.lineCount,\n\t\t\tlines: this.getAllLines().map((s) => ({\n\t\t\t\tlineId: s.lineId,\n\t\t\t\ttext: s.text.substring(0, 80), // truncate for readability\n\t\t\t\ttextHash: s.textHash,\n\t\t\t\tdirty: s.dirty,\n\t\t\t\tisVariableDef: s.isVariableDef,\n\t\t\t\tisEmpty: s.isEmpty,\n\t\t\t\thasBytecode: s.bytecodes.length > 0,\n\t\t\t\thasResult: s.results.length > 0,\n\t\t\t\tinlineSolveCount: s.inlineSolveCount,\n\t\t\t\treads: s.reads,\n\t\t\t\twrites: s.writes,\n\t\t\t})),\n\t\t};\n\t}\n}\n","/**\n * CompilationWorkerManager, main-thread bridge to the compilation worker.\n *\n * Manages the worker lifecycle and provides a batch compilation API.\n * Receives transferred ArrayBuffers from the worker and reconstructs\n * BytecodeProgram objects with zero-copy TypedArray views.\n *\n * Usage:\n * const manager = new CompilationWorkerManager();\n * const bytecodeByLineId = await manager.compileBatch(items, docModel);\n *\n * Thread safety:\n * Each result includes `compiledAgainstHash`. Before storing bytecode\n * in the DocumentModel, we validate via `docModel.isBytecodeValid()`.\n * If the line was edited between dispatch and response, the bytecode\n * is discarded, the next evaluate() will recompile it synchronously.\n */\n\nimport { DocumentModel } from \"@solve-js/engine/DocumentModel\";\nimport type { BytecodeProgram } from \"@solve-js/parser/BytecodeBuilder\";\nimport { engineWorkerFactory } from \"@solve-js/workers/WorkerFactory\";\n\n// ── Types ─────────────────────────────────────────────────────────────────\n\n/** One line sent to a worker for compilation. */\nexport interface CompileRequestItem {\n\tlineId: number;\n\texpression: string;\n\ttextHash: number;\n}\n\n/** Compiled bytecode returned from a worker, or the error that stopped it. */\nexport interface CompileResponseItem {\n\tlineId: number;\n\t/** Hash of the expression text when the compile was dispatched (for safety validation). */\n\tcompiledAgainstHash: number;\n\tprogram: BytecodeProgram;\n\treads: string[];\n\twrites: string[];\n\tisVariableDef: boolean;\n\terror: string | null;\n}\n\n// Raw result from worker (before BytecodeProgram reconstruction)\ninterface WorkerCompileResult {\n\tlineId: number;\n\tcompiledAgainstHash: number;\n\topcodesBuffer: ArrayBuffer;\n\tnumbersBuffer: ArrayBuffer;\n\topcodesLength: number;\n\tnumbersLength: number;\n\tstrings: string[];\n\treads: string[];\n\twrites: string[];\n\tisVariableDef: boolean;\n\terror: string | null;\n}\n\n// ── CompilationWorkerManager ──────────────────────────────────────────────\n\n/**\n * Compiles lines on a background worker.\n *\n * A response is discarded when the line changed between dispatch and return,\n * because bytecode compiled against stale text would produce a result for an\n * expression the user has already edited. The next evaluation recompiles it\n * synchronously.\n */\nexport class CompilationWorkerManager {\n\tprivate worker: Worker | null = null;\n\tprivate nextId = 1;\n\tprivate pending = new Map<\n\t\tnumber,\n\t\t{\n\t\t\tresolve: (results: CompileResponseItem[]) => void;\n\t\t\treject: (error: Error) => void;\n\t\t}\n\t>();\n\n\tconstructor() {}\n\n\t/**\n\t * Ensure the worker is started (lazy initialization).\n\t *\n\t * Starts it from the factory a host registered with\n\t * `setEngineWorkerFactory`. With no factory there is no worker, which is\n\t * the ordinary case: the throw travels as a rejected promise out of\n\t * `compileBatch` (it is `async`), and its callers already treat that as\n\t * \"compile this line on the main thread instead\". This is the same contract\n\t * the throwing worker stub had before the factory replaced it.\n\t */\n\tprivate ensureWorker(): Worker {\n\t\tif (this.worker) return this.worker;\n\n\t\tconst factory = engineWorkerFactory();\n\t\tif (factory === null) {\n\t\t\tthrow new Error(\"no engine worker factory registered, so compilation stays on the main thread\");\n\t\t}\n\t\tthis.worker = factory();\n\n\t\tthis.worker.onmessage = (event: MessageEvent) => {\n\t\t\tconst data = event.data;\n\t\t\tif (!data || data.type !== \"COMPILE_RESULT\") return;\n\n\t\t\tconst pending = this.pending.get(data.id);\n\t\t\tif (!pending) return;\n\n\t\t\tthis.pending.delete(data.id);\n\n\t\t\tconst results: CompileResponseItem[] = [];\n\t\t\tfor (const raw of data.results as WorkerCompileResult[]) {\n\t\t\t\tresults.push(this.reconstructResult(raw));\n\t\t\t}\n\t\t\tpending.resolve(results);\n\t\t};\n\n\t\tthis.worker.onerror = (err) => {\n\t\t\t// Reject all pending requests on worker error\n\t\t\tfor (const [id, pending] of this.pending) {\n\t\t\t\tpending.reject(\n\t\t\t\t\tnew Error(`Compilation worker error: ${err.message}`)\n\t\t\t\t);\n\t\t\t\tthis.pending.delete(id);\n\t\t\t}\n\t\t};\n\n\t\treturn this.worker;\n\t}\n\n\t/**\n\t * Batch-compile expressions in the worker.\n\t *\n\t * Sends a batch of { lineId, expression, textHash } to the worker,\n\t * which compiles each and transfers bytecode ArrayBuffers back.\n\t * Bytecode is reconstructed into BytecodeProgram objects.\n\t *\n\t * Results are returned in the same order as the input items.\n\t *\n\t * @param items Expressions to compile.\n\t * @returns Compiled bytecode for each item (or error).\n\t */\n\tasync compileBatch(items: CompileRequestItem[]): Promise<CompileResponseItem[]> {\n\t\tif (items.length === 0) return [];\n\n\t\tconst worker = this.ensureWorker();\n\t\tconst id = this.nextId++;\n\n\t\treturn new Promise<CompileResponseItem[]>((resolve, reject) => {\n\t\t\tthis.pending.set(id, { resolve, reject });\n\n\t\t\tworker.postMessage({\n\t\t\t\ttype: \"COMPILE_BATCH\",\n\t\t\t\tid,\n\t\t\t\titems,\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Store worker-compiled bytecode into the DocumentModel, with safety\n\t * validation. Only stores bytecode for lines whose text has not changed\n\t * since the compilation request was dispatched.\n\t *\n\t * Results for the same lineId are batched, all bytecodes from\n\t * successful compilations are passed to updateLineCompiled in a single\n\t * call. This supports multi-expression lines (inline solves).\n\t *\n\t * @param results Compiled results from the worker.\n\t * @param doc The target document model.\n\t * @returns Number of results successfully stored (passed safety check).\n\t */\n\tstoreResults(\n\t\tresults: CompileResponseItem[],\n\t\tdoc: DocumentModel\n\t): number {\n\t\t// Batch results by lineId (multiple inline solves share the same lineId)\n\t\tconst byLineId = new Map<\n\t\t\tnumber,\n\t\t\t{\n\t\t\t\tbytecodes: BytecodeProgram[];\n\t\t\t\treads: Set<string>;\n\t\t\t\twrites: Set<string>;\n\t\t\t\tisVariableDef: boolean;\n\t\t\t\texpressions: string[];\n\t\t\t\ttextHash: number;\n\t\t\t}\n\t\t>();\n\t\tlet validCount = 0;\n\n\t\tfor (const result of results) {\n\t\t\tif (result.error) continue;\n\n\t\t\t// Safety check: has the line been edited since dispatch?\n\t\t\tconst state = doc.getLineById(result.lineId);\n\t\t\tif (!state) continue;\n\n\t\t\t// Validate that the line text hasn't changed since dispatch.\n\t\t\tif (state.textHash !== result.compiledAgainstHash) continue;\n\n\t\t\t// Batch by lineId\n\t\t\tlet batch = byLineId.get(result.lineId);\n\t\t\tif (!batch) {\n\t\t\t\tbatch = {\n\t\t\t\t\tbytecodes: [],\n\t\t\t\t\treads: new Set(),\n\t\t\t\t\twrites: new Set(),\n\t\t\t\t\tisVariableDef: false,\n\t\t\t\t\texpressions: state.expressions.length > 0 ? [...state.expressions] : [],\n\t\t\t\t\ttextHash: result.compiledAgainstHash,\n\t\t\t\t};\n\t\t\t\tbyLineId.set(result.lineId, batch);\n\t\t\t}\n\n\t\t\tbatch.bytecodes.push(result.program);\n\t\t\tfor (const r of result.reads) batch.reads.add(r);\n\t\t\tfor (const w of result.writes) batch.writes.add(w);\n\t\t\tif (result.isVariableDef) batch.isVariableDef = true;\n\t\t\tvalidCount++;\n\t\t}\n\n\t\t// Store batched results\n\t\tlet stored = 0;\n\t\tfor (const [lineId, batch] of byLineId) {\n\t\t\tif (batch.bytecodes.length === 0) continue;\n\t\t\t// Preserve inlineSolveCount from the LineState so the evaluator\n\t\t\t// knows this line has multiple expressions (avoids re-extraction).\n\t\t\tconst state = doc.getLineById(lineId);\n\t\t\tconst inlineSolveCount = state?.inlineSolveCount ?? 0;\n\t\t\tdoc.updateLineCompiled(\n\t\t\t\tlineId,\n\t\t\t\tbatch.expressions,\n\t\t\t\tbatch.bytecodes,\n\t\t\t\t[...batch.reads],\n\t\t\t\t[...batch.writes],\n\t\t\t\tbatch.isVariableDef,\n\t\t\t\tinlineSolveCount,\n\t\t\t);\n\t\t\tstored += batch.bytecodes.length;\n\t\t}\n\n\t\treturn stored;\n\t}\n\n\t/**\n\t * Terminate the worker and clean up.\n\t */\n\tterminate(): void {\n\t\tif (this.worker) {\n\t\t\tthis.worker.terminate();\n\t\t\tthis.worker = null;\n\t\t\tthis.pending.clear();\n\t\t}\n\t}\n\n\t/**\n\t * Whether the worker is currently active.\n\t */\n\tget isActive(): boolean {\n\t\treturn this.worker !== null;\n\t}\n\n\t// ── Private helpers ────────────────────────────────────────────────\n\n\t/**\n\t * Reconstruct a BytecodeProgram from transferred ArrayBuffers.\n\t *\n\t * The transferred ArrayBuffers are zero-copy, we create TypedArray\n\t * views directly over them. No data is copied or serialized.\n\t */\n\tprivate reconstructResult(raw: WorkerCompileResult): CompileResponseItem {\n\t\tif (raw.error) {\n\t\t\treturn {\n\t\t\t\tlineId: raw.lineId,\n\t\t\t\tcompiledAgainstHash: raw.compiledAgainstHash,\n\t\t\t\tprogram: { opcodes: new Uint8Array(0), numbers: new Float64Array(0), strings: [], hasAsync: false },\n\t\t\t\treads: [],\n\t\t\t\twrites: [],\n\t\t\t\tisVariableDef: false,\n\t\t\t\terror: raw.error,\n\t\t\t};\n\t\t}\n\n\t\t// Reconstruct TypedArrays from transferred ArrayBuffers.\n\t\t// These are views over the same memory, zero-copy from worker.\n\t\tconst opcodes =\n\t\t\traw.opcodesLength > 0\n\t\t\t\t? new Uint8Array(raw.opcodesBuffer, 0, raw.opcodesLength)\n\t\t\t\t: new Uint8Array(0);\n\t\tconst numbers =\n\t\t\traw.numbersLength > 0\n\t\t\t\t? new Float64Array(raw.numbersBuffer, 0, raw.numbersLength)\n\t\t\t\t: new Float64Array(0);\n\n\t\tconst program: BytecodeProgram = {\n\t\t\topcodes,\n\t\t\tnumbers,\n\t\t\tstrings: raw.strings,\n\t\t\thasAsync: false,\n\t\t};\n\n\t\treturn {\n\t\t\tlineId: raw.lineId,\n\t\t\tcompiledAgainstHash: raw.compiledAgainstHash,\n\t\t\tprogram,\n\t\t\treads: raw.reads,\n\t\t\twrites: raw.writes,\n\t\t\tisVariableDef: raw.isVariableDef,\n\t\t\terror: null,\n\t\t};\n\t}\n}\n","/**\n * PageManager, page-based LRU eviction + directional preloading.\n *\n * Groups document lines into fixed-size pages (128 lines each) and manages\n * three temperature tiers:\n * • Hot (viewport ± 3 pages): Keep bytecode + results\n * • Warm (viewport ± 6 pages): Keep bytecode, evict results\n * • Cold (beyond ± 6 pages): Evict bytecode + results (except variable defs)\n *\n * Variable definition bytecode is **never evicted** because it forms the\n * backbone of the dependency graph and VM checkpoints.\n *\n * Preloading: When the user scrolls in a consistent direction, the next\n * 1–2 pages are pre-compiled via the background worker so bytecode is\n * cache-hot by the time those lines scroll into view.\n *\n * Usage:\n * const pm = new PageManager();\n * // After evaluation:\n * pm.maintainAfterEval(viewport, doc);\n * // After scroll (viewport-only change):\n * pm.maintainAfterEval(viewport, doc);\n * const targets = pm.getPreloadTargets(viewport, doc);\n * // dispatch targets to compilation worker\n */\n\nimport { DocumentModel } from \"@solve-js/engine/DocumentModel\";\n// Deliberately the shared lexer, not an engine's own.\n//\n// `classifyLine` and `findInlineSolves` are character-level scans for headings,\n// comment markers, code fences and backtick spans. Neither consults the\n// keyword, unit or operator tables, so every lexer returns the same answer and\n// there is nothing per-engine to respect. Pinned by\n// __tests__/lexer/LineClassificationIsVocabularyIndependent.spec.ts, which\n// compares a bare lexer against one carrying extra vocabulary.\n//\n// This matters because these are free functions with no engine to ask. Making\n// them engine-aware would mean changing their signatures and every caller for\n// no behavioural difference.\nimport { sharedLexer } from \"@solve-js/lexer/Lexer\";\n\n// ── Constants ────────────────────────────────────────────────────────────\n\n/** Number of lines per page. Tunable, 128 balances granularity with overhead. */\nexport const PAGE_SIZE = 128;\n\n/** Number of pages on each side of the viewport considered \"hot.\" */\nconst HOT_PAGE_RADIUS = 3;\n\n/** Number of pages on each side of the viewport considered \"warm\" (includes hot). */\nconst WARM_PAGE_RADIUS = 6;\n\n/** Number of pages to preload ahead of the scroll direction. */\nconst PRELOAD_PAGE_COUNT = 2;\n\n/**\n * Pages just beyond the warm range that are \"newly cold\", they were warm\n * on the previous scroll and now need bytecode eviction. Once evicted, they\n * stay cold and don't need re-eviction on subsequent frames (the inner\n * `evictPageBytecode` checks for non-null bytecode and short-circuits).\n *\n * This buffer lets us skip iterating the vast majority of cold pages on\n * every scroll, reducing `maintainAfterEval` from O(total pages) to\n * O(hot + warm + buffer) ≈ O(1) for any document size.\n */\nconst COLD_EVICT_BUFFER = HOT_PAGE_RADIUS;\n\n// ── PageManager ──────────────────────────────────────────────────────────\n\n/**\n * Splits a long document into pages so only what is near the viewport is held\n * in full.\n *\n * A document is edited constantly and most of it is off screen. Paging keeps\n * the cost of a keystroke proportional to what is visible rather than to the\n * length of the document.\n */\nexport class PageManager {\n\t/** Previous viewport for scroll direction detection. null = no previous viewport. */\n\tprivate lastViewportStart: number | null = null;\n\n\t/** Saved scroll direction from the most recent maintainAfterEval call. */\n\tprivate savedDirection: \"down\" | \"up\" | null = null;\n\n\t/** Per-page access counter for LRU tracking. */\n\tprivate pageAccess: Map<number, number> = new Map();\n\n\t/** Monotonically increasing access sequence number. */\n\tprivate accessSeq = 0;\n\n\t// ── Static helpers ─────────────────────────────────────────────────\n\n\t/** Get the page number for a 1-based line number (0-based page index). */\n\tstatic pageForLine(lineNumber: number): number {\n\t\treturn Math.floor((lineNumber - 1) / PAGE_SIZE);\n\t}\n\n\t/** Get the 1-based inclusive line range for a page. */\n\tstatic pageRange(\n\t\tpageNum: number,\n\t\tdocLineCount: number\n\t): { startLine: number; endLine: number } {\n\t\treturn {\n\t\t\tstartLine: pageNum * PAGE_SIZE + 1,\n\t\t\tendLine: Math.min((pageNum + 1) * PAGE_SIZE, docLineCount),\n\t\t};\n\t}\n\n\t// ── Public API ─────────────────────────────────────────────────────\n\n\t/**\n\t * Detect scroll direction from viewport movement.\n\t * Returns null on first call (no previous viewport) or no movement.\n\t */\n\tdetectDirection(newViewport: { startLine: number }): \"down\" | \"up\" | null {\n\t\tif (this.lastViewportStart === null) return null;\n\t\tif (newViewport.startLine > this.lastViewportStart) return \"down\";\n\t\tif (newViewport.startLine < this.lastViewportStart) return \"up\";\n\t\treturn null;\n\t}\n\n\t/**\n\t * Maintain page tiers after evaluation.\n\t *\n\t * 1. Records the new viewport position\n\t * 2. Touches hot pages (viewport ± HOT_PAGE_RADIUS) for LRU\n\t * 3. Evicts cold/warm pages based on distance from viewport\n\t *\n\t * Call this after every `evaluate()`, `setViewport()`, or `evaluateAll()`.\n\t */\n\tmaintainAfterEval(\n\t\tviewport: { startLine: number; endLine: number },\n\t\tdoc: DocumentModel\n\t): void {\n\t\t// ── Capture direction BEFORE updating lastViewportStart ───\n\t\tthis.savedDirection = this.detectDirection(viewport);\n\t\tthis.lastViewportStart = viewport.startLine;\n\n\t\tconst docLineCount = doc.lineCount;\n\t\tconst viewportStartPage = PageManager.pageForLine(viewport.startLine);\n\t\tconst viewportEndPage = PageManager.pageForLine(viewport.endLine);\n\t\tconst lastPage = PageManager.pageForLine(docLineCount);\n\n\t\t// Compute hot/warm page ranges in O(1) using range math:\n\t\t// A page is within RADIUS of the viewport span iff it falls in\n\t\t// [viewportStartPage - RADIUS, viewportEndPage + RADIUS].\n\t\tconst hotStart = Math.max(0, viewportStartPage - HOT_PAGE_RADIUS);\n\t\tconst hotEnd = Math.min(lastPage, viewportEndPage + HOT_PAGE_RADIUS);\n\t\tconst warmStart = Math.max(0, viewportStartPage - WARM_PAGE_RADIUS);\n\t\tconst warmEnd = Math.min(lastPage, viewportEndPage + WARM_PAGE_RADIUS);\n\n\t\t// Touch hot pages to keep them fresh in LRU\n\t\tfor (let p = hotStart; p <= hotEnd; p++) {\n\t\t\tthis.touchPage(p);\n\t\t}\n\n\t\t// Evict warm-but-not-hot pages: keep bytecode, evict results only\n\t\tfor (let p = warmStart; p <= warmEnd; p++) {\n\t\t\tif (p >= hotStart && p <= hotEnd) continue;\n\t\t\tthis.evictPageResults(p, doc, docLineCount);\n\t\t}\n\n\t\t// Evict \"newly cold\" pages: a buffer zone just outside the warm\n\t\t// range. Pages beyond this buffer were already evicted on prior\n\t\t// scrolls and don't need re-eviction.\n\t\t// ── Left side (just above page 0, just below warm range) ──\n\t\tconst coldLeftEnd = warmStart - 1;\n\t\tconst coldLeftStart = Math.max(0, coldLeftEnd - COLD_EVICT_BUFFER + 1);\n\t\tfor (let p = coldLeftStart; p <= coldLeftEnd; p++) {\n\t\t\tthis.evictPageBytecode(p, doc, docLineCount);\n\t\t}\n\t\t// ── Right side (just above warm range, just below lastPage) ──\n\t\tconst coldRightStart = warmEnd + 1;\n\t\tconst coldRightEnd = Math.min(lastPage, coldRightStart + COLD_EVICT_BUFFER - 1);\n\t\tfor (let p = coldRightStart; p <= coldRightEnd; p++) {\n\t\t\tthis.evictPageBytecode(p, doc, docLineCount);\n\t\t}\n\t}\n\n\t/**\n\t * Get lines ahead of the viewport that should be pre-compiled.\n\t *\n\t * Looks PRELOAD_PAGE_COUNT pages ahead of the current scroll direction.\n\t * Only returns lines that are dirty, don't already have bytecode,\n\t * and are not empty/markdown-only.\n\t *\n\t * @param viewport The current viewport.\n\t * @param doc The document model.\n\t * @returns Items ready to send to the compilation worker.\n\t */\n\tgetPreloadTargets(\n\t\tviewport: { startLine: number; endLine: number },\n\t\tdoc: DocumentModel\n\t): Array<{ lineId: number; expression: string; textHash: number }> {\n\t\t// Use the direction saved by the last maintainAfterEval call.\n\t\t// We cannot re-detect because maintainAfterEval already updated\n\t\t// lastViewportStart to the current viewport position.\n\t\tconst direction = this.savedDirection;\n\t\tif (!direction) return [];\n\n\t\tconst items: Array<{ lineId: number; expression: string; textHash: number }> = [];\n\t\t// Use viewport end page for downward preload start, start page for upward.\n\t\t// This ensures we preload *beyond* the full viewport span, not just its start.\n\t\tconst viewportEndPage = PageManager.pageForLine(viewport.endLine);\n\t\tconst viewportStartPage = PageManager.pageForLine(viewport.startLine);\n\t\tconst lastPage = PageManager.pageForLine(doc.lineCount);\n\n\t\tconst pages: number[] = [];\n\t\tif (direction === \"down\") {\n\t\t\t// Preload pages immediately after the viewport's hot radius beyond its end\n\t\t\tconst startPage = viewportEndPage + HOT_PAGE_RADIUS + 1;\n\t\t\tconst endPage = Math.min(startPage + PRELOAD_PAGE_COUNT - 1, lastPage);\n\t\t\tfor (let p = startPage; p <= endPage && p <= lastPage; p++) {\n\t\t\t\tpages.push(p);\n\t\t\t}\n\t\t} else {\n\t\t\t// Preload pages immediately before the viewport's hot radius before its start\n\t\t\tconst firstPreloadPage = viewportStartPage - HOT_PAGE_RADIUS - 1;\n\t\t\tfor (let p = firstPreloadPage; p > firstPreloadPage - PRELOAD_PAGE_COUNT && p >= 0; p--) {\n\t\t\t\tpages.push(p);\n\t\t\t}\n\t\t}\n\n\t\tfor (const pageNum of pages) {\n\t\t\tconst range = PageManager.pageRange(pageNum, doc.lineCount);\n\t\t\tfor (let pos = range.startLine; pos <= range.endLine; pos++) {\n\t\t\t\tconst state = doc.getLineAt(pos);\n\t\t\t\tif (!state) continue;\n\t\t\t\tif (!state.dirty) continue;\n\t\t\t\tif (state.bytecodes.length > 0 && !state.isVariableDef) continue;\n\t\t\t\tif (state.isEmpty) continue;\n\n\t\t\t\t// Extract expressions using the shared lexer to handle inline\n\t\t\t\t// solves (s`...`). For full-line expressions, this returns [text].\n\t\t\t\t// For inline solve lines, this returns each extracted expression.\n\t\t\t\tif (state.expressions.length > 0) {\n\t\t\t\t\t// Use pre-extracted expressions (from prior evaluation)\n\t\t\t\t\tfor (const expression of state.expressions) {\n\t\t\t\t\t\tif (!expression.trim()) continue;\n\t\t\t\t\t\titems.push({\n\t\t\t\t\t\t\tlineId: state.lineId,\n\t\t\t\t\t\t\texpression,\n\t\t\t\t\t\t\ttextHash: state.textHash,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Expressions not yet extracted, use lexer to find inline solves\n\t\t\t\t\tconst inlineSpans = sharedLexer.findInlineSolves(state.text);\n\t\t\t\t\tif (inlineSpans.length > 0) {\n\t\t\t\t\t\tfor (const span of inlineSpans) {\n\t\t\t\t\t\t\tif (!span.expression.trim()) continue;\n\t\t\t\t\t\t\titems.push({\n\t\t\t\t\t\t\t\tlineId: state.lineId,\n\t\t\t\t\t\t\t\texpression: span.expression,\n\t\t\t\t\t\t\t\ttextHash: state.textHash,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Full-line expression\n\t\t\t\t\t\tconst expression = state.text.trim();\n\t\t\t\t\t\tif (expression) {\n\t\t\t\t\t\t\titems.push({\n\t\t\t\t\t\t\t\tlineId: state.lineId,\n\t\t\t\t\t\t\t\texpression,\n\t\t\t\t\t\t\t\ttextHash: state.textHash,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn items;\n\t}\n\n\t/**\n\t * Reset internal state (e.g., after document switch).\n\t */\n\tclear(): void {\n\t\tthis.pageAccess.clear();\n\t\tthis.accessSeq = 0;\n\t\tthis.lastViewportStart = null;\n\t\tthis.savedDirection = null;\n\t}\n\n\t// ── Private helpers ─────────────────────────────────────────────────\n\n\t/** Record a page access with a monotonic sequence number. */\n\tprivate touchPage(pageNum: number): void {\n\t\tthis.pageAccess.set(pageNum, ++this.accessSeq);\n\t}\n\n\t/**\n\t * Evict results from all non-variable-def lines in a page.\n\t * Bytecode is preserved so Tier 2 execution works on scroll-back.\n\t */\n\tprivate evictPageResults(\n\t\tpageNum: number,\n\t\tdoc: DocumentModel,\n\t\tdocLineCount: number\n\t): void {\n\t\tconst range = PageManager.pageRange(pageNum, docLineCount);\n\t\tfor (let pos = range.startLine; pos <= range.endLine; pos++) {\n\t\t\tconst state = doc.getLineAt(pos);\n\t\t\tif (state && !state.isVariableDef && state.results.length > 0) {\n\t\t\t\tstate.results = [];\n\t\t\t\tstate.result = null;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Evict bytecode + results from all non-variable-def lines in a page.\n\t * Variable definition bytecode is **never** evicted, it forms the\n\t * backbone of the DAG and VM checkpoints.\n\t *\n\t * Evicted lines are marked dirty so they get Tier 1 re-evaluation\n\t * when scrolled back into view.\n\t */\n\tprivate evictPageBytecode(\n\t\tpageNum: number,\n\t\tdoc: DocumentModel,\n\t\tdocLineCount: number\n\t): void {\n\t\tconst range = PageManager.pageRange(pageNum, docLineCount);\n\t\tfor (let pos = range.startLine; pos <= range.endLine; pos++) {\n\t\t\tconst state = doc.getLineAt(pos);\n\t\t\tif (!state || state.isVariableDef) continue;\n\n\t\t\tif (state.bytecodes.length > 0 || state.results.length > 0) {\n\t\t\t\tstate.bytecodes = [];\n\t\t\t\tstate.results = [];\n\t\t\t\tstate.result = null;\n\t\t\t\tdoc.markDirty(state.lineId);\n\t\t\t}\n\t\t}\n\t}\n}\n","import { ExpressionEngine } from \"@solve-js/engine/ExpressionEngine\";\nimport {\n\tDocumentModel,\n\tLineChange,\n\tLineState,\n\tViewportRange,\n} from \"@solve-js/engine/DocumentModel\";\nimport { Value, ValueType, enableValueArena, disableValueArena, errorValue, persistentValue, isArenaActive } from \"@solve-js/vm/Value\";\nimport { DependencyGraph, isPrefixedEdgeKey } from \"@solve-js/vm/DependencyGraph\";\nimport { VMCheckpointer } from \"@solve-js/vm/VMCheckpoints\";\nimport { isEmptyLine } from \"@solve-js/engine/ExpressionEngineSafety\";\n// Deliberately the shared lexer, not an engine's own.\n//\n// `classifyLine` and `findInlineSolves` are character-level scans for headings,\n// comment markers, code fences and backtick spans. Neither consults the\n// keyword, unit or operator tables, so every lexer returns the same answer and\n// there is nothing per-engine to respect. Pinned by\n// __tests__/lexer/LineClassificationIsVocabularyIndependent.spec.ts, which\n// compares a bare lexer against one carrying extra vocabulary.\n//\n// This matters because these are free functions with no engine to ask. Making\n// them engine-aware would mean changing their signatures and every caller for\n// no behavioural difference.\nimport { sharedLexer } from \"@solve-js/lexer/Lexer\";\nimport { CompilationWorkerManager, type CompileRequestItem } from \"@solve-js/engine/CompilationWorkerManager\";\nimport { PageManager } from \"@solve-js/engine/PageManager\";\nimport type { BytecodeProgram } from \"@solve-js/parser/BytecodeBuilder\";\nimport { EngineError } from \"@solve-js/errors/UnifiedErrorFramework\";\nimport { withTagEdges } from \"@solve-js/packages/tags/TagScanner\";\nimport { sharedGlobalVariableStore, globalDagKey } from \"@solve-js/vm/GlobalVariableStore\";\n\n// ── EvalTier (diagnostic enum) ──────────────────────────────────────────\n\n/**\n * How much work a line's evaluation required.\n *\n * The tiers exist because most lines in a document have not changed. A line\n * that is clean and cached costs far less than one being compiled fresh, and\n * knowing which happened is how a slow document gets diagnosed.\n */\nexport enum EvalTier {\n\t/** Full pipeline: Lex → Parse → Compile → Execute (visible + dirty). */\n\tTier1 = 1,\n\t/** Execute-only from cached bytecode (visible + cached). */\n\tTier2 = 2,\n\t/** Compile-only for dependency tracking (invisible). Executes only variable assignments. */\n\tTier3 = 3,\n\t/** Skipped, already clean or non-evaluable. */\n\tSkipped = 0,\n}\n\n// ── EvalLineResult ──────────────────────────────────────────────────────\n\n/** Outcome for one line, including which tier handled it. */\nexport interface EvalLineResult {\n\t/** The line's persistent ID from DocumentModel. */\n\tlineId: number;\n\t/** 1-based line position. */\n\tlineNumber: number;\n\t/** Which tier was used. */\n\ttier: EvalTier;\n\t/** The first evaluation result, or null on error / non-evaluable. */\n\tresult: Value | null;\n\t/** All result groups (one per expression/inline-solve), or undefined if skipped. */\n\tresults?: Value[][];\n\t/** Error message, or null. */\n\terror: string | null;\n}\n\n// ── EvalResult ──────────────────────────────────────────────────────────\n\n/** Outcome for a whole evaluation pass, with per-tier counts. */\nexport interface EvalResult {\n\t/** Per-line evaluation results. */\n\tlines: EvalLineResult[];\n\t/** Map of line numbers → flattened results for quick lookup. */\n\tresultMap: Map<number, Value[]>;\n\t/** Number of lines processed at each tier. */\n\ttierCounts: { tier1: number; tier2: number; tier3: number; skipped: number };\n}\n\n// ── ThreeTierEvaluator ──────────────────────────────────────────────────\n\n/**\n * Whether a definition's right-hand side reads the name it defines.\n *\n * `extractReadsAndWrites` records every `:name = ...` as reading `name` once,\n * a convention the graph relies on, so one occurrence says nothing. A second\n * is the right-hand side.\n */\n/**\n * The names these programs define as functions, from the bodies compiled\n * alongside them.\n *\n * The checkpoint chain keeps a variable and a function of the same name in\n * separate bags and asks which bag a written name goes in. The VM cannot\n * say: once `f(x) = x + 1` is defined above, it holds a function named `f`\n * whether the line that just ran defined it or set the variable `f`. The\n * bytecode can.\n */\nfunction functionsDefinedBy(bytecodes: readonly BytecodeProgram[]): Set<string> {\n\tconst names = new Set<string>();\n\tfor (const program of bytecodes) {\n\t\tif (program.userFunctionBodies === undefined) continue;\n\t\tfor (const body of program.userFunctionBodies) names.add(body.name);\n\t}\n\treturn names;\n}\n\nfunction readsItself(reads: readonly string[], name: string): boolean {\n\tlet seen = 0;\n\tfor (const read of reads) if (read === name && ++seen === 2) return true;\n\treturn false;\n}\n\n/**\n * Orchestrates three-tier evaluation over a persistent DocumentModel.\n *\n * ── Tier assignment ─────────────────────────────────────────────────\n * | Tier | Condition | Action |\n * |───────|────────────────────────────────────|───────────────────────────────|\n * | **1** | Visible + Dirty (new/changed) | Full pipeline: lex→parse→compile→execute |\n * | **2** | Visible + Cached (scroll into view)| Execute from cached bytecode |\n * | **3** | Invisible + Dirty | Compile-only; execute only variable defs |\n * | Skip | Clean, empty, or non-evaluable | No action |\n *\n * ── Evaluation order ─────────────────────────────────────────────────\n * Lines are always processed in ascending document order (line 1 → end)\n * so that variable assignments flow correctly through the shared VM.\n * Tier 2 relies on this: by the time a clean cached line is reached,\n * the VM already contains all variables from preceding Tier-1 lines.\n *\n * ── Thread safety ────────────────────────────────────────────────────\n * Tier 1 (visible+dirty) compilation runs synchronously on the main thread\n * for immediate rendering. Tier 3 (invisible+dirty) compilation can be\n * dispatched to a Web Worker via `dispatchBackgroundCompiles()`. Worker-\n * compiled bytecode is stored in the DocumentModel and validated via\n * `isBytecodeValid()` to ensure the line text hasn't changed between\n * dispatch and response.\n */\nexport class ThreeTierEvaluator {\n\tprivate doc: DocumentModel;\n\tprivate engine: ExpressionEngine;\n\tprivate dag: DependencyGraph;\n\tprivate checkpointer: VMCheckpointer | null;\n\tprivate compilationWorker: CompilationWorkerManager | null = null;\n\tprivate pageManager: PageManager;\n\n\t/**\n\t * The text hash each line had when the graph last recorded what it reads,\n\t * by line id.\n\t *\n\t * A positional edge is recorded at run time and pinned, so it outlives the\n\t * text that produced it until the line runs again. Between an edit and\n\t * that run the graph still describes the old text, and a line edited out\n\t * of the viewport is compiled there without running, so the window can be\n\t * as long as the reader likes. The document model records the edit as a\n\t * new hash, and this is the hash the graph's edges for the line describe:\n\t * a line arriving at the pass with a different one has its positions\n\t * forgotten before anything else happens to it. See\n\t * {@link forgetPositionsOfEditedText}.\n\t */\n\tprivate textHashOfRecordedEdges: Map<number, number> = new Map();\n\n\t/**\n\t * The names each line genuinely reads among those it writes, by line id.\n\t *\n\t * `:v3 = v3 + 3` reads `v3` on its right-hand side; `:v3 = 44` does not,\n\t * although the graph records both as readers of `v3` by convention. The\n\t * difference is visible only in the per-expression extraction, where the\n\t * name appears twice, and only while Tier 1 has that in hand; the stored\n\t * read set is deduplicated. Recorded there, read by Tier 2, which puts such\n\t * a name back to the prefix before the line runs. Keyed by id so a\n\t * structural edit does not lose it.\n\t */\n\tprivate selfReadingWrites: Map<number, string[]> = new Map();\n\n\t/**\n\t * The lines that sit on a cycle, by line id. See {@link settleCycles}.\n\t */\n\tprivate cycleMemberIds: Set<number> = new Set();\n\t/**\n\t * The lowest position a structural edit moved, awaiting the next pass, or\n\t * `MAX_SAFE_INTEGER` when nothing is pending.\n\t *\n\t * A structural edit shifts every line from here down to a new position, and\n\t * the answer such a line still holds was computed for where it used to be.\n\t * The next pass knows the viewport, so it can clear exactly the stale ones,\n\t * the moved lines below the range it runs; see\n\t * {@link forgetMovedBelowViewport}. Recorded rather than acted on at edit\n\t * time because a moved line ABOVE the range is one a fresh pass still shows.\n\t */\n\tprivate movedFloor = Number.MAX_SAFE_INTEGER;\n\n\t/**\n\t * Unsubscribe from sharedGlobalVariableStore, set in the constructor\n\t * called from terminateWorker(). See the subscription itself below for\n\t * why this only marks lines dirty and never re-evaluates synchronously.\n\t */\n\tprivate globalUnsubscribe: (() => void) | null = null;\n\n\t/**\n\t * @param doc The persistent document model.\n\t * @param engine The expression engine (shared VM is accessed via engine.getVM()).\n\t * @param checkpointer Optional VM state checkpointer. If provided, the evaluator\n\t * will create checkpoints after variable-definition lines and support fast VM\n\t * restoration via `restoreTo()`. If omitted, checkpointing is disabled.\n\t */\n\tconstructor(\n\t\tdoc: DocumentModel,\n\t\tengine: ExpressionEngine,\n\t\tcheckpointer?: VMCheckpointer\n\t) {\n\t\tthis.doc = doc;\n\t\tthis.engine = engine;\n\t\tthis.dag = engine.getDag();\n\t\t// Built by default, because without one the engine answers wrong.\n\t\t//\n\t\t// A name written on more than one line has a value per position, and the\n\t\t// VM holds whichever write ran last. Anything re-running part of a\n\t\t// document therefore reads the state the document ENDS in unless this\n\t\t// rebuilds the state that position actually has. `setViewport` has always\n\t\t// asked for that (see {@link restoreTo}) and got nothing, so scrolling to\n\t\t// a line above a redefinition changed its answer: `:x = 1` / `x + 100`\n\t\t// with `:x = 99` below it showed 101, and 199 once scrolled to.\n\t\t//\n\t\t// It was an optional argument, which meant every host that did not know\n\t\t// to pass one got that. A caller may still supply its own, to share a\n\t\t// chain or to inspect it.\n\t\tthis.checkpointer = checkpointer ?? new VMCheckpointer(engine.getVM());\n\t\tthis.pageManager = new PageManager();\n\n\t\t// The async batcher re-runs a handful of lines out of the document when\n\t\t// a value arrives, against the same VM. Without the chain it reads what\n\t\t// the last full pass left, which is the state at the END of the\n\t\t// document, and a name written on more than one line is then wrong\n\t\t// everywhere below its second definition. Sharing the chain lets it\n\t\t// rebuild the state each line actually sits in. See\n\t\t// `AsyncResolutionBatcher.checkpointer`.\n\t\tthis.engine.getBatcher().checkpointer = this.checkpointer;\n\n\t\t// Lets the engine answer \"what's line N's cached result\" for\n\t\t// cross-line features (prev/line<N>/aggregation) without owning\n\t\t// document lifecycle itself. See ExpressionEngine.makeLineContext().\n\t\tthis.engine.setDocumentModel(this.doc);\n\n\t\t// ── Cross-document global-variable propagation ──────────────────\n\t\t// GlobalVariableAsyncResolver (via preflight) handles a line's FIRST\n\t\t// resolution when a global it reads wasn't known yet. This handles\n\t\t// the ONGOING case: a line that already has a real (non-pending)\n\t\t// value for `global :x` needs to go dirty again when some OTHER\n\t\t// document writes a NEW value to x, so this document's next\n\t\t// evaluate() picks up the change, \"dealing with the DAG across\n\t\t// pages\", not just first-resolution.\n\t\tthis.globalUnsubscribe = sharedGlobalVariableStore.subscribe((name) => {\n\t\t\t// Mark-dirty ONLY, never synchronously re-evaluate here.\n\t\t\t// enableValueArena/disableValueArena (Value.ts) is a single\n\t\t\t// non-reentrant module-level flag; evaluate()/setViewport() both\n\t\t\t// wrap their body in it, so a re-entrant evaluate() call from\n\t\t\t// inside this callback (itself possibly firing from INSIDE\n\t\t\t// another evaluate() call, via a STORE_GLOBAL_VAR opcode in some\n\t\t\t// other document being evaluated concurrently) would disable the\n\t\t\t// arena out from under the still-running outer call. This\n\t\t\t// mirrors applyTransaction()'s existing contract exactly: mark\n\t\t\t// dirty, let the caller's own evaluate() cadence pick it up.\n\t\t\tfor (const lineNumber of this.dag.getAffectedLines(globalDagKey(name))) {\n\t\t\t\tthis.doc.markDirtyByLineNumber(lineNumber);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Evaluate all lines needed to render the given viewport.\n\t *\n\t * Processes lines from 1 to `viewport.endLine` in document order.\n\t * Dirty lines in the viewport get Tier-1 full pipeline; clean cached\n\t * lines get Tier-2 bytecode execution. Lines after the viewport\n\t * get Tier-3 compile-only (with variable-def execution).\n\t *\n\t * @returns Results for all processed lines, including tier metadata.\n\t */\n\tevaluate(viewport: ViewportRange, signal?: AbortSignal): EvalResult {\n\t\t// ── One AbortController Per Keystroke ────────────────────────\n\t\t// Link the UI layer's keystroke signal to the engine so that\n\t\t// all per-evaluation AbortControllers created during this call\n\t\t// are canceled when the user types a new keystroke.\n\t\tthis.engine.setKeystrokeSignal(signal ?? null);\n\n\t\t// ── Phase 5.3: Enable arena for zero-allocation Value reuse ──\n\t\tenableValueArena();\n\t\ttry {\n\t\t\t// Re-seed running-total accumulators before the from-line-1 pass. A\n\t\t\t// `total += 5` line reads its own running value, so re-running it in\n\t\t\t// place would read the previous evaluation's total and double-count.\n\t\t\t// Reset each accumulator variable and mark every line that touches\n\t\t\t// it dirty, so the loop below recomputes the total from its seed,\n\t\t\t// top to bottom. (No-op when the document has no accumulators.)\n\t\t\tthis.reseedAccumulators();\n\n\t\t\t// The units in scope before the pass, so one that disappears during\n\t\t\t// it can be noticed.\n\t\t\tconst unitsBefore = this.engine.userUnitNames();\n\n\t\t\tconst lines: EvalLineResult[] = [];\n\t\t\tconst resultMap = new Map<number, Value[]>();\n\t\t\tconst tierCounts = { tier1: 0, tier2: 0, tier3: 0, skipped: 0 };\n\n\t\t\t// Process from line 1 to the end of the viewport for correct VM state.\n\t\t\t// We go to viewport.endLine because Tier 3 for invisible lines can be\n\t\t\t// done separately via backgroundCompile().\n\t\t\tconst docEnd = this.doc.lineCount;\n\t\t\tconst evalEnd = Math.min(viewport.endLine, docEnd);\n\t\t\tthis.forgetMovedBelowViewport(evalEnd);\n\n\t\t\t// Walked once, rather than descended into per position. See\n\t\t\t// `DocumentModel.getLineStatesInRange`.\n\t\t\tconst states = this.doc.getLineStatesInRange(1, evalEnd);\n\n\t\t\tfor (let pos = 1; pos <= evalEnd; pos++) {\n\t\t\t\tconst state = states[pos - 1];\n\t\t\t\tif (!state) {\n\t\t\t\t\ttierCounts.skipped++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst inViewport = pos >= viewport.startLine && pos <= viewport.endLine;\n\n\t\t\t\tconst lineResult = this.evaluateSingleLine(state, pos, inViewport);\n\t\t\t\tlines.push(lineResult);\n\n\t\t\t\tif (lineResult.tier === EvalTier.Tier1) tierCounts.tier1++;\n\t\t\t\telse if (lineResult.tier === EvalTier.Tier2) tierCounts.tier2++;\n\t\t\t\telse if (lineResult.tier === EvalTier.Tier3) tierCounts.tier3++;\n\t\t\t\telse tierCounts.skipped++;\n\n\t\t\t\tif (lineResult.results && inViewport) {\n\t\t\t\t\tresultMap.set(pos, lineResult.results.flat());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// A line that read a position it had not read before may have\n\t\t\t// closed a cycle, and the end of the pass is when every edge the\n\t\t\t// walk would follow describes a run of the text that holds it.\n\t\t\tthis.settleCycles();\n\n\t\t\t// ── Phase 5.2g: Page-based LRU eviction ──────────────────────\n\t\t\t// Evict bytecode/results from cold/warm pages to bound memory.\n\t\t\tthis.pageManager.maintainAfterEval(viewport, this.doc);\n\n\t\t\t// Every line has registered by now and the document is current, so a\n\t\t\t// name the pass reported as possibly undefined can be decided. See\n\t\t\t// `ExpressionEngine.settleOrphanedNames`.\n\t\t\tthis.engine.settleOrphanedNames();\n\n\t\t\t// A user unit that was in scope when the pass began and is not now\n\t\t\t// has to reach the lines that used it, since a unit is expanded\n\t\t\t// while a line is compiled and those lines hold bytecode built\n\t\t\t// around it. Compared rather than counted, so a line that redefines\n\t\t\t// the same unit every pass does not invalidate for ever.\n\t\t\tconst unitsAfter = new Set(this.engine.userUnitNames());\n\t\t\tif (unitsBefore.some((name) => !unitsAfter.has(name))) {\n\t\t\t\tthis.engine.invalidateForRemovedUserUnits();\n\t\t\t}\n\n\t\t\treturn { lines, resultMap, tierCounts };\n\t\t} finally {\n\t\t\t// Clear keystroke signal to prevent stale signal references\n\t\t\t// from being used by subsequent evaluations from other code paths.\n\t\t\tthis.engine.setKeystrokeSignal(null);\n\n\t\t\t// Phase 5.3: Always disable arena, even on exception.\n\t\t\t// Prevents arena Values from leaking into subsequent evaluations or tests.\n\t\t\tdisableValueArena();\n\t\t}\n\t}\n\n\t/**\n\t * Background-compile invisible dirty lines beyond the viewport (Tier 3 only).\n\t *\n\t * Compiles expressions to discover reads/writes for the dependency graph\n\t * without executing display-only expressions. Variable definitions are\n\t * executed to maintain VM state for future Tier-2 executions.\n\t *\n\t * This is intended to be called after evaluate() so visible lines are\n\t * rendered first, then background work fills in the dependency graph.\n\t *\n\t * **Phase 5.2h:** This synchronous method is retained for environments\n\t * without Worker support. Prefer `dispatchBackgroundCompiles()` which\n\t * offloads compilation to a Web Worker with Transferable bytecode.\n\t */\n\tbackgroundCompile(viewport: ViewportRange): EvalLineResult[] {\n\t\tconst results: EvalLineResult[] = [];\n\t\tconst docEnd = this.doc.lineCount;\n\t\tconst startPos = viewport.endLine + 1;\n\n\t\tfor (let pos = startPos; pos <= docEnd; pos++) {\n\t\t\tconst state = this.doc.getLineAt(pos);\n\t\t\tif (!state) continue;\n\n\t\t\t// Skip clean lines, already compiled + executed\n\t\t\tif (!state.dirty) continue;\n\n\t\t\t// Skip already-compiled Tier 3 lines, they have bytecode\n\t\t\t// but were compiled without execution (non-variable-def).\n\t\t\t// Recompiling is wasteful since the text hasn't changed\n\t\t\t// (text change clears bytecodes via editLine).\n\t\t\tif (state.bytecodes.length > 0 && !state.isVariableDef) continue;\n\n\t\t\tconst lineResult = this.evaluateSingleLine(state, pos, false);\n\t\t\tresults.push(lineResult);\n\t\t}\n\n\t\treturn results;\n\t}\n\n\t/**\n\t * Dispatch background compilation to a Web Worker (Phase 5.2h).\n\t *\n\t * Collects invisible dirty lines beyond the viewport that need compilation,\n\t * sends them to the compilation worker, and asynchronously stores the\n\t * transferred bytecode in the DocumentModel when the worker responds.\n\t *\n\t * This is the non-blocking alternative to `backgroundCompile()`. The worker\n\t * compiles expressions with Transferable ArrayBuffers (zero-copy postMessage),\n\t * so bytecode appears on the main thread without serialization overhead.\n\t *\n\t * Lines that already have cached bytecode (from a previous worker pass or\n\t * synchronous compile) are skipped, only truly uncompiled dirty lines are\n\t * sent to the worker.\n\t *\n\t * **Usage:** Call after `evaluate()` so visible lines render first, then\n\t * this fills the bytecode cache for future Tier-2 scrolls.\n\t *\n\t * @param viewport The current visible range. Lines beyond viewport.endLine\n\t * that are dirty and don't have bytecode are dispatched.\n\t */\n\tdispatchBackgroundCompiles(viewport: ViewportRange): void {\n\t\t// Collect invisible dirty lines that need compilation\n\t\tconst items = this.collectInvisibleCompileTargets(viewport);\n\t\tif (items.length === 0) return;\n\n\t\t// Lazy-init the worker (only if there are items to compile)\n\t\tif (!this.compilationWorker) {\n\t\t\tthis.compilationWorker = new CompilationWorkerManager();\n\t\t}\n\n\t\t// Fire-and-forget: send to worker, store results when they arrive\n\t\tthis.compilationWorker.compileBatch(items).then((results) => {\n\t\t\tthis.compilationWorker!.storeResults(results, this.doc);\n\t\t}).catch((_err) => {\n\t\t\t// Worker failure is non-fatal, next evaluate() will compile\n\t\t\t// these expressions synchronously.\n\t\t});\n\t}\n\n\t/**\n\t * Terminate the compilation worker if active, and unsubscribe from\n\t * sharedGlobalVariableStore. Call this when the evaluator is no longer\n\t * needed to clean up resources, every call site that retires a\n\t * ThreeTierEvaluator (document switch, pane destroy()) already calls\n\t * this unconditionally, so folding the global-store unsubscribe in here\n\t * needs no new call sites anywhere.\n\t */\n\tterminateWorker(): void {\n\t\tif (this.compilationWorker) {\n\t\t\tthis.compilationWorker.terminate();\n\t\t\tthis.compilationWorker = null;\n\t\t}\n\t\tif (this.globalUnsubscribe) {\n\t\t\tthis.globalUnsubscribe();\n\t\t\tthis.globalUnsubscribe = null;\n\t\t}\n\t}\n\n\t/**\n\t * Get the DocumentModel (read-only access for decoration building).\n\t */\n\tgetDoc(): DocumentModel {\n\t\treturn this.doc;\n\t}\n\n\t/**\n\t * Evaluate all dirty lines in the document, regardless of viewport.\n\t * Used for full re-evaluation after plugin register/unregister.\n\t */\n\tevaluateAll(signal?: AbortSignal): EvalResult {\n\t\tconst viewport = { startLine: 1, endLine: this.doc.lineCount };\n\t\tconst result = this.evaluate(viewport, signal);\n\t\t// evaluate() already calls maintainAfterEval internally\n\t\treturn result;\n\t}\n\n\t/**\n\t * Zero-allocation viewport evaluation, the Phase 5.2e \"holy grail.\"\n\t *\n\t * **Key insight:** When the user scrolls (viewport-only change, no edits),\n\t * we don't need to re-evaluate from line 1. Instead:\n\t *\n\t * 1. Restore the VM to just before the viewport via the nearest checkpoint.\n\t * 2. Evaluate ONLY the visible lines (Tier 2 for clean cached, Tier 1 for dirty).\n\t * 3. Lines before the viewport are completely skipped, their state lives in\n\t * the VM checkpointer's prototypal chain.\n\t *\n\t * **Correctness guard:** If any variable-definition line before the viewport\n\t * is dirty (e.g., the user edited a variable def that hasn't been\n\t * re-evaluated yet), we clear stale checkpoints and fall back to `evaluate()`\n\t * which processes from line 1 and rebuilds fresh checkpoints. This\n\t * guarantees that stale checkpoints are never used as restoration targets.\n\t * Only variable-def lines matter here, `VMCheckpointer.snapshot()` only\n\t * records state for lines that write a variable, so a dirty plain-expression\n\t * line before the viewport has no checkpoint to invalidate (see\n\t * `DocumentModel.hasAnyDirtyVariableDefLineBefore()`).\n\t *\n\t * **Performance:** O(visible lines) instead of O(document length). Target:\n\t * < 1ms for a typical ~30-line viewport, independent of document size.\n\t *\n\t * @param viewport The visible line range.\n\t * @returns Results for visible lines only. Lines before the viewport are\n\t * not included in `lines[]` or `resultMap`.\n\t */\n\tsetViewport(viewport: ViewportRange, signal?: AbortSignal): EvalResult {\n\t\t// ── One AbortController Per Keystroke ────────────────────────\n\t\t// Link the UI layer's keystroke signal to the engine.\n\t\tthis.engine.setKeystrokeSignal(signal ?? null);\n\n\t\t// ── Correctness guard: dirty lines before the viewport run first ──\n\t\t// A dirty line above the viewport may define something the viewport\n\t\t// reads, so the pass runs from line 1 and reaches it. The chain is\n\t\t// kept: a dirty line replaces its own entry when it runs, and a clean\n\t\t// line's entry, which nothing here runs again, is the one the lines\n\t\t// below need. Clearing it left them reading an empty prefix.\n\t\tif (viewport.startLine > 1 && this.hasDirtyLinesBefore(viewport.startLine)) {\n\t\t\t// evaluate() handles its own arena enable/disable and signal cleanup.\n\t\t\treturn this.evaluate(viewport, signal);\n\t\t}\n\n\t\t// ── Running totals are re-seeded here too ──\n\t\t// A `total += 5` line compiles to no bytecode, so a clean one cannot be\n\t\t// re-run from cache: it has to go back through the full pipeline. That\n\t\t// was already true, and harmless while the VM was never rewound, because\n\t\t// the total simply stayed where the last pass left it. Restoring to the\n\t\t// line before the viewport rewinds it, and a viewport containing the\n\t\t// accumulator lines then found nothing to rebuild them from:\n\t\t// `spent += 10` / `spent += 20` / `spent` answered\n\t\t// `Undefined variable: spent` on a scroll. Marking them dirty sends them\n\t\t// to Tier 1, which recomputes each total from its seed.\n\t\tthis.reseedAccumulators();\n\n\t\t// ── Phase 5.2g: Page-based LRU eviction (MUST run before preload) ──\n\t\t// maintainAfterEval captures the scroll direction and updates lastViewportStart\n\t\t// BEFORE preloadNextPages reads the direction for preloading.\n\t\tthis.pageManager.maintainAfterEval(viewport, this.doc);\n\n\t\t// ── Phase 5.2g: Detect scroll direction & preload ───────────\n\t\tthis.preloadNextPages(viewport);\n\n\t\t// ── Restore VM state from nearest checkpoint before the viewport ──\n\t\t// This sets all variables that were defined at or before startLine-1.\n\t\tthis.restoreTo(viewport.startLine - 1);\n\n\t\t// ── Phase 5.3: Enable arena for zero-allocation Tier 2 execution ──\n\t\tenableValueArena();\n\t\ttry {\n\t\t\t// ── Evaluate only visible lines ──\n\t\t\tconst result = this.collectEvalResults(viewport.startLine, viewport.endLine);\n\t\t\treturn result;\n\t\t} finally {\n\t\t\t// Clear keystroke signal to prevent stale signal references.\n\t\t\tthis.engine.setKeystrokeSignal(null);\n\n\t\t\t// Phase 5.3: Always disable arena, even on exception.\n\t\t\t// Prevents cross-test contamination from arena leaks.\n\t\t\tdisableValueArena();\n\t\t}\n\t}\n\n\t/**\n\t * Apply incremental line-level changes to the document model.\n\t *\n\t * **Phase 5.2f:** Replaces the O(N) `setDocument()` + full re-evaluation\n\t * with O(changed) incremental updates. Key benefits:\n\t *\n\t * 1. Unchanged lines retain their persistent lineIds → bytecode survives\n\t * 2. Only changed + DAG-downstream lines are marked dirty → Tier 1 re-evaluation\n\t * 3. Clean lines in viewport use Tier 2 (cached bytecode execution)\n\t * 4. Clean lines outside viewport are skipped entirely\n\t *\n\t * The DAG is fully cleared after propagation: shifted lines would have\n\t * stale entries keyed by old line numbers, so the DAG is rebuilt from\n\t * scratch during the subsequent `evaluate()` call.\n\t *\n\t * **Caller should follow up with `evaluate(viewport)`** to re-evaluate\n\t * dirty lines from line 1 and rebuild the DAG + checkpoints.\n\t *\n\t * @param changes Line-level changes to apply. Must be non-overlapping.\n\t * @returns Metadata about the applied changes.\n\t */\n\tapplyTransaction(changes: LineChange[]): {\n\t\tinserted: number[];\n\t\tremoved: number[];\n\t} {\n\t\t// ── Phase 1: Collect DAG writes + downstream lineIds ───────────\n\t\t// Must happen BEFORE applyChanges() because line numbers are still\n\t\t// valid at this point. We collect writes from deleted lines and\n\t\t// resolve downstream consumers to lineIds (not line numbers) so\n\t\t// they survive the position shifts that applyChanges() causes.\n\t\tconst allWrites = new Set<string>();\n\n\t\tfor (const change of changes) {\n\t\t\tfor (let i = 0; i < change.deleteCount; i++) {\n\t\t\t\tconst lineNum = change.startLine + i;\n\t\t\t\tconst writes = this.dag.getWrites(lineNum);\n\t\t\t\tfor (const w of writes) {\n\t\t\t\t\tallWrites.add(w);\n\t\t\t\t}\n\t\t\t\t// Clean up DAG references for this line, and forget any name the\n\t\t\t\t// deleted line was the last to define.\n\t\t\t\tthis.undefineOrphans(this.dag.removeLine(lineNum));\n\t\t\t\t// A deleted line will never be compiled again, so it cannot drop\n\t\t\t\t// its own definitions on the way through. Losing one reaches the\n\t\t\t\t// lines that used it here rather than at the end of a pass,\n\t\t\t\t// because this runs before the next pass begins and that\n\t\t\t\t// comparison would see the unit already gone.\n\t\t\t\tif (this.engine.undefineUserUnitsFrom(this.doc.getLineAt(lineNum)?.lineId ?? -1)) {\n\t\t\t\t\tthis.engine.invalidateForRemovedUserUnits();\n\t\t\t\t}\n\t\t\t\t// And its cached bytecode. The dependency graph was already\n\t\t\t\t// pruned here; the LineCache was not, so a deleted line kept its\n\t\t\t\t// entry until the whole cache was dropped on a document switch.\n\t\t\t\t// Editing a long document over a session accumulated entries for\n\t\t\t\t// line numbers that no longer existed.\n\t\t\t\tthis.engine.getLineCache().removeAllForLine(lineNum);\n\t\t\t}\n\t\t}\n\n\t\t// Resolve downstream consumers to persistent lineIds BEFORE the\n\t\t// structural change shifts line numbers. After applyChanges(),\n\t\t// we mark these lineIds dirty, their positions don't matter.\n\t\tconst downstreamLineIds = new Set<number>();\n\t\tconst positionalReaderLineIds = new Set<number>();\n\n\t\t// Every line that reads a position joins them, because a structural\n\t\t// edit changes what a position means without changing a character of\n\t\t// the line that reads it. `line 5` names different text once a line is\n\t\t// inserted above it, `prev` names a different neighbour, and an `above`\n\t\t// aggregate covers a different block. None of that reaches the loop\n\t\t// above, which follows the names a deleted line wrote.\n\t\t//\n\t\t// A reader that moved counts too, not only one whose target moved:\n\t\t// `line 5 + 4` sitting at position 4 is a reference, and after an\n\t\t// insert above it, sitting at position 5, it is a self-reference. A\n\t\t// settled pass reports that; the edited document answered from the\n\t\t// value it had.\n\t\t//\n\t\t// Their answers go as well as their dirty flag, which is the half that\n\t\t// was missing. Marking a line dirty says it must run again; it does not\n\t\t// stop another line reading what it said in the meantime, and after a\n\t\t// structural edit what it said was about a document that no longer\n\t\t// exists. Two positional readers that end up reading each other then\n\t\t// chase those old answers instead of reporting the cycle: `line 3 + 5`\n\t\t// above an `average above` that covers it took the average's previous\n\t\t// value, computed a new one, and the average recomputed from that, by a\n\t\t// smaller amount each pass, landing wherever the passes ran out. With\n\t\t// nothing to chase, both report the cycle, which is what a pass over the\n\t\t// same text reports.\n\t\t//\n\t\t// Only on a structural edit, and only these lines. An ordinary edit\n\t\t// leaves every position meaning what it meant, so a reader's answer is\n\t\t// still about this document and taking it away would show an error to\n\t\t// whoever asked before it ran again.\n\t\tfor (const reader of this.dag.linesReadingAPosition()) {\n\t\t\tconst state = this.doc.getLineAt(reader);\n\t\t\tif (!state) continue;\n\t\t\tdownstreamLineIds.add(state.lineId);\n\t\t\tpositionalReaderLineIds.add(state.lineId);\n\t\t}\n\t\tfor (const writeVar of allWrites) {\n\t\t\tconst affected = this.dag.getAffectedLines(writeVar);\n\t\t\tfor (const lineNum of affected) {\n\t\t\t\tconst state = this.doc.getLineAt(lineNum);\n\t\t\t\tif (state) {\n\t\t\t\t\tdownstreamLineIds.add(state.lineId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// ── Phase 2: Apply structural changes to DocumentModel ─────────\n\t\tconst result = this.doc.applyChanges(changes);\n\n\t\t// ── Phase 3: The chain follows the lines to their new positions ──\n\t\t// It used to be cleared here and rebuilt as lines ran, but a clean\n\t\t// line above the viewport never runs, so its entry never came back\n\t\t// and a line below asking what the prefix held was told nothing.\n\t\tthis.checkpointer?.renumber((lineId) => this.doc.getLinePosition(lineId));\n\t\t// A deleted line's names hold what the rest of the document leaves\n\t\t// them, which is where a pass over the remaining text would leave them;\n\t\t// until now they held what the deleted line had written.\n\t\tfor (const written of allWrites) this.engine.restoreToPrefix(written, Number.MAX_SAFE_INTEGER);\n\n\t\t// ── Phase 4: Mark DAG-downstream lines dirty by lineId ─────────\n\t\t// Using lineId instead of line number is position-agnostic:\n\t\t// lines that shifted due to insertions/deletions above them are\n\t\t// still correctly targeted. Lines that were deleted (lineId no\n\t\t// longer in the doc) are silently ignored by markDirty().\n\t\tfor (const lineId of downstreamLineIds) {\n\t\t\tthis.doc.markDirty(lineId);\n\t\t}\n\t\tfor (const lineId of positionalReaderLineIds) {\n\t\t\tthis.doc.forgetResult(lineId);\n\t\t}\n\n\t\t// Every line from the earliest change downward now sits at a different\n\t\t// position, so a result it still holds was computed for where it used to\n\t\t// be. The stale one must not outlive the edit below the viewport, where a\n\t\t// pass never reaches it and a fresh pass shows nothing: a line reading\n\t\t// that position read the stale answer back as a real one (`line 4`\n\t\t// reading a line that had moved to position 4 reported its error rather\n\t\t// than \"not evaluated yet\"). It is cleared at the next evaluate, once the\n\t\t// viewport is known, since a moved line ABOVE the viewport is one a fresh\n\t\t// pass still shows and must be kept. See {@link forgetMovedBelowViewport}.\n\t\tconst earliestChange = changes.reduce((lowest, change) => Math.min(lowest, change.startLine), Number.MAX_SAFE_INTEGER);\n\t\tthis.movedFloor = Math.min(this.movedFloor, earliestChange);\n\n\t\t// ── Phase 5: Clear DAG to avoid phantom entries ────────────────\n\t\t// Entries keyed by old line numbers are stale after structural\n\t\t// changes. Rather than updating shifted entries, we clear the DAG\n\t\t// and let the subsequent evaluate() call rebuild it from scratch.\n\t\tthis.dag.clear();\n\n\t\t// A deleted line's id is never seen again, so its entries are dead weight.\n\t\tfor (const lineId of result.removed) {\n\t\t\tthis.textHashOfRecordedEdges.delete(lineId);\n\t\t\tthis.selfReadingWrites.delete(lineId);\n\t\t\tthis.cycleMemberIds.delete(lineId);\n\t\t}\n\n\t\treturn {\n\t\t\tinserted: result.inserted,\n\t\t\tremoved: result.removed,\n\t\t};\n\t}\n\n\t/**\n\t * Forget the positions the graph says a line reads, once its text changed.\n\t *\n\t * A positional edge is discovered while the line runs and pinned, because\n\t * the text cannot put it back when the line re-registers. That is right\n\t * while the text is the same and wrong the moment it is not: `prev + 1`\n\t * edited to `7` went on reading line 1 in the graph until it ran, and a\n\t * line edited out of the viewport is compiled there without running, so\n\t * it could go on for as long as the reader liked. Three of the attempts\n\t * recorded on #444 found cycles that had already been edited away for\n\t * exactly that reason.\n\t *\n\t * Keyed on the edit itself, which the document model records as a new\n\t * text hash, rather than on anything the graph knows. Done at the line's\n\t * first visit after the edit, whatever tier that visit takes, and only\n\t * when the line is dirty, since an edit always dirties, so a clean line's\n\t * text is the text its edges describe. The run that follows records what\n\t * the new text reads, and {@link DependencyGraph.reconcilePositionReads}\n\t * keeps that exact from then on.\n\t */\n\tprivate forgetPositionsOfEditedText(state: LineState, lineNumber: number): void {\n\t\tconst recordedFor = this.textHashOfRecordedEdges.get(state.lineId);\n\t\tif (recordedFor === state.textHash) return;\n\t\tif (recordedFor !== undefined) this.dag.forgetPositionReads(lineNumber);\n\t\tthis.textHashOfRecordedEdges.set(state.lineId, state.textHash);\n\t}\n\n\t/**\n\t * Keep the record of which lines sit on a cycle current.\n\t *\n\t * A cycle can only appear or vanish with an edge, so the record is\n\t * recomputed only when the graph changed this pass, by a walk from what\n\t * changed: readers that gained a position, lines whose edge set changed\n\t * (a dropped positional edge included, which is how a cycle is broken), and\n\t * the readers of a name whose producers changed, which is how a name that\n\t * pinned a cycle is withdrawn. A line whose status flips is marked to run\n\t * again, so it runs the other way on the next pass. A settled pass changes\n\t * nothing and walks nothing.\n\t *\n\t * What membership does is decided where a line runs: see\n\t * {@link evaluateSingleLine} and `ExpressionEngine.setLineOnCycle`. Earlier\n\t * versions reset a member's answer here instead, at the end of the pass,\n\t * and that was one line too late: the number the member computed had\n\t * already been read by the lines below it during the pass. Deciding how the\n\t * member runs means no number ever exists to be read.\n\t */\n\tprivate settleCycles(): void {\n\t\tconst gained = this.dag.takeReadersThatGainedAPosition();\n\t\tconst changes = this.dag.takeEdgeChanges();\n\t\tconst roots = new Set<number>(gained);\n\t\tfor (const line of changes.lines) roots.add(line);\n\t\tfor (const key of changes.keys) {\n\t\t\tif (isPrefixedEdgeKey(key)) continue;\n\t\t\tfor (const reader of this.dag.directConsumersOf(key)) roots.add(reader);\n\t\t}\n\t\tif (roots.size === 0) return;\n\t\t// Every current member is re-examined as well. A cycle broken by an edit\n\t\t// to a line that merely fed it is no longer reachable from that line, so\n\t\t// a walk from the change alone left the members marked for ever.\n\t\tif (this.cycleMemberIds.size > 0) {\n\t\t\tconst lineCount = this.doc.lineCount;\n\t\t\tfor (let position = 1; position <= lineCount; position++) {\n\t\t\t\tconst state = this.doc.getLineAt(position);\n\t\t\t\tif (state !== undefined && this.cycleMemberIds.has(state.lineId)) roots.add(position);\n\t\t\t}\n\t\t}\n\t\tthis.recomputeCycleMembership(roots);\n\t}\n\n\t/**\n\t * Recompute which lines sit on a cycle, for every line the change reaches.\n\t *\n\t * Tarjan's components, iteratively: a `prev` chain can be thousands of\n\t * lines deep, which is deeper than a recursive walk should go. A line the\n\t * walk visits is on a cycle if its component has more than one member, and\n\t * off one otherwise; a line the walk does not reach kept its edges and its\n\t * status. Membership is by line id, so a structural edit does not lose it;\n\t * such an edit clears the graph and every line re-registers on the next\n\t * pass, so the walk then reaches everything.\n\t */\n\tprivate recomputeCycleMembership(roots: ReadonlySet<number>): void {\n\t\tconst index = new Map<number, number>();\n\t\tconst low = new Map<number, number>();\n\t\tconst onStack = new Set<number>();\n\t\tconst stack: number[] = [];\n\t\tconst frames: { line: number; targets: number[]; next: number }[] = [];\n\t\tlet visited = 0;\n\t\tconst enter = (line: number): void => {\n\t\t\tindex.set(line, visited);\n\t\t\tlow.set(line, visited);\n\t\t\tvisited++;\n\t\t\tstack.push(line);\n\t\t\tonStack.add(line);\n\t\t\tframes.push({ line, targets: this.dependantsOf(line), next: 0 });\n\t\t};\n\t\tconst setMembership = (line: number, onCycle: boolean): void => {\n\t\t\tconst state = this.doc.getLineAt(line);\n\t\t\tif (state === undefined) return;\n\t\t\tif (onCycle === this.cycleMemberIds.has(state.lineId)) return;\n\t\t\tif (onCycle) this.cycleMemberIds.add(state.lineId);\n\t\t\telse this.cycleMemberIds.delete(state.lineId);\n\t\t\t// It runs the other way from now on, so it has to run.\n\t\t\tthis.doc.markDirty(state.lineId);\n\t\t};\n\n\t\tfor (const root of roots) {\n\t\t\tif (index.has(root)) continue;\n\t\t\tenter(root);\n\t\t\twhile (frames.length > 0) {\n\t\t\t\tconst frame = frames[frames.length - 1];\n\t\t\t\tif (frame.next < frame.targets.length) {\n\t\t\t\t\tconst target = frame.targets[frame.next++];\n\t\t\t\t\tif (!index.has(target)) enter(target);\n\t\t\t\t\telse if (onStack.has(target)) low.set(frame.line, Math.min(low.get(frame.line)!, index.get(target)!));\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tframes.pop();\n\t\t\t\tif (frames.length > 0) {\n\t\t\t\t\tconst parent = frames[frames.length - 1].line;\n\t\t\t\t\tlow.set(parent, Math.min(low.get(parent)!, low.get(frame.line)!));\n\t\t\t\t}\n\t\t\t\tif (low.get(frame.line) !== index.get(frame.line)) continue;\n\t\t\t\tconst members: number[] = [];\n\t\t\t\tlet member: number;\n\t\t\t\tdo {\n\t\t\t\t\tmember = stack.pop()!;\n\t\t\t\t\tonStack.delete(member);\n\t\t\t\t\tmembers.push(member);\n\t\t\t\t} while (member !== frame.line);\n\t\t\t\tfor (const line of members) setMembership(line, members.length > 1);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * The lines whose answers are computed from this line's: the readers of its\n\t * position, and the readers of each name it writes.\n\t *\n\t * Both kinds of edge, because a cycle can run through either. `line 2 + 5`\n\t * above `prev + 5` is a cycle of positions; `:v1 = v2 + 5` above\n\t * `:v2 = v1 + 8` is a cycle of names; `spent += line 3` above `spent` is\n\t * one of each. A walk that followed positions alone found the first and\n\t * declared the other two out of scope.\n\t *\n\t * Edges point the way the graph's consumer index points, from a line to\n\t * what reads it, which is the transpose of \"what this line reads\". The\n\t * strongly connected components of a graph and its transpose are the same\n\t * sets, so the walk finds the same cycles, and the consumer index is the one\n\t * the graph keeps clear of a definition's conventional read of its own name\n\t * and of a running total's read of its own total. Followed as raw reads,\n\t * every definition was a self-loop and every twice-defined name a cycle.\n\t *\n\t * Only variable names among the keys: a tag, a global or a data source is\n\t * not a line's answer, and a cycle cannot run through one.\n\t */\n\tprivate dependantsOf(line: number): number[] {\n\t\tconst out = [...this.dag.getAffectedLinesByPosition(line)];\n\t\tfor (const key of this.dag.getWrites(line)) {\n\t\t\tif (isPrefixedEdgeKey(key)) continue;\n\t\t\tfor (const reader of this.dag.directConsumersOf(key)) out.push(reader);\n\t\t\t// A running total's other steppers depend on this one through the\n\t\t\t// total, and the graph keeps a writer out of its own name's consumers,\n\t\t\t// so that dependency is recovered here for an accumulator name alone.\n\t\t\t// `spent += line 2` above `spent += 9` is a cycle: the step below\n\t\t\t// folds in the step above, and the step above reads the line below.\n\t\t\t// Ordered, because the fold is: a step depends on the steps above it,\n\t\t\t// never on those below. Without the order a plain column of steps was\n\t\t\t// a cycle.\n\t\t\tif (this.engine.isAccumulatorName(key)) {\n\t\t\t\tfor (const stepper of this.dag.getProducers(key)) if (stepper > line) out.push(stepper);\n\t\t\t}\n\t\t}\n\t\treturn out;\n\t}\n\n\t// ── Private helpers ─────────────────────────────────────────────────\n\n\t/**\n\t * Reset running-total accumulators and mark the lines that touch them\n\t * dirty, so the from-line-1 pass in {@link evaluate} recomputes each total\n\t * from its seed instead of reading its own previous evaluation's value.\n\t *\n\t * A `total += 5` line compiles to no bytecode (it rides the symbolic\n\t * channel), so a clean one would be skipped by the tier dispatch rather\n\t * than re-run. It must re-run to re-apply its delta over the freshly-reset\n\t * base, so every line that WRITES an accumulator name is forced dirty (the\n\t * DAG drops a self-writing line from its own variable's consumers, so\n\t * getAffectedLines can't surface them). Pure readers of the total keep\n\t * their real bytecode and re-run via Tier 2 once the total is correct, so\n\t * they need no special handling. Cheap: a no-op unless the document\n\t * actually uses `+=`/`-=`, and accumulators are rare.\n\t */\n\tprivate reseedAccumulators(): void {\n\t\tconst names = this.engine.resetAccumulators();\n\t\tif (names.size === 0) return;\n\t\t// Read from the lines themselves, not from the dependency graph.\n\t\t//\n\t\t// A structural edit clears the graph (see {@link applyTransaction}) and\n\t\t// lets the next pass rebuild it, so asking the graph what each line\n\t\t// writes answers nothing at all on the pass that follows an insert or a\n\t\t// delete. No accumulator line was marked, so none re-ran, and the totals\n\t\t// below the edit kept the previous pass's sum: inserting `spent += 2`\n\t\t// above `spent += 7` left the second line reading 7 rather than 9. The\n\t\t// line's own write set is recorded on the line and survives the edit.\n\t\tconst docEnd = this.doc.lineCount;\n\t\tfor (let pos = 1; pos <= docEnd; pos++) {\n\t\t\tconst state = this.doc.getLineAt(pos);\n\t\t\tif (state === undefined || state.writes.length === 0) continue;\n\t\t\tfor (const written of state.writes) {\n\t\t\t\tif (names.has(written)) {\n\t\t\t\t\tthis.doc.markDirtyByLineNumber(pos);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Collect evaluation results for a contiguous range of lines.\n\t *\n\t * Used by both `evaluate()` (startLine=1) and `setViewport()` (any start).\n\t * All lines in the range are treated as in-viewport (visible), callers that\n\t * need the invisible/dirty → Tier 3 handling should use `evaluate()` instead.\n\t *\n\t * @param startLine First line to evaluate (1-based, inclusive).\n\t * @param endLine Last line to evaluate (1-based, inclusive). Clamped to docEnd.\n\t */\n\t/**\n\t * Clear the stale answers a structural edit left below the viewport.\n\t *\n\t * An insert or a delete moves every line from {@link movedFloor} down to a\n\t * new position; the answer each still holds was computed for its old one. A\n\t * moved line inside the range about to run is re-run and gets a fresh answer,\n\t * and a moved line above it is one a fresh pass driven to this viewport still\n\t * shows, so both are left alone. A moved line BELOW the range is neither: the\n\t * pass never reaches it, nothing re-runs it, and a fresh pass shows nothing\n\t * there, yet the incremental path kept its old answer, which a line reading\n\t * that position then read back as a real one (#458). Those are cleared here,\n\t * from just past `evalEnd` (or the earliest move, whichever is lower down) to\n\t * the end of the document. Consumed once: a later pass over a wider viewport\n\t * re-runs whichever of them it now covers.\n\t *\n\t * @param evalEnd - The last line this pass will run.\n\t */\n\tprivate forgetMovedBelowViewport(evalEnd: number): void {\n\t\tif (this.movedFloor === Number.MAX_SAFE_INTEGER) return;\n\t\tconst from = Math.max(evalEnd + 1, this.movedFloor);\n\t\tconst docEnd = this.doc.lineCount;\n\t\tfor (let position = from; position <= docEnd; position++) {\n\t\t\tconst moved = this.doc.getLineAt(position);\n\t\t\tif (moved) this.doc.forgetResult(moved.lineId);\n\t\t}\n\t\tthis.movedFloor = Number.MAX_SAFE_INTEGER;\n\t}\n\n\tprivate collectEvalResults(startLine: number, endLine: number): EvalResult {\n\t\tconst lines: EvalLineResult[] = [];\n\t\tconst resultMap = new Map<number, Value[]>();\n\t\tconst tierCounts = { tier1: 0, tier2: 0, tier3: 0, skipped: 0 };\n\n\t\tconst docEnd = this.doc.lineCount;\n\t\tconst evalEnd = Math.min(endLine, docEnd);\n\t\tthis.forgetMovedBelowViewport(evalEnd);\n\n\t\tfor (let pos = startLine; pos <= evalEnd; pos++) {\n\t\t\tconst state = this.doc.getLineAt(pos);\n\t\t\tif (!state) continue;\n\n\t\t\t// All processed lines are in-viewport for setViewport, or conditionally\n\t\t\t// in-viewport for evaluate (handled by caller). We pass `true` here\n\t\t\t// because evaluateSingleLine's `inViewport` param controls Tier 1 vs\n\t\t\t// Tier 3 dispatch; callers must manage this distinction externally.\n\t\t\t//\n\t\t\t// evaluate() handles this by passing `inViewport` per-line; it loops\n\t\t\t// directly rather than using this helper for that reason.\n\t\t\tconst lineResult = this.evaluateSingleLine(state, pos, true);\n\t\t\tlines.push(lineResult);\n\n\t\t\tif (lineResult.tier === EvalTier.Tier1) tierCounts.tier1++;\n\t\t\telse if (lineResult.tier === EvalTier.Tier2) tierCounts.tier2++;\n\t\t\telse if (lineResult.tier === EvalTier.Tier3) tierCounts.tier3++;\n\t\t\telse tierCounts.skipped++;\n\n\t\t\tif (lineResult.results) {\n\t\t\t\tresultMap.set(pos, lineResult.results.flat());\n\t\t\t}\n\t\t}\n\n\t\t// The same end-of-pass check `evaluate` makes; a viewport pass runs\n\t\t// lines and records what they read just as a full one does.\n\t\tthis.settleCycles();\n\n\t\treturn { lines, resultMap, tierCounts };\n\t}\n\n\t/**\n\t * Check whether any **variable-definition** line before `position`\n\t * (1-based, exclusive) is dirty.\n\t *\n\t * Used by `setViewport()` to decide whether to fall back to `evaluate()`:\n\t * if a variable-def before the viewport is dirty, the checkpoint state\n\t * `restoreTo()` would use may be stale and we need to reprocess from\n\t * line 1 to rebuild checkpoints correctly.\n\t *\n\t * Deliberately narrower than `DocumentModel.hasAnyDirtyLineBefore()`:\n\t * checkpoints only snapshot variable-def lines (see VMCheckpointer), so a\n\t * dirty plain-expression line before the viewport can't have invalidated\n\t * one, there's nothing checkpointed for it to invalidate. Using the\n\t * broader check here previously caused a real perf bug: `PageManager`'s\n\t * cold-page eviction marks evicted non-variable-def lines dirty, so\n\t * scrolling far into a large, variable-def-free document would trip this\n\t * guard, fall back to `evaluate()`, which recompiles those lines via\n\t * Tier 3 (never clearing their dirty flag by design), causing the very\n\t * next `maintainAfterEval()` to re-evict and re-dirty them, a\n\t * self-sustaining loop that pinned every subsequent `setViewport()` call\n\t * to the cost of a full re-evaluation instead of O(visible lines).\n\t *\n\t * Delegates to DocumentModel.hasAnyDirtyVariableDefLineBefore(), which\n\t * tracks dirty lineIds incrementally instead of scanning every line up to\n\t * `position` on every call. This used to be a real per-scroll cost\n\t * (benchmarked at ~10ms scrolled near the bottom of a 20k-line document)\n\t * since it fired on every viewport change, not just edits.\n\t */\n\tprivate hasDirtyLinesBefore(position: number): boolean {\n\t\treturn this.doc.hasAnyDirtyVariableDefLineBefore(position);\n\t}\n\n\t/**\n\t * Evaluate a single line using the appropriate tier.\n\t *\n\t * Tier assignment logic:\n\t * - Empty/markdown-only lines → skipped\n\t * - Dirty + in-viewport → Tier 1 (full pipeline)\n\t * - Dirty + not in viewport → Tier 3 (compile-only, execute variable defs)\n\t * - Clean + has bytecode + in viewport → Tier 2 (execute from cache)\n\t * - Clean + no bytecode → skipped (non-evaluable)\n\t *\n\t * Around the dispatch, the graph is kept honest about what the line reads\n\t * by position: an edited line's old positions go before it runs, and a\n\t * line that executed is cut back to the positions that run read. Only an\n\t * executing tier is reconciled, since a line compiled without running, or\n\t * skipped, read nothing for a reason that says nothing about its text.\n\t */\n\tprivate evaluateSingleLine(\n\t\tstate: LineState,\n\t\tlineNumber: number,\n\t\tinViewport: boolean\n\t): EvalLineResult {\n\t\t// An edit always dirties, so a clean line's edges describe its text and\n\t\t// the map is not consulted for it: the check costs the pass nothing on\n\t\t// the lines that are most of it.\n\t\tif (state.dirty) this.forgetPositionsOfEditedText(state, lineNumber);\n\t\t// A line on a cycle runs the way a single fresh pass runs it: the names\n\t\t// it reads hold what the lines above left, and a line below it is not\n\t\t// yet evaluated. That is the answer `parseDocument` gives such a line,\n\t\t// and it is stable, where a number found in the VM from the previous\n\t\t// pass is the start of a chase. See `ExpressionEngine.setLineOnCycle`.\n\t\tconst onCycle = this.cycleMemberIds.has(state.lineId);\n\t\tthis.engine.setLineOnCycle(onCycle);\n\t\tif (onCycle) {\n\t\t\tfor (const read of state.reads) {\n\t\t\t\tif (!isPrefixedEdgeKey(read)) this.engine.restoreToPrefix(read, lineNumber);\n\t\t\t}\n\t\t}\n\t\tconst lineResult = this.dispatchLine(state, lineNumber, inViewport);\n\t\t// A member's edges follow its run like any other line's. Its run is cut\n\t\t// short on purpose (a forward read stops a range early), which is why\n\t\t// every aggregate declares its whole span before reading any of it;\n\t\t// keeping a member's forward edges regardless kept an edge to a line\n\t\t// the member had stopped reading, and that phantom cycle outlived the\n\t\t// real one.\n\t\tif (lineResult.tier === EvalTier.Tier1 || lineResult.tier === EvalTier.Tier2) {\n\t\t\tthis.dag.reconcilePositionReads(lineNumber);\n\t\t}\n\t\treturn lineResult;\n\t}\n\n\t/** The tier dispatch itself; see {@link evaluateSingleLine} for what wraps it. */\n\tprivate dispatchLine(\n\t\tstate: LineState,\n\t\tlineNumber: number,\n\t\tinViewport: boolean\n\t): EvalLineResult {\n\t\tconst baseResult: Omit<EvalLineResult, \"tier\" | \"result\" | \"error\"> = {\n\t\t\tlineId: state.lineId,\n\t\t\tlineNumber,\n\t\t};\n\n\t\t// A clean line outside the viewport has nothing to do, and nothing has\n\t\t// to be worked out in order to know that.\n\t\t//\n\t\t// It is the last case the dispatch below reaches, so it used to arrive\n\t\t// there having paid for an emptiness scan of its text and an expression\n\t\t// extraction, neither of which can change the answer: the line is not\n\t\t// dirty, so it is not compiled, and it is not visible, so it is not\n\t\t// executed. A pass runs from line 1 to the end of the viewport, so on a\n\t\t// long document scrolled to the bottom almost every line took that\n\t\t// route, and an edit cost the distance from line 1 rather than the size\n\t\t// of the viewport.\n\t\t//\n\t\t// The two paths below this also mark an empty line clean and record\n\t\t// that it is empty. Both are already true of a line that is clean: it\n\t\t// has been through here before, on the pass that cleaned it.\n\t\tif (!state.dirty && !inViewport) {\n\t\t\treturn { ...baseResult, tier: EvalTier.Skipped, result: null, error: null };\n\t\t}\n\n\t\t// Skip empty/markdown-only lines\n\t\tif (state.isEmpty || isEmptyLine(state.text)) {\n\t\t\tthis.deregisterIfDirty(state, lineNumber);\n\t\t\tstate.isEmpty = true;\n\t\t\tthis.doc.markClean(state.lineId);\n\t\t\treturn { ...baseResult, tier: EvalTier.Skipped, result: null, error: null };\n\t\t}\n\n\t\t// Extract all evaluable expressions (may be multiple inline solves)\n\t\tconst { expressions, inlineSolveCount } = this.extractExpressions(state);\n\t\tif (expressions.length === 0) {\n\t\t\tthis.deregisterIfDirty(state, lineNumber);\n\t\t\tstate.isEmpty = true;\n\t\t\tthis.doc.markClean(state.lineId);\n\t\t\treturn { ...baseResult, tier: EvalTier.Skipped, result: null, error: null };\n\t\t}\n\n\t\t// Determine the expression to evaluate (only needed for dirty lines)\n\t\tif (state.dirty) {\n\t\t\tif (inViewport) {\n\t\t\t\t// ── Tier 1: Visible + Dirty → Full Pipeline ──────────\n\t\t\t\treturn this.evaluateTier1(state, lineNumber, expressions, inlineSolveCount, baseResult);\n\t\t\t} else {\n\t\t\t\t// ── Tier 3: Invisible + Dirty → Compile-only ─────────\n\t\t\t\t// Skip recompilation if already compiled by a previous Tier 3 pass.\n\t\t\t\t// Non-variable-def lines keep dirty=true after Tier 3 (so they get\n\t\t\t\t// Tier 1 when scrolled into view), but recompiling identical text\n\t\t\t\t// produces the same bytecode and DAG entries. Text changes clear\n\t\t\t\t// bytecodes via DocumentModel.editLine(), so a length check is safe.\n\t\t\t\tif (state.bytecodes.length > 0 && state.bytecodes.length === expressions.length && !state.isVariableDef) {\n\t\t\t\t\treturn { ...baseResult, tier: EvalTier.Skipped, result: null, error: null };\n\t\t\t\t}\n\t\t\t\treturn this.evaluateTier3(state, lineNumber, expressions, inlineSolveCount, baseResult);\n\t\t\t}\n\t\t}\n\n\t\t// Line is clean\n\t\tif (inViewport && state.bytecodes.length > 0) {\n\t\t\t// ── Tier 2: Visible + Cached → Execute from bytecode ────\n\t\t\treturn this.evaluateTier2(state, lineNumber, baseResult);\n\t\t}\n\n\t\t// Clean, not in viewport, or no bytecode → skip\n\t\treturn { ...baseResult, tier: EvalTier.Skipped, result: null, error: null };\n\t}\n\n\t/**\n\t * A line edited into something with nothing to evaluate stops defining.\n\t *\n\t * A heading, a comment or a blank is skipped before anything is compiled, so\n\t * it never reached the registration that tells the graph what it writes, and\n\t * its old edges stood. Turning `:v3 = 19` into `# a heading` therefore left\n\t * `v3` defined for the rest of the session, and a line reading it went on\n\t * answering, or blamed the wrong name: `v3 * v0` reported `v0` as the\n\t * undefined one where a fresh pass reports `v3`.\n\t *\n\t * Registering it with no edges is what says so. Only when the line is dirty,\n\t * since a line that was already empty has nothing to withdraw, and its own\n\t * write set is dropped as part of registering nothing. What it wrote goes\n\t * back to the prefix first, as it does when a line is compiled again: the\n\t * graph unbinds only a name no other line produces, and `:f = 5` under\n\t * `f(x) = x + 4` produces `f` too, so emptying it left the variable and\n\t * emptying the function's line left the function.\n\t *\n\t * Any unit it defined goes the same way, and for the same reason: a line\n\t * drops its own definitions as it is compiled again, which a line nothing\n\t * compiles never reaches. Whether that has to reach the lines that used the\n\t * unit is decided once at the end of the pass, by comparing the units in\n\t * scope before and after it.\n\t */\n\tprivate deregisterIfDirty(state: LineState, lineNumber: number): void {\n\t\tif (!state.dirty) return;\n\t\tfor (const written of state.writes) this.engine.restoreToPrefix(written, lineNumber);\n\t\tstate.reads = [];\n\t\tstate.writes = [];\n\t\tthis.registerWithTags(lineNumber, [], []);\n\t\tthis.engine.undefineUserUnitsFrom(state.lineId);\n\t\t// And its checkpoint, for the same reason Tier 1 drops one for a line\n\t\t// that wrote nothing: a heading defines nothing.\n\t\tthis.checkpointer?.dropCheckpointAt(lineNumber);\n\t}\n\n\t/**\n\t * Register a line's edges, with the groups its text joins and asks about.\n\t *\n\t * The evaluator registers from three places and the engine from two, and a\n\t * line registered twice with different edge sets loses whichever went first.\n\t * They all route through the same helper so the sets cannot disagree.\n\t */\n\tprivate registerWithTags(lineNumber: number, reads: string[], writes: string[]): void {\n\t\tconst text = this.doc.getLineAt(lineNumber)?.text ?? \"\";\n\t\tconst edges = withTagEdges(text, reads, writes);\n\t\tthis.undefineOrphans(this.dag.registerLine(lineNumber, edges.reads, edges.writes));\n\t}\n\n\t/**\n\t * Forget names that no line defines any more.\n\t *\n\t * The graph reports a key when the line that wrote it stops doing so and no\n\t * other line writes it. The VM's variable store only ever accumulated, so\n\t * the value outlived the line: deleting `:x = 12` left `x + 4` answering\n\t * `16` for the rest of the session, and editing away `1 sprint = 2 weeks`\n\t * left the conversions below it working.\n\t *\n\t * The checkpoint chain is told as well, since it records what each line\n\t * wrote and would otherwise put the name back on the next restore.\n\t *\n\t * Only variable names: the key space also holds category tags, globals, data\n\t * sources and line positions, and none of those is a VM binding. A prefix is\n\t * what tells them apart, and a variable's key is the bare name.\n\t */\n\tprivate undefineOrphans(orphaned: readonly string[]): void {\n\t\tthis.engine.forgetOrphanedNames(orphaned);\n\t}\n\n\t/**\n\t * Tier 1: Full pipeline, lex, parse, compile, execute.\n\t * Uses the engine's existing evaluateLine() which handles all pipeline\n\t * stages including DAG updates and LineCache population.\n\t *\n\t * Supports multiple expressions per line (inline solves). Evaluates each\n\t * expression left-to-right through the engine so variable definitions in\n\t * earlier solves update the VM state before later solves are evaluated.\n\t * Reads/writes are aggregated across all expressions for the DAG.\n\t */\n\tprivate evaluateTier1(\n\t\tstate: LineState,\n\t\tlineNumber: number,\n\t\texpressions: string[],\n\t\tinlineSolveCount: number,\n\t\tbaseResult: Omit<EvalLineResult, \"tier\" | \"result\" | \"error\">\n\t): EvalLineResult {\n\t\t// Whatever this line used to define, it does not any more until it says\n\t\t// so again while being compiled below.\n\t\t//\n\t\t// Done here rather than while compiling, because compiling can be\n\t\t// skipped: the bytecode cache is keyed by the expression's TEXT, so a\n\t\t// line edited into something another line already says is a cache hit\n\t\t// and never reaches the compiler. Editing the definition in\n\t\t// `5 sprints in weeks` / `spent` / `1 sprint = 4 weeks` to `spent` is\n\t\t// exactly that, and the unit survived the line that declared it.\n\t\t//\n\t\t// A line that still is a definition re-registers it as it compiles, and\n\t\t// the pass compares the units in scope before and after itself, so\n\t\t// dropping and re-adding the same one invalidates nothing.\n\t\tthis.engine.undefineUserUnitsFrom(state.lineId);\n\n\t\tconst allResults: Value[][] = [];\n\t\tconst allBytecodes: BytecodeProgram[] = [];\n\t\tconst allReads = new Set<string>();\n\t\tconst allWrites = new Set<string>();\n\t\tlet hasVariableDef = false;\n\t\tlet lastValue: Value | null = null;\n\t\tlet firstError: string | null = null;\n\t\tlet anyFailed = false;\n\t\t/** Set when any expression returned a value still waiting on a resolver. */\n\t\tlet anyPending = false;\n\n\t\t// Evaluate each expression independently, a failure in one expression\n\t\t// (e.g., parse error in s`bad syntax`) must not prevent other expressions\n\t\t// on the same line from being evaluated and having their results stored.\n\t\t// Without per-expression error handling, same-line cross-reference inline\n\t\t// solves (s`:a = 5` s`:b = a + 3` s`a + b`) would lose ALL results when\n\t\t// the third expression throws because 'b' references cross a VM state\n\t\t// boundary or the engine encounters a transient error.\n\t\t// Before this line runs, each name it wrote holds what the lines above\n\t\t// left, as it does when a pass from scratch reaches the line. A Tier 1\n\t\t// line is dirty, so its recorded writes may be about text that has\n\t\t// changed; that is fine either way, since a name the new text no\n\t\t// longer writes is one the line no longer defines. See\n\t\t// `ExpressionEngine.restoreToPrefix` for what this closes.\n\t\tfor (const written of state.writes) this.engine.restoreToPrefix(written, lineNumber);\n\n\t\t// Names an earlier expression on this line has already set, so a later\n\t\t// one on the same line that fails does not undo it.\n\t\tconst definedEarlierOnThisLine = new Set<string>();\n\t\t// And the names this line writes that its right-hand side reads, for\n\t\t// Tier 2; see {@link selfReadingWrites}.\n\t\tconst selfReading: string[] = [];\n\t\tfor (const expression of expressions) {\n\t\t\tif (!expression.trim()) continue;\n\n\t\t\tlet value: Value[] | null = null;\n\t\t\tlet entry: { bytecode: BytecodeProgram; readVariables: string[]; writeVariable: string | null } | undefined;\n\n\t\t\ttry {\n\t\t\t\t// evaluateLine returns the single evaluated Value; this evaluator\n\t\t\t\t// keeps its results grouped as Value[] per expression (allResults\n\t\t\t\t// is Value[][]), so wrap the one value into its group here.\n\t\t\t\tconst evaluated = this.engine.evaluateLine(lineNumber, expression);\n\t\t\t\tvalue = [evaluated];\n\t\t\t\tlastValue = evaluated;\n\t\t\t\t// Sync the DocumentModel from the LineCache.\n\t\t\t\t// Use get(lineNumber, expression) instead of getEntryForLine(lineNumber)\n\t\t\t\t// because multiple expressions on the same line share the same lineNumber\n\t\t\t\t// and getEntryForLine always returns the FIRST entry (Map insertion order).\n\t\t\t\tentry = this.engine.getLineCache().get(lineNumber, expression) as typeof entry;\n\t\t\t} catch (e) {\n\t\t\t\tconst errorMessage = e instanceof Error ? e.message : String(e);\n\t\t\t\tif (!firstError) firstError = errorMessage;\n\t\t\t\tanyFailed = true;\n\t\t\t\tvalue = null;\n\t\t\t}\n\n\t\t\t// An unresolved async value does not throw, so `anyFailed` alone would\n\t\t\t// let this line be marked clean while its data has not arrived. Once\n\t\t\t// clean, nothing re-runs the resolver preflight and the value stays\n\t\t\t// pending for good. Treat it as not-yet-complete instead.\n\t\t\tif (value && value.some((v) => v.type === ValueType.Pending)) {\n\t\t\t\tanyPending = true;\n\t\t\t}\n\n\t\t\tif (value) {\n\t\t\t\tallResults.push(value);\n\t\t\t} else {\n\t\t\t\t// Expression failed, push an ErrorValue sentinel so results[] stays\n\t\t\t\t// aligned with expressions[] and bytecodes[] indices. Downstream code\n\t\t\t\t// checking result.type === Error will find it, vs a raw null that NPEs.\n\t\t\t\tallResults.push([errorValue(\"eval_failed\", firstError ?? \"unknown error\")]);\n\t\t\t}\n\n\t\t\t// Failed means the store never happened, which is a throw (or, below, a\n\t\t\t// right-hand side that did not compile). An error that comes back as a\n\t\t\t// value is stored like any other value: errors are values in this\n\t\t\t// engine, `:a = line 9 + 1` leaves `a` holding one, and a reader of `a`\n\t\t\t// reports it. Treating that as a failure too made both paths delete the\n\t\t\t// name instead, which the two of them agreed on and a pass through\n\t\t\t// parseDocument does not.\n\t\t\tconst failed = value === null;\n\t\t\tif (entry) {\n\t\t\t\tallBytecodes.push(entry.bytecode);\n\t\t\t\tfor (const r of entry.readVariables) allReads.add(r);\n\t\t\t\tif (entry.writeVariable) {\n\t\t\t\t\tallWrites.add(entry.writeVariable);\n\t\t\t\t\thasVariableDef = true;\n\t\t\t\t\tif (readsItself(entry.readVariables, entry.writeVariable)) selfReading.push(entry.writeVariable);\n\t\t\t\t\t// The write stays declared whatever the answer was: the line is\n\t\t\t\t\t// still the one that defines the name, and a running total that\n\t\t\t\t\t// dropped its write when its step failed was never re-seeded\n\t\t\t\t\t// again, stuck on the error after the line it read was fixed.\n\t\t\t\t\t//\n\t\t\t\t\t// What a failed definition may not do is keep its own old value.\n\t\t\t\t\t// A pass from scratch skips the store, leaving the name as the\n\t\t\t\t\t// lines above had it, and this puts it back there. Unless an\n\t\t\t\t\t// earlier expression on this same line set it, in which case that\n\t\t\t\t\t// is what the lines above left and it is already in place. A\n\t\t\t\t\t// pending value is not a failure: it has not arrived yet.\n\t\t\t\t\tif (failed && !definedEarlierOnThisLine.has(entry.writeVariable)) {\n\t\t\t\t\t\tthis.engine.restoreToPrefix(entry.writeVariable, lineNumber);\n\t\t\t\t\t} else if (!failed) {\n\t\t\t\t\t\tdefinedEarlierOnThisLine.add(entry.writeVariable);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Fallback: LineCache missed, compile expression ourselves\n\t\t\t\ttry {\n\t\t\t\t\tconst { program, reads, writes } = this.engine.compileExpression(expression);\n\t\t\t\t\tallBytecodes.push(program);\n\t\t\t\t\tfor (const r of reads) allReads.add(r);\n\t\t\t\t\tfor (const w of writes) allWrites.add(w);\n\t\t\t\t\tif (writes.length > 0) hasVariableDef = true;\n\t\t\t\t\tfor (const w of writes) if (readsItself(reads, w)) selfReading.push(w);\n\t\t\t\t\t// The same restore as the cached branch above; this is the branch\n\t\t\t\t\t// a throwing right-hand side takes, since a throw leaves no cache\n\t\t\t\t\t// entry behind, and it was the one path that kept its old value.\n\t\t\t\t\tfor (const w of writes) {\n\t\t\t\t\t\tif (failed && !definedEarlierOnThisLine.has(w)) this.engine.restoreToPrefix(w, lineNumber);\n\t\t\t\t\t\telse if (!failed) definedEarlierOnThisLine.add(w);\n\t\t\t\t\t}\n\t\t\t\t} catch (compileErr) {\n\t\t\t\t\t// Push empty bytecode, expression will recompile on next pass.\n\t\t\t\t\t// The expression still failed to compile, but reads/writes were\n\t\t\t\t\t// already extracted from its tokens before the parse attempt\n\t\t\t\t\t// compileExpression() surfaces them via the thrown error's\n\t\t\t\t\t// context. Register them anyway so the DAG knows this line\n\t\t\t\t\t// depends on those variables and re-evaluates it once they\n\t\t\t\t\t// become defined, instead of losing the dependency entirely.\n\t\t\t\t\tallBytecodes.push({ opcodes: new Uint8Array(0), numbers: new Float64Array(0), strings: [], hasAsync: false });\n\t\t\t\t\tif (compileErr instanceof EngineError && compileErr.context) {\n\t\t\t\t\t\tconst errReads = compileErr.context.reads;\n\t\t\t\t\t\tconst errWrites = compileErr.context.writes;\n\t\t\t\t\t\tif (Array.isArray(errReads)) for (const r of errReads) allReads.add(r);\n\t\t\t\t\t\tif (Array.isArray(errWrites)) {\n\t\t\t\t\t\t\tfor (const w of errWrites) {\n\t\t\t\t\t\t\t\tallWrites.add(w);\n\t\t\t\t\t\t\t\t// A definition that does not even compile set nothing.\n\t\t\t\t\t\t\t\tif (!definedEarlierOnThisLine.has(w)) this.engine.restoreToPrefix(w, lineNumber);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst reads = [...allReads];\n\t\tconst writes = [...allWrites];\n\n\t\t// Always store results, even partial ones. If any expression failed\n\t\t// the line stays dirty so the failed expression(s) get retried on the\n\t\t// next evaluation pass. But successfully-evaluated expressions' results\n\t\t// are preserved so the DAG, UI decorations, and downstream consumers\n\t\t// can use them immediately.\n\t\tif (anyFailed || anyPending) {\n\t\t\t// Store partial results via updateLineCompiled (doesn't clear dirty).\n\t\t\t// The successful expressions' bytecodes are cached so Tier 2 works\n\t\t\t// for them on the next scroll pass.\n\t\t\tthis.doc.updateLineCompiled(\n\t\t\t\tstate.lineId,\n\t\t\t\texpressions,\n\t\t\t\tallBytecodes,\n\t\t\t\treads,\n\t\t\t\twrites,\n\t\t\t\thasVariableDef,\n\t\t\t\tinlineSolveCount,\n\t\t\t);\n\t\t\t// Also set results for the successful expressions\n\t\t\tstate.results = allResults;\n\t\t\tstate.result = allResults[0]?.[0] ?? null;\n\t\t\tstate.inlineSolveCount = inlineSolveCount;\n\t\t\tstate.expressions = expressions;\n\t\t} else {\n\t\t\t// All expressions succeeded, store full results and mark clean\n\t\t\tthis.doc.updateLineResult(\n\t\t\t\tstate.lineId,\n\t\t\t\tallResults,\n\t\t\t\tallBytecodes,\n\t\t\t\texpressions,\n\t\t\t\treads,\n\t\t\t\twrites,\n\t\t\t\thasVariableDef,\n\t\t\t\tinlineSolveCount,\n\t\t\t);\n\t\t}\n\n\t\tif (selfReading.length > 0) this.selfReadingWrites.set(state.lineId, selfReading);\n\t\telse this.selfReadingWrites.delete(state.lineId);\n\n\t\t// Register reads/writes in DAG (aggregated across all expressions).\n\t\t// Always register, even lines with no reads/writes (pure expressions\n\t\t// like \"2+2\") need DAG entries so downstream queries for line presence work.\n\t\tthis.registerWithTags(lineNumber, reads, writes);\n\n\t\t// ── Checkpoint after variable definition ──\n\t\t// A pending value is not a value worth checkpointing: restoring it would\n\t\t// reinstate the unresolved placeholder rather than the eventual result.\n\t\t// A line that failed is snapshotted too. Its names were put back to\n\t\t// the prefix as it failed, so what the VM holds for them now is right,\n\t\t// and skipping the snapshot left the entry from before the edit in the\n\t\t// chain: `:v0 = 49` edited into a line that throws went on telling the\n\t\t// lines below that the prefix held 49. A pending value is still left\n\t\t// out, since it has not arrived.\n\t\tif (this.checkpointer && writes.length > 0 && !anyPending) {\n\t\t\tthis.checkpointer.snapshot(lineNumber, state.lineId, writes, functionsDefinedBy(allBytecodes));\n\t\t} else if (this.checkpointer && writes.length === 0) {\n\t\t\t// A line that defines nothing any more has nothing for the chain to\n\t\t\t// say about it, and what it used to say is what the lines below would\n\t\t\t// otherwise be told the prefix holds.\n\t\t\tthis.checkpointer.dropCheckpointAt(lineNumber);\n\t\t}\n\n\t\treturn {\n\t\t\t...baseResult,\n\t\t\ttier: EvalTier.Tier1,\n\t\t\tresult: lastValue,\n\t\t\tresults: allResults,\n\t\t\terror: firstError,\n\t\t};\n\t}\n\n\t/**\n\t * Tier 2: Execute from cached bytecode only.\n\t * Skips lexing, parsing, and compiling, runs the pre-compiled bytecode\n\t * against the engine's shared VM. Supports multiple bytecodes per line\n\t * (inline solves), each is executed left-to-right so variable definitions\n\t * in earlier bytecodes update the VM before later ones run.\n\t * Assumes the VM already has correct variable state from preceding\n\t * Tier-1 evaluations.\n\t */\n\tprivate evaluateTier2(\n\t\tstate: LineState,\n\t\tlineNumber: number,\n\t\tbaseResult: Omit<EvalLineResult, \"tier\" | \"result\" | \"error\">\n\t): EvalLineResult {\n\t\tif (state.bytecodes.length === 0) {\n\t\t\treturn { ...baseResult, tier: EvalTier.Skipped, result: null, error: null };\n\t\t}\n\n\t\tconst results: Value[][] = [];\n\t\tlet lastValue: Value | null = null;\n\t\tlet firstError: string | null = null;\n\t\tlet anyFailed = false;\n\n\t\t// Execute each bytecode independently, a failure in one should not\n\t\t// prevent other bytecodes on the same line from executing. Same-line\n\t\t// inline solves with variable definitions (s`:a = 5` s`a + 3`) rely\n\t\t// on earlier bytecodes updating the VM before later ones execute.\n\t\t// A clean line's VM state is the prefix, with one exception a pass from\n\t\t// scratch never has: a definition that reads the name it writes finds\n\t\t// its own previous answer there. `:v3 = v3 + 3` climbed by three a pass\n\t\t// once the `:v3 = 44` above it was edited away, where from scratch\n\t\t// nothing defines `v3` and the line reports so. The prefix is put back\n\t\t// first, per expression, and again after one that fails, since a failed\n\t\t// store leaves the name holding this line's old answer too.\n\t\t// The line's own record of what it reads and writes, which moves with the\n\t\t// line. The line cache is keyed by position and is not renumbered by a\n\t\t// structural edit, so a line that moved found no entry there and its\n\t\t// self-read went unrestored: `:v3 = v3 + 3` climbed by three a pass once\n\t\t// the `:v3 = 44` above it was deleted rather than edited.\n\t\t//\n\t\t// Genuinely reads it: the graph's convention records every definition as\n\t\t// a reader of its own name once, so the name has to appear among the\n\t\t// reads a second time, from the right-hand side, before this fires. That\n\t\t// keeps the prefix lookup off the ordinary `:x = 5`, which is most\n\t\t// definitions and would otherwise pay for it on every pass.\n\t\tfor (const written of this.selfReadingWrites.get(state.lineId) ?? []) {\n\t\t\tthis.engine.restoreToPrefix(written, lineNumber);\n\t\t}\n\t\tconst cache = this.engine.getLineCache();\n\t\tconst definedEarlierOnThisLine = new Set<string>();\n\t\tfor (let i = 0; i < state.bytecodes.length; i++) {\n\t\t\tconst bytecode = state.bytecodes[i];\n\t\t\tif (bytecode.opcodes.length === 0) continue;\n\t\t\t// Per expression where the cache still has the entry, so a later\n\t\t\t// expression that fails does not undo an earlier one on the same line;\n\t\t\t// the line's whole write set otherwise, which a moved line falls back to.\n\t\t\tconst entry = cache.get(lineNumber, state.expressions[i] ?? \"\") as\n\t\t\t\t{ readVariables: string[]; writeVariable: string | null } | undefined;\n\t\t\tconst writtenHere: readonly string[] = entry !== undefined\n\t\t\t\t? (entry.writeVariable === null ? [] : [entry.writeVariable])\n\t\t\t\t: state.writes;\n\t\t\tlet failed = false;\n\t\t\ttry {\n\t\t\t\tconst value = this.engine.executeCached(bytecode, lineNumber);\n\t\t\t\tlastValue = value;\n\t\t\t\tresults.push([value]);\n\t\t\t} catch (e) {\n\t\t\t\tconst errorMessage = e instanceof Error ? e.message : String(e);\n\t\t\t\tif (!firstError) firstError = errorMessage;\n\t\t\t\tanyFailed = true;\n\t\t\t\tfailed = true;\n\t\t\t\t// Push error sentinel to maintain results[i] ↔ bytecodes[i] alignment\n\t\t\t\tresults.push([errorValue(\"exec_failed\", errorMessage)]);\n\t\t\t}\n\t\t\tfor (const written of writtenHere) {\n\t\t\t\tif (failed && !definedEarlierOnThisLine.has(written)) this.engine.restoreToPrefix(written, lineNumber);\n\t\t\t\telse if (!failed) definedEarlierOnThisLine.add(written);\n\t\t\t}\n\t\t}\n\n\t\t// Update DAG: re-register reads/writes from the cached metadata.\n\t\t// Always register, even empty reads/writes so DAG line-presence queries work.\n\t\tthis.registerWithTags(lineNumber, state.reads, state.writes);\n\n\t\t// A line that ran nothing keeps the result it already had.\n\t\t//\n\t\t// Some expressions compile to a program with no opcodes: a unit\n\t\t// definition (`1 sprint = 2 weeks`) and an equation stored for later\n\t\t// (`y + 3 = 10`) both do their work while being compiled, and have\n\t\t// nothing left to execute. The loop above skips those programs, so\n\t\t// `results` comes back empty, and assigning it unconditionally replaced\n\t\t// the answer Tier 1 had computed with nothing. The line showed\n\t\t// `sprint defined` on the pass that compiled it and went blank on the\n\t\t// next one, with the document untouched, because a live document is\n\t\t// evaluated again and again rather than once.\n\t\t//\n\t\t// Executing nothing produces no new result, which is not the same as\n\t\t// producing an empty one.\n\t\tif (results.length > 0) {\n\t\t\tstate.results = results;\n\t\t\tstate.result = results[0][0] ?? null;\n\t\t} else if (state.result !== null && isArenaActive()) {\n\t\t\t// Kept by value, not by reference.\n\t\t\t//\n\t\t\t// A pass runs with the Value arena on, and a result that never came\n\t\t\t// back through the VM's HALT is arena-owned: nothing cloned it on\n\t\t\t// the way out, because the program had no opcodes to return from.\n\t\t\t// Holding that object means holding a slot the arena hands to a\n\t\t\t// later line, and the line's own answer is then overwritten in\n\t\t\t// place. `1 sprint = 2 weeks` read `sprint defined` and then, once\n\t\t\t// the line below it had run, read that line's number instead.\n\t\t\t// Every stored result, keeping the line's shape: a line can hold\n\t\t\t// several expressions, and all of them can be definitions.\n\t\t\tstate.results = state.results.map((group) => group.map(persistentValue));\n\t\t\tstate.result = state.results[0]?.[0] ?? persistentValue(state.result);\n\t\t}\n\t\tif (anyFailed) {\n\t\t\t// Mark dirty so failed bytecodes are re-compiled (Tier 1) next pass\n\t\t\tthis.doc.markDirty(state.lineId);\n\t\t}\n\n\t\t// ── Checkpoint after a line that writes ──\n\t\t// Tier 1 does this for a line it compiled; a line running from cache\n\t\t// writes the same names to the VM and has to record them the same way.\n\t\t// Without it the chain has a hole wherever a clean line sits between\n\t\t// two dirty ones, and `snapshot`'s re-run truncation would drop\n\t\t// entries that nothing ever puts back.\n\t\tif (this.checkpointer && state.writes.length > 0 && !anyFailed) {\n\t\t\tthis.checkpointer.snapshot(lineNumber, state.lineId, state.writes, functionsDefinedBy(state.bytecodes));\n\t\t}\n\n\t\treturn {\n\t\t\t...baseResult,\n\t\t\ttier: EvalTier.Tier2,\n\t\t\t// The stored result, for the same reason: a caller reading this pass's\n\t\t\t// answer for the line must see what the line says, not the nothing\n\t\t\t// that running no opcodes returned.\n\t\t\tresult: results.length > 0 ? lastValue : state.result,\n\t\t\tresults: results.length > 0 ? results : state.results,\n\t\t\terror: firstError,\n\t\t};\n\t}\n\n\t/**\n\t * Tier 3: Compile-only for invisible lines.\n\t * Lex → Parse → Compile to discover reads/writes for the dependency graph.\n\t * Executes the bytecode ONLY if the line defines a variable (isVariableDef\n\t * or writes.length > 0), because variable assignments affect VM state\n\t * that other lines depend on. Pure expression lines are compiled but NOT\n\t * executed, saving CPU for large documents.\n\t *\n\t * Supports multiple expressions per line (inline solves). Each is compiled\n\t * separately; variable-def expressions are also executed.\n\t */\n\tprivate evaluateTier3(\n\t\tstate: LineState,\n\t\tlineNumber: number,\n\t\texpressions: string[],\n\t\tinlineSolveCount: number,\n\t\tbaseResult: Omit<EvalLineResult, \"tier\" | \"result\" | \"error\">\n\t): EvalLineResult {\n\t\tconst allBytecodes: BytecodeProgram[] = [];\n\t\tconst allReads = new Set<string>();\n\t\tconst allWrites = new Set<string>();\n\t\tlet hasVariableDef = false;\n\t\tlet lastResult: Value | null = null;\n\t\tlet firstError: string | null = null;\n\t\tlet anyFailed = false;\n\t\t/** Set when a result is still waiting on a resolver. */\n\t\tlet anyPending = false;\n\n\t\t// The same discipline as Tier 1, because a definition runs here too:\n\t\t// before it does, each name it wrote holds what the lines above left,\n\t\t// and a definition that fails is put back there. Without it a line\n\t\t// scrolled out of view kept the value it had before the edit, and one\n\t\t// that read its own name climbed by its step on every pass, for as long\n\t\t// as it stayed out of view. See `ExpressionEngine.restoreToPrefix`.\n\t\tfor (const written of state.writes) this.engine.restoreToPrefix(written, lineNumber);\n\t\tconst definedEarlierOnThisLine = new Set<string>();\n\t\tconst selfReading: string[] = [];\n\n\t\t// Compile each expression independently, a parse error in one\n\t\t// should not prevent other expressions from being compiled and\n\t\t// having their reads/writes registered in the DAG.\n\t\tfor (const expression of expressions) {\n\t\t\tif (!expression.trim()) continue;\n\n\t\t\tlet compiled: { program: BytecodeProgram; reads: string[]; writes: string[] };\n\t\t\ttry {\n\t\t\t\tcompiled = this.engine.compileExpression(expression);\n\t\t\t} catch (e) {\n\t\t\t\tconst errorMessage = e instanceof Error ? e.message : String(e);\n\t\t\t\tif (!firstError) firstError = errorMessage;\n\t\t\t\tanyFailed = true;\n\t\t\t\t// Push empty bytecode placeholder for alignment\n\t\t\t\tallBytecodes.push({ opcodes: new Uint8Array(0), numbers: new Float64Array(0), strings: [], hasAsync: false });\n\t\t\t\t// A definition that does not compile set nothing. Its names are\n\t\t\t\t// still registered, as Tier 1 registers them, so the line runs\n\t\t\t\t// again once they are defined.\n\t\t\t\tif (e instanceof EngineError && e.context) {\n\t\t\t\t\tconst errReads = e.context.reads;\n\t\t\t\t\tconst errWrites = e.context.writes;\n\t\t\t\t\tif (Array.isArray(errReads)) for (const r of errReads) allReads.add(r);\n\t\t\t\t\tif (Array.isArray(errWrites)) {\n\t\t\t\t\t\tfor (const w of errWrites) {\n\t\t\t\t\t\t\tallWrites.add(w);\n\t\t\t\t\t\t\tif (!definedEarlierOnThisLine.has(w)) this.engine.restoreToPrefix(w, lineNumber);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst { program, reads, writes } = compiled;\n\t\t\tallBytecodes.push(program);\n\t\t\tfor (const r of reads) allReads.add(r);\n\t\t\tfor (const w of writes) allWrites.add(w);\n\t\t\tif (writes.length > 0) hasVariableDef = true;\n\t\t\tfor (const w of writes) if (readsItself(reads, w)) selfReading.push(w);\n\n\t\t\tif (writes.length > 0 && program.opcodes.length > 0) {\n\t\t\t\t// Variable definitions MUST execute to maintain VM state\n\t\t\t\ttry {\n\t\t\t\t\tlastResult = this.engine.executeCached(program, lineNumber);\n\t\t\t\t\t// Same reasoning as Tier 1: a pending result does not throw,\n\t\t\t\t\t// and marking the line clean would strand it unresolved.\n\t\t\t\t\tif (lastResult && lastResult.type === ValueType.Pending) {\n\t\t\t\t\t\tanyPending = true;\n\t\t\t\t\t}\n\t\t\t\t\tfor (const w of writes) definedEarlierOnThisLine.add(w);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconst errorMessage = e instanceof Error ? e.message : String(e);\n\t\t\t\t\tif (!firstError) firstError = errorMessage;\n\t\t\t\t\tanyFailed = true;\n\t\t\t\t\t// A definition that threw stored nothing; see Tier 1.\n\t\t\t\t\tfor (const w of writes) {\n\t\t\t\t\t\tif (!definedEarlierOnThisLine.has(w)) this.engine.restoreToPrefix(w, lineNumber);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst reads = [...allReads];\n\t\tconst writes = [...allWrites];\n\n\t\t// Store compile-only state in DocumentModel, even partial results\n\t\t// preserve successful expressions' bytecodes and DAG data.\n\t\tthis.doc.updateLineCompiled(\n\t\t\tstate.lineId,\n\t\t\texpressions,\n\t\t\tallBytecodes,\n\t\t\treads,\n\t\t\twrites,\n\t\t\thasVariableDef,\n\t\t\tinlineSolveCount,\n\t\t);\n\n\t\t// Register reads/writes in DAG regardless, partial data is valid.\n\t\t// Always register, even empty reads/writes for DAG line-presence queries.\n\t\tthis.registerWithTags(lineNumber, reads, writes);\n\n\t\tif (selfReading.length > 0) this.selfReadingWrites.set(state.lineId, selfReading);\n\t\telse this.selfReadingWrites.delete(state.lineId);\n\n\t\tif (hasVariableDef && lastResult && !anyFailed && !anyPending) {\n\t\t\tstate.results = [[lastResult]];\n\t\t\tstate.result = lastResult;\n\t\t\tthis.doc.markClean(state.lineId);\n\t\t}\n\n\t\t// ── Checkpoint after variable definition ────────────\n\t\t// Taken whether or not the line failed, as in Tier 1: its names were\n\t\t// put back to the prefix as it failed, so what the VM holds for them\n\t\t// now is right, and the entry from before the edit is the wrong thing\n\t\t// to keep. A pending value is left out, since it has not arrived. A\n\t\t// line that defines nothing any more has nothing for the chain to say.\n\t\tif (this.checkpointer && writes.length > 0 && !anyPending) {\n\t\t\tthis.checkpointer.snapshot(lineNumber, state.lineId, writes, functionsDefinedBy(allBytecodes));\n\t\t} else if (this.checkpointer && writes.length === 0) {\n\t\t\tthis.checkpointer.dropCheckpointAt(lineNumber);\n\t\t}\n\n\t\treturn { ...baseResult, tier: EvalTier.Tier3, result: lastResult, results: hasVariableDef && lastResult && !anyFailed && !anyPending ? [[lastResult]] : undefined, error: firstError };\n\t}\n\n\t// ── Public checkpoint API (used by Phase 5.2e setViewport) ──────\n\n\t/**\n\t * Restore the VM to the state at or just after the given line number.\n\t *\n\t * Finds the nearest checkpoint at or before `lineNumber` and replays\n\t * all variable definitions from the checkpoint chain into the VM.\n\t * After calling this, the VM is ready to evaluate lines starting at\n\t * `lineNumber + 1` without re-evaluating all preceding lines.\n\t *\n\t * **Usage:** Phase 5.2e's `setViewport()` calls `restoreTo(viewport.startLine - 1)`\n\t * before evaluating only the newly visible lines. This is the key to\n\t * O(visible lines) scrolling.\n\t *\n\t * @param lineNumber The line number to restore to. Variables defined\n\t * at lines ≤ this number will be available in the VM.\n\t */\n\trestoreTo(lineNumber: number): void {\n\t\tif (this.checkpointer) {\n\t\t\tthis.checkpointer.restoreTo(lineNumber);\n\t\t}\n\t}\n\n\t/**\n\t * Get the VM checkpointer, or null if checkpointing is disabled.\n\t */\n\tgetCheckpointer(): VMCheckpointer | null {\n\t\treturn this.checkpointer;\n\t}\n\n\t/**\n\t * Get the PageManager (Phase 5.2g).\n\t * Exposed for testing.\n\t */\n\tgetPageManager(): PageManager {\n\t\treturn this.pageManager;\n\t}\n\n\t// ── Phase 5.2g: Directional preloading ──────────────────────────\n\n\t/**\n\t * Preload the next 1–2 pages in the current scroll direction.\n\t *\n\t * Called during `setViewport()` (scroll-only path). Collects dirty\n\t * uncompiled lines in pages just beyond the viewport and dispatches\n\t * them to the background compilation worker so bytecode is ready\n\t * before the user scrolls those lines into view.\n\t */\n\tprivate preloadNextPages(viewport: ViewportRange): void {\n\t\tconst targets = this.pageManager.getPreloadTargets(viewport, this.doc);\n\t\tif (targets.length === 0) return;\n\n\t\t// Lazy-init worker if needed\n\t\tif (!this.compilationWorker) {\n\t\t\tthis.compilationWorker = new CompilationWorkerManager();\n\t\t}\n\n\t\t// Fire-and-forget: worker compiles, stores bytecode on response\n\t\tthis.compilationWorker.compileBatch(targets).then((results) => {\n\t\t\tthis.compilationWorker!.storeResults(results, this.doc);\n\t\t}).catch((_err) => {\n\t\t\t// Non-fatal, next evaluate() will compile synchronously\n\t\t});\n\t}\n\n\t/**\n\t * Collect invisible dirty lines that need background compilation.\n\t *\n\t * Iterates lines beyond `viewport.endLine`, filtering for:\n\t * - Dirty lines (need re-compilation)\n\t * - Non-empty, non-markdown lines\n\t * - No existing bytecode (skip already-compiled Tier 3 lines)\n\t *\n\t * Returns CompileRequestItem[] suitable for CompilationWorkerManager.\n\t */\n\tprivate collectInvisibleCompileTargets(viewport: ViewportRange): CompileRequestItem[] {\n\t\tconst items: CompileRequestItem[] = [];\n\t\tconst docEnd = this.doc.lineCount;\n\t\tconst startPos = viewport.endLine + 1;\n\n\t\tfor (let pos = startPos; pos <= docEnd; pos++) {\n\t\t\tconst state = this.doc.getLineAt(pos);\n\t\t\tif (!state) continue;\n\n\t\t\t// Skip clean lines\n\t\t\tif (!state.dirty) continue;\n\n\t\t\t// Skip already-compiled lines\n\t\t\tif (state.bytecodes.length > 0 && !state.isVariableDef) continue;\n\n\t\t\t// Skip empty/markdown-only lines\n\t\t\tif (state.isEmpty || isEmptyLine(state.text)) continue;\n\n\t\t\tconst { expressions } = this.extractExpressions(state);\n\t\t\tif (expressions.length === 0) continue;\n\n\t\t\t// Create one CompileRequestItem per expression (inline solves\n\t\t\t// on the same line share the same lineId + textHash). The worker\n\t\t\t// compiles each independently; storeResults batches by lineId.\n\t\t\tfor (const expression of expressions) {\n\t\t\t\tif (!expression.trim()) continue;\n\t\t\t\titems.push({\n\t\t\t\t\tlineId: state.lineId,\n\t\t\t\t\texpression,\n\t\t\t\t\ttextHash: state.textHash,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn items;\n\t}\n\n\t/**\n\t * Extract all evaluable expressions from a LineState.\n\t *\n\t * For full-line expressions: returns `{ expressions: [trimmedText], inlineSolveCount: 0 }`.\n\t * For inline solve lines: returns `{ expressions: [...allSolves], inlineSolveCount: N }`.\n\t * For pre-extracted (cached) expressions: returns the cached array.\n\t *\n\t * Inline solves are extracted left-to-right via the sharedLexer, so variable\n\t * definitions in earlier solves (e.g., `s\\`x = 5\\` more text s\\`x + 1\\``)\n\t * correctly update the VM state before later solves are evaluated.\n\t */\n\tprivate extractExpressions(state: LineState): { expressions: string[]; inlineSolveCount: number } {\n\t\t// Use pre-extracted expressions if available (from cache / previous evaluation)\n\t\tif (state.expressions.length > 0) {\n\t\t\treturn { expressions: state.expressions, inlineSolveCount: state.inlineSolveCount };\n\t\t}\n\n\t\tconst trimmed = state.text.trim();\n\t\tif (trimmed.length === 0) return { expressions: [], inlineSolveCount: 0 };\n\n\t\t// Check for inline solve syntax: s`expression`\n\t\tconst inlineSpans = sharedLexer.findInlineSolves(state.text);\n\t\tif (inlineSpans.length > 0) {\n\t\t\treturn {\n\t\t\t\texpressions: inlineSpans.map(s => s.expression),\n\t\t\t\tinlineSolveCount: inlineSpans.length,\n\t\t\t};\n\t\t}\n\n\t\t// Full-line expression\n\t\treturn { expressions: [trimmed], inlineSolveCount: 0 };\n\t}\n}\n","/**\n * Whole-document evaluation through the incremental engine.\n *\n * The engine has two ways to read a document, and they are not interchangeable.\n * {@link ExpressionEngine.parseDocument} is the light batch pass: it scans the\n * text, reads earlier lines' results, and skips markdown, which is everything\n * line references, category tags and table columns need. What it has no way to\n * do is re-run an earlier line with a variable bound to a trial value, which is\n * exactly what goal seek (`solve line N for x = target`) is, so goal seek comes\n * back as a `GOAL_SEEK_NO_DOCUMENT` error through `parseDocument`.\n *\n * The re-run primitive lives on the incremental `DocumentModel` +\n * `ThreeTierEvaluator` path (a live editor drives it on every keystroke). This\n * helper stands that path up for a single pass and hands back the familiar\n * {@link ParsingResult}, so a caller reads the answers exactly as it would from\n * `parseDocument`, goal seek included. A documentation notepad that wants goal\n * seek to resolve, and the tests that prove those examples, both go through\n * here.\n *\n * Two boundaries, each deliberate:\n *\n * - It builds a fresh model and evaluator per call and disposes the evaluator\n * before returning, which suits occasional evaluation, not the keystroke loop\n * a real editor runs against one long-lived `ThreeTierEvaluator`.\n * - Unlike `parseDocument`, it does not classify a markdown table's own rows as\n * skippable: they reach the evaluator as expressions and error. A document\n * that mixes a raw table with goal seek is the one case that wants both paths,\n * the table read through `parseDocument` and the goal seek through here.\n */\nimport type { ExpressionEngine } from \"@solve-js/engine/ExpressionEngine\";\nimport { DocumentModel } from \"@solve-js/engine/DocumentModel\";\nimport { ThreeTierEvaluator } from \"@solve-js/engine/ThreeTierEvaluator\";\nimport { VMCheckpointer } from \"@solve-js/vm/VMCheckpoints\";\nimport { findInlineSolvesInLine } from \"@solve-js/engine/ExpressionEngineSafety\";\nimport { Value, ValueType } from \"@solve-js/vm/Value\";\nimport type {\n\tParsedLine,\n\tParsingResult,\n\tUnifiedParsingOptions,\n\tInlineSolvePosition,\n} from \"@solve-js/types/ParsingResult\";\n\n/** Read the human-readable message off an error Value (`unit` holds it, `value` holds the code). */\nfunction errorMessageOf(value: Value): string {\n\treturn typeof value.unit === \"string\" ? value.unit : String(value.value);\n}\n\n/**\n * Evaluate a whole document with full cross-line context, including the line\n * re-runs goal seek needs, and return per-line results in the same shape\n * {@link ExpressionEngine.parseDocument} produces.\n *\n * @param engine - The engine to borrow for the pass. Its current document model\n * (if any) is restored before returning.\n * @param input - The document text, newline-separated.\n * @param _options - Accepted so a call reads the same as `parseDocument`'s, but\n * not consulted: the incremental pass reads the document as markdown regardless.\n */\nexport function evaluateDocument(\n\tengine: ExpressionEngine,\n\tinput: string,\n\t_options: UnifiedParsingOptions = { inputType: \"markdown\" },\n): ParsingResult {\n\tconst previousDocumentModel = engine.getDocumentModel();\n\n\tconst doc = new DocumentModel();\n\tdoc.setDocument(input);\n\t// The constructor wires the model onto the engine (engine.setDocumentModel),\n\t// which is what makeLineContext reads to expose the re-run primitive.\n\t// The chain the evaluator records as it goes, and the async batcher reads\n\t// when a value arrives later. Without it a re-run of a few lines reads the\n\t// variable state the document ENDS in rather than the state each line sits\n\t// in, which is wrong for any name defined more than once.\n\tconst evaluator = new ThreeTierEvaluator(doc, engine, new VMCheckpointer(engine.getVM()));\n\n\ttry {\n\t\tconst lineCount = doc.lineCount;\n\t\tevaluator.evaluate({ startLine: 1, endLine: lineCount });\n\n\t\tconst lines: ParsedLine[] = [];\n\t\tconst errors: string[] = [];\n\t\t// Document character offsets are not carried on the line model, so they\n\t\t// are accumulated here to keep startPosition/endPosition faithful to\n\t\t// parseDocument's, one newline between lines.\n\t\tlet offset = 0;\n\n\t\tfor (let n = 1; n <= lineCount; n++) {\n\t\t\tconst state = doc.getLineAt(n)!;\n\t\t\tconst text = state.text;\n\t\t\tconst startPosition = offset;\n\t\t\tconst endPosition = offset + text.length;\n\t\t\toffset = endPosition + 1; // + the newline that separated this line from the next\n\n\t\t\tconst hasInlineSolves = state.inlineSolveCount > 0;\n\t\t\tlet inlineSolves: InlineSolvePosition[] = [];\n\t\t\tlet expression: string | null = null;\n\t\t\tlet result: Value | null = null;\n\n\t\t\tif (state.isEmpty) {\n\t\t\t\t// Prose, a heading, or a blank line: nothing to report.\n\t\t\t} else if (hasInlineSolves) {\n\t\t\t\t// Zip the located spans against the line's per-expression results,\n\t\t\t\t// which the model holds in the same left-to-right order.\n\t\t\t\tinlineSolves = findInlineSolvesInLine(text, n).map((span, i) => {\n\t\t\t\t\tconst value = state.results[i]?.[0] ?? null;\n\t\t\t\t\tif (value && value.type === ValueType.Error) {\n\t\t\t\t\t\tconst message = errorMessageOf(value);\n\t\t\t\t\t\terrors.push(`Line ${n}: ${message}`);\n\t\t\t\t\t\treturn { ...span, result: null, error: message };\n\t\t\t\t\t}\n\t\t\t\t\treturn { ...span, result: value, error: null };\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\texpression = state.expressions[0] ?? null;\n\t\t\t\t// A returned error (goal seek with no document, a faulted\n\t\t\t\t// conversion) stays in `result` as an error-typed Value, exactly\n\t\t\t\t// as parseDocument leaves it, so both document passes report a\n\t\t\t\t// line the same way and a caller can compare them value for\n\t\t\t\t// value. It is also gathered into `errors` for a flat list.\n\t\t\t\tresult = state.result ?? null;\n\t\t\t\tif (result && result.type === ValueType.Error) {\n\t\t\t\t\terrors.push(`Line ${n}: ${errorMessageOf(result)}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlines.push({\n\t\t\t\tlineNumber: n,\n\t\t\t\ttext,\n\t\t\t\tstartPosition,\n\t\t\t\tendPosition,\n\t\t\t\tisEmpty: state.isEmpty,\n\t\t\t\thasInlineSolves,\n\t\t\t\tinlineSolves,\n\t\t\t\texpression,\n\t\t\t\t// Always null: the incremental pass stores a line's failure as an\n\t\t\t\t// error-typed Value in `result` (above), never as a thrown error\n\t\t\t\t// the way parseDocument sets this field. Kept for shape parity.\n\t\t\t\terror: null,\n\t\t\t\tresult,\n\t\t\t});\n\t\t}\n\n\t\treturn { lines, totalLines: lineCount, errors };\n\t} finally {\n\t\t// Drop this pass's evaluator (unsubscribes it from the shared global\n\t\t// store) and put back whatever document the host had wired, so borrowing\n\t\t// the engine for one pass leaves nothing behind.\n\t\tevaluator.terminateWorker();\n\t\tengine.setDocumentModel(previousDocumentModel);\n\t}\n}\n"]}
|