Changelog
Assembled from every changelog in the repository: the root CHANGELOG.md for tooling, CI and documentation, and each crate's own CHANGELOG.md. Every crate is versioned and released on its own, so a release is named by crate and version. Each crate's reference page shows its latest release notes.
Unreleased
Repository (tooling, CI, documentation)
Added
Documentation site (VitePress) for https://openbimrs.github.io/ifcx/: home, install and getting-started guides for Rust, JavaScript and Python whose code runs in the gate, one reference page per crate, capabilities, upstream parity and drift evidence, ADRs, the assembled changelog, contributing and releasing.
xtask(publish = false) generates every derivable page and region from the manifests, READMEs, changelogs, sources and binding declarations;cargo run -p xtask -- docs --check,todo --check, the site build andscripts/check-leakage.pyrun in the gate'srustsection (#64).The demo viewer works on phones: a compact top bar, a 3D view that fills the screen, nodes and details in a bottom sheet behind tabs, touch orbit and tap-to-pick, and framing that fits portrait screens (#62).
demo/: a static three.js viewer (Vite, pinned versions and lockfile) that composes.ifcxfiles in the browser with@openbim/ifcxbuilt from source, exports GLB, and shows the node tree, picked meshes' IFCX paths and attributes, and validation failures. Samples are the repository's own fixtures..github/workflows/pages.ymldeploys it to https://openbimrs.github.io/ifcx/ (#47).The
bindingsgate section and CI also check@openbim/ifcxas packed, including its browser builds in headless Chrome (crates/openbim-ifcx-wasm/scripts/build-npm-pkg.sh, #46).Weekly upstream drift check (
.github/workflows/upstream-drift.yml,scripts/upstream-drift.sh): runs every opt-in upstream check against buildingSMART/IFC5-development's default branch with anifcx.devimport mirror fetched in the job, compares the results withscripts/upstream-drift/baseline.txt(1a63082), reports which upstream branches moved since the last run, and opens or updates a singleupstream-driftissue on failure or difference. Not part of the CI gate (#35).Fuzzing (#41):
fuzz/holds cargo-fuzz targetsread,compose,validate,pcd,pcd_lzf,points_base64,geometry, andscene_glb, in their own workspace with a pinned nightly, outside the MSRV gate.fuzz/run.shruns one with seeds fromfuzz/seeds/and the hand-written fixtures, debug assertions, and memory and time limits..github/workflows/fuzz.ymlruns every target for one minute on pull requests touching the fuzzed crates and for ten minutes weekly, keeping the corpus; it is not part of the required gate. CONTRIBUTING.md documents it.Language bindings:
openbim-ifcx-wasm(npm@openbim/ifcx) andopenbim-ifcx-py(PyPIopenbim-ifcx) over a sharedopenbim-ifcx-binding-core, as inopenbimrs/ifc.Release workflow publishes
openbim-ifcx-wasmto npm andopenbim-ifcx-pyto PyPI (four abi3 wheels and an sdist) by trusted publishing in thenpmjs.comandpypi.orgenvironments;scripts/release-crate.pykeepspackage.jsonandpyproject.tomlin step withCargo.toml. Aworkflow_dispatchrun rehearses a release.scripts/gate.shhas sectionsrustandbindings; the bindings section builds the npm package and the wheel and runs the Node and Python suites against them. CI runs the sections as parallel jobs with a singleStandalone IFCX gateverdict.scripts/gltf-validate.sh: opt-in export of the geometry fixtures and, withIFCX_UPSTREAM_DIR, every upstream example to GLB, validated with the pinned Khronos glTF validator (scripts/gltf/). All 50 files at1a63082pass with no errors or warnings (#9).Release tooling:
scripts/release-crate.pyand.github/workflows/release.ymlpublish one crate per<crate>-v<version>tag to crates.io, as inopenbimrs/ifc. Per-crate changelogs replace the single root entry list.scripts/upstream-parity.sh: opt-in check that composes every example of a local buildingSMART/IFC5-development checkout (IFCX_UPSTREAM_DIR), and the crate's fixtures, with upstream's TypeScript (bundled from the checkout at run time) and with the newcompose-jsonexample ofopenbim-ifcx, and reports matches and differences per case. All 47 upstream examples at1a63082compose equal to upstream (#17).Repository scaffold:
openbim-ifcxcrate with a status constant only, verification gate, pinned CI, and Dependabot for action pins.ADR 0001 recording that IFC5 / IFCX is its own family, not an IFC schema version.
ADR 0002 fixing the crate split: one core crate for file model and composition, a separate crate for the IFC4 bridge.
Issue templates, labels, Discussions, and the IFCX project board.
Changed
- GitHub Pages serves the documentation site at https://openbimrs.github.io/ifcx/, with rustdoc under
/api/rustdoc/and the viewer moved to https://openbimrs.github.io/ifcx/viewer/ (Vitebase), all built and deployed together by.github/workflows/pages.yml, which also checks the built site for buildingSMART material against an upstream checkout (#64). - Workspace crates live under
crates/, as inopenbimrs/ifc. - License changed from AGPL-3.0 to MIT before any code or release.
- MSRV is 1.88, matching
openbim-ifcandopenbim-step.
Fixed
scripts/gltf-validate.shexports the fixtures with--resolve-imports, soimports-panel-type.ifcxconverts, and names an upstream example it cannot export instead of exiting silently. Found by the first upstream drift run (#56).
openbim-ifcx
Changed
- Breaking: a layer now overrides the layers it imports, as agreed upstream in buildingSMART/IFC5-development#144 (2026-10-05): "imported data should come 'before' the main data".
LayerStackBuilderreturns the layers in federation order: every layer after its own imports, recursively; sibling imports in written order, so a later import overrides an earlier one; each layer once, where it is first reached; the main layer last.main → [a, b],a → [c]now givesc, a, b, main(wasmain, a, c, b), andmain → a → bgivesb, a, main(wasmain, a, b). Code relying on the old order, in which an import overrode its importer and the main layer had the lowest priority, now composes and validates differently whenever an import carriesdataor redefines a schema. Upstream's code at1a63082still uses the old order until a pending upstream pull request lands (#36). - Breaking:
LayerStack::layers,keysandinto_layersreturn the federation order, so the main layer is last, not first;LayerStack::mainstill returns the main layer. Forbuild_all,mainis now the last layer of the stack, the strongest named one (was the first named one), and the federated header is its header (#36). - Breaking:
federateandfederate_ownedtake the header of the last file, the strongest, instead of the first; schemas and data are merged as before.LayerStack::federateandinto_federatedthus keep the main layer's header (#36). LayerStackBuilder::allow_cycles(true)skips the import that closes a cycle, as before; under the new order the layer that import names comes after its importer, and the main layer stays last (main → a → maingivesa, main) (#36).LayerStackBuilderwalks imports without recursion, so a long import chain cannot overflow the stack.integrityis still checked on every import edge (#36).
openbim-ifcx-geometry
Changed
ifcx2glb --resolve-importsstacks each layer after the layers it imports, followingopenbim-ifcx's new layer order (buildingSMART/IFC5-development#144), so a layer overrides its imports. The library API is unchanged (#36).
openbim-ifcx-binding-core
Not released on its own (publish = false): it ships inside the npm and PyPI packages, whose changelogs name the releases.
Changed
LayerSetwith imports stacks its layers withLayerStackBuilder::build_allinstead of building its own synthetic main layer; the order and the reports are unchanged. Federation and composition move the parsed layers instead of copying them (federate_owned,LayerStack::into_federated,flatten_owned) (#42, #43).- Breaking:
LayerSetwith imports puts every layer after the layers it imports, so a layer overrides its imports (buildingSMART/IFC5-development#144; was: an import overrode its importer). The federated header is the strongest layer's, the last one, with or without imports (was the first layer's). Composition and validation results without imports are unchanged (#36).
Added
- The host-independent half of the language bindings:
Document(read, lossless write, header, validation against the file's own schemas),LayerSet(layers weakest first, optional in-memory imports under a synthetic main layer, composition into a{path, attributes, children}tree, federated validation, GLB export throughopenbim-ifcx-geometry), validation reports with stable failurekindcodes, andBindingErrorwith the stable codesread,write,layer,compose,invalid-argumentandglb.
openbim-ifcx-py
Changed
- Breaking: with imports resolved, a layer now overrides the layers it imports, as agreed upstream in buildingSMART/IFC5-development#144 (was: an import overrode the layer importing it).
compose,validateand GLB export give different results when an import carriesdataor redefines a schema that a layer also sets. Layers given without imports, and imports that carry only schemas, compose as before; a later layer still overrides an earlier one and everything it imports (#36).
openbim-ifcx-wasm
Changed
- Breaking: with imports resolved, a layer now overrides the layers it imports, as agreed upstream in buildingSMART/IFC5-development#144 (was: an import overrode the layer importing it).
compose,validateand GLB export give different results when an import carriesdataor redefines a schema that a layer also sets. Layers given without imports, and imports that carry only schemas, compose as before; a later layer still overrides an earlier one and everything it imports (#36).
Releases
openbim-ifcx-wasm 0.2.1 (2026-10-03)
Changed
Built on
openbim-ifcx0.1.1 andopenbim-ifcx-geometry0.1.1: validation with imports checks every layer against the schemas of all layers, flattening and composition no longer copy attribute values, and the fuzzing fixes apply (deep schema inheritance, oversized coordinates, exponential scene walks, malformed PCD headers).The README, which is the npm page, links the documentation site (https://openbimrs.github.io/ifcx/) and the viewer at its new address, https://openbimrs.github.io/ifcx/viewer/ (#64).
openbim-ifcx-wasm 0.2.0 (2026-10-03)
Added
- Browser builds in the same npm package (#46): next to the Node CommonJS build,
bundler/(wasm-bindgen --target bundler, the default export for bundlers such as webpack) andweb/(--target web, imported as@openbim/ifcx/web, with an asyncinit()that loads the wasm module).package.jsonexportsselects the build (nodecondition: CommonJS; otherwise the bundler build), with TypeScript declarations for each. The Node API and entry point are unchanged. fetchImports(layers, { baseUrl, fetch, imports, signal }): resolves the layers' imports recursively in JavaScript withfetch(or any function returning aResponse, bytes or text) and returns them keyed by importurifor the existing in-memoryimportsoption. The Rust crates still perform no network access (ADR 0002). A file that cannot be fetched rejects with anIfcxErrorwith the new codefetch.scripts/build-npm-pkg.sh(wasbuild-node-pkg.sh) binds all three targets, runs the Node suite, andtools/check-package.mjschecks the packed tarball: Noderequireandimport, a webpack bundle (webpack pinned intools/package-lock.json), and both browser builds in headless Chrome, parsing, validating, composing, fetching imports and exporting GLB from the repository's fixtures.
Changed
- Through
openbim-ifcx-binding-core: layers withimportsare stacked byopenbim-ifcx'sLayerStackBuilder::build_all, and composition moves the parsed layers instead of copying them. Layer order, results and reports are unchanged (#42, #43).
openbim-ifcx-py 0.1.2 (2026-10-03)
Changed
Built on
openbim-ifcx0.1.1 andopenbim-ifcx-geometry0.1.1: validation with imports checks every layer against the schemas of all layers, flattening and composition no longer copy attribute values, and the fuzzing fixes apply (deep schema inheritance, oversized coordinates, exponential scene walks, malformed PCD headers).The README, which is the PyPI page, links the documentation site (https://openbimrs.github.io/ifcx/) and the viewer at its new address, https://openbimrs.github.io/ifcx/viewer/ (#64).
openbim-ifcx-geometry 0.1.1 (2026-10-03)
No breaking change. Requires openbim-ifcx 0.1.1.
Added
ifcx2glb --resolve-importsloads every layer'simportsthroughopenbim_ifcx::layers::FsResolverbefore exporting, stacked as upstream'sifcx composedoes;--mirror PREFIX=DIR(repeatable) maps a URI prefix such ashttps://ifcx.dev/to an offline copy. The example now flattens withflatten_owned(#42, #43).- Test fixture
imports-panel-type.ifcx, whose panels inherit a type only its import defines, andtests/imports.rs, which exports it with the import resolved (#42). - Dev-dependency on
openbim-ifcxwith featurefs, for the example and its test. SceneOptions::max_visits(default 10 million) andSceneOptions::max_path_bytes(default 1 GiB), with builders andDEFAULT_*constants, bound the render-scene walk. When one is reached the walk stops with aSceneWarningKind::LimitReachedwarning (#41).SceneWarningKind::OutOfRangefor geometry and instances whose coordinates the scene'sf32buffers and matrices cannot hold (#41).
Fixed
RenderScene::from_compositionno longer visits an exponential number of paths: composition shares sub-trees, so 40 nodes naming the next one twice as children described 2^40 instances. Deep trees with long child names no longer build instance paths without bound. Found while fuzzing (#41).- Coordinates beyond
f32range (for example a translation of5e299) no longer produce infinite render matrices, buffers, or origins, whichto_glbwrote as JSONnull; such geometry and instances are left out with anOutOfRangewarning.to_glbwrites an infinitealphaCutoffasf32::MAXand a NaN one as0.5. Found by fuzzing (#41). PointCloud::from_pcdreturnsInvalidPcdfor ASCII data whoseCOUNTvalues overflowusizewhen summed, instead of panicking with overflow checks on (or reading the wrong column without). Found while fuzzing (#41).
openbim-ifcx-py 0.1.1 (2026-10-03)
Fixed
- The source distribution carries
LICENSEat its root, where its metadata names it. PyPI rejected the 0.1.0 sdist for the missing file, so 0.1.0 is available as wheels only.
openbim-ifcx 0.1.1 (2026-10-03)
No breaking change: every addition below is new API, and existing functions and types keep their signatures.
Added
LayerStack::validatechecks a layer stack built with its imports: the schemas of every layer, merged asfederatedoes, against the attributes of every layer, merged per path asflattendoes. An attribute whose schema lives only in an imported file now validates.validate_flatchecks the output offlattenorflatten_owned(#42).LayerStackBuilder::build_allstacks several layers, weakest first, as the imports of a main layer without data, as upstream'sifcx composedoes (#42).flatten_owned,FlatNode::merge_owned,federate_owned, andLayerStack::into_federatedconsume their input and move nodes and attribute values instead of copying them. In the opt-inupstream_compositiontest (release build) flatteningTekla Housedrops from 88 ms to 18 ms andRailway_project_IFC5from 60 ms to 8 ms (#43).
Changed
IfcxFile::validatemerges attributes per path by reference instead of copying every value; the report is unchanged (#43).
Migration
- To flatten without copying, replace
flatten(&file.data)withflatten_owned(file.data)where the nodes are not needed afterwards, andstack.federate()withstack.into_federated().flattenover borrowed nodes still copies each attribute value, as before. - To validate a file with its imports, build its layer stack and call
stack.validate()instead offile.validate().
Fixed
IfcxFile::validateandvalidate_attributeswalk schemainheritswith an explicit stack and check each inherited schema once per value. A long inheritance chain overflowed the stack, and diamond-shaped inheritance doubled the work per level (22 levels took seconds and reported one failure four million times); a schema reached along several paths now reports its failures once. Found while fuzzing (#41).
openbim-ifcx-geometry 0.1.0 (2026-10-03)
First release: decoders for ifcx_alpha transforms, meshes, curves, point clouds and presentation; a flat render scene over a composed tree; and GLB export.
Added
openbim-ifcx-geometrywrites aRenderSceneas binary glTF 2.0 (to_glb,GlbOptions,GlbError): shared meshes per buffer and material,TRIANGLES,LINES, andPOINTSwithCOLOR_0, PBR materials, node names set to IFCX paths, and a root node with the scene origin and the Z-up to Y-up rotation. Theifcx2glbexample converts IFCX layers to.glb; the opt-inscripts/gltf-validate.shchecks exports with the Khronos glTF validator (#9).openbim-ifcx-geometrybuilds a flat render scene from a composition (RenderScene::from_composition,from_root): instances with node path, world transform,f32render matrix, material, and bounds; sharedf32mesh (with vertex normals), line, and point buffers decoded once per shared attribute value and instanced; a material table;f64world bounds; and a render origin (SceneOptions,Origin) that keeps georeferenced models precise. Invisible subtrees are left out, materials resolve through ancestors, and malformed values becomeSceneWarnings. This completes world transforms through the hierarchy (#3) and presentation resolution (#7) (#8).openbim-ifcx-geometrydecodesifcx_alphageometry attributes:usd::xformopinto an affineTransform(USD row-vector layout,f64) withworld_from_parentfor hierarchy composition (#3, tree walk pending);usd::usdgeom::meshinto aTriangleMeshwith face normals (#4); andusd::usdgeom::basiscurvesinto polylines, with non-linear curve types reported as unsupported (#5). Malformed values return a typedDecodeError.openbim-ifcx-geometrydecodes point clouds per node frompoints::array,points::base64, andpcd::base64(PCDascii,binary,binary_compressed) intoPointCloudwithf64positions and optional linear RGB colours. Malformed base64 or PCD returnsDecodeError::InvalidBase64,ByteLength, orInvalidPcd; PCD layouts upstream cannot read either returnUnsupportedPcd(#6).openbim-ifcx-geometrydecodes visibility,diffuseColor,opacity, andgltf::materialper node (NodePresentation) and resolves visibility and materials over a node's ancestors with the reference viewer's precedence (is_visible,resolve_basic_material,resolve_mesh_material) (#7).openbim-ifcx-geometryAttributestrait reads attributes from anIfcxNode,FlatNode,ComposedNode, or a plain map, treatingnullas absent.openbim-ifcx-geometryscaffold for renderer-neutral geometry, viewer helpers, and GLB export.
Removed
openbim_ifcx_geometry::PACKAGE_STATUS, replaced by the decoders.
openbim-ifcx-py 0.1.0 (2026-10-03)
First version, for PyPI as openbim-ifcx (publish = false on crates.io).
Added
- Python bindings for
openbim-ifcx, built with pyo3 and maturin as one abi3 wheel for CPython 3.9+.IfcxFile.parsereads a file fromstrorbytes;writewrites it back losslessly;to_dict,header,node_countandvalidateexpose it as plain dicts.compose(layers, imports=None)returns the composed tree andvalidate(layers, imports=None)a structured report over all layers, with imports resolved from memory when given.export_glb(layers, imports=None, *, y_up=True, origin_on_root=True)writes the composed model as binary glTF 2.0. Parsing, composing, validating and GLB export release the GIL. - Every failure raises
IfcxErrorwith a stablecode. scripts/check-python.shbuilds the wheel, installs it into a throwaway uv venv and runs the Python suite against it.
openbim-ifcx-wasm 0.1.0 (2026-10-03)
First version, for npm as @openbim/ifcx (publish = false on crates.io).
Added
- WebAssembly bindings for
openbim-ifcx, a CommonJS package for Node 18+ with TypeScript declarations.IfcxFile.parsereads a file from a string or aUint8Array;writewrites it back losslessly;toJSON,header,nodeCountandvalidateexpose it as plain objects.compose(layers, { imports })returns the composed tree andvalidate(layers, { imports })a structured report over all layers, with imports resolved from memory when given.exportGlb(layers, { imports, yUp, originOnRoot })writes the composed model as binary glTF 2.0. - Every failure throws an
IfcxErrorwith a stablecode. scripts/build-node-pkg.shbuilds the package with the pinnedwasm-bindgenCLI and runs the Node suite against it.
openbim-ifcx 0.1.0 (2026-10-03)
First release: lossless reading and writing, layer flattening and composition, import resolution, and schema validation for ifcx_alpha.
Added
openbim-ifcxfixturegeometry-model.ifcx: a small validated model with a column type mesh and axis shared by two placed occurrences, a slab, and USD transform, mesh, curve, and visibility attributes (#17).openbim-ifcxflattens layered nodes by path in layer order (flatten,FlatNode), matching upstreamFlattenCompositionInput; attribute values are shared throughArc(#13).openbim-ifcxcomposes flattened nodes into a resolved tree (compose,Composition,ComposedNode,ComposeError), matching upstreamComposeNodeandCreateArtificialRoot. Instances share composed sub-trees throughArc; reference cycles, including ones throughhead/childreferences that upstream misses, and unknown references are typed errors (#14).openbim-ifcx::layersresolvesimportsforifcx_alphathrough a caller-suppliedLayerResolver:LayerStackBuilderloads the main layer and its imports once each in upstream order,LayerStack::federatemerges schemas and data in that order, and typedLayerErrors report missing layers, import cycles, unreadable layers, andintegritymismatches.MemoryResolverserves layers from memory;FsResolver(featurefs) reads local files and maps URI prefixes to offline mirrors.integrityis checked as SRI-stylesha256/sha384/sha512digests (featureintegrity, default on; without it such imports are rejected). In upstream order an import overrides the layer importing it; seedocs/capabilities.md(#15).openbim-ifcxreads and writesifcx_alphafiles losslessly (IfcxFile::from_json_*,to_json_*): typed header, imports, schemas, and nodes; unknown fields kept at every level;nullchildren and inherits preserved; read errors report kind, line, and column (#12).openbim-ifcxchecks attribute values against the file'sschemas(IfcxFile::validate,validate_attributes,validate_nodes), following upstream'sschema-validation.tsbut reporting every failure with node path, attribute id, and JSON pointer in aValidationReport.Integerrejects fractions and arraymin/maxare enforced, unlike upstream;Blobvalues are accepted unchecked andquantityKindis not checked (#16).