Skip to content
Shapemetry

@huukhanhnguyen/model / schema/registry

schema/registry

ArgDef

ArgDef = { attributes?: Record<string, unknown>; default?: string; key: string; max?: string; min?: string; step?: string; type: string; }

The RUNTIME arg definition — the registry contract for OperationDefinition.args. The AUTHORED/catalog arg shape (typed label/tooltip/options/description fields) is NodeArgEntry (schema/nodesRegistry.ts); nodesRegistry/wiring.ts's toArgDef packs those into the opaque attributes bag below, so the two layers never drift.

attributes?

optional attributes?: Record<string, unknown>

default?

optional default?: string

key

key: string

max?

optional max?: string

min?

optional min?: string

step?

optional step?: string

type

type: string


ChainState

ChainState = ObjectData | undefined

Mid-chain value: one *Data document. Empty stream is undefined.


ComputeArgs

ComputeArgs = Record<string, unknown>

Evaluated + coerced arg values, keyed by ArgDef.key.


ComputeContext

ComputeContext = { annotationStyles?: AnnotationStyle[]; flat: boolean; imageUrlOf?: (name) => string; materials?: Material[]; sampleTexture?: (key) => unknown; store?: StoreHost; textStyles?: TextStyle[]; }

  • store — session asset registry (fonts/materials/images/models), immutable during eval.

annotationStyles?

optional annotationStyles?: AnnotationStyle[]

flat

flat: boolean

True when the calling node evaluates in a 2D context: the owning chain is declared flat (ModelJSON.flat / Component.flat — a step can never declare itself flat), or it sits in a "flat" lane (a sheet's ad hoc operations — see schema/model.ts's FLAT_LANE_METHODS). Enforcement (flat vocabulary gate, point-arg z=0, per-step force-flatten) is engine-owned in DataNode; methods may read this to keep their own output planar. Always false for value nodes.

imageUrlOf?

optional imageUrlOf?: (name) => string

Resolve a url-method parameter KEY to its literal url (falls back to the ref itself when no parameter matches, so a direct https:/data: URI passes through unchanged) — imageData's name arg.

Parameters

name

string

Returns

string

materials?

optional materials?: Material[]

The model's materials[] lane.

sampleTexture?

optional sampleTexture?: (key) => unknown

Resolve a textures-lane chain to its evaluated ImageBuffer (displace).

Parameters

key

string

Returns

unknown

store?

optional store?: StoreHost

textStyles?

optional textStyles?: TextStyle[]

The model's textStyles[] / annotationStyles[] lanes, RESOLVED. A drafting mark builds its drawn form at evaluate time, which is when the style has to be known — see nodesRegistry/wiring.ts's STYLE_AWARE_METHODS. A name matching neither a record here nor the builtin "standard" throws rather than drawing something arbitrary.


DataDefinition

DataDefinition = OperationDefinitionBase & { compute?: (input, args, context) => ChainState | Promise<ChainState>; create?: (args, context) => ChainState | Promise<ChainState>; foldRole: "producer" | "transform"; }


FoldRole

FoldRole = "binding" | "producer" | "transform" | "step"

How a method participates in evaluation — orthogonal to the value type: a binding returns "length", a producer returns "objectData[]", a material step returns "material"; the fold role is what the engine dispatches on.


FunctionNamespaces

FunctionNamespaces = Record<string, Record<string, (...args) => unknown>>


OperationDefinition

OperationDefinition = ValueDefinition | DataDefinition | StepDefinition


OperationDefinitionBase

OperationDefinitionBase = { _hooks?: NodeHooks; args?: ArgDef[]; attributes?: Record<string, unknown>; defaultKey: string; foldRole: FoldRole; method: string; return: TypeName; }

  • method — dispatch + uniqueness key, globally unique across the WHOLE registry.
  • defaultKey — friendly base for auto-generated node keys ("arc" → arc1, arc2…).
  • return — TypeName from the type table (list form with [] when yielding a list).
  • foldRole — binding | producer | transform | step (see module doc).
  • attributes — opaque frontend metadata; never read by the engine for graph law.

_hooks?

optional _hooks?: NodeHooks

Lifecycle observers attached to the DECLARATION — composed onto every node built from this definition (see nodes/hooks.ts). Never serialized; a definition is a runtime registry object, not document JSON.

args?

optional args?: ArgDef[]

attributes?

optional attributes?: Record<string, unknown>

defaultKey

defaultKey: string

foldRole

foldRole: FoldRole

method

method: string

return

return: TypeName


RegistryInput

RegistryInput = { builtinMaterials?: Material[]; builtinParameterMethods?: Record<string, string>; builtinParameters?: string[]; catalogParameters?: string[]; namespaces?: FunctionNamespaces; nodes?: OperationDefinition[]; types?: TypeDef[]; }

The flat input createRegistry(input) merges onto the engine's own builtins. List order = display/catalog order (there is deliberately no order metadata).

  • namespaces — PascalCase expression namespaces (Point., Vector., …). Names become reserved (node keys cannot shadow them).
  • types — extra value types. These are INERT: display + coerce hook only — they never join the core dimension lattice (unit inference).
  • builtinParameters — reserved parameter keys auto-seeded per model (e.g. lengthX/lengthY/lengthZ).
  • builtinParameterMethods — for each builtinParameters/catalogParameters key, the value method required to legitimately claim it (e.g. { lengthX: "length", lengthY: "length" }). A builtin key absent here is reserved outright: no node may ever take it.
  • catalogParameters — reserved app-layer identity parameter keys (host-injected; core ships the mechanism empty — no domain vocabulary) (manufacturer/sku/price/currency/productUrl). Reserved exactly like builtinParameters (a key is claimable only by a value node whose method matches builtinParameterMethods) but deliberately NOT part of that set: builtinParameters doubles as the auto-seed list AND the host-facing filter (the viewer element, the /embed postMessage SDK and the ConfiguratorPanel all hide builtinParameters from their parameter lists), and catalog params must STAY VISIBLE — a shop needs price/currency in parashape:ready/parashape:change and in the evaluate API output. Never auto-seeded: a model without catalog metadata simply omits them.
  • builtinMaterials — named material records a node set's own generators stamp by name (e.g. the tree node's bark/leaf textures) but that don't live in any document's materials[] lane. Merged UNDER the doc lane by the engine's material collector (Model.ts's collectMaterials) — a doc record with the same name always overrides a builtin one.

builtinMaterials?

optional builtinMaterials?: Material[]

builtinParameterMethods?

optional builtinParameterMethods?: Record<string, string>

builtinParameters?

optional builtinParameters?: string[]

catalogParameters?

optional catalogParameters?: string[]

namespaces?

optional namespaces?: FunctionNamespaces

nodes?

optional nodes?: OperationDefinition[]

types?

optional types?: TypeDef[]


StepDefinition

StepDefinition = OperationDefinitionBase & { foldRole: "step"; }

Linear-lane step (materials/layers/styles/textStyles/annotationStyles/ cameras/views/animations/tables/textures, plus objects[] properties[]). No data-fold create/compute — the lane's own evaluator is the sole consumer.


TypeName

TypeName = string

A TYPE_REGISTRY name, optionally with a list suffix (objectData[], length[]). INPUT arg types and OUTPUT returns share this vocabulary.


ValueDefinition

ValueDefinition = OperationDefinitionBase & { compute?: (args, context) => unknown | Promise<unknown>; foldRole: "binding"; }

Last updated: 📖 6 min readEdit on GitHub