@huukhanhnguyen/model / schema/references
schema/references
LaneReference
LaneReference = {
carrier:ReferenceCarrier;from:string;label?:string;mechanism:"reference"|"key"|"name"|"scene";note:string;to:string;via:string; }
One declared cross-lane edge.
carrier
carrier:
ReferenceCarrier
from
from:
string
Lane that holds the reader ("scene" = the root operations fold), or lane.branch when the lane is a CHAIN whose parts reach different places. A component is a mini-model — parameters plus operations — and every reference it makes is written in the BODY: the definition itself is a function of its own signature and reaches nothing. Saying components reads tables attributes the body's edge to the definition, which is the difference between "a definition depends on host state" (false, and the thing isolation exists to prevent) and "code inside it does" (true).
label?
optionallabel?:string
What to call this edge on the Graph page. Needed for hook edges, whose via is an INTERNAL wire name (ResolveTextureOptions.resolveTable) that an author never writes and cannot look up — drawn raw it reads as a mystery. Say the author-facing thing instead ("Table.get"). Arg edges name a real method.arg, so they need none.
mechanism
mechanism:
"reference"|"key"|"name"|"scene"
note
note:
string
to
to:
string
Lane the reader reaches, same lane.branch form when it matters.
via
via:
string
method.arg when carrier is "arg" (checked against the registry and the lane method tables); the hook's Type.field when "hook" (checked to exist in the engine source); a short phrase when "expression".
ReferenceCarrier
ReferenceCarrier =
"arg"|"hook"|"expression"
WHERE an edge physically lives. Three answers, and forcing all three into method.arg is how this table first got written — which produced entries like "views→materials via viewSection.depth", an arg that has nothing to do with materials, picked only because the guard demanded a method and an arg.
arg— an author writes the target's key/name in that arg.hook— nobody writes it: the resolver is HANDED the lane the document, real in the dependency graph.expression— any expression in the reading lane may reach it (a parameter key, Table.get, Material.get).
COLLECTION_LANES
constCOLLECTION_LANES: ("patterns"|"animations"|"materials"|"layers"|"styles"|"textStyles"|"annotationStyles"|"cameras"|"views"|"components"|"textures"|"tables"|"sheets")[]
Lanes that have a CollectionNode — i.e. lanes whose MEMBERSHIP (which entries exist, in what order) can be pulled as a real dependency, separate from any entry's body.
Only the KEYED record lanes qualify: an entry needs a key to be addressable, and materials/layers/styles carry theirs in an expression name arg, so they have no key until something evaluates them.
tables is DELIBERATELY absent even though it is keyed. A table chain already references tables in the other direction (a table chain reading itself), so serving a collection of them would make the lane relation two-way — exactly the shape this file exists to surface rather than create. None has a consumer either. animations and components were cut too (2026-07-26): each already sits on a declared relation (faceSkeleton.skeleton, placeComponent, and — since 2026-08-14's consumer-holds-the-reference redesign — BoneEntity.*Track / Operation.animation reaching INTO animations), and serving its membership added edges no consumer justifies — a collection is wired when a reader exists, not ahead of one.
SSOT for both Model._laneEntries (what it will serve) and the Graph page (what it draws feeding the collection). A lane in one and not the other is the kind of silent divergence this file exists to stop.
LANE_REFERENCES
constLANE_REFERENCES:LaneReference[]
LANE_SCOPE
constLANE_SCOPE:Record<string,LaneScope>
Lane → how deep a name resolves inside its body. DERIVED from LANES plus the two PSEUDO-lanes the edge table below has to name (parameters and scene are the root header and body, not arrays of entries).
Notes on the non-obvious classifications, which live here because this is the file that explains the mechanism:
components/texturesare functions:.parentcut, entered only through placement args.sheetsruns its ad hoc operations through evaluateFlatOperations, which shares the same isolated-scope builder and (since 2026-07-26) is handed the sheet's ownparameters— a function with a real signature rather than an isolated scope with nothing in it.
The title of a sheet is deliberately NOT covered by any of this: a title is lane HEADER data (what a drawing index reads through CollectionNode), so Model._evaluateLaneExpression evaluates it against the model's parameters WITH the lane's own layered on top.
LaneScope
Re-exports LaneScope