Skip to content
Shapemetry

@huukhanhnguyen/model / schema/lanes

schema/lanes

LaneBodyFieldName

LaneBodyFieldName = typeof LANE_BODY_FIELDS[number]


LaneCarrier

LaneCarrier = Record<string, unknown>

Anything shaped like a document — a stored ModelJSON, or a live ModelScope paired with the Model fields. Indexed loosely on purpose: this is the one place lane names are read as data, and every caller has a different static type for the same arrays.


LaneChain

LaneChain = { columns?: unknown[]; entities?: unknown[]; lane: LaneName; opening?: unknown[]; operations?: unknown[]; parameters?: unknown[]; }

One lane entry reduced to the node lists a doc-wide walk must visit, plus the lane it came from. A node-entry lane yields the entry itself as a one-element operations list, so a consumer never special-cases it.

columns?

optional columns?: unknown[]

entities?

optional entities?: unknown[]

NOT a node list — Component.entities is baked geometry (the static-geometry door for importers/AI). Carried here because a geometry migration has to reach it; a NODE walk ignores it.

lane

lane: LaneName

opening?

optional opening?: unknown[]

operations?

optional operations?: unknown[]

parameters?

optional parameters?: unknown[]


LaneDescriptor

LaneDescriptor = { base: string; clearEmptyArgs?: true; collection?: true; entry: "chain" | "node"; identity: "key" | "name"; references?: { arg: string; method: string; }[]; scope: LaneScope; space: "model" | "paper"; stepLane: boolean; storage: "scope" | "model"; }

base

base: string

Base string for a minted identity ("view" → view1, view2…).

clearEmptyArgs?

optional clearEmptyArgs?: true

Empty arg input CLEARS the arg instead of storing "" (the cameras authoring convention).

collection?

optional collection?: true

Has a CollectionNode — its MEMBERSHIP is pullable as a dependency, separate from any entry's body. Wired only where a reader exists.

entry

entry: "chain" | "node"

chain — the entry is a mini-document (parameters/operations/…). node — the entry IS one authored node (a material, a camera).

identity

identity: "key" | "name"

key field, or a quoted string literal in a name arg.

references?

optional references?: { arg: string; method: string; }[]

The by-name consumers that block a delete and are rewritten on rename. A LIST, because one lane can be named from more than one carrier: patterns is named by a hatch AND by a material's cut / surface pattern, and a single-carrier field silently ignored the other two.

arg

arg: string

method

method: string

scope

scope: LaneScope

space

space: "model" | "paper"

Which SPACE the lengths inside this lane live in.

model follows the document's display unit — switch mm→m and its literals are rewritten. paper is physical sheet millimetres and must NEVER be rescaled: an A3 sheet is 420 mm wide whether the model is measured in millimetres, metres or inches.

stepLane

stepLane: boolean

A STEP lane: every step in an entry's body returns exactly the lane's own singular type (material, view…), so RETURN_LANE_OF can map that return back to this lane.

false marks a lane whose entries are NOT step chains and never will be: components is a lite MODEL (own parameters + a data-stream body) and sheets is a flat WORKSPACE chain (it places views/tables by key AND carries ad hoc flat content drawn straight on the paper). Neither has a return: "<singular>" node.

Required, not optional, on purpose: adding a lane must not silently inherit either answer.

storage

storage: "scope" | "model"

Which object owns the array. The FIELD NAME always equals the lane name, so a consumer resolves it as scope[lane] / model[lane].


LaneName

LaneName = typeof LANE_NAMES[number]


LaneScope

LaneScope = "porous" | "isolated"

How deep a name resolves from inside the lane's body. See the long form in references.ts — porous climbs owner.parent to the model root, so the whole model shares one namespace; isolated has that link cut, so the lane is a FUNCTION whose own parameters are the only names its body sees.


LANE_BODY_FIELDS

const LANE_BODY_FIELDS: readonly ["parameters", "operations", "opening", "columns"]

The body fields a lane entry may carry — what an offline transform or a doc-wide walk has to visit. A node-entry lane has none: the entry itself is the node.


LANE_NAMES

const LANE_NAMES: readonly ["materials", "layers", "patterns", "styles", "textStyles", "annotationStyles", "cameras", "animations", "components", "textures", "tables", "views", "sheets"]

Every lane of a ModelJSON document. The two PSEUDO-lanes (parameters and scene, the root header and body) are deliberately absent: they are not arrays of entries, they are the root collection itself. references.ts adds them where an edge table needs to name them.


LANES

const LANES: Record<LaneName, LaneDescriptor>

Missing a key here is a COMPILE error, which is the whole point: the descriptor cannot drift from the lane list it describes.


MODEL_SPACE_LANES

const MODEL_SPACE_LANES: ("patterns" | "animations" | "materials" | "layers" | "styles" | "textStyles" | "annotationStyles" | "cameras" | "views" | "components" | "textures" | "tables" | "sheets")[]

The lanes whose lengths follow the model's display unit.


STEP_LANES

const STEP_LANES: ("patterns" | "animations" | "materials" | "layers" | "styles" | "textStyles" | "annotationStyles" | "cameras" | "views" | "components" | "textures" | "tables" | "sheets")[]

The lanes whose entries are step chains — see LaneDescriptor.stepLane.


laneChains()

laneChains(carrier, filter?): LaneChain[]

Every lane entry of a document, as node lists to walk. THE replacement for the hand-written [doc.components, doc.sheets, doc.textures, …] arrays that each offline transform used to carry — those are exactly the lists that fall behind when a lane is added.

filter narrows by descriptor (e.g. unit conversion takes only space === "model"), so a consumer states its RULE instead of its list.

Parameters

carrier

LaneCarrier

filter?

(descriptor, lane) => boolean

Returns

LaneChain[]

Last updated: 📖 5 min readEdit on GitHub