This site describes solve-engine as it is on main: 2.43.0, which npm does not have yet. npm installs 2.40.0, so a page may show an answer that version does not give yet.
DependencyGraph
Defined in: packages/engine/src/vm/DependencyGraph.ts:371
Dependency graph for variable and data-source tracking across document lines.
Tracks which lines read/write which variables, and propagates changes through the graph when a variable is modified. Supports:
- Variable dependency tracking (registerLine, getAffectedLines)
- Data-source dependency tracking (registerLineDataSourceDependency)
- Topological ordering of affected lines (getAffectedLinesInOrder)
- Efficient removal of deleted lines (removeLine)
Constructors
Section titled “Constructors”Constructor
Section titled “Constructor”new DependencyGraph(): DependencyGraph;Returns
Section titled “Returns”DependencyGraph
Methods
Section titled “Methods”clear()
Section titled “clear()”clear(): void;Defined in: packages/engine/src/vm/DependencyGraph.ts:1692
Clear all dependency graph state. Called on document switch or engine reset.
Returns
Section titled “Returns”void
directConsumersOf()
Section titled “directConsumersOf()”directConsumersOf(key): ReadonlySet<number>;Defined in: packages/engine/src/vm/DependencyGraph.ts:1524
The lines that read key, one edge away.
The consumer index, which registerLine keeps clear of the line
that writes the key: a definition’s read of its own name is a convention
for the graph’s benefit, not a dependency, and x += 1 reads its total
to step it, not to depend on another line. That is what makes this index
the right one for finding a cycle, where the raw reads would make every
definition a self-loop and every twice-defined name a two-cycle.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
key | string | An edge key. |
Returns
Section titled “Returns”ReadonlySet<number>
The 1-based readers, or an empty set.
dropDataSourceReads()
Section titled “dropDataSourceReads()”dropDataSourceReads(lineNumber): number;Defined in: packages/engine/src/vm/DependencyGraph.ts:644
Forget every external data source a line was recorded as reading.
A data-source read is pinned (see registerLineDataSourceDependency): it was discovered while the line ran, so re-registering the line from its text keeps it. That is right for a live line and wrong for a frozen one, which reads its answer from the engine’s store and never the source again. Left in place, the pinned read kept the line a consumer of the query, so the batcher re-ran it whenever the value landed and a background refresh kept fetching for it. Only data-source keys are dropped: they are never part of a cycle, so no other edge, and no cycle bookkeeping, changes.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
lineNumber | number | The 1-based line whose data-source reads to drop. |
Returns
Section titled “Returns”number
How many were dropped.
forgetPositionReads()
Section titled “forgetPositionReads()”forgetPositionReads(lineNumber): void;Defined in: packages/engine/src/vm/DependencyGraph.ts:1183
Forget every position and tag this line was recorded reading.
For a line whose text has just changed, before it runs: whatever the old text read is not evidence about the new one, and a rule consulting the graph between the edit and the run would otherwise be told the old edges. The next run records what the new text reads. A data-source pin is discovered the same way but is not about the text, and stays until the line is removed.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
lineNumber | number | 1-based line whose positions are to go |
Returns
Section titled “Returns”void
getAffectedLines()
Section titled “getAffectedLines()”getAffectedLines(changedVariable): Set<number>;Defined in: packages/engine/src/vm/DependencyGraph.ts:714
Find all lines affected by a changed variable via BFS through the consumer graph.
When a variable is modified (e.g., :x = 5 changes to :x = 10), this returns
all lines that transitively depend on it, lines that read x, lines that read
variables written by those lines, and so on.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
changedVariable | string | The variable name that changed |
Returns
Section titled “Returns”Set<number>
Set of line numbers that need re-evaluation
getAffectedLinesByDataSource()
Section titled “getAffectedLinesByDataSource()”getAffectedLinesByDataSource(dataSourceId, queryKey): ReadonlySet<number>;Defined in: packages/engine/src/vm/DependencyGraph.ts:1423
Find all lines affected by a data source update.
When an async data source resolves (e.g., currency rate fetch completes), this returns all lines that depend on that specific data query.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
dataSourceId | string | The data source identifier |
queryKey | string[] | The query key that was updated |
Returns
Section titled “Returns”ReadonlySet<number>
Set of line numbers that need re-evaluation
getAffectedLinesByPosition()
Section titled “getAffectedLinesByPosition()”getAffectedLinesByPosition(lineNumber): ReadonlySet<number>;Defined in: packages/engine/src/vm/DependencyGraph.ts:1388
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
lineNumber | number |
Returns
Section titled “Returns”ReadonlySet<number>
getAffectedLinesInOrder()
Section titled “getAffectedLinesInOrder()”getAffectedLinesInOrder(startVariable): number[];Defined in: packages/engine/src/vm/DependencyGraph.ts:763
Phase 1.4 DAG-walk optimization: return affected lines in dependency-safe topological order. Uses Kahn’s algorithm (BFS-based) to ensure every line is evaluated AFTER all lines it depends on have been processed.
This is more correct than ascending line-number sort, which fails when variable definitions and their consumers are not in document order.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
startVariable | string |
Returns
Section titled “Returns”number[]
Line numbers in topological order (producers before consumers).
getConsumers()
Section titled “getConsumers()”getConsumers(variable): ReadonlySet<number>;Defined in: packages/engine/src/vm/DependencyGraph.ts:1494
Get all line numbers that consume (read) a given variable.
A position’s key (linePositionEdgeKey) is answered as getAffectedLinesByPosition answers the position.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
variable | string | The variable name, or any other edge key |
Returns
Section titled “Returns”ReadonlySet<number>
Set of line numbers that read this variable, or empty set if none
getDependencies()
Section titled “getDependencies()”getDependencies(lineNumber): ReadonlySet<string>;Defined in: packages/engine/src/vm/DependencyGraph.ts:1559
The keys a line that WRITES something reads.
The qualifier is the whole of it, and the reason this doc is longer than the method. The map behind this is filled in registerLine only on the branch that stores a write set, so a line that reads a name and defines nothing answers with an empty set rather than with what it reads. It is not “the variables a line depends on”; it is the dependencies recorded alongside a definition.
That is deliberate, and pinned by a test: it is what lets a redefinition
break the old chain rather than depend on itself. It is also a trap, and it
has been walked into. The async batcher ordered the lines it was about to
re-run by asking this what each one read, so a line defining nothing
answered with nothing and got no ordering constraint at all: rate * 2 was
run before the line that fetched rate and read the value from before the
fetch.
getReads is the question that was meant there, and is almost always the one wanted: every key a line reads, whether or not it writes anything.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
lineNumber | number | The line number to query |
Returns
Section titled “Returns”ReadonlySet<string>
The keys recorded alongside this line’s write set, or an empty set if it writes nothing
getProducers()
Section titled “getProducers()”getProducers(key): ReadonlySet<number>;Defined in: packages/engine/src/vm/DependencyGraph.ts:1530
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
key | string |
Returns
Section titled “Returns”ReadonlySet<number>
getReads()
Section titled “getReads()”getReads(lineNumber): ReadonlySet<string>;Defined in: packages/engine/src/vm/DependencyGraph.ts:1583
Every key a line reads, whether or not it writes anything.
getDependencies answers this only for a line that writes, because the map behind it is filled alongside the write set. That makes it the wrong question to ask when ordering a set of lines: a line that reads a name and defines nothing is exactly the line whose reads say where it has to come, and it answered with nothing. The batcher ordered such a line before the line producing what it read for that reason.
Includes any data-source key the line was pinned to, since that is a read
of the line like any other, and a line: key (see
linePositionEdgeKey) for each position it was recorded reading.
Those are made from the line’s entry when asked, since the graph holds a
span rather than a key per position.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
lineNumber | number | The line number to query |
Returns
Section titled “Returns”ReadonlySet<string>
The keys this line reads, or an empty set if none. A new set when the line reads a position, the caller’s to keep.
getSnapshot()
Section titled “getSnapshot()”getSnapshot(): DagSnapshot;Defined in: packages/engine/src/vm/DependencyGraph.ts:1640
Get a serializable snapshot of the entire dependency graph for diagnostics.
Returns plain objects (not Maps/Sets) so consumers don’t need to reach into private fields. Used by playground diagnostic tabs for DAG visualization.
Returns
Section titled “Returns”getWrites()
Section titled “getWrites()”getWrites(lineNumber): ReadonlySet<string>;Defined in: packages/engine/src/vm/DependencyGraph.ts:1630
Get all variables that a line writes (assigns to).
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
lineNumber | number | The line number to query |
Returns
Section titled “Returns”ReadonlySet<string>
Set of variable names this line writes, or empty set if none
hasDownwardPositionRead()
Section titled “hasDownwardPositionRead()”hasDownwardPositionRead(): boolean;Defined in: packages/engine/src/vm/DependencyGraph.ts:1287
Whether any recorded positional edge points from a reader to a line below it.
The precondition for a positional cycle, and so for the walk that looks for one; see downwardPositionReads. A tag edge can reach a line below its reader, and which lines it reaches is decided when asked, so any tag edge counts.
Returns
Section titled “Returns”boolean
True while at least one such edge is recorded
keysInUse()
Section titled “keysInUse()”keysInUse(): Set<string>;Defined in: packages/engine/src/vm/DependencyGraph.ts:1602
Every key some line reads or writes: the names, tags, globals and data
sources the graph indexes. No line: key is among them.
What a caller wanting the document’s vocabulary asks for, rather than building a getSnapshot, which also spells out every positional edge.
Returns
Section titled “Returns”Set<string>
The keys, each once.
linesReadingADataSource()
Section titled “linesReadingADataSource()”linesReadingADataSource(): number[];Defined in: packages/engine/src/vm/DependencyGraph.ts:1613
The lines that read an external data source.
Returns
Section titled “Returns”number[]
Their 1-based numbers, ascending.
linesReadingAPosition()
Section titled “linesReadingAPosition()”linesReadingAPosition(): Iterable<number>;Defined in: packages/engine/src/vm/DependencyGraph.ts:1336
Every line that read some position’s result, whichever position it was.
For a structural edit, which changes what a position means rather than
what any line says. Inserting a line moves everything below it, so line 5
now names different text, prev names a different neighbour, and an
above aggregate covers a different block, all without a character
changing on the line that reads them.
Deliberately not filtered by which positions moved. A reader whose target shifted has to re-run, and so does one that shifted past its own target and became a self-reference, and the second is not visible from the target alone. Positional readers are a small minority of a document’s lines, so re-running all of them costs almost nothing and cannot be wrong.
Returns
Section titled “Returns”Iterable<number>
The 1-based line numbers doing the reading, valid until the next structural change.
positionReadersOf()
Section titled “positionReadersOf()”positionReadersOf(lineNumber): Generator<number, void, undefined>;Defined in: packages/engine/src/vm/DependencyGraph.ts:1368
The lines reading lineNumber’s position, one at a time, as
getAffectedLinesByPosition finds them but without collecting them:
a reader can come more than once (through a span and through a tag), the
line itself never does. For a walk that holds many of these at once, so it
holds no set of readers per line; the cycle walk is one, and a running
total after every line of a ledger has as many readers as lines above it.
The graph must not change while one is being read.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
lineNumber | number | 1-based line whose readers are wanted. |
Returns
Section titled “Returns”Generator<number, void, undefined>
positionsReadBy()
Section titled “positionsReadBy()”positionsReadBy(lineNumber): number[];Defined in: packages/engine/src/vm/DependencyGraph.ts:1214
The positions a line has been recorded reading, as line numbers.
The forward direction of getAffectedLinesByPosition: that answers “who reads this position”, this answers “which positions does this line read”. Both directions are what finding a cycle takes. A tag edge is not a position and is not listed; readsAnyPosition says whether a line holds either kind.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
lineNumber | number | 1-based line doing the reading |
Returns
Section titled “Returns”number[]
The positions it has read, in no particular order; empty if none
readsAnyPosition()
Section titled “readsAnyPosition()”readsAnyPosition(lineNumber): boolean;Defined in: packages/engine/src/vm/DependencyGraph.ts:1240
Whether a line has been recorded reading another line’s result, by position or through a category tag.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
lineNumber | number | 1-based line doing the reading |
Returns
Section titled “Returns”boolean
True while it holds at least one such edge
reconcilePositionReads()
Section titled “reconcilePositionReads()”reconcilePositionReads(lineNumber): void;Defined in: packages/engine/src/vm/DependencyGraph.ts:1118
Cut a line’s recorded positions back to the ones its last run read.
For the evaluator to call once a line has executed. A positional read is
discovered while the line runs, and a position the line has stopped
reading cannot be discovered that way, so without this the recorded set
only ever grew: prev + 1 edited to 7 went on reading line 1 in the
graph for the rest of the session, and total above kept its edges to
the lines above a heading that had cut its block short. Anything asking
the graph what a line reads was told what it used to read, and a cycle
that the heading had broken was still a cycle to the graph, while a cycle
that its removal re-closed was not new to it and so was never noticed.
Tag edges are cut back the same way.
A run that read no position at all leaves the line with none. A line that did not execute (compiled only, or skipped) must not be reconciled, since it read nothing for a reason that says nothing about its text.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
lineNumber | number | 1-based line that has just executed |
Returns
Section titled “Returns”void
registerLine()
Section titled “registerLine()”registerLine( lineNumber, reads, writes): readonly string[];Defined in: packages/engine/src/vm/DependencyGraph.ts:518
Register a line’s variable reads and writes in the dependency graph.
If re-registering the same line (e.g., after editing), old consumer references are cleaned up first. Write-variables are removed from the consumer set so that redefinition breaks the old dependency chain.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
lineNumber | number | 1-based line number in the document |
reads | string[] | Variable names this line reads |
writes | string[] | Variable names this line writes (assigns to) |
Returns
Section titled “Returns”readonly string[]
registerLineDataSourceDependency()
Section titled “registerLineDataSourceDependency()”registerLineDataSourceDependency( lineNumber, dataSourceId, queryKey): void;Defined in: packages/engine/src/vm/DependencyGraph.ts:674
Register a line’s dependency on an external data source (e.g., currency rate, OSRS GE price).
When the data source updates, getAffectedLinesByDataSource returns all lines that depend on this data, enabling targeted re-evaluation.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
lineNumber | number | 1-based line number in the document |
dataSourceId | string | Unique identifier for the data source (e.g., “currency”, “osrs-ge”) |
queryKey | string[] | Query key array identifying the specific data (e.g., [“USD”, “EUR”]) |
Returns
Section titled “Returns”void
registerLineFigureSpan()
Section titled “registerLineFigureSpan()”registerLineFigureSpan( lineNumber, first, last): void;Defined in: packages/engine/src/vm/DependencyGraph.ts:1044
Record that lineNumber reads the figures on lines first to last: every
line there except a summary line, which a span of figures passes over.
One interval however long the span, for the same reason as registerLinePositionDependency. What a section total reads is its block less the totals inside it, which is every other line of a ledger, so recording the members one by one was a sparse set the length of the block for every total in it. Whether a line in the span is a summary is asked of the document when the edge is followed back (see setDocumentView), so two totals of one section do not read each other, and a cycle is only found where there is one.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
lineNumber | number | 1-based line doing the reading |
first | number | First line of the span, 1-based |
last | number | Last line of the span, inclusive; a span with last < first reads nothing |
Returns
Section titled “Returns”void
registerLinePositionDependency()
Section titled “registerLinePositionDependency()”registerLinePositionDependency(lineNumber, dependsOnLine): void;Defined in: packages/engine/src/vm/DependencyGraph.ts:956
Record that lineNumber read the result of line dependsOnLine.
A positional read is discovered while the line runs, the same way a data source is, and for the same reason it outlives the next registration of this line, which recovers its edges from the text, where a position it reached for at run time does not appear.
The edge is held as a number in the reader’s own entry, a contiguous span
and a sparse set, and nowhere else: “which lines read line k” is answered
by an interval index over those spans (see getAffectedLinesByPosition),
so an above aggregate over a thousand lines costs one entry, not a
thousand keys in three indexes (#733).
A line depending on itself is dropped rather than recorded, since it would be a cycle the ordering has to break and says nothing.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
lineNumber | number | 1-based line doing the reading |
dependsOnLine | number | 1-based line whose result it read |
Returns
Section titled “Returns”void
registerLineTagDependency()
Section titled “registerLineTagDependency()”registerLineTagDependency(lineNumber, tag): void;Defined in: packages/engine/src/vm/DependencyGraph.ts:1013
Record that lineNumber read the members of the category tag tag, or of
every tag when tag is EVERY_TAG.
One edge on the tag, however many lines carry it. total of #food reads
every line tagged #food, and recording a position per member made a
ledger with a running tag total after each entry cost the square of its
length in the graph. Which lines the edge reaches is answered when asked,
from the tags each line carries now (see setDocumentView), so a
member joining or leaving the group is seen without the reader recording
anything. The same run half as a position keeps it exact: a tag the last
run did not read is dropped by reconcilePositionReads.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
lineNumber | number | 1-based line doing the reading |
tag | string | The tag’s name without its #, in any case, or EVERY_TAG |
Returns
Section titled “Returns”void
removeLine()
Section titled “removeLine()”removeLine(lineNumber): readonly string[];Defined in: packages/engine/src/vm/DependencyGraph.ts:1435
Remove a line from the dependency graph (e.g., when a line is deleted from the document).
Cleans up all consumer references, write registrations, and data source dependencies for the removed line. O(k) where k is the number of variables the line reads.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
lineNumber | number | The line number being removed |
Returns
Section titled “Returns”readonly string[]
setDocumentView()
Section titled “setDocumentView()”setDocumentView(view): void;Defined in: packages/engine/src/vm/DependencyGraph.ts:1068
Tell the graph what it needs to know about the document’s lines to follow a tag edge or a span of figures back to the lines it reaches.
The document model knows a line’s text and the graph does not, so the path that owns the model hands this over; the incremental pass does so whenever it builds a line context. Kept across clear, which a structural edit calls, since the view reads the model as it is now.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
view | DocumentView | null | How to read a line, by 1-based position; null when no document backs the graph, and then a tag edge reaches no line and a span of figures reaches every line in it. |
Returns
Section titled “Returns”void
takeEdgeChanges()
Section titled “takeEdgeChanges()”takeEdgeChanges(): { keys: readonly string[]; lines: readonly number[];};Defined in: packages/engine/src/vm/DependencyGraph.ts:1253
What changed in the graph since this was last called: the lines whose edge set changed, and the keys whose producer set changed. Taking it clears it. See edgesChangedThisPass.
Returns
Section titled “Returns”{ keys: readonly string[]; lines: readonly number[];}The changed lines and keys, each possibly empty.
keys: readonly string[];lines: readonly number[];takeReadersThatGainedAPosition()
Section titled “takeReadersThatGainedAPosition()”takeReadersThatGainedAPosition(): readonly number[];Defined in: packages/engine/src/vm/DependencyGraph.ts:1269
The readers that recorded a new position since this was last called, and an empty list until one does.
Taking the list clears it. See readersThatGainedAPosition.
Returns
Section titled “Returns”readonly number[]
The 1-based readers, in the order they recorded