Mesh export
All evaluated outputs can produce a mesh and shared mesh formats:MeshData contains flat typed arrays:
Tessellation options
Exact B-Rep output is tessellated on demand:STEP and BREP
Use OCCT:CadError with an EXPORT_UNSUPPORTED
diagnostic because export() is a direct operation on a live output. Select a
compatible kernel before evaluation.
Deterministic single-product STEP
This strong contract was added in 0.2.0. The 0.1.1 package retained
ordinary weak native STEP export.
createOcctKernel() stock runtime advertises the
optional
KERNEL_STEP_EXPORT_PROTOCOL_VERSION === 1 capability. This versioned
capability is stronger than nativeExports containing "step":
nativeExports says only that the format can be written. Inspect the kernel’s
capability object—not the kernel itself—before relying on the stronger
contract:
"valid" result contains the strong immutable snapshot; "absent" means
only weak STEP availability, while "malformed" means the advertised envelope
is invalid. output.export("step") without a second argument remains compatible
with weak native exporters; a supporting kernel uses deterministic defaults.
Passing any options object, including {}, requests the strong contract; an
absent or malformed stepExport envelope throws CadError with
KERNEL_CAPABILITY_MISSING instead of silently ignoring the request.
The strong contract covers one STEP product. A supporting kernel that can
write an output only as several products refuses the strong request with
CadError EXPORT_UNSUPPORTED and details.reason === "multiple-products".
The stock OCCT kernel does this for an output that holds several disjoint
solids, such as a union of separated boxes or a subtraction that splits its
target. Without a second argument, output.export("step") then falls back to
the weak native STEP bytes instead of failing.
Explicit wasm, moduleFactory, and attestedRuntime initialization remains
on weak raw STEP export even when the supplied artifact is derived from stock
OCCT. InvariantCAD omits stepExport until that exact writer artifact has a
separate qualification boundary.
Pass deterministic metadata, cancellation, and a caller-specific output
ceiling through the STEP-only overload:
FILE_NAME uses the design document name and its
timestamp is 1970-01-01T00:00:00. A solid output uses its output alias for both
product ID and name, with an empty description. A part uses its authored part
number when present, otherwise its part-node ID, as the product ID; its
part-node ID is the product name, and its authored part description is the
product description.
Automatic defaults never make a valid document fail to export. Document names,
output aliases, part numbers, and descriptions may contain text that the rules
below reject for explicit metadata, so each defaulted field is normalized
instead: control characters become spaces and unpaired UTF-16 surrogates
become U+FFFD. An empty required default becomes InvariantCAD Shape. When
the resolved strings would exceed the metadata budget, the longest defaulted
values are truncated at Unicode scalar boundaries until they fit; explicit
overrides are never altered. Defaults longer than the whole budget share it in
field order. Only as much authored or explicit text as the budget could hold is
read, so a very long name or description costs no more work than the budget.
Pass an explicit metadata field to control the exact text.
An explicit timestamp must be a real calendar value in
YYYY-MM-DDTHH:MM:SS form. It remains deterministic because it is
caller-supplied input; changing it intentionally changes the bytes. The four
identity/description values accept Unicode scalar text. Apostrophes use STEP’s
doubled-apostrophe convention. Literal backslashes and non-ASCII scalars are
encoded with ISO-10303-21 \X2\...\X0\ or \X4\...\X0\ directives as
required. Explicit values that contain control characters or unpaired UTF-16
surrogates are rejected. Explicit fileName, productId, and productName
values must be nonempty.
Invalid option structure, explicit metadata text, calendar values, or the
advertised authored-metadata budget throw CadError with
EXPORT_OPTIONS_INVALID. Its path is a JSON Pointer relative to the
StepExportOptions input, such as /metadata/timestamp or
/maxOutputBytes. An already-aborted valid signal still throws AbortError;
low-level kernel.exportShape(...) calls retain their kernel-specific
validation exceptions.
On the stock OCCT kernel, exceeding maxOutputBytes, the default output
ceiling, or a fixed input, scan, or entity ceiling throws CadError with
RESOURCE_LIMIT_EXCEEDED, both from output.export(...) and from low-level
kernel.exportShape(...). details.resource names the ceiling (for example
"maxOutputBytes"), details.limit gives its value, and details.actual
gives the measured size when it is known.
Those defaults are a field-specific identity mapping, not a general metadata
pass-through. Root document metadata, arbitrary part metadata, material
identity, and the active configuration are omitted. A caller may explicitly
override a semantically corresponding supported STEP field, but protocol v1
provides no arbitrary key/value metadata channel. It does not silently fold
material data into the description.
For one solid or part, the stock writer emits one AP214IS product; it writes
several disjoint solids as an assembly of several products, which the strong
contract refuses as described above. InvariantCAD then applies a
purpose-built structural ISO-10303-21 scanner and transformer to the exact
FILE_NAME and PRODUCT string fields; it does not use an unbounded regular
expression rewrite. The default output ceiling is 64 MiB and the combined
metadata ceiling is 64 KiB measured over the five resolved strings as
UTF-8. Part 21 escaping can expand that text; the expanded representation
counts toward the 64 MiB output ceiling. Input traversal, scan work, and entity
count also have fixed ceilings, and the scan checks cancellation.
The underlying stock native writer is synchronous and necessarily materializes
its original complete STEP string before InvariantCAD can apply post-write
limits or observe cancellation again. Metadata shape, content, timestamp, and
the authored UTF-8 budget are validated and encoded before that writer starts,
so known-invalid metadata never triggers native serialization. The writer
cannot be interrupted mid-call. After it returns, cancellation is checked
before, during, and after bounded scanning and before successful return.
maxOutputBytes limits the transformed bytes returned to the caller; it does
not cap the native writer’s peak allocation or its original string. Isolate
untrusted or potentially pathological native work in a disposable worker or
process.
Byte equality is guaranteed only for the same backend shape representation,
options, resolved metadata, implementation, and exact runtime artifact. It
does not claim that geometrically equivalent B-Reps serialize identically,
make bytes portable as a cross-runtime cache identity, or canonicalize
third-party STEP. Exact aggregate STEP for assemblies and body-set results
remains unsupported; export retained single solid or part leaves explicitly.
Save in Node.js
Send from a web server
export(...) is typed as Uint8Array, which TypeScript reads as
Uint8Array<ArrayBufferLike>. With the DOM library, BodyInit and BlobPart
accept only views backed by an ArrayBuffer, so the snippets on this page copy
the bytes with Uint8Array.from(...) before handing them to Response or
Blob. The returned bytes are already detached from native memory, so the
evaluated design can be disposed as soon as the copy exists.
Verified single-body import
This public workflow was added in 0.2.0. It is not present in
invariantcad@0.1.1.createImportedBodyDocument(...) returns a CadResult; unwrap it before
persisting or evaluating the document. It captures and validates the
commitment but does not read, fetch, or hash the bytes. The caller computes
the SHA-256 digest and exact byte length. locations are ordered hints passed
to the resolver; InvariantCAD never interprets them as files, URLs, or package
paths.
The protocol-v1 media and unit policy is closed:
The explicit
format selects the native reader. mediaType is committed
provenance and resolver context; it is never sniffed or used to infer the
format. Healing is fixed to { mode: "none" }, and the strong kernel contract
must produce exactly one valid positive-volume solid. Multiple solids, shells,
loose topology, approximate conversion, and weak-import fallback are rejected.
Persist only the opaque document facade:
options.limits applies to the whole parse, including that subset check. A
document admitted only under raised limits, such as one with more than the
default 100,000 resource locations, needs the same limits when it is
serialized again and as documentLimits when it is evaluated.
Imported-body document format
The JSON thatstringifyImportedBodyDocument writes is a fixed, minimal
subset of Document v7. It carries schema
https://invariantcad.dev/schema/document/v7 and version 7, and contains
exactly:
- one resource commitment:
digest,byteLength,mediaType, and optionallocations; - one
importedBodynode that references that resource, with itsformat,units,healing: { mode: "none" }, andexpected: "single-solid"; - one output with the same name as the node, referencing it directly as a
solid; - empty
parametersand the fixedunits: { length: "mm", angle: "rad" }.
{ pretty: true } as:
parseImportedBodyDocument, write them only
with stringifyImportedBodyDocument, and evaluate them only with
Evaluator.evaluateImportedBody. General Document v7 stays repository-staged,
so the general document APIs refuse imported-body JSON instead of
misinterpreting it: parseDocument, parseDocumentValue, validateDocument,
migrateDocument, hashDesignFeatures, analyzeDesignImpact, and
Evaluator.evaluate return one IR_INVALID diagnostic at / whose hint names
the imported-body functions, and stringifyDocument, hashDocument, and
cloneDocument throw a TypeError with the same message.
The subset is frozen. Repository tests pin its canonical bytes and their
SHA-256, and a future public Document v7 will keep accepting every document in
this subset with the same meaning, so imported-body JSON saved by 0.2 stays
readable.
Evaluation requires an exact B-Rep evaluator with the strong document-body
import capability and a caller-supplied resolver:
maxResourceBytes must be a non-negative safe integer, signal an
AbortSignal, and documentLimits a plain record of document limits. An
invalid option returns IR_INVALID with a path rooted at /options, such as
/options/resourceLimits/maxResourceBytes, before the resolver runs.
Creation diagnostics name the definition field at fault, such as /name,
/definition/id, or /definition/resource/digest, and parse diagnostics
point into the document JSON, such as /resources/housingStep/mediaType.
The resolver receives a frozen request containing the authored ID, digest,
length, media type, location hints, and optional signal. Resolution,
byte-length admission, and SHA-256 verification finish before
importDocumentBody(...) runs. A successful EvaluatedImportedBody exposes
exact B-Rep measurement, topology, tessellation, and capability-gated
single-solid STEP/BREP export. Dispose that result independently; it owns the
imported native shape but borrows the evaluator and kernel.
This executable module creates an exact OCCT box, exports STEP entirely in
memory, commits and canonically persists the one-body document, resolves the
same bytes, imports them through the strong boundary, inspects measurement and
topology, and re-exports deterministic STEP:
examples/docs/imported-body-round-trip.ts.
This slice does not provide imported-body healing or repair, multiple-body
selection, body sets, external components, assemblies, automatic location I/O,
media-type inference, a public general Document v7 alias, or integration with
the ordinary v6 design builder and evaluate(...). Those remain separate
product milestones.
Browser download
Repository-only product-document staging
The ordinary v6 design builder still does not expose an imported-body or body-set feature node. The narrow public workflow above is separate from the repository-only broader product grammar. The repository containsstagedBodySetDesignV7(...), a source-only authoring facade for the exact graph
admitted by the staged evaluators. This is executable Document v7 evidence, not
an end-user product-document workflow: the package-root DesignDocument
alias, ordinary builder/evaluator, and migration target remain on the frozen v6
model, and no package subpath exposes the facade. The public one-body facade
does not make these broader types callable.
The staged facade authors:
- typed length, angle, mass-density, and scalar parameters, named parameter configurations, document-owned materials, typed part-material substitutions, and assembly-definition instance-suppression overrides;
- direct boxes, cylinders, and spheres by privately composing the existing v6 builder;
- owner-bound solid DAGs whose leaves are those primitives or imported bodies
and whose internal nodes are ordered
union(...),subtract(...),intersect(...), or transform operations, with typedtranslate(...),rotate(...),scale(...), andmirror(...)conveniences; - document-owned commitments with an explicit SHA-256 digest, byte length, media type, ordered location hints, and detached metadata;
- owner-bound imported-body leaves for STEP with file units or text/binary BREP
with an explicit
mm,cm,m, orinlength unit; - owner-bound body sets whose dense, non-empty ordered memberships reference facade-created primitive/import/Boolean/transform DAG roots;
- owner-bound parts whose geometry is one staged solid DAG root or one admitted body set;
- external-part handles that bind one named part output from a committed InvariantCAD document resource without publishing a local feature node;
- external-assembly handles that bind one named assembly output from a committed InvariantCAD document resource without publishing a local feature node; and
- acyclic nested fixed-placement product assemblies whose instances reference owned local parts, already-completed local assemblies, or either external handle and select inherited, base, or named child configuration contexts, plus outputs that directly target an imported body, body set, part, or staged product assembly.
none and expects one solid. Its explicit
format chooses the reader; committed mediaType is provenance and resolver
context, not reader inference. The caller supplies the digest and byte-length
commitments; the facade does not read or hash the resource bytes. Ordered
resource locations are resolver hints only. Neither the facade nor the staged
evaluators perform file, URL, or package I/O. Authoring methods validate
namespaces, commitments, owner identity, material and configuration references,
dense collections, and unique member IDs. Build applies caller-selected
DesignDocumentLimits and strict v7 validation before returning detached,
deeply frozen v7 data. Immediate authoring ceilings default to
10,000 resources, 100,000 aggregate location hints, and 16 MiB of
aggregate UTF-8 location text. An individual body set is also rejected before
copying above the default 1,000,000 structural-value ceiling. Caller-selected
build limits can be stricter but cannot raise these immediate safeguards.
Parameter, configuration, material, part, assembly-instance, placement,
resource, import, unit-policy, and body-member options must be plain own-data
records, and required collections must be dense. Accessor-backed and unknown
fields are rejected without invocation or silent interpretation, and caller
metadata is detached.
The repository has four source-only staged geometry-evaluator slices:
- A direct imported-body output admits bytes only through the caller-supplied resolver, verifies the declared byte length and SHA-256 digest, and invokes the kernel’s strong exact single-solid import contract.
- A direct body-set output preserves authored member order, stable member IDs, optional names, and detached metadata. Every listed member is active and no member is treated as a primary body. Its solid reference may point to a box, cylinder, sphere, imported-body leaf, or a bounded Boolean/transform DAG rooted in those leaves. Shared graph nodes are evaluated once without collapsing authored memberships.
- A direct part output preserves part metadata and effective material/density
provenance while retaining an explicit single-solid or body-set geometry
branch. Its geometry must be one supported solid DAG root or one body set
admitted by the preceding slice. The staged facade authors exactly this part
boundary through
material(...),part(...), configuration.partMaterial(...), and direct partoutput(...). - A product-assembly output iteratively expands active local assemblies,
resolves direct external-part outputs, and expands one fixed external
subassembly boundary into its child-local part leaves. It preserves full
root-to-leaf occurrence paths, root-relative placements composed parent first,
and each part occurrence’s effective
inherit,base, or named configuration context. Every containing assembly’s context controls that definition’s suppression and placement expressions, including explicitfalseunsuppression, before component admission. Active local and external leaves may use the same single-solid or multibody geometry admitted by the direct part slice, including verified imported leaves.
Document-scoped external resources
The staged product evaluator resolves external document JSON from the root document’s resource registry before resolving geometry resources declared by those children. Its resolver requests carry a frozendocumentScope:
request.id: two child documents may
legitimately reuse the same document-local resource ID while resolving
different committed bytes. Ordinary resolveResourcesV7(...) requests remain
unscoped; documentScope belongs to this repository-only staged session, not a
new public 0.2.0 resolver contract.
Resolution is deterministic and two-phase. All selected external document
commitments are preflighted and resolved before child assembly expansion or
part preparation. Prepared child batches and kernel capabilities are then
preflighted before any child geometry resource callback, and all child geometry
bytes are admitted before document-body import callbacks run. Repeated
(documentScope, resourceId) commitments reuse verified retained bytes; the
same resource ID in a different scope remains distinct.
Native primitive/Boolean/transform body sets can run on either exact or
approximate kernels, and the result records the kernel representation as
mesh, brep, or sdf together with its exactness claim. Stock OCCT
constructs exact B-Rep geometry with partial Boolean history; a matched owned
facade supplies complete bounded evolution for the current Boolean only when it
proves the existing feature-scoped protocol. That promise cannot upgrade
partial history already carried by an operand, and the result’s exact flag
describes geometry rather than complete history. Manifold constructs
approximate mesh geometry with no topology snapshots. Evaluation never falls
back from one backend to the other. An imported member, including an imported
leaf below Booleans or transforms, is different: the complete body set must use
an exact brep kernel with strong single-solid document-body import support.
Imported bytes cross the same verified-resource boundary as a direct
imported-body output. There is no weak importShape(...) fallback and no
automatic conversion to a mesh representation.
Executable stock-OCCT evidence resolves a commitment-verified STEP box,
subtracts a native tool, transforms the result, inspects partial topology,
exports STEP, and restores the native ownership baseline. Separate real
Manifold evidence evaluates configuration-sensitive native Boolean graphs
through nested products, BOM, mass, suppression, limits, and complete cleanup.
External-product OCCT evidence separately resolves a child document and its
scoped STEP resource, evaluates an imported/translated/Boolean child part beside
a native part through a nested local subassembly, reuses both exact child leaves
across two fixed-subassembly occurrences, exports retained child solids as STEP,
produces aggregate mesh/STL, and restores the native ownership baseline.
The staged body-set result owns its distinct kernel shapes until disposal.
Each member can produce a detached mesh and measurements. Topology and native
single-body STEP/BREP export remain gated by the kernel’s separate
capabilities. A shared leaf can be evaluated once without collapsing its
distinct authored memberships; shared Boolean and transform nodes are memoized
the same way. Aggregate tessellation preserves the authored member sequence,
while aggregate STL and OBJ export are mesh-derived and therefore explicitly
approximate/lossy even when the source bodies are exact. No exact aggregate
STEP or BREP export is available: the evaluator does not fuse the bodies,
invent a compound, or otherwise reinterpret the authored set. The selected
kernel must advertise and implement boolean before resolver or shape work,
and malformed optional exact-evolution metadata fails closed. A null or exactly
zero-volume subtraction/intersection returns error-level EMPTY_RESULT; an
impossible empty union or invalid measurement is a kernel protocol failure.
Every remaining Boolean result must be classified by the kernel as exactly one
body. A disjoint union or a subtraction that splits its target returns
EVALUATION_UNSUPPORTED with details.reason set to multiple-bodies;
loose or empty non-body topology uses non-body-topology. The evaluator does
not split such a result into invented members or exchange it as a multibody
file; author each body as an explicit body-set member instead.
There is no staged allowEmpty option. Failure releases every distinct
acquired or intermediate shape exactly once. Success keeps those shapes live
until the staged design is disposed; subsequent facade and per-body solid
operations then fail deterministically. The supplied kernel is borrowed and is
never disposed by this operation.
The staged operation bounds selected outputs, caller parameter overrides,
authored memberships, distinct primitive/import leaves, all solid-graph nodes,
all Boolean target/tool and transform input edges, authored transform
operations, documents, and resolved resources. Repeated Boolean tool
references count as repeated edges. The solid-DAG ceilings are
maxSolidGraphNodes, maxSolidDependencyLinks, and
maxTransformOperations, each defaulting to 100,000; maxDistinctSolids
separately counts leaves. Those admission limits do not bound mesh buffers
created by later facade calls; applications handling hostile or very large
tessellations still need a disposable worker or process and an
application-owned output ceiling.
For a direct part, a named configuration can replace the authored materialId.
An explicit part density still overrides the effective material density, and a
legacy material label or material name is never used as a catalogue lookup.
Length and density parameters first resolve in the selected configuration and
may then be specialized by bounded caller overrides. The one-row part BOM and
physicalMassProperties() use one uniform effective density across all
memberships. Mass is additive over independent bodies: repeated aliases and
spatial overlaps each contribute once per authored membership rather than
being fused or unioned. The numeric result inherits the selected backend’s
measurement quality; a kernel capability marked exact is not a separate proof
of mathematically exact floating-point mass.
Part-level mesh() and STL/OBJ export are aggregate tessellation views and are
therefore approximate/lossy. Exact native export remains a per-solid or
per-body capability. Multibody parts do not expose aggregate geometric
measurement, exact aggregate STEP/BREP, a cross-body topology namespace,
interference subtraction, or primary-body inference. Bare body sets still have
no aggregate mass contract; the part supplies the uniform-density semantics.
For a product assembly, repeated part instances retain distinct full occurrence
paths and BOM quantities. One local part result is reused for equal
(part, effective configuration) contexts; one direct external-part result is
reused for equal (resource, output, effective child configuration) contexts.
Fixed-subassembly leaves reuse geometry by
(resource, child part node, effective child configuration). Distinct external
output aliases retain distinct component, diagnostic, and BOM identity even
when they select the same child part or assembly and share the same evaluated
geometry. Neither optimization is a cross-run cache or geometric-equivalence
claim.
Aggregate product mesh, binary/ASCII STL, and OBJ apply each root-relative
occurrence placement and remain approximate/lossy. Exact native export remains
available from a retained child solid when its kernel supports the requested
format: the stock-OCCT product evidence exports the child as STEP. The product
itself rejects STEP/BREP aggregate export with EXPORT_UNSUPPORTED; evaluation
does not fuse child solids or invent a compound. The contextual BOM keeps
unlike component/configuration states separate, while physical mass applies
each effective density and placement before combining mass, center of mass, and
inertia.
A suppressed assembly edge prunes its entire subtree before descendant
admission or work accounting. Traversal is bounded by maxAssemblyDepth,
maxOccurrencePathSegments, scanned-instance, active-occurrence, placement,
and downstream part/resource ceilings. Solid-graph nodes, all Boolean
target/tool and transform input edges, and transform operations are counted
globally by (node, effective configuration) across active occurrences:
repeated occurrences in one context share the charge, while different contexts
are independent. Repeated Boolean references still charge every authored edge.
An active external part or fixed subassembly is evaluated only after its
committed child document has been admitted. Child inherit maps parent base to
child base or a parent named configuration to the same child ID; explicit
base and named selectors target the child directly. Descendant selectors,
suppression, and placement expressions are interpreted inside that child
document. Root caller parameter overrides never flow into it.
An external-part handle must select a direct part output, and an
external-assembly handle must select a direct assembly output. The latter may
expand child-local parts and nested local assemblies. Each active occurrence
path may cross only that one external-document boundary: a suppressed nested
external component is inert, while an active one is rejected before nested
resolution or child geometry/kernel work. The facade cannot construct a local
cycle because it accepts only already-completed same-builder local assembly
handles; strict document admission and evaluation reject hand-authored
recursive local graphs.
The authoring facade, all four staged geometry-evaluator slices, and the
kernel-independent datum resolver remain repository-only source work:
none is exported by the public 0.2.0 package root, available from a package
subpath, or integrated with the public builder or CLI. The facade can author
datum points, axes, planes, and coordinate systems, but they are resolved by
selected node ID rather than published as design outputs or consumed by
sketches or shape algebra.
The facade authors direct external-part and external-assembly handles, but not a
recursive external product graph. External part outputs and the local part
leaves reached through one selected external assembly must still be admitted by
the existing staged part evaluator; the product evaluator does not widen the
child’s supported feature families. A migrated v6 part backed by an extrusion,
for example, is rejected before geometry or scoped child-resource work. Cyclic
local-assembly graphs, Boolean or transform inputs that are body sets, parts, or
assemblies, other body-consuming operations, per-body materials, and general
solid graphs beyond primitive/import/Boolean/transform DAGs remain unsupported.
The facade cannot publish a direct primitive or a generic Boolean or transformed
solid output either; generic solid geometry must be retained by a body set or
part. The narrower imported-body handle remains the only directly publishable
solid-like staged reference.
The operations perform no location I/O and admit no healing beyond none. A
resource’s mediaType is committed provenance passed to the resolver; it is
not used to choose the reader or accepted as a substitute for the imported-body
node’s explicit format. Assembly mates, motion, interference/collision,
assembly-wide topology or geometric measurement, and exact aggregate STEP/BREP
remain unsupported. The narrow public one-body facade applies the closed
protocol-v1 format/media/unit table documented above; that policy does not
promote this broader product grammar.
There are deliberately two kernel boundaries:
nativeImportsplusimportShape(...)is weak exchange support. It may return any shape accepted by the backend.documentBodyImportplusimportDocumentBody(...)is the versioned strong boundary required by a canonical imported-body node.
{ mode: "from-file" }. Text and binary BREP
are unitless, so they require { mode: "declared", length: "mm" | "cm" | "m" | "in" }. Protocol v1 requires { healing: { mode: "none" } }.