From bad45f78c9267584ed0cd5d63bc08d7c55e9dbe8 Mon Sep 17 00:00:00 2001 From: "tau-release-bot[bot]" Date: Mon, 26 Oct 2026 06:02:04 +0000 Subject: [PATCH 1/4] chore(release): libassimp v0.4.0 --- .nx/version-plans/allocator-lto.md | 5 ---- .nx/version-plans/backend-parity-packaging.md | 5 ---- .nx/version-plans/engine-export-depth.md | 5 ---- .nx/version-plans/lightusd-engine.md | 7 ----- .nx/version-plans/linux-mimalloc.md | 5 ---- .nx/version-plans/obj-indexed-import.md | 5 ---- .nx/version-plans/pkg-pr-new-previews.md | 5 ---- .../runtime-streaming-diagnostics.md | 7 ----- .../untrusted-input-hardening.md | 5 ---- .nx/version-plans/windows-mimalloc.md | 5 ---- CHANGELOG.md | 30 +++++++++++++++++++ package.json | 2 +- 12 files changed, 31 insertions(+), 55 deletions(-) delete mode 100644 .nx/version-plans/allocator-lto.md delete mode 100644 .nx/version-plans/backend-parity-packaging.md delete mode 100644 .nx/version-plans/engine-export-depth.md delete mode 100644 .nx/version-plans/lightusd-engine.md delete mode 100644 .nx/version-plans/linux-mimalloc.md delete mode 100644 .nx/version-plans/obj-indexed-import.md delete mode 100644 .nx/version-plans/pkg-pr-new-previews.md delete mode 100644 .nx/version-plans/runtime-streaming-diagnostics.md delete mode 100644 .nx/version-plans/untrusted-input-hardening.md delete mode 100644 .nx/version-plans/windows-mimalloc.md diff --git a/.nx/version-plans/allocator-lto.md b/.nx/version-plans/allocator-lto.md deleted file mode 100644 index 73ac550..0000000 --- a/.nx/version-plans/allocator-lto.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -libassimp: patch ---- - -The Wasm build uses dlmalloc instead of mimalloc: the binary is 60 kB smaller and large conversions reach a 12–52% lower linear-memory high-water mark at the same speed, which leaves more of the 4 GiB limit for the input. The native addon is built with link-time optimization, shrinking it from 30.5 MB to about 19 MB. diff --git a/.nx/version-plans/backend-parity-packaging.md b/.nx/version-plans/backend-parity-packaging.md deleted file mode 100644 index b84c43f..0000000 --- a/.nx/version-plans/backend-parity-packaging.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -libassimp: minor ---- - -Move option and post-process descriptions out of the root entry into a new lazy `libassimp/capabilities` subpath, which exports the same `assimpCapabilities` registry with a `description` on every option and step. The root entry's static JavaScript shrinks from 218.2 kB to 79.3 kB (43.0 kB to 15.2 kB gzip), and descriptions no longer repeat their type and default. The format matrix, determinism fingerprints and USD suite now run on the native backend as well as Wasm, with every remaining difference documented in the test. esbuild's default browser build no longer fails on the Node-only `node:fs/promises` import, and CI now installs the packed tarball into Vite, webpack 5, Next.js (Turbopack), esbuild and a CDN-style host and converts a model in Chromium in each. diff --git a/.nx/version-plans/engine-export-depth.md b/.nx/version-plans/engine-export-depth.md deleted file mode 100644 index 396b685..0000000 --- a/.nx/version-plans/engine-export-depth.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -libassimp: minor ---- - -Exports are faster and use less memory. A GLB or glTF export keeps the imported scene's shared vertices indexed instead of writing every triangle corner out separately. Exporters that only read the scene no longer copy it first. Vertex joining uses a hash table, the OBJ, STL, PLY, COLLADA and STEP writers stream their text, the glTF buffer grows geometrically, and a GLB's binary chunk is read in place. STEP output no longer depends on memory addresses, and a point-cloud PLY now exports to STL and PLY. The new 3MF export option `requireManifold` (default `true`) can be set to `false` to write open surfaces as 3MF objects of type `other` instead of failing. Files whose scene graph nests deeper than 1000 levels, or whose nodes form a cycle, are rejected with `IMPORT_FAILED` instead of overflowing the stack. 3MF and COLLADA files with out-of-range indices are rejected the same way instead of being read out of bounds. diff --git a/.nx/version-plans/lightusd-engine.md b/.nx/version-plans/lightusd-engine.md deleted file mode 100644 index e751c72..0000000 --- a/.nx/version-plans/lightusd-engine.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -libassimp: minor ---- - -Move the USD importer and exporter to LightUSD (the successor to TinyUSDZ) and add the `usdc` export target. USD sublayers, references and payloads, including USDA, USDC and MaterialX sidecars, resolve through `resolve`, and a composed scene exports as a self-contained USDZ that passes OpenUSD's ARKit compliance checks. Large USD scenes convert several times faster in a fraction of the memory. The Node-API worker gets an 8 MiB stack, Wasm `dispose` releases the instance's memory, and the module-level `convert` recycles its shared Wasm instance after a job grows linear memory past 512 MiB. - -**Breaking.** The USD export options `materialX`, `subdivision`, `volumes` and `optimizeForMobile` are removed, and passing one now rejects with `INVALID_OPTIONS`; strip them from saved settings before upgrading (see the 0.4 upgrade guide). USD output changes for the same input: UVs are no longer flipped, and the exporters author `metersPerUnit` on the stage instead of baking the unit scale into geometry, so USD bytes, texture coordinates and transforms differ from 0.3. diff --git a/.nx/version-plans/linux-mimalloc.md b/.nx/version-plans/linux-mimalloc.md deleted file mode 100644 index 406c81f..0000000 --- a/.nx/version-plans/linux-mimalloc.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -libassimp: patch ---- - -The Linux native addon allocates through mimalloc and links libstdc++ statically, which makes large conversions about 8–14% faster with byte-identical output, for 2–4% more peak memory. The addon no longer depends on the system `libstdc++.so.6`. diff --git a/.nx/version-plans/obj-indexed-import.md b/.nx/version-plans/obj-indexed-import.md deleted file mode 100644 index b9e62ce..0000000 --- a/.nx/version-plans/obj-indexed-import.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -libassimp: patch ---- - -OBJ files import as indexed meshes: face corners that share a position, texture coordinate and normal share one vertex. Converting a 1024 × 1024 OBJ grid to GLB peaks about 19% lower in memory with byte-identical output. Face-normal generation no longer reads past the end of a mesh's texture coordinates, colours or tangents when it splits a shared vertex. diff --git a/.nx/version-plans/pkg-pr-new-previews.md b/.nx/version-plans/pkg-pr-new-previews.md deleted file mode 100644 index 5bd92ea..0000000 --- a/.nx/version-plans/pkg-pr-new-previews.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -libassimp: patch ---- - -Publish installable package previews for same-repository pull requests, verify the hosted root and native-package graph before merge, and refresh security-sensitive build dependencies. diff --git a/.nx/version-plans/runtime-streaming-diagnostics.md b/.nx/version-plans/runtime-streaming-diagnostics.md deleted file mode 100644 index efb115a..0000000 --- a/.nx/version-plans/runtime-streaming-diagnostics.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -libassimp: minor ---- - -Stream converted output to the host instead of assembling it in the engine, and report what happened during a conversion. Wasm output chunks land in a resizable host buffer, returned without a final join on Node.js and moved to a fixed-length buffer in browsers, whose Web APIs reject resizable views; native output bytes are handed to JavaScript without a copy. With JSPI or native, the engine frees the entry file once it is imported. Every result now carries `warnings`, per-phase `timings` and `peakMemoryBytes`, and `onProgress` reports throttled `import`, `postProcess` and `export` progress on both backends. Allocation failure reports `LIMIT_EXCEEDED` and leaves the instance usable, a Wasm runtime that traps is replaced on the next conversion, and `recycleAboveBytes` lets any instance drop a large idle Wasm heap. Node.js gains `createDirectoryResolver(root)`, a sidecar resolver confined to one directory. - -Breaking: outputs are named after the entry file's stem with the target's canonical extension (`chair.obj` to `step` gives `chair.step`, not `result.stp`), so USDZ members and every sidecar reference change bytes too, and output bytes are typed `Uint8Array` (on Node.js a Wasm output's buffer may be resizable). `dispose()` returns a Promise that settles after queued work drains. A native backend that cannot load reports `BACKEND_UNAVAILABLE` instead of `IMPORT_FAILED`, and anything the engine throws that is not already an `AssimpError` rejects as `ENGINE_FAILED` with the original value as `cause`; `LIMIT_EXCEEDED`, `ABORTED` and `INTERNAL` are also new codes. Empty or repeated input names reject with `INVALID_OPTIONS`. Unset `AssimpError` context fields are absent rather than `undefined`. The native addon no longer exports `importFormats` or `exportFormats`; use `assimpCapabilities`. diff --git a/.nx/version-plans/untrusted-input-hardening.md b/.nx/version-plans/untrusted-input-hardening.md deleted file mode 100644 index 32b91ce..0000000 --- a/.nx/version-plans/untrusted-input-hardening.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -libassimp: patch ---- - -Harden the native addons and document untrusted input. Every native build compiles the engine with `-fstack-protector-strong` and `_FORTIFY_SOURCE=2` and links with full RELRO on Linux, and with Control Flow Guard and Spectre mitigations on Windows. The Wasm link fails on any undefined symbol instead of shipping a runtime abort stub. `SECURITY.md` and the new "Untrusted input" guide give the threat model: use `backend: 'wasm'` (or an isolated child process) for files you did not write. Each release carries a CycloneDX SBOM of the npm and C/C++ dependencies, attested against the npm tarballs, and OSV scans the engine's C/C++ commit pins. diff --git a/.nx/version-plans/windows-mimalloc.md b/.nx/version-plans/windows-mimalloc.md deleted file mode 100644 index d5ba4f4..0000000 --- a/.nx/version-plans/windows-mimalloc.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -libassimp: patch ---- - -The Windows native addon also allocates through mimalloc, which makes large conversions about 6–16% faster with byte-identical output, for 4–16% more peak memory. macOS keeps the system allocator. diff --git a/CHANGELOG.md b/CHANGELOG.md index be2ff9d..72958f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,33 @@ +## 0.4.0 (2026-10-26) + +### 🚀 Features + +- Move option and post-process descriptions out of the root entry into a new lazy `libassimp/capabilities` subpath, which exports the same `assimpCapabilities` registry with a `description` on every option and step. The root entry's static JavaScript shrinks from 218.2 kB to 79.3 kB (43.0 kB to 15.2 kB gzip), and descriptions no longer repeat their type and default. The format matrix, determinism fingerprints and USD suite now run on the native backend as well as Wasm, with every remaining difference documented in the test. esbuild's default browser build no longer fails on the Node-only `node:fs/promises` import, and CI now installs the packed tarball into Vite, webpack 5, Next.js (Turbopack), esbuild and a CDN-style host and converts a model in Chromium in each. ([#57](https://github.com/taucad/libassimp/pull/57)) +- Stream converted output to the host instead of assembling it in the engine, and report what happened during a conversion. Wasm output chunks land in a resizable host buffer, returned without a final join on Node.js and moved to a fixed-length buffer in browsers, whose Web APIs reject resizable views; native output bytes are handed to JavaScript without a copy. With JSPI or native, the engine frees the entry file once it is imported. Every result now carries `warnings`, per-phase `timings` and `peakMemoryBytes`, and `onProgress` reports throttled `import`, `postProcess` and `export` progress on both backends. Allocation failure reports `LIMIT_EXCEEDED` and leaves the instance usable, a Wasm runtime that traps is replaced on the next conversion, and `recycleAboveBytes` lets any instance drop a large idle Wasm heap. Node.js gains `createDirectoryResolver(root)`, a sidecar resolver confined to one directory. ([#57](https://github.com/taucad/libassimp/pull/57)) + + Breaking: outputs are named after the entry file's stem with the target's canonical extension (`chair.obj` to `step` gives `chair.step`, not `result.stp`), so USDZ members and every sidecar reference change bytes too, and output bytes are typed `Uint8Array` (on Node.js a Wasm output's buffer may be resizable). `dispose()` returns a Promise that settles after queued work drains. A native backend that cannot load reports `BACKEND_UNAVAILABLE` instead of `IMPORT_FAILED`, and anything the engine throws that is not already an `AssimpError` rejects as `ENGINE_FAILED` with the original value as `cause`; `LIMIT_EXCEEDED`, `ABORTED` and `INTERNAL` are also new codes. Empty or repeated input names reject with `INVALID_OPTIONS`. Unset `AssimpError` context fields are absent rather than `undefined`. The native addon no longer exports `importFormats` or `exportFormats`; use `assimpCapabilities`. + +- Move the USD importer and exporter to LightUSD (the successor to TinyUSDZ) and add the `usdc` export target. USD sublayers, references and payloads, including USDA, USDC and MaterialX sidecars, resolve through `resolve`, and a composed scene exports as a self-contained USDZ that passes OpenUSD's ARKit compliance checks. Large USD scenes convert several times faster in a fraction of the memory. The Node-API worker gets an 8 MiB stack, Wasm `dispose` releases the instance's memory, and the module-level `convert` recycles its shared Wasm instance after a job grows linear memory past 512 MiB. ([#47](https://github.com/taucad/libassimp/pull/47)) + + **Breaking.** The USD export options `materialX`, `subdivision`, `volumes` and `optimizeForMobile` are removed, and passing one now rejects with `INVALID_OPTIONS`; strip them from saved settings before upgrading (see the 0.4 upgrade guide). USD output changes for the same input: UVs are no longer flipped, and the exporters author `metersPerUnit` on the stage instead of baking the unit scale into geometry, so USD bytes, texture coordinates and transforms differ from 0.3. + +- Exports are faster and use less memory. A GLB or glTF export keeps the imported scene's shared vertices indexed instead of writing every triangle corner out separately. Exporters that only read the scene no longer copy it first. Vertex joining uses a hash table, the OBJ, STL, PLY, COLLADA and STEP writers stream their text, the glTF buffer grows geometrically, and a GLB's binary chunk is read in place. STEP output no longer depends on memory addresses, and a point-cloud PLY now exports to STL and PLY. The new 3MF export option `requireManifold` (default `true`) can be set to `false` to write open surfaces as 3MF objects of type `other` instead of failing. Files whose scene graph nests deeper than 1000 levels, or whose nodes form a cycle, are rejected with `IMPORT_FAILED` instead of overflowing the stack. 3MF and COLLADA files with out-of-range indices are rejected the same way instead of being read out of bounds. ([#57](https://github.com/taucad/libassimp/pull/57)) + +### 🩹 Fixes + +- The Wasm build uses dlmalloc instead of mimalloc: the binary is 60 kB smaller and large conversions reach a 12–52% lower linear-memory high-water mark at the same speed, which leaves more of the 4 GiB limit for the input. The native addon is built with link-time optimization, shrinking it from 30.5 MB to about 19 MB. ([#57](https://github.com/taucad/libassimp/pull/57)) +- The Windows native addon also allocates through mimalloc, which makes large conversions about 6–16% faster with byte-identical output, for 4–16% more peak memory. macOS keeps the system allocator. ([#57](https://github.com/taucad/libassimp/pull/57)) +- Harden the native addons and document untrusted input. Every native build compiles the engine with `-fstack-protector-strong` and `_FORTIFY_SOURCE=2` and links with full RELRO on Linux, and with Control Flow Guard and Spectre mitigations on Windows. The Wasm link fails on any undefined symbol instead of shipping a runtime abort stub. `SECURITY.md` and the new "Untrusted input" guide give the threat model: use `backend: 'wasm'` (or an isolated child process) for files you did not write. Each release carries a CycloneDX SBOM of the npm and C/C++ dependencies, attested against the npm tarballs, and OSV scans the engine's C/C++ commit pins. ([#57](https://github.com/taucad/libassimp/pull/57)) +- Publish installable package previews for same-repository pull requests, verify the hosted root and native-package graph before merge, and refresh security-sensitive build dependencies. ([#37](https://github.com/taucad/libassimp/pull/37)) +- OBJ files import as indexed meshes: face corners that share a position, texture coordinate and normal share one vertex. Converting a 1024 × 1024 OBJ grid to GLB peaks about 19% lower in memory with byte-identical output. Face-normal generation no longer reads past the end of a mesh's texture coordinates, colours or tangents when it splits a shared vertex. ([#57](https://github.com/taucad/libassimp/pull/57)) +- The Linux native addon allocates through mimalloc and links libstdc++ statically, which makes large conversions about 8–14% faster with byte-identical output, for 2–4% more peak memory. The addon no longer depends on the system `libstdc++.so.6`. ([#57](https://github.com/taucad/libassimp/pull/57)) + +### ❤️ Thank You + +- Claude +- Claude Opus 5.5 (1M context) +- Richard Fontein @rifont + ## 0.3.0 (2026-09-05) ### 🚀 Features diff --git a/package.json b/package.json index ebdbfd9..78495a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libassimp", - "version": "0.3.0", + "version": "0.4.0", "description": "Assimp for TypeScript with native Node-API and WebAssembly backends: import 69 3D formats and export 16 formats.", "keywords": [ "3d", From 9416e62780f31a3c530387174dfff0024df78ef9 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 06:28:24 +0000 Subject: [PATCH 2/4] docs(release): split 0.4.0 changelog blocks under the prose word ceiling Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01YAfJLnmDHZzoNfHQ6SLqEo --- CHANGELOG.md | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72958f8..f0172a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,22 +2,36 @@ ### 🚀 Features -- Move option and post-process descriptions out of the root entry into a new lazy `libassimp/capabilities` subpath, which exports the same `assimpCapabilities` registry with a `description` on every option and step. The root entry's static JavaScript shrinks from 218.2 kB to 79.3 kB (43.0 kB to 15.2 kB gzip), and descriptions no longer repeat their type and default. The format matrix, determinism fingerprints and USD suite now run on the native backend as well as Wasm, with every remaining difference documented in the test. esbuild's default browser build no longer fails on the Node-only `node:fs/promises` import, and CI now installs the packed tarball into Vite, webpack 5, Next.js (Turbopack), esbuild and a CDN-style host and converts a model in Chromium in each. ([#57](https://github.com/taucad/libassimp/pull/57)) -- Stream converted output to the host instead of assembling it in the engine, and report what happened during a conversion. Wasm output chunks land in a resizable host buffer, returned without a final join on Node.js and moved to a fixed-length buffer in browsers, whose Web APIs reject resizable views; native output bytes are handed to JavaScript without a copy. With JSPI or native, the engine frees the entry file once it is imported. Every result now carries `warnings`, per-phase `timings` and `peakMemoryBytes`, and `onProgress` reports throttled `import`, `postProcess` and `export` progress on both backends. Allocation failure reports `LIMIT_EXCEEDED` and leaves the instance usable, a Wasm runtime that traps is replaced on the next conversion, and `recycleAboveBytes` lets any instance drop a large idle Wasm heap. Node.js gains `createDirectoryResolver(root)`, a sidecar resolver confined to one directory. ([#57](https://github.com/taucad/libassimp/pull/57)) +- Move option and post-process descriptions out of the root entry into a new lazy `libassimp/capabilities` subpath, which exports the same `assimpCapabilities` registry with a `description` on every option and step. The root entry's static JavaScript shrinks from 218.2 kB to 79.3 kB (43.0 kB to 15.2 kB gzip), and descriptions no longer repeat their type and default. - Breaking: outputs are named after the entry file's stem with the target's canonical extension (`chair.obj` to `step` gives `chair.step`, not `result.stp`), so USDZ members and every sidecar reference change bytes too, and output bytes are typed `Uint8Array` (on Node.js a Wasm output's buffer may be resizable). `dispose()` returns a Promise that settles after queued work drains. A native backend that cannot load reports `BACKEND_UNAVAILABLE` instead of `IMPORT_FAILED`, and anything the engine throws that is not already an `AssimpError` rejects as `ENGINE_FAILED` with the original value as `cause`; `LIMIT_EXCEEDED`, `ABORTED` and `INTERNAL` are also new codes. Empty or repeated input names reject with `INVALID_OPTIONS`. Unset `AssimpError` context fields are absent rather than `undefined`. The native addon no longer exports `importFormats` or `exportFormats`; use `assimpCapabilities`. + The format matrix, determinism fingerprints and USD suite now run on the native backend as well as Wasm, with every remaining difference documented in the test. esbuild's default browser build no longer fails on the Node-only `node:fs/promises` import, and CI now installs the packed tarball into Vite, webpack 5, Next.js (Turbopack), esbuild and a CDN-style host and converts a model in Chromium in each. ([#57](https://github.com/taucad/libassimp/pull/57)) +- Stream converted output to the host instead of assembling it in the engine, and report what happened during a conversion. Wasm output chunks land in a resizable host buffer, returned without a final join on Node.js and moved to a fixed-length buffer in browsers, whose Web APIs reject resizable views; native output bytes are handed to JavaScript without a copy. With JSPI or native, the engine frees the entry file once it is imported. -- Move the USD importer and exporter to LightUSD (the successor to TinyUSDZ) and add the `usdc` export target. USD sublayers, references and payloads, including USDA, USDC and MaterialX sidecars, resolve through `resolve`, and a composed scene exports as a self-contained USDZ that passes OpenUSD's ARKit compliance checks. Large USD scenes convert several times faster in a fraction of the memory. The Node-API worker gets an 8 MiB stack, Wasm `dispose` releases the instance's memory, and the module-level `convert` recycles its shared Wasm instance after a job grows linear memory past 512 MiB. ([#47](https://github.com/taucad/libassimp/pull/47)) + Every result now carries `warnings`, per-phase `timings` and `peakMemoryBytes`, and `onProgress` reports throttled `import`, `postProcess` and `export` progress on both backends. Allocation failure reports `LIMIT_EXCEEDED` and leaves the instance usable, a Wasm runtime that traps is replaced on the next conversion, and `recycleAboveBytes` lets any instance drop a large idle Wasm heap. Node.js gains `createDirectoryResolver(root)`, a sidecar resolver confined to one directory. ([#57](https://github.com/taucad/libassimp/pull/57)) + + Breaking: outputs are named after the entry file's stem with the target's canonical extension (`chair.obj` to `step` gives `chair.step`, not `result.stp`), so USDZ members and every sidecar reference change bytes too, and output bytes are typed `Uint8Array` (on Node.js a Wasm output's buffer may be resizable). `dispose()` returns a Promise that settles after queued work drains. + + A native backend that cannot load reports `BACKEND_UNAVAILABLE` instead of `IMPORT_FAILED`, and anything the engine throws that is not already an `AssimpError` rejects as `ENGINE_FAILED` with the original value as `cause`; `LIMIT_EXCEEDED`, `ABORTED` and `INTERNAL` are also new codes. Empty or repeated input names reject with `INVALID_OPTIONS`. Unset `AssimpError` context fields are absent rather than `undefined`. The native addon no longer exports `importFormats` or `exportFormats`; use `assimpCapabilities`. + +- Move the USD importer and exporter to LightUSD (the successor to TinyUSDZ) and add the `usdc` export target. USD sublayers, references and payloads, including USDA, USDC and MaterialX sidecars, resolve through `resolve`, and a composed scene exports as a self-contained USDZ that passes OpenUSD's ARKit compliance checks. Large USD scenes convert several times faster in a fraction of the memory. + + The Node-API worker gets an 8 MiB stack, Wasm `dispose` releases the instance's memory, and the module-level `convert` recycles its shared Wasm instance after a job grows linear memory past 512 MiB. ([#47](https://github.com/taucad/libassimp/pull/47)) **Breaking.** The USD export options `materialX`, `subdivision`, `volumes` and `optimizeForMobile` are removed, and passing one now rejects with `INVALID_OPTIONS`; strip them from saved settings before upgrading (see the 0.4 upgrade guide). USD output changes for the same input: UVs are no longer flipped, and the exporters author `metersPerUnit` on the stage instead of baking the unit scale into geometry, so USD bytes, texture coordinates and transforms differ from 0.3. -- Exports are faster and use less memory. A GLB or glTF export keeps the imported scene's shared vertices indexed instead of writing every triangle corner out separately. Exporters that only read the scene no longer copy it first. Vertex joining uses a hash table, the OBJ, STL, PLY, COLLADA and STEP writers stream their text, the glTF buffer grows geometrically, and a GLB's binary chunk is read in place. STEP output no longer depends on memory addresses, and a point-cloud PLY now exports to STL and PLY. The new 3MF export option `requireManifold` (default `true`) can be set to `false` to write open surfaces as 3MF objects of type `other` instead of failing. Files whose scene graph nests deeper than 1000 levels, or whose nodes form a cycle, are rejected with `IMPORT_FAILED` instead of overflowing the stack. 3MF and COLLADA files with out-of-range indices are rejected the same way instead of being read out of bounds. ([#57](https://github.com/taucad/libassimp/pull/57)) +- Exports are faster and use less memory. A GLB or glTF export keeps the imported scene's shared vertices indexed instead of writing every triangle corner out separately. Exporters that only read the scene no longer copy it first. Vertex joining uses a hash table, the OBJ, STL, PLY, COLLADA and STEP writers stream their text, the glTF buffer grows geometrically, and a GLB's binary chunk is read in place. + + STEP output no longer depends on memory addresses, and a point-cloud PLY now exports to STL and PLY. The new 3MF export option `requireManifold` (default `true`) can be set to `false` to write open surfaces as 3MF objects of type `other` instead of failing. + + Files whose scene graph nests deeper than 1000 levels, or whose nodes form a cycle, are rejected with `IMPORT_FAILED` instead of overflowing the stack. 3MF and COLLADA files with out-of-range indices are rejected the same way instead of being read out of bounds. ([#57](https://github.com/taucad/libassimp/pull/57)) ### 🩹 Fixes - The Wasm build uses dlmalloc instead of mimalloc: the binary is 60 kB smaller and large conversions reach a 12–52% lower linear-memory high-water mark at the same speed, which leaves more of the 4 GiB limit for the input. The native addon is built with link-time optimization, shrinking it from 30.5 MB to about 19 MB. ([#57](https://github.com/taucad/libassimp/pull/57)) - The Windows native addon also allocates through mimalloc, which makes large conversions about 6–16% faster with byte-identical output, for 4–16% more peak memory. macOS keeps the system allocator. ([#57](https://github.com/taucad/libassimp/pull/57)) -- Harden the native addons and document untrusted input. Every native build compiles the engine with `-fstack-protector-strong` and `_FORTIFY_SOURCE=2` and links with full RELRO on Linux, and with Control Flow Guard and Spectre mitigations on Windows. The Wasm link fails on any undefined symbol instead of shipping a runtime abort stub. `SECURITY.md` and the new "Untrusted input" guide give the threat model: use `backend: 'wasm'` (or an isolated child process) for files you did not write. Each release carries a CycloneDX SBOM of the npm and C/C++ dependencies, attested against the npm tarballs, and OSV scans the engine's C/C++ commit pins. ([#57](https://github.com/taucad/libassimp/pull/57)) +- Harden the native addons and document untrusted input. Every native build compiles the engine with `-fstack-protector-strong` and `_FORTIFY_SOURCE=2` and links with full RELRO on Linux, and with Control Flow Guard and Spectre mitigations on Windows. The Wasm link fails on any undefined symbol instead of shipping a runtime abort stub. `SECURITY.md` and the new "Untrusted input" guide give the threat model: use `backend: 'wasm'` (or an isolated child process) for files you did not write. + + Each release carries a CycloneDX SBOM of the npm and C/C++ dependencies, attested against the npm tarballs, and OSV scans the engine's C/C++ commit pins. ([#57](https://github.com/taucad/libassimp/pull/57)) - Publish installable package previews for same-repository pull requests, verify the hosted root and native-package graph before merge, and refresh security-sensitive build dependencies. ([#37](https://github.com/taucad/libassimp/pull/37)) - OBJ files import as indexed meshes: face corners that share a position, texture coordinate and normal share one vertex. Converting a 1024 × 1024 OBJ grid to GLB peaks about 19% lower in memory with byte-identical output. Face-normal generation no longer reads past the end of a mesh's texture coordinates, colours or tangents when it splits a shared vertex. ([#57](https://github.com/taucad/libassimp/pull/57)) - The Linux native addon allocates through mimalloc and links libstdc++ statically, which makes large conversions about 8–14% faster with byte-identical output, for 2–4% more peak memory. The addon no longer depends on the system `libstdc++.so.6`. ([#57](https://github.com/taucad/libassimp/pull/57)) From c471201dcbfeb7cf7cfd6ce88b6fc55bcb22de30 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 06:28:41 +0000 Subject: [PATCH 3/4] docs(release): format the 0.4.0 changelog Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01YAfJLnmDHZzoNfHQ6SLqEo --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0172a2..6d409b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Move option and post-process descriptions out of the root entry into a new lazy `libassimp/capabilities` subpath, which exports the same `assimpCapabilities` registry with a `description` on every option and step. The root entry's static JavaScript shrinks from 218.2 kB to 79.3 kB (43.0 kB to 15.2 kB gzip), and descriptions no longer repeat their type and default. The format matrix, determinism fingerprints and USD suite now run on the native backend as well as Wasm, with every remaining difference documented in the test. esbuild's default browser build no longer fails on the Node-only `node:fs/promises` import, and CI now installs the packed tarball into Vite, webpack 5, Next.js (Turbopack), esbuild and a CDN-style host and converts a model in Chromium in each. ([#57](https://github.com/taucad/libassimp/pull/57)) + - Stream converted output to the host instead of assembling it in the engine, and report what happened during a conversion. Wasm output chunks land in a resizable host buffer, returned without a final join on Node.js and moved to a fixed-length buffer in browsers, whose Web APIs reject resizable views; native output bytes are handed to JavaScript without a copy. With JSPI or native, the engine frees the entry file once it is imported. Every result now carries `warnings`, per-phase `timings` and `peakMemoryBytes`, and `onProgress` reports throttled `import`, `postProcess` and `export` progress on both backends. Allocation failure reports `LIMIT_EXCEEDED` and leaves the instance usable, a Wasm runtime that traps is replaced on the next conversion, and `recycleAboveBytes` lets any instance drop a large idle Wasm heap. Node.js gains `createDirectoryResolver(root)`, a sidecar resolver confined to one directory. ([#57](https://github.com/taucad/libassimp/pull/57)) @@ -32,6 +33,7 @@ - Harden the native addons and document untrusted input. Every native build compiles the engine with `-fstack-protector-strong` and `_FORTIFY_SOURCE=2` and links with full RELRO on Linux, and with Control Flow Guard and Spectre mitigations on Windows. The Wasm link fails on any undefined symbol instead of shipping a runtime abort stub. `SECURITY.md` and the new "Untrusted input" guide give the threat model: use `backend: 'wasm'` (or an isolated child process) for files you did not write. Each release carries a CycloneDX SBOM of the npm and C/C++ dependencies, attested against the npm tarballs, and OSV scans the engine's C/C++ commit pins. ([#57](https://github.com/taucad/libassimp/pull/57)) + - Publish installable package previews for same-repository pull requests, verify the hosted root and native-package graph before merge, and refresh security-sensitive build dependencies. ([#37](https://github.com/taucad/libassimp/pull/37)) - OBJ files import as indexed meshes: face corners that share a position, texture coordinate and normal share one vertex. Converting a 1024 × 1024 OBJ grid to GLB peaks about 19% lower in memory with byte-identical output. Face-normal generation no longer reads past the end of a mesh's texture coordinates, colours or tangents when it splits a shared vertex. ([#57](https://github.com/taucad/libassimp/pull/57)) - The Linux native addon allocates through mimalloc and links libstdc++ statically, which makes large conversions about 8–14% faster with byte-identical output, for 2–4% more peak memory. The addon no longer depends on the system `libstdc++.so.6`. ([#57](https://github.com/taucad/libassimp/pull/57)) From d27c81a7cf0200576c5fb968ab1e0f3e8a3f040e Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 08:27:36 +0000 Subject: [PATCH 4/4] chore(release): libassimp v0.4.0 Fold the changelog fixes into the release and credit Claude once. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01YAfJLnmDHZzoNfHQ6SLqEo --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d409b9..efc909e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,6 @@ ### ❤️ Thank You - Claude -- Claude Opus 5.5 (1M context) - Richard Fontein @rifont ## 0.3.0 (2026-09-05)