Store
Session-global singleton. Four read-only Maps — the source of truth for everything the engine needs before it can run.
import { store } from "@parashape/parametric"
await store.load(bundle)The Store is the ASYNC layer of the architecture: it scans the RAW model JSON for the engine loader parameters (loadModel/loadFont/loadImage) and pre-loads their assets before the graph ever evaluates — the Store's own job, not a registry hook. The graph itself never does async I/O; a missing asset at evaluate time is a lookup error, never a fetch.
Stores
| Property | Type | Description |
|---|---|---|
store.models | Map<string, unknown> | Raw model JSONs by ID. Engine reads from here instead of fetching. |
store.materials | Map<string, MaterialDefJSON> | Platform-native materials by name (host-bridge palette, e.g. SketchUp) — see registerMaterial. Model-declared (createMaterial parameter) materials live in the node graph, not here. |
store.fonts | Map<string, FontData> | Opentype-parsed font objects by name or URL. Must be pre-loaded in Phase 1 (fetch+parse is async) so the text generator can read metrics synchronously in Phase 2. |
store.images | Map<string, string> | Image URIs by name or URL. |
Fill the stores
| Method | Description |
|---|---|
store.load(bundle, { rootId? }) | Main entry point. Accept { model, deps } bundle, register the model JSONs, scan them for loader parameters (fonts/images/sub-models), then load fonts async. Does not touch materials — register those separately. |
store.registerModel(id, json, ns?) | Register one raw model JSON directly (also scans it for loader parameters). |
store.registerFromModel(json, ns?) | Scan a raw JSON for loader parameters (loadFont/loadImage/loadModel) and pre-register their assets; image keys take the optional namespace prefix. |
store.registerMaterial(mat) | Add one material by name; no-op if that name is already registered. |
store.registerMaterials(mats) | Bulk-add platform materials (e.g. SketchUp palette). |
store.registerFont(name, src) | Queue a font URL for loading under a declared name. |
store.queueFontUrl(src) | Queue a font URL; the loaded font registers under its PostScript name (used by the loadFont scan). |
store.registerImage(key, uri) | Store an image URI. |
store.loadFonts() | Fetch + parse all queued fonts in parallel. Called automatically by load(). |
Read from stores
| Method | Returns | Description |
|---|---|---|
store.getModel(id) | unknown | Raw model JSON by ID. |
store.getMaterial(name) | MaterialDefJSON | null | Material by name. |
store.getFont(key) | FontData | null | Parsed font by name or URL. |
store.getImage(key) | string | null | Image URI by key. |
store.reset() | void | Clear all stores — tests or new session. |
Two-phase usage
Phase 1 fills the stores (async OK). Phase 2 evaluates — fully synchronous, reads stores directly, no fetching.
// Phase 1 — async
const bundle = await db.fetchWithDeps("modelA") // server resolves full dep graph flat
await store.load(bundle, { rootId: "modelA" }) // fills models + images + fonts
store.registerMaterials(sketchup.getMaterials()) // platform palette into store.materials
// Phase 2 — sync, reads from stores
const registry = createRegistry(nodeRegistry)
const model = Model.fromJSON(store.getModel("modelA"), { registry, assets: store })
const result = model.evaluate(params)Platform bridge
A host bridge registers its native palette so a material argument passed as a plain name string (not a node reference) resolves via store.getMaterial() — used internally by argument coercion:
store.registerMaterials(
sketchup.getMaterials().map(m => ({
name: m.display_name,
baseColor: "#" + m.color.to_hex, // hex string
}))
)
// Entity can now reference "Oak" even without a `createMaterial` parameter declaring itSub-model namespacing
Materials/layers/fonts inside a placed sub-model are namespaced by the placing placeModel node's own key — this happens during model evaluation, not in the Store — so two placements of the same source never collide:
placeModel key "door" places a sub-model declaring material "Oak" → becomes "door:Oak"
entity ref targets material "Oak" → stamped "door:Oak" by the engine
renderer looks up "door:Oak" → found ✓store.registerFromModel(json, ns?) takes the same kind of optional prefix for the assets it scans (images only, today).
ModelBundle
type ModelBundle = {
model: unknown // main model JSON
deps?: Record<string, unknown> // id → dep JSON, flat (server resolved graph)
}