release: 0.0.1

This commit is contained in:
Milovann Yanatchkov 2026-09-01 10:37:09 +02:00
commit 34ca011b92
6 changed files with 198 additions and 0 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
/target
/pkg
Cargo.lock
.claude/
.opencode/

22
Cargo.toml Normal file
View file

@ -0,0 +1,22 @@
[package]
name = "bimr-wasm"
version = "0.0.1"
edition = "2024"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
eval = { git = "https://gitaec.org/rvba/bimr-engine.git" }
parser = { git = "https://gitaec.org/rvba/bimr-engine.git" }
# Pinned to the factory's wasm-bindgen CLI (Makefile WASM_BINDGEN_VERSION).
wasm-bindgen = "=0.2.114"
# Local checkouts override the forge pins (same pattern as bimr-engine's
# kernel patch) so the factory builds against sibling repos.
[patch."https://gitaec.org/rvba/bimr-engine.git"]
eval = { path = "../bimr-engine/eval" }
parser = { path = "../bimr-engine/parser" }
[patch."https://gitaec.org/rvba/bimr-kernel.git"]
bimr-kernel = { path = "../bimr-kernel" }

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Milovann Yanatchkov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

23
Makefile Normal file
View file

@ -0,0 +1,23 @@
.PHONY: test pkg clean
# Native tests — wasm_bindgen fns are plain Rust; sessions work on the host.
test:
cargo test
# wasm-bindgen web pkg (consumed by the factory `build-wasm-white` and
# node/smoke.mjs). Pins mirror the factory: wasm-bindgen 0.2.114, Binaryen.
pkg:
rustup target add wasm32-unknown-unknown
cargo build --release --target wasm32-unknown-unknown
$(HOME)/.cargo/bin/wasm-bindgen --target web --out-dir pkg \
target/wasm32-unknown-unknown/release/bimr_wasm.wasm
command -v wasm-opt >/dev/null && \
wasm-opt -O pkg/bimr_wasm_bg.wasm -o pkg/bimr_wasm_bg.wasm || true
# Node smoke test over the web pkg.
smoke: pkg
node node/smoke.mjs
clean:
cargo clean
rm -rf pkg

60
node/smoke.mjs Normal file
View file

@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: 2026 Milovann Yanatchkov
// SPDX-License-Identifier: MIT
// smoke.mjs — Node test of the wasm-bindgen web pkg (engine.test.mjs pattern
// from bimr-web). Run via `make smoke` after `make pkg`.
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
function section(name) {
console.log(`\n# ${name}`);
}
async function loadWasm() {
const jsUrl = new URL("../pkg/bimr_wasm.js", import.meta.url).href;
const bimrWasm = await import(jsUrl);
const wasmBytes = readFileSync(new URL("../pkg/bimr_wasm_bg.wasm", import.meta.url));
await bimrWasm.default({ module_or_path: wasmBytes });
return bimrWasm;
}
const WALL_SRC = `p1 = Point(0,0,0)
p2 = Point(500,0,0)
l1 = Line(p1,p2)
w1 = Wall(l1,20,300)`;
async function main() {
const wasm = await loadWasm();
section("surface — whole-source only (D8)");
{
const exports = Object.keys(wasm).filter((k) => typeof wasm[k] === "function" && !k.startsWith("__"));
assert.deepEqual(
exports.sort(),
["compile", "default", "initSync"],
"no session exports, no side queries",
);
}
section("compile() — wall to IFCX");
{
const out = wasm.compile(WALL_SRC);
assert.match(out, /wall-001/);
assert.match(out, /ifcx_alpha/);
assert.match(out, /faceVertexIndices/);
}
section("compile() — error handling");
{
const out = wasm.compile("w1 = Wall(missing)");
assert.match(out, /error/);
}
console.log("\nAll smoke tests passed.");
}
main().catch((e) => {
console.error(e);
process.exit(1);
});

67
src/lib.rs Normal file
View file

@ -0,0 +1,67 @@
// SPDX-FileCopyrightText: 2026 Milovann Yanatchkov
// SPDX-License-Identifier: MIT
//! bimr-wasm — browser adapter for the [`bimr-engine`](https://git.rvba.fr/rvba/bimr-engine) eval.
//!
//! **Whole-source only**: the adapter exposes exactly one export —
//! `compile(src)`. There is no session layer, no per-call construction, and
//! no side queries: Python emits `.bimr` text and calls `compile()` once,
//! the same path as the CLI. The `.bimr` intermediate is the compatibility
//! layer; whole-source is the single entry point for the CLI, web Python,
//! and LLM paths.
//!
//! Errors return `{"error": "..."}` JSON strings.
use wasm_bindgen::prelude::*;
fn parse_or_error(src: &str) -> Result<parser::ast::Program, String> {
parser::parse(src).map_err(|errors| {
let msg = errors
.iter()
.map(|e| format!("{e:?}"))
.collect::<Vec<_>>()
.join("; ");
format!("{{\"error\": \"{}\"}}", msg.replace('"', "'"))
})
}
/// Compile a `.bimr` source string to IFCX JSON.
///
/// Returns a JSON string — either an IFCX document on success, or
/// `{"error": "..."}` on parse/eval failure.
#[wasm_bindgen]
pub fn compile(src: &str) -> String {
let program = match parse_or_error(src) {
Ok(p) => p,
Err(e) => return e,
};
match eval::eval_program(&program, "model.bimr") {
Ok(store) => eval::to_ifcx(&store),
Err(e) => format!("{{\"error\": \"{e}\"}}"),
}
}
#[cfg(test)]
mod tests {
use super::*;
const WALL_SRC: &str = "\
p1 = Point(0,0,0)
p2 = Point(500,0,0)
l1 = Line(p1,p2)
w1 = Wall(l1,20,300)";
#[test]
fn compile_wall_to_ifcx() {
let out = compile(WALL_SRC);
assert!(out.contains("wall-001"), "missing wall-001: {out}");
assert!(out.contains("ifcx_alpha"));
assert!(out.contains("faceVertexIndices"));
}
#[test]
fn compile_errors_are_error_json() {
let out = compile("w1 = Wall(missing)");
assert!(out.contains("error"), "expected error json: {out}");
}
}