Skip to content
Shapemetry

@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?

optional label?: 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

const COLLECTION_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

const LANE_REFERENCES: LaneReference[]


LANE_SCOPE

const LANE_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 / textures are functions: .parent cut, entered only through placement args.
  • sheets runs its ad hoc operations through evaluateFlatOperations, which shares the same isolated-scope builder and (since 2026-07-26) is handed the sheet's own parameters — 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

Last updated: 📖 4 min readEdit on GitHub