Repository navigation
v3.0.0
⚠️ Breaking Changes
-
v3 is a ground-up modernisation: OCCT V8 (GA), native WebAssembly exceptions, ES modules, full TypeScript bindings with idiomatic JSDoc, content-addressed build caching, and reproducible builds via pinned dependency commits. (#4, #3, #5, #6)
Full v2 → v3 migration guide: BREAKING_CHANGES.md — every consumer-visible change with Before / After code samples and migration steps.
Highlights
- OCCT V8.0.1 (GA, up from V7.6.2) and Emscripten 6.0.5 (up from 3.1.14).
- libclang 18.1.1 for the bindgen parser (up from
15.0.6.1), paired with vendored LLVM 17.0.6 libc++ + clang resource headers to satisfy the LLVM project's libc++/clang N-1 compat-window policy. This is what makes the v3 bindings accurate for OCCT V8 — libclang 18 exposestemplateTypedefs, sees throughDEFINE_STANDARD_HANDLEexpansions, and resolvesNCollection_*template instantiations that v2's libclang 15 either skipped, mislabelled asUNEXPOSED, or surfaced as duplicate registrations. The parse environment is hermetic:src/ocjs_bindgen/config/paths.pyroutes libclang at the vendored libc++ headers and clang resource directory, not at the host system's clang. - Native WebAssembly exceptions (
-fwasm-exceptions) replace JSinvoke_*trampolines: ~12% gzipped size overhead vs. the prior ~80%, with zero happy-path performance cost.WebAssembly.Exceptionis decodable end-to-end through instance helpers such asoc.getExceptionMessage— see §C. - Performance vs. V7.6.2: 22-31% faster boolean operations, 16-19% faster fillets, 23-29% faster complex models. Full numbers in Appendix G.
- ES module distribution —
"type": "module"; the package root selects and initializes a supported variant, self-locates its WASM, and exports the live instance plus bound values.libcascade/initis the shared lazy selector, whilelibcascade/single/initandlibcascade/multi/initare genuinely fixed runtime and type contracts that let bundlers exclude the other glue. Matchingsingle/wasmandmulti/wasmsubpaths support explicit relocation — see §A. - Suffix-free overload symbols — a single
gp_Pnt,BRepPrimAPI_MakeBox,BRepBuilderAPI_MakeEdge, etc., with a value-based dispatcher that picks the right C++ overload from your argument types. The_N-suffixed overload subclasses are gone except for genuinely ambiguous same-arity cases. Same-arity dispatch is now unified acrossclass_function+ instance overload sets, fixing v2's silently-clobbered registrations. See §B1 and §D7. Dispatcher cost measured at ~264 ns/call (~5 µs per typical CAD render, 0.003–0.011% of wall time) — see BENCHMARKS.md §3. - Output-parameter return shape redesigned — methods with class output parameters (
gp_Pnt&,Bnd_Box&,GProp_GProps&, …) mutate the caller's instance in place and read directly from it (no envelope mirroring). Methods with primitive / enum / elided-Handle outputs return a structured envelope; v2's{ current: 0 }placeholders are gone. See §B2. - Non-const
Handle<T>&output positions elided from the JS signature — callers no longer allocatenew oc.Handle_<T>()placeholders for output-only Handle slots. Drop the position from the call entirely and read the freshly-assigned Handle from the envelope. ~2.29× wall-clock speedup on affected call sites. See §B3. TopoDSnamespace bridge —oc.TopoDS.Edge(shape),oc.TopoDS.Face(shape), etc., replacing v2's mix ofprototype.Edge,_TopoDS_Edge, manualgetPointerpatterns. See §D1.- Baseline WebAssembly SIMD (
-msimd128) on by default. Relaxed-SIMD ops (-mrelaxed-simd) are gated behindOCJS_RELAXED_SIMD=1because Safari 26.x cannot parse them. -sWASM_BIGINTon by default — eliminates the Emscripten i64 legalisation pass.- Full TypeScript bindings with Doxygen-derived JSDoc rendered correctly in Monaco IntelliSense, fixed-width primitive types (
int8_t,uint32_t,int64_t) mapped to TS scalars, sized-tuple emission for C array parameters, and string enums. - File-backed custom build inputs —
additionalCppFilesdrives generated bindings and per-buildadditionalBindFilesdrives raw Embind. Ordered paths and SHA-256 digests are recorded in build manifests and provenance. Symbol.disposelifecycle on every disposable binding — explicit resource management is wired in for both class wrappers and envelope returns that own Handle resources. The disposer is idempotent and alias-safe.- Reproducible builds via
DEPS.json(every external dependency — OCCT, rapidjson, freetype, Emscripten, LLVM 17 — pinned to an exact commit hash) plus aprovenance.jsonsidecar shipped alongside every release artefact. - Cached, incremental builds through Nx with content-addressed inputs — 10-30 minute clean builds become seconds on a hit.
Breaking changes
Each entry deep-links into BREAKING_CHANGES.md for Before / After code samples and migration steps.
- A — Module loading — ESM-only universal eager root, shared and fixed lazy initializers, single- and multi-threaded artifacts, zero-configuration initialization, and matching WASM relocation subpaths (no
dist/*deep imports). - B — JS / TS API surface —
_N-suffixed overload subclasses collapsed to single symbols; output-parameter return shape redesigned (class outputs mutate in place, primitives / enums / elided Handles ride a structured envelope); non-constHandle<T>&output positions elided from the JS signature; envelope native-return field isenvelope.returnValue(reserved to avoid collision with OCCT parameters namedresult). - C — WebAssembly exception handling — caught exceptions are
WebAssembly.Exceptioninstances; decode viaoc.getExceptionMessage. - D — OCCT V8 API —
TopoDSnamespace bridge replaces direct namespace binding;Bnd_Box::Getremoved (useCornerMin/CornerMax);Poly_Triangulationnormals API now value-returning;BRepMesh_IncrementalMeshconstructor signature;TopoDS_Shape::HashCoderemoved with no shipped replacement; same-arity overload dispatch unified across static + instance variants; JS-indistinguishableint/size_tNCollection pairs collapsed at codegen time (V8'ssize_tmigration). - E — Removed symbol families —
OpenGl_*/Aspect_Window/ rest ofTKOpenGl(headless target);TopOpe*(useBOPAlgo_*/BRepAlgoAPI_*); legacyStandard_Transient-based collections (use auto-discoveredNCollection_*);GCE2d_*aliases (useGC_*2d). - F — Build flag changes — source-build CLI replaced (
src/buildFromYaml.py→build-wasm.shwith explicit subcommands and an optional--config <name>selecting an optimisation profile fromconfigurations.json); referencefull.ymlbundled inside the Docker image as the starting point for custom builds; native WebAssembly exceptions on by default; Emscripten flag renames and additions from the 3.x → 6.x toolchain upgrade.
Build system
- Native WASM exceptions as the baseline link mode — replaces
-fexceptionsJSinvoke_*trampolines with-fwasm-exceptions, cutting exception-build size overhead from ~80% to ~12% gzipped. build-wasm.shunified entry point (DockerENTRYPOINT+ host CLI parity) — explicit subcommands (full,link,validate,generate,bindings,sources,pch),--help, optional--config <name>fromconfigurations.json, and build-summary output.- Named compile configurations in
configurations.json—single-threaded(production default),single-threaded-smallest(size-tuned-Os),multi-threaded(SAB threading),multi-threaded-browser(resizable-buffer browsers), anddebug. Link-only BigInt and eval-ctors settings live in each YAML'semccFlags. - Reference
full.ymlbundled inside the Docker image at/opencascade.js/build-configs/full.yml— the complete symbol list the published tarball builds from. Extract it as a starting point using the trim-symbols guide. DEPS.jsondependency pinning — every external dependency (OCCT, rapidjson, freetype, Emscripten, LLVM 17) is pinned to an exact commit hash with version metadata.clone-deps.shautomates setup.- Hermetic libclang parse environment —
src/ocjs_bindgen/config/paths.pyis now the single source of truth for include resolution during the bindgen discover pass. It points libclang at the vendored LLVM 17 libc++ headers + clang resource directory (matching libclang 18's expectations under the N-1 policy) and at the OCCT flat-include symlink farm, so source generation is reproducible across hosts and immune to whatever system clang the developer happens to have. The same parse contract is validated ondarwin-arm64,linux-x86_64, andlinux-aarch64withuv-managed Python 3.14.4; native final-link bytes may differ by host toolchain. - Build provenance — every build emits a
provenance.jsonsidecar capturing toolchain versions, source commits, compile / link flags, cache key, and output sizes. Shipped alongside the WASM in the published tarball. - Nx-based caching — the full pipeline (
apply-patches→pch→generate-bindings→compile-bindings→compile-sources→link→validate→provenance) is wired through Nx with content-addressed inputs (including git-ignored files), so partial rebuilds are surgical. Clean full build ≈ 30 minutes; cache hits skip compilation entirely. Stale.ofiles compiled with one set of flags are invalidated when the flags change. - Validation harness (
build-wasm.sh validate <yaml>) — post-build checks that every requested symbol has a compiled.o, the.wasmexists at a reasonable size, and Emscripten EH helpers are present in the linked JS glue when requested. - Configurable optimisation —
wasm-optruns at-O4for production configs and-O3for size-tuned;--traps-never-happenis enabled;OCJS_EXTRA_CFLAGSpasses arbitrary flags through toemcc. - Updated Dockerfile to
emscripten/emsdk:6.0.5with pinned digest, dependencies cloned at exact commits fromDEPS.json, entrypoint viabuild-wasm.shwith env-var passthrough; a Docker E2E validation script is included.
Tests
- Comprehensive smoke suite under
tests/smoke/— primitives, smart pointers, topology, transforms, wire/face building, fillets/chamfers, sweep/loft, OBJ I/O, XCAF, intersections, output params, enum dispatch, BRep tool overloads, embind machinery, missing-OCCT-modules detection, suffix-free overload resolution, Handle-output elision, exception decode,Symbol.disposedisposal, and modernNCollectionbindings. - Type-only tests (
tests/types.test-d.ts,enum-dispatch.test-d.ts,enums.test-d.ts,namespaces.test-d.ts,output-params.test-d.ts,disposable-containers.test-d.ts) — lock the published.d.tsshape against regression. - Bindgen output-shape regression (
tests/bindgen-output-shape.test.ts) — asserts the codegen never emits envelope fields that mirror concrete class outputs and that class outputs always forward via*val::as<T*>(allow_raw_pointers()). - Semantic-diagnostic and
dts-validationharnesses — type-check the codegen output end-to-end and track theanycount in the generated.d.tsto prevent silent type-resolution regressions.
Documentation
- New BREAKING_CHANGES.md — single comprehensive v2 → v3 consumer migration guide with Before / After code samples for every breaking change.
- Rewritten README.md with quick start, Docker workflow, environment-variable reference, and customisation pointers.
- BUILD_SYSTEM.md — full
OCJS_*env-var matrix, configuration authoring guide, and the v2 → v3 build-system migration table. - Custom emcc flags — size vs. speed, LTO, defines, and
wasm-opt. - YAML schema — build configuration and customisation reference.
Source pinning (this release)
Full commit hashes live in DEPS.json.
- OCCT
V8_0_1(GA, commitb8f597c6) - rapidjson post-1.1.0 (commit
24b5e7a8) - freetype
VER-2-13-0(commitde8b92dd) - Emscripten
6.0.5(LLVM 24; digestsha256:76a44fff…) - libclang
18.1.1(Python binding, pinned inpyproject.tomlanduv.lock; up from v2's15.0.6.1) - LLVM
17.0.6(vendored prebuilt — parse-side libc++ + clang resource headers; N-1 compat with libclang 18.1.1)
❤️ Thank You
- Richard Fontein @rifont