{"_id":"@dreamlab-ai/vowl-wasm","name":"@dreamlab-ai/vowl-wasm","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.2":{"name":"@dreamlab-ai/vowl-wasm","type":"module","description":"VOWL ontology visualisation engine in Rust/WASM: OWL parsing, force-directed layout (Barnes-Hut, SIMD), and rendering data for web explorers. A clean-room reimplementation of the WebVOWL visual notation.","version":"0.1.2","license":"MIT","repository":{"type":"git","url":"git+https://github.com/DreamLab-AI/vowl-wasm.git"},"main":"vowl_wasm.js","types":"vowl_wasm.d.ts","sideEffects":false,"keywords":["ontology","owl","wasm","visualisation","graph"],"publishConfig":{"access":"public"},"_id":"@dreamlab-ai/vowl-wasm@0.1.2","bugs":{"url":"https://github.com/DreamLab-AI/vowl-wasm/issues"},"homepage":"https://github.com/DreamLab-AI/vowl-wasm#readme","_integrity":"sha512-gAIzNEJKV3QdLE919RPn9aUvWzG1N6MTfxYulsRHm8ti7kVetGZc7NQBktHXzpahmeA1FDYqlUsg9RBjtnDsJg==","_resolved":"/home/devuser/workspace/.tmp/claude-1000/-home-devuser-workspace-VisionFlow/08c88bdd-488d-4dc5-953c-7d28f72888fb/scratchpad/vowl-wasm-0.1.2-pkg.tgz","_from":"file:vowl-wasm-0.1.2-pkg.tgz","_nodeVersion":"22.23.1","_npmVersion":"10.9.8","dist":{"integrity":"sha512-gAIzNEJKV3QdLE919RPn9aUvWzG1N6MTfxYulsRHm8ti7kVetGZc7NQBktHXzpahmeA1FDYqlUsg9RBjtnDsJg==","shasum":"5e647277fb2254bc8f327f0f4b8fffb3a529924e","tarball":"https://registry.npmjs.org/@dreamlab-ai/vowl-wasm/-/vowl-wasm-0.1.2.tgz","fileCount":6,"unpackedSize":306733,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIF+Zaq9dL5KIPdTKrb85cpIYZbaO5wno0LunHylv5245AiEA10ZbJr+RThGDfihXp9PsDn39Zk6jC2QissSQ4t9utkA="}]},"_npmUser":{"name":"dreamlab-ai","email":"npm-dreamlab-ai@thedreamlab.uk"},"directories":{},"maintainers":[{"name":"dreamlab-ai","email":"npm-dreamlab-ai@thedreamlab.uk"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/vowl-wasm_0.1.2_1788709462511_0.4387075703908496"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-06T15:44:22.341Z","0.1.2":"2026-09-06T15:44:22.667Z","modified":"2026-09-06T15:44:22.849Z"},"maintainers":[{"name":"dreamlab-ai","email":"npm-dreamlab-ai@thedreamlab.uk"}],"description":"VOWL ontology visualisation engine in Rust/WASM: OWL parsing, force-directed layout (Barnes-Hut, SIMD), and rendering data for web explorers. A clean-room reimplementation of the WebVOWL visual notation.","homepage":"https://github.com/DreamLab-AI/vowl-wasm#readme","keywords":["ontology","owl","wasm","visualisation","graph"],"repository":{"type":"git","url":"git+https://github.com/DreamLab-AI/vowl-wasm.git"},"bugs":{"url":"https://github.com/DreamLab-AI/vowl-wasm/issues"},"license":"MIT","readme":"# vowl-wasm\n\n[![crates.io](https://img.shields.io/crates/v/vowl-wasm.svg)](https://crates.io/crates/vowl-wasm)\n[![docs.rs](https://docs.rs/vowl-wasm/badge.svg)](https://docs.rs/vowl-wasm)\n[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)\n\nAn ontology visualisation engine in Rust, compiled to WebAssembly.\n\nIt turns an OWL ontology into something a browser can draw: parse the ontology,\nbuild a typed graph from it, settle that graph with a force-directed layout, and\nhand the result to JavaScript — either as structured data, or as a pointer into\nWASM linear memory that a rendering worker reads without copying.\n\n## Attribution\n\nThis is a **clean-room Rust reimplementation** of the **VOWL** visual notation\ndefined by [VisualDataWeb/WebVOWL](https://github.com/VisualDataWeb/WebVOWL)\n(MIT, © 2014–2019 Vincent Link, Steffen Lohmann, Eduard Marbach, Stefan Negru,\nVitalis Wiens). WebVOWL is the origin of the visual notation and of the ontology\nJSON shape this crate reads; none of its JavaScript, fixtures, or assets are\nvendored here. Both licence notices are carried in [`LICENSE`](./LICENSE).\n\nThis crate is not affiliated with or endorsed by the WebVOWL project or TIB.\n\n## Install\n\n```toml\n[dependencies]\nvowl-wasm = \"0.1\"\n```\n\nFrom JavaScript, via the published bundle:\n\n```bash\nnpm install @dreamlab-ai/vowl-wasm\n```\n\n## Rust usage\n\n```rust\nuse vowl_wasm::bindings::WebVowl;\n\nlet mut vowl = WebVowl::new();\n\nvowl.load_ontology(r#\"{\n    \"class\": [\n        { \"id\": \"c1\", \"label\": \"Person\",       \"type\": \"owl:Class\" },\n        { \"id\": \"c2\", \"label\": \"Organisation\", \"type\": \"owl:Class\" }\n    ],\n    \"property\": [\n        { \"id\": \"p1\", \"label\": \"worksFor\", \"type\": \"owl:ObjectProperty\",\n          \"domain\": \"c1\", \"range\": \"c2\" }\n    ]\n}\"#).expect(\"valid ontology\");\n\nvowl.init_simulation().expect(\"graph is loaded\");\nvowl.run_simulation(120).expect(\"graph is loaded\");\n\nassert_eq!(vowl.get_node_count(), 2);\n```\n\n## JavaScript usage\n\n```js\nimport init, { WebVowl } from '@dreamlab-ai/vowl-wasm';\n\nawait init();\n\nconst vowl = new WebVowl();\nvowl.loadOntology(ontologyJson);\nvowl.initSimulation();\nvowl.runSimulation(300);\n\nconsole.log(vowl.getNodeCount(), vowl.getStatistics(), vowl.getMetadata());\n```\n\n### Reading positions back\n\n`getNodePositions()` returns a `Float32Array` of interleaved `x, y` in node\nindex order. That index order is **stable for the lifetime of a loaded graph** —\nticking, pinning, filtering and folding never reorder or remove nodes — so map\nindex to id once and then read only floats per frame:\n\n```js\nconst ids = vowl.getNodeIds();        // once, after loadOntology\n\nfunction frame() {\n  vowl.tick();\n  const xy = vowl.getNodePositions(); // ids[i] <-> xy[2i], xy[2i + 1]\n  for (let i = 0; i < ids.length; i++) {\n    moveNode(ids[i], xy[2 * i], xy[2 * i + 1]);\n  }\n}\n```\n\nTo reuse one buffer instead of allocating per frame, use\n`writePositionsInto(buf)`, which fills a caller-owned `Float32Array` and returns\nthe number of floats written:\n\n```js\nconst xy = new Float32Array(vowl.getNodeCount() * 2);\nvowl.tick();\ndraw(xy.subarray(0, vowl.writePositionsInto(xy)));\n```\n\nLabels, types and edges still come from `getGraphData()`, which stays behind the\n`debug-serde` feature.\n\nFor a rendering worker, `NggExplorer` avoids per-frame serialisation entirely:\n\n```js\nimport init, { NggExplorer } from '@dreamlab-ai/vowl-wasm';\n\nconst wasm = await init();\nconst explorer = new NggExplorer();\nexplorer.loadCsr(tierBytes);\n\n// A zero-copy view over the live position buffer.\nconst positions = new Float32Array(\n  wasm.memory.buffer, explorer.positionsPtr(), explorer.positionsLen()\n);\n\nwhile (!explorer.isFinished()) {\n  explorer.tick();\n  draw(positions); // no copy, no JSON\n}\n```\n\nThe view is invalidated by the next `loadCsr` and by any WASM memory growth —\nre-read `positionsPtr()` after either.\n\n## Public API\n\nThis is the **frozen 0.1.0 surface**. Names and signatures will not change\nwithout a major version bump; later 0.1.x releases only add to it.\n\n### `WebVowl` — always available\n\n| JavaScript | Purpose |\n|---|---|\n| `new WebVowl()` | Construct an empty instance |\n| `loadOntology(json)` | Parse the ontology and build the graph |\n| `initSimulation()` | Seed the force simulation |\n| `runSimulation(n)` | Advance `n` steps |\n| `tick()` | Advance one step |\n| `isFinished()` | Has the layout settled? |\n| `getAlpha()` | Current annealing temperature |\n| `setCenter(x, y)` | Move the layout centre |\n| `setLinkDistance(d)` | Spring rest length |\n| `setChargeStrength(s)` | Repulsion strength |\n| `getNodeCount()` / `getEdgeCount()` | Graph size |\n| `getNodeIds()` | Node ids, in index order |\n| `getNodePositions()` | Live `x, y` per node as a `Float32Array` |\n| `writePositionsInto(buf)` | Copy those positions into a caller-owned buffer |\n| `getStatistics()` | Degree, connected components, OWL2 metrics |\n| `getMetadata()` | Ontology header: IRI, version, title, description |\n\nFiltering, folding and pinning are also always available: `filterByDegree`,\n`filterHierarchy`, `toggleDatatypes`, `toggleSetOperators`, `resetVisibility`,\n`getNodeDegree`, `pinNode`, `unpinNode`, `isPinned`, `resetPins`,\n`getPinnedCount`.\n\n### Feature-gated\n\n| JavaScript | Feature |\n|---|---|\n| `new NggExplorer()` — `loadCsr`, `tick`, `isFinished`, `getAlpha`, `nodeCount`, `edgeCount`, `positionsPtr`, `positionsLen`, `writePositionsInto`, `setParam`, `reheat` | `ngg1` |\n| `parseMarkdownOntology(md)`, `validateOWL2(md)` | `markdown-ontology` |\n| `checkNodeClick(origin, direction)` | `interaction` |\n| `getGraphData()` | `debug-serde` |\n\n## Features\n\nEvery optional feature is **off by default**. The general-purpose engine —\nOWL/JSON parsing, the graph model, force layout (Barnes-Hut quadtree and the CSR\nsimulation), OWL2 validation, pinning and statistics — is always compiled.\n\n| Feature | Default | What it adds |\n|---|---|---|\n| `ngg1` | off | NGG1 binary tier format and the `NggExplorer` zero-copy binding |\n| `markdown-ontology` | off | Markdown ontology parser and the filesystem loader built on it. Pulls in `regex` |\n| `interaction` | off | Ray-sphere hit testing for click and selection |\n| `debug-serde` | off | Legacy full-graph serde export, for diagnostics |\n| `parallel` | off | Data-parallel ontology loading via rayon. **Native only** |\n| `simd` | off | WASM SIMD128 force accumulation; needs the `simd128` target feature |\n\nEach feature compiles standalone, and the combinations are checked in CI.\n\n**The published npm bundle is built with `--features ngg1,interaction`.**\n\n`markdown-ontology` is deliberately *not* in it. It is the only thing that needs\na regex engine, and `regex` + `aho-corasick` + `memchr` were 68% of the compiled\ncode in 0.1.0 — the single largest thing in the download, for a parser neither\nin-estate consumer calls. Leaving it out takes the bundle from 1,477,462 to\n232,052 bytes (90,675 gzipped). If you need `parseMarkdownOntology` or\n`validateOWL2` in the browser, build the bundle yourself with the feature on:\n\n```bash\nwasm-pack build --target web --release --scope dreamlab-ai \\\n  --features ngg1,markdown-ontology,interaction\n```\n\n`parallel`, `simd` and `debug-serde` are likewise off in the published bundle;\nbuild from source to enable them.\n\n## Building\n\n```bash\ncargo test --all-features\ncargo clippy --all-targets --all-features -- -D warnings\n\n# The published bundle\nwasm-pack build --target web --release --scope dreamlab-ai \\\n  --features ngg1,interaction\n```\n\n`pkg/` is a build artefact and is not committed. CI uploads it as a workflow\nartifact, and tagged releases attach it as `vowl-wasm-<version>-pkg.tgz` and\npublish that same tarball to npm as `@dreamlab-ai/vowl-wasm` through npm\ntrusted publishing (OIDC from `release.yml`; no token is stored anywhere and\nprovenance attestations are generated automatically). To republish an existing\ntag, run the Release workflow with the tag as its input; it skips versions the\nregistry already has.\n\nMSRV: **1.85**.\n\n## Migrating from `webvowl-wasm` / `@dreamlab-ai/webvowl-wasm`\n\nThis crate replaces three divergent in-tree copies of `webvowl-wasm`. Migrating:\n\n1. **Package name.** `webvowl-wasm` → `@dreamlab-ai/vowl-wasm`. Replace any\n   `file:` path dependency with a real semver pin.\n\n2. **Generated file names change**, because the crate's lib name changed from\n   `webvowl_wasm` to `vowl_wasm`:\n\n   | Before | After |\n   |---|---|\n   | `webvowl_wasm.js` | `vowl_wasm.js` |\n   | `webvowl_wasm.d.ts` | `vowl_wasm.d.ts` |\n   | `webvowl_wasm_bg.wasm` | `vowl_wasm_bg.wasm` |\n\n   Anything that references those files by path — a bundler `optimizeDeps`\n   entry, a hand-written `init(url)` call, a CDN URL, a CSP hash — needs\n   updating. Importing the package by name is unaffected.\n\n3. **Version resets to 0.1.0.** Neither of the old copies (1.0.0 and 0.3.4)\n   described this API, so the numbering starts over. There is no implied\n   compatibility with either.\n\n4. **Feature gates.** `NggExplorer`, the markdown parser and `checkNodeClick`\n   were unconditional before and are now behind `ngg1`, `markdown-ontology` and\n   `interaction`. The published bundle enables `ngg1` and `interaction`; a\n   consumer building from source must ask for them, and anyone who needs the\n   markdown parser in the browser must build with `markdown-ontology` on\n   (see [Features](#features) for why it is not in the bundle).\n\n5. **Layout defaults changed.** `LayoutConfig::default()` now carries\n   general-purpose force constants rather than values tuned for one\n   1,155-node ontology. The old numbers are available as\n   `LayoutConfig::large_graph()`. This affects `WebVowl` only — `NggExplorer`\n   and `CsrSimulation` have their own parameters and are unchanged.\n\n## Licence\n\nMIT. See [`LICENSE`](./LICENSE), which carries both this project's notice and\nthe upstream WebVOWL notice.\n","readmeFilename":"README.md","_rev":"1-e0d063ad554b224109777930ecb0bd40"}