BREP_plugins
Headless, native/WASM JavaScript CAD packages, backed by Boa and exact kernel
solids. Provision trusted PackageBundle objects explicitly with Runtime::new.
Saved documents contain only plugins: [PluginPin]; loading a document never
installs its code. Preserve old bundles by digest when installing updates.
Runtime::execute_history scopes the kernel extension provider. For observed
history, exports and nested-library operations, run the ordinary kernel call
inside Runtime::with_provider. Scopes restore on return and unwinding. Each
history document selects its own exact pins; component snapshots containing
plugins conservatively replay instead of trusting cached display geometry.
Registry owns serializable immutable feature/action/workbench/panel/annotation descriptors.
for_document selects exact pins and rejects missing/duplicate dependencies.
Runtime::from_validated accepts only metadata produced by a trusted worker;
it checks package ownership without executing code on the receiving UI thread.
ESM supports package-local relative imports only. Each install/callback creates
a fresh interpreter, evaluates the real module graph and invokes its default or
named install(app) export. Installation is atomic. Callback registrations are
validated again against installed metadata before invocation. Synchronous
callbacks reject Promise/thenable returns. No DOM, filesystem, network, ambient
time or random API is supplied. Loop/recursion limits and host operation caps
are not hard memory or wall-clock isolation; native geometry cannot be preempted.
Feature context exposes params, persistentData, featureId, units (mm),
and geometry: sphere({radius}), cube({sizeX,sizeY,sizeZ}),
cylinder({radius,height}) (+Y), transform(handle,{position,rotationEuler,scale})
(angles in degrees), boolean('UNION'|'SUBTRACT'|'INTERSECT',a,b),
reference(name) and query(handle) ({volume}). References must appear in
saved parameters/persistent inputs and remain subject to the component fence.
Solids are owned locally until all returned outputs validate; exceptions discard
the arena without touching upstream solids. Outputs publish as featureId:key.
Opaque handles cannot be saved in persistent state or reused by another call.
Actions receive host input (params, selection, document) and return an
ActionPlan. ctx.document.addFeature(type,params,persistentData),
updateFeature(id,params) and deleteFeature(id) only stage commands;
ctx.notify(text) stages notifications. The host must validate the whole plan,
recompute, check document/selection revision, commit pins and commands atomically,
and create a single undo checkpoint. The runtime does not mutate a document.
V1 schemas support number (finite min/max), string, boolean and
reference_selection. Unknown parameter keys except reserved string id fail.
Arbitrary callback predicates, async callbacks and geometry adapters beyond the
methods above are explicitly unsupported. Module evaluation
must settle in the job drain; asynchronous host work belongs in host transport.
The examples below show declarative panels and typed annotations.
Declarative panels
app.registerPanel({id,label,controls}) registers static host-rendered content.
IDs use the package namespace. Workbenches claim panel IDs through panels.
Controls are tagged by type:
app.;
Action/form control IDs are unique local keys. Both link a named same-package
registered action; forms derive fields from that action's schema. params
defaults to {}. A form can leave required fields for the user to fill; a static
action must supply valid parameters or schema defaults. No panel callback runs
during a UI frame. addSidePanel aliases registerPanel; addToolbarButton
aliases registerAction, including its required named ID/run callback. Declare
workbench membership explicitly. Aliases use the same registry and lifecycle.
Panels allow at most 128 controls; tables allow 32 columns, 256 rows, and 4096 bytes per cell. Text controls allow 16384 bytes. Unknown controls/properties, foreign action IDs and unresolved workbench panels reject the whole package. Registry deserialization defaults absent panel/annotation lists to empty, so older validated metadata and existing v1 bundles remain supported.
Typed annotations
app.registerAnnotation({id,label,inputParamsSchema,execute(ctx)}) contributes a
PMI annotation provider, separate from solid features. The same pinned callback
runs synchronously during native/WASM worker replay; the Rust PMI adapter draws
the result. The public Runtime::execute_annotation(pins, annotation, context)
implements the kernel ExtensionProvider method of the same signature.
app.;
Context contains evaluated/defaulted params, persistentData, annotationId,
units (mm), and dependencies. Only schema-declared reference_selection
parameters populate dependencies[referenceName].point with a Rust-resolved
world point. Unresolved references fail before the callback. This context has
no solid handles or document mutation API.
Rust-backed constructors return validated PluginAnnotationOutput values:
ctx.annotation.note({text,position}): world-positioned text.ctx.annotation.leader({text,targets,defaultLabel,dot?}): leader targets;dotdefaults to false.ctx.annotation.linear({text,a,b,defaultLabel,component?}): dimension between two points, optionalcomponentX/Y/Z. Rust projects the endpoint onto that axis and computesvalueandunit:mm, matching the rendered span.
Callbacks may instead return the typed object directly:
{text,geometry,defaultLabel,references:[],value:null,unit:"",persistentData:null}.
The last four fields are optional. geometry is one of
{kind:"note",position}, {kind:"leader",targets,dot}, or
{kind:"linear",a,b,component?}. defaultLabel and every point are finite
three-number arrays. Coordinates are bounded to ±1e12; text is nonempty and at
most 16384 bytes; targets/references are capped at 256; unit is "", "mm" or
"deg". Zero-length linear geometry, unprojected raw component geometry, other geometry
kinds, unknown result fields,
nonfinite JSON and Promise/thenable results fail explicitly. Output references
must name declared resolved dependencies; the runtime records all declared
references even when omitted by the callback. Input/output JSON is capped at
1 MiB, as is registration metadata. Primitive calls share the 256 host-operation
limit. Persistent data remains JSON, and changes are returned for the enclosing
host transaction to apply only after successful replay.