Skip to main content
Create a builder with design(name, options?). Every returned authoring handle is immutable and owned by that builder. Only handles returned by builder methods are accepted: a SolidRef, PartRef, MaterialRef, or other handle created with its exported class constructor is rejected even when it names the builder as owner. The AssemblyBuilder and ConfigurationBuilder passed to assembly(...) and configuration(...) callbacks close when the callback returns or throws, so later calls through an escaped builder throw instead of being silently dropped. An assembly stores only the instances that instance(...) added, and a sketch stores only the entity and constraint records its SketchBuilder methods placed in entities or constraints, each in the same map and under the same key, so a raw record or a record moved between the two maps throws. A configuration ID stays reserved while its callback runs, so a nested configuration(...) call with the same ID throws. The builder copies caller-supplied metadata, transform operations, assembly placements, and topology selections into its own records, so changing those caller objects later cannot alter a built document, and authoring never freezes them. Expression IR is the exception: expressions are immutable, so raw IR passed to fromExpressionIR is validated and frozen in place. Option records, and the lists and records that reach a document, including path points and segments, loft profiles, Boolean tools, transform operations, placements, plane IR, topology selection IR, sketch entity and constraint records, raw loop edge uses, and profile holes, are read once through their own data properties, enumerable or not. Caller accessors never run and inherited fields are never used, so every check applies to exactly the value the document stores. Option records must be plain objects; fields a method does not use are ignored, except in part(...), which rejects them.

Document definitions

Primitive and profile nodes

Feature nodes

Backend support is checked during evaluation, not authoring. A document may be valid while a selected kernel lacks one of its features.

Product structure

Repository-only staged product authoring

The source tree contains:
This is a deliberately narrow facade for authoring bounded primitive/import/Boolean/transform solid DAGs, acyclic fixed-placement products with one external-document boundary per active path, and datum nodes admitted by the four staged geometry evaluators and the datum resolver. Its implementation lives in src/internal/document-v7-body-set-authoring.ts; it is not exported from the package root or any package subpath. The public design(...) builder, DesignDocument alias, evaluator, and migration target remain on frozen v6. This is repository-only staged work, not public 0.2.0 authoring. The facade privately composes the v6 builder for admitted parameters and native primitives, then adds staged configuration, material, resource, imported-body, body-set, part, local assembly, external component, and datum records. Its complete admitted surface is: The relevant staged source signatures are:
Every staged reference is owned by the builder that created it. Resource IDs, parameter IDs, material IDs, configuration IDs, node IDs, output names, and body-member IDs must be valid and unique in their applicable namespace. The facade rejects foreign or forged geometry, material, part, assembly, external-component, resource, and output handles. External handles require an owned resource whose media type is application/vnd.invariantcad.document+json; they retain a valid requested output ID and publish no local feature node. The facade constructs canonical references from captured identity rather than dispatching through mutable handle methods. Datum authoring returns owner-bound, frozen handles reserved for later feature consumers; no method in this slice consumes one. A body-set member keeps its stable ID, authored position, optional name, and detached JSON metadata; multiple memberships may deliberately reference the same owned solid DAG root. Every listed member is active, and the facade neither selects nor infers a primary body. A staged part wraps either one owned primitive, imported-body, Boolean, or transformed-solid DAG root, or one owned body set. It cannot wrap another part or assembly, or use a body set, part, or assembly as a Boolean or transform input. material(...) authors a document-owned definition with an explicit mass-density expression. part(...) accepts the existing PartOptions contract: part number, description, detached metadata, optional explicit density, and either a legacy descriptive material string or an owned materialRef, never both. A configuration can substitute another owned material for an owned part. The selected material determines effective material identity and supplies density when needed; an explicit part density still wins. A legacy label and a material name never perform catalogue lookup. Length, angle, density, and scalar parameters may be specialized by a named configuration and then by bounded caller overrides during evaluation.

Staged solid Boolean and transform DAGs

StagedSolidRefV7 is the owner-bound handle shared by direct primitive leaves, imported-body leaves, Boolean results, and transform results. union(...), subtract(...), and intersect(...) require one target plus a dense, non-empty tool list. Every operand must be a solid handle issued by the same builder. A Boolean result can feed a later Boolean, transform, body-set membership, or part. The evaluator invokes exactly one kernel Boolean with the target plus a frozen tool array in authored order. Backend implementation strategy is kernel-owned, but it may not reorder operands or reinterpret the authored operation. transform(...) requires at least one operation and captures its dense ordered operation list as plain own data. translate(...), rotate(...), scale(...), and mirror(...) author one typed operation each; their vectors use length, angle, scalar, and scalar expressions respectively. A transform result can feed another transform, a Boolean, body-set membership, or a part’s single-solid geometry. The staged evaluator plans the selected closure iteratively, deduplicates shared nodes, and executes every dependency before its consuming Boolean or transform. maxDistinctSolids counts primitive and imported leaves. maxSolidGraphNodes counts all distinct admitted solid nodes, maxSolidDependencyLinks counts every transform input edge plus every Boolean target and tool edge, including repeated tool references, and maxTransformOperations counts the ordered operations stored on distinct transform nodes. Each defaults to 100,000 in the staged body-set, part, and product-assembly evaluators. Transform expressions must resolve to finite values. Scale components must be nonzero, and a mirror normal must be finite and nonzero. The selected kernel must advertise and implement transform; each transform must return a fresh, valid, positive-volume owned shape. Failure is transactional and releases all shapes created for the graph. The selected kernel must advertise the boolean feature and implement the Boolean method before resource resolution or shape acquisition. Every created node must return a fresh, valid owned shape. A subtraction or intersection that returns a null shape or exactly zero volume fails with EMPTY_RESULT; an impossible empty union, negative/non-finite volume, or any returned shape that aliases an owned operand or earlier result fails as a kernel protocol violation. Repeated authored operand references remain allowed and are charged as separate dependency edges. Staged evaluation has no allowEmpty option. Failure is transactional and releases every acquired intermediate exactly once. Stock OCCT evaluates this graph as exact B-Rep geometry and retains its normal per-solid topology/native-export capabilities. Its Boolean history remains partial unless an owned facade honestly advertises the existing feature-scoped exact indexed-evolution protocol for boolean. A present malformed exact-evolution envelope fails before resource or kernel work rather than silently downgrading history. That promise covers the current Boolean operation; it does not upgrade partial history already carried by an imported or otherwise partial operand, and exact: true describes geometry rather than complete persistent history. Manifold evaluates native primitive/Boolean/transform graphs as approximate mesh geometry and exposes no topology snapshots. The evaluator never retries an exact failure on Manifold or silently converts an exact graph to a mesh graph. Any imported leaf, transformed or Boolean-composed, additionally requires the strong exact B-Rep single-solid document-import capability, so it cannot fall back to Manifold’s weak or absent import path. This is not general shape algebra. A transform input must be an owned StagedSolidRefV7 created by a primitive, imported body, Boolean, or prior transform, and the same restriction applies to every Boolean operand. A body set, part, or assembly cannot be Boolean-composed or transformed. A generic Boolean or transformed solid also cannot be published directly by output(...): it must be retained by a body set or part. The one exception to generic-solid output is the narrower direct StagedImportedBodyRefV7 output already admitted by the import evaluator.

Fixed-placement products

The staged assembly(id, build) method passes one StagedLocalAssemblyBuilderV7 to its callback. Each instance(...) in this slice references an owned local part, an already-completed owned local assembly, or an owned external-part or external-assembly handle. A local assembly handle is published only after its callback succeeds, so the facade can reference earlier local definitions but cannot author a local cycle. Foreign or forged handles are rejected. externalPart(resource, output) and externalAssembly(resource, output) require a committed InvariantCAD-document resource owned by the same staged builder. They select a valid named part or assembly output without publishing a local feature node. Product evaluation admits the committed child document before child geometry work. A selected external assembly may contain child-local parts and bounded nested local assemblies; each active occurrence path may cross only one external-document boundary. An instance ID is stable within its containing assembly. Evaluation joins those IDs into a full root-to-leaf path for each emitted part occurrence. Its placement is an ordered list of translate, rotate, scale, or mirror operations, and its configuration selector is exactly one of:
  • inherit, which evaluates a local component in the containing assembly’s context and maps root base to child base or a root named ID to the same ID in an external document;
  • base, which evaluates a local component in the root base context or an external component in the child base context; or
  • named, which selects the referenced configuration in the applicable root or child document.
Omitted instance options mean an empty placement, suppressed: false, and { mode: "inherit" }. The configuration active for every containing assembly controls its definition-scoped instance suppression and placement expressions. configuration.instanceSuppressed(...) can target any owned assembly definition. Its suppressed argument defaults to true, so omitting it writes a suppression override; pass false to reactivate an instance authored with suppressed: true. Only an instance with no instanceSuppressed(...) override in the active configuration keeps its authored value. Suppression is resolved before component support is checked, so suppressing an assembly edge prunes its entire subtree, including external or otherwise unsupported descendants. A suppressed nested external component therefore performs no descendant resolution or kernel work. An active direct external part or fixed subassembly is supported, but an active external descendant inside the admitted child would cross a second document boundary and is rejected before nested resolution or child geometry/kernel work. Occurrence IDs are unique only within their containing assembly. Repeating one part or subassembly in several instances is expected and does not copy its document definition. Iterative evaluation expands active assemblies in authored depth-first leaf order and composes edge placements parent first. Bounded caller parameter overrides apply to root-document contexts only and never cross an external boundary. Per-occurrence inherit, base, and named selection can therefore produce different geometry, material, or density from a shared part definition. Evaluation reuses local parts for equal (part, effective configuration) states and direct external parts for equal (resource, output, child configuration) states. Fixed-subassembly leaves share geometry by resource, child part node, and child configuration; distinct assembly output aliases retain separate component, diagnostic, and BOM identity. Every external component of one child document and child configuration, whether reached through externalPart(...) or externalAssembly(...), is prepared in one batch, so the solid graph of a child part used both ways is constructed once and counted once toward maxDistinctSolids, maxSolidGraphNodes, maxSolidDependencyLinks, and maxTransformOperations, while keeping separate component and BOM identity. The direct selection and the fixed-subassembly leaf selection of that part still count their bodies separately toward maxPartBodies. Direct part evaluation may additionally deduplicate solid-DAG acquisition within a configuration batch. Authoring does not create a persistent or cross-run cache commitment. Product evaluation separately bounds nesting with maxAssemblyDepth and the aggregate stored leaf-path length with maxOccurrencePathSegments, in addition to its external-document, instance, occurrence, placement, contextual-part, solid, material, document, and resource ceilings. Solid-graph nodes, all solid dependency edges, and transform operations are counted globally across the active product. Repeated occurrences in the same effective context reuse that work, different contexts are charged independently, and suppressed subtrees are pruned before these counters advance. Hand-authored cyclic local dependencies fail strict document admission, and evaluation also rejects a recursive path rather than looping. The four datum methods author ordinary Document v7 nodes with stable node IDs. A point carries a length-valued position. An axis adds one scalar direction. A plane adds scalar positive-X and normal directions, while a coordinate system adds scalar positive-X and positive-Y directions. Their owned handles establish builder identity, but datums are not admitted by output(...): callers pass datum node IDs to the separate source-only datum resolver. That resolver is kernel-independent, normalizes admitted directions, and returns deterministic orthonormal right-handed frames. It rejects non-finite or zero directions and requires each normalized authored direction pair to have an absolute dot product no greater than 1e-12 before deterministic re-orthonormalization. Authoring a datum does not yet make it usable as a sketch plane or shape-algebra input. A resource commitment describes bytes that an application resolver may later supply. Its ordered locations are hints only: authoring and evaluation never dereference them. The facade does not read bytes or compute a digest or byte length; the caller supplies those commitments. The imported-body method, rather than mediaType, chooses the reader. STEP always uses { mode: "from-file" }. Text and binary BREP require { mode: "declared", length: "mm" | "cm" | "m" | "in" }. All staged imports emit { healing: { mode: "none" } } and expected: "single-solid"; there is no healing switch, weak-import fallback, or automatic exact-to-mesh conversion. build({ limits }) accepts a partial DesignDocumentLimits override and strictly parses the completed document. A failed strict result is raised as a CadError; no invalid document escapes. During authoring, resources also use the current default ceilings of 10,000 definitions, 100,000 aggregate location hints, and 16 MiB of aggregate UTF-8 location text. One bodySet(...) call is additionally rejected before copying if it exceeds the default 1,000,000 structural-value ceiling, and one instance placement cannot contain more operations than that ceiling. The completed assembly instance list is charged to maxStructuralValues during build. Final build limits can be stricter; they do not raise these immediate safeguards. Authoring methods fail immediately when a handle comes from another builder, a namespace is duplicated, a required collection is sparse or empty, member IDs repeat, a commitment is malformed, a configuration references foreign definitions, an immediate authoring ceiling is exceeded, or a parameter, configuration, material, part, assembly instance, placement, resource, import, unit-policy, datum, or body-member option record contains an accessor or unknown field. Those inputs must be plain own-data records; required arrays must be dense own-data arrays. Accessors are not invoked and unsupported semantics are rejected rather than silently discarded. Namespace checks also hold across re-entrant calls: if a callback or a Proxy trap registers the same node, configuration, material, or resource ID while another call is still capturing its inputs, one of the two calls fails with the duplicate error and neither definition is silently replaced at build. Build then applies caller-selected document limits and strict v7 validation before returning the detached, recursively frozen document. This facade does not author generic solid or direct primitive outputs, recursive external-document graphs, cyclic local assembly graphs, datum-backed sketches, Booleans over body sets/parts/assemblies, transforms over body sets/parts/assemblies, other body-consuming features, per-body materials, or general solid graphs beyond primitive/import/Boolean/transform DAGs. It also does not add datum design outputs, CLI support, location I/O, reader inference, healing, a primary-body rule, cross-body or assembly topology, aggregate geometric measurement, mates, motion, interference/collision, or exact aggregate STEP/BREP export. Part- and product-level STL/OBJ export is an aggregate tessellation view and is explicitly approximate/lossy; exact native export and topology remain per-solid or per-body capabilities. These omissions are product boundaries, not implicit fallbacks. The staged facade remains repository-only work, and the public 0.2.0 document alias remains v6.

ID rules

All IDs use the shared stable ID grammar and must be unique in their namespace. Avoid generated array positions or user-facing labels that may change. Prefer IDs such as housing-side-a, left-bearing-seat, and manufacturing-assembly.

Programmer errors

Methods throw TypeError or RangeError for invalid direct calls, including:
  • duplicate or malformed IDs
  • references from another builder, including a parameter used anywhere inside an expression, transform operation, placement, or topology predicate
  • wrong-dimensional expressions
  • wrong topology kind
  • invalid option enum/literal values, non-boolean flags, and non-string labels or descriptions
  • part(...) option fields other than partNumber, description, material, materialRef, massDensity, and metadata, even when the options come from a variable that TypeScript’s excess-property check skips
  • segments or divisions values that are not integers within the document grammar’s bounds
  • empty required lists
  • non-positive or non-finite numeric sketch and path tolerances
  • metadata that is not plain JSON data, such as non-finite numbers, Map, Set, Date, or class instances, functions, BigInts, sparse arrays, accessor properties, or cycles
  • metadata with more than the default maxStructuralValues of 1,000,000 values or nested deeper than the default maxNestingDepth of 128, counting a shared subtree in full, at its own depth, everywhere it occurs, as the writer and parser do
  • sparse or accessor-backed lists, such as polylinePath points, compositePath segments, loft profiles, Boolean tools, transform operations, sketch polyline points, loop edges, and profile holes
  • option records, transform operations, plane IR, path segments, raw loop edge uses, and topology selections whose fields are accessors, inherited, or not held by a plain record
  • option records with more than 1,000,000 own fields, the default maxStructuralValues, which are refused before any field is copied
  • raw expression IR, such as IR passed to fromExpressionIR, whose validation would visit more than 1,000,000 nodes and scheduled operands, counting a shared operand each time it is scheduled; a min or max whose operand list alone exceeds what remains of that budget is refused before the list is frozen or copied
  • cyclic topology selections, selections with more query nodes than the default maxTopologyQueryNodes limit of 100,000 or more structural values than the default maxStructuralValues of 1,000,000, and selections nested deeper than the default maxNestingDepth of 128; like the parser, these limits count a shared query in full, at its own depth, everywhere it occurs
  • references targeting the wrong direct solid
These failures indicate an invalid model definition or unvalidated UI input, not a geometry-kernel failure. Because every input is checked when its method is called, build() output satisfies the strict document grammar, and parseDocument accepts the text stringifyDocument writes for it unless semantic validation finds a modeling problem or the document exceeds the parse limits. build() itself does not run semantic validation or apply document size limits. Unresolvable topology selections, unclosed sketch loops, and similar model errors are reported as diagnostics by validateDocument, parseDocument, and evaluation. Expression values are resolved only during evaluation. An expression-valued tolerance (shell, offset, or topology predicate), a zero direction vector, or a negative radius is therefore reported as an evaluation diagnostic, such as TOPOLOGY_SELECTOR_INVALID, rather than thrown while authoring.