Store is the engine's session-global asset registry: raw model JSONs by id, parsed fonts by name, and platform-native materials by name. It is JSON-level only — it never instantiates a Model and it never fetches. A host (browser app, SketchUp bridge, server function) does the actual network/file I/O and hands the result to Store; the engine only ever does synchronous Map lookups against it during evaluation. This is the core doctrine: the engine never fetches — a missing asset at eval time is a lookup error, not a network call.
Import
import { store } from "@parametron/parametric"store is a ready-to-use singleton exported from the package root — most hosts use it directly rather than constructing their own. The class is also exported (Store) if you need an isolated instance (tests, multi-session servers).
Loading a bundle
The host fetches a model plus its transitive dependencies (already resolved server-side, flat) and hands it to store.load(). This is the one async phase in the whole pipeline; everything downstream is synchronous:
const bundle = await db.fetchWithDeps("modelA") // host fetches
await store.load(bundle) // register + loadFonts
store.registerMaterials(sketchup.materials) // platform bridge
const model = Model.fromJSON(store.getModel("modelA") as ModelJSON, { registry, assets: store })load(bundle, opts?):
- Registers every JSON in
bundle.deps(id → JSON) that isn't already known, scanning each for assets. - Registers
bundle.modelitself, underopts.rootIdif given. - Awaits
loadFonts()so every font referenced by the bundle is parsed before you evaluate.
type ModelBundle = {
model: unknown
deps?: Record<string, unknown>
}Registering fonts
registerFromModel(json) walks a raw model JSON's static fonts[] lane (v22) and queues each { name, url } for async load — it also recurses into embedded loadModel docs (a nested sub-model can carry its own fonts). This runs automatically inside load()/registerModel(); call it directly if you're registering JSON you didn't fetch through a bundle.
store.registerFont("Oak Sans", "https://cdn.example.com/oak-sans.ttf")
await store.loadFonts() // parses all pending fonts in parallel, non-fatal on failureloadFonts() fetches each pending URL, parses it with the font domain's parse(), and stores the result under its name. A font that fails to load is silently dropped — the font-typed arg coercer falls back to the fallback font rather than throwing (see packages/parametric/src/domains/font.ts).
Materials and layers need no such pre-load step: the document's own materials[]/layers[] lanes are static records, read straight off the model scope during evaluation. registerMaterial(s) exists only for platform-native materials (see below).
Registering models and materials directly
store.registerModel(id, json) // JSON by id, without a bundle fetch
store.registerMaterial({ name: "Walnut", baseColor: "#5c4326" })
store.registerMaterials([...]) // batch formregisterMaterial is lowest-priority: it only inserts if the name isn't already registered, so a document's own materials[] lane record always wins over a platform default. This is how a host bridge (e.g. a SketchUp palette) offers fallback materials without shadowing anything the model author declared explicitly:
export type MaterialDefJSON = {
name: string
baseColor?: string // hex color: "#rrggbb" or "#rrggbbaa"
texture?: string // https:// or data: URI
opacity?: number
metallic?: number
roughness?: number
}MaterialDefJSON is a compatible subset of the document's static materials[] lane record shape (MaterialJSON adds tileWidth/tileHeight/colorFactor), so a lookup result is interchangeable wherever those extra fields aren't needed — see Schema & validation for the document-side MaterialJSON type.
Lookup
store.getModel(id) // unknown — raw JSON, or undefined
store.getFont(key) // parsed font data, or null
store.getMaterial(name) // MaterialDefJSON, or nullThese three are the only reads the rest of the system performs, and all are synchronous. getFont/getMaterial are what a font-/material-typed arg resolves through during coercion — see the arg-coercion pass in Registry & contracts. getModel is host-facing (e.g. a catalog opening a model by id); the evaluation graph itself does not call it — see below.
Wiring into evaluation
Pass the Store as assets when building or evaluating a model:
import { evaluateModel } from "@parametron/parametric"
const result = evaluateModel(json, params, { registry, assets: store })or via Model.fromJSON(json, { registry, assets: store }). The Store then flows onto the model's scope (scope.assets) and every nested sub-scope, and reaches ComputeContext.store for node compute/create hooks and the arg coercer. A font-typed or material-typed string arg resolves through assets.getFont/assets.getMaterial at coercion time; everything else ignores the Store entirely. Evaluation with no assets at all is valid — fonts fall back to the built-in fallback font, and font/material args that resolve to nothing just come back null.
Model composition does not use the Store
A loadModel parameter embeds the full sub-model JSON as its input — there is no Store id/URL indirection for model composition. store.getModel/registerModel exist for host-facing lookup (a catalog or file browser opening a model by id), not for the engine's own graph traversal: placeModel resolves its source by key against the already-embedded sub-model handle, never through the Store. If you need to prove this to yourself, the test suite does exactly that — a Store subclass whose getModel throws still evaluates a two-level nested model composition without ever tripping it (packages/parametric/src/__tests__/bundle.test.ts).
Reset
store.reset()Clears models, materials, fonts, and any still-pending font loads. Useful between tests or when starting a fresh session against the shared singleton.
See also
- Model & pull graph — where
scope.assetslives and how it's inherited by nested sub-scopes. - Registry & contracts —
ComputeContext.store, arg type coercion (font/materialstring → object). - Render contract — how resolved materials/fonts end up in
EvaluateResult/SceneJSONoutput. - Schema & validation — the document's own static
materials[]/layers[]/fonts[]lanes.