File format — .mbl, .mblext and import/export
A bench travels as a single JSON document with the .mbl extension. The guiding decision (from the plan) is that the DSL source is the authority and the compiled document is a cache — so a .mbl always carries the source, and can always be recompiled if the cache is missing or stale.
The .mbl container
{
"mblVersion": 1,
"type": "bench",
"engineApiVersion": 1,
"name": "Bancada 260x60",
"workbenches": ["static"], // categories the bench uses
"units": "mm", // always millimeters
"sources": [{ "name": "main.js", "content": "export default defineBench(…)" }],
"entrySourceName": "main.js",
"document": { /* compiled cache — components/connections/drivers */ },
"meta": {
"envelope": { "x": 2600, "y": 902, "z": 600 },
"bom": { "items": [ … ], "totals": { … } },
"createdWith": "mybenchlab"
}
}
- sources / entrySourceName — the authority. Multi-source benches (with
group({ source })) list every file; the entry is the one compiled first. - document — a cache. It may be absent (an exported converter output, for instance); the importer recompiles from the sources.
- meta.envelope / meta.bom — the declared outer box and the bill of materials, filled from the compiled document at export time.
Importing and exporting
From the benches list, Import .mbl reads a file and inserts it as a new bench for you; Export .mbl downloads the current bench (recompiled so the cache and BOM are fresh). The same operations are available as the Meteor methods benches.importMbl / benches.exportMbl and — pure and framework-free — as serializeMbl / parseMbl in the engine's sim/mbl layer. parseMbl validates the version, units, sources and type, and tolerates a missing document.
Custom extensions (.mblext)
A distributable pack of reusable kinds/junctions is the same container with type: "extension" and a manifest:
{
"mblVersion": 1,
"type": "extension",
"manifest": { "id": "demo.brackets", "version": "1.0.0", "categories": ["static"] },
"sources": [{ "name": "kinds.js", "content": "/* register*/defineComponent code */" }]
}
A bench references extensions through extensions: [{ id, version, embedded? }]. v1 supports embedded extensions only — the bench carries a full copy of the pack inline for portability; its sources are prepended to the bench's own sources at compile time (namespaced <extId>/<name>). A bare { id, version } reference without an embedded copy cannot be resolved yet: there is no registry or marketplace in v1.
Security. An extension's sources are executable code, at the same trust level as a bench's own sources — which the platform already evaluates in a sandbox (new Function, nowindow/DOM/Meteor). Importing a third-party extension therefore runs third-party code under that sandbox. A permission/registry model is a prerequisite before any marketplace; v1 stays with embedded, user-provided packs, andbenches.importMblrejects standalone extension files.
Importing a scene3d scene
The MetalScout scene3d format (declarative JSON: parts positioned by origin, extruded along +Z, XYZ-degree rotations) converts to a .mbl bench with convertScene3dToMbl({ scene }). Each part maps to a static kind — tubeSquare→steelTubeSquare, tubeRectangular→steelTubeRectangular, sheet→steelSheet, angle→steelAngle, a wood box→woodPanel, and anything else to genericSolid (a bounding-box stand-in). The converter reconciles the two coordinate conventions — scene3d's origin-extrusion vs the engine's centered boxes — so the world bounding boxes match exactly. v1 emits explicit transforms only, with no inferred junctions; connectivity still holds because touching parts count as one body. The converter stamps statics.importedFrom('scene3d') into the generated source, which tells the static validator this is machine-import provenance — so contact-only connectivity is accepted silently (an *authored* bench without junctions gets the W_STATIC_CONTACT_NO_JOINT nudge instead).
Bill of materials
computeBom({ document }) groups identical parts (same kind + same params, ignoring cosmetic color) into lines with a quantity, a unit weight and a rolled-up total, plus grand totals. Weight comes from each kind's dimensions().weightKg — the same honest figure the validators and doc blocks report. The BOM is embedded in meta.bom on export: the natural bridge to a MetalScout material list, with no extra AI step.
Each BOM line also carries a structured descriptor (plan §19) — { material, format, dims }, canonical English fields in millimeters derived from the part's params (e.g. { material: 'steel', format: 'rectangular-tube', dims: { w: 30, h: 50, wall: 2, length: 1200 } }). It is purely additive to meta.bom — the mblVersion is unchanged — so older readers ignore it and newer ones can map each part straight onto a catalog item. The workspace Materiais panel exports the list as CSV or JSON, and the bench_bom MCP tool returns the same structured BOM (descriptors + quantity + weight + a pt-BR label) for agents to consume.