8000 feat(wasi): Phase 8.1b — wasm32-wasip1 server-side WASM support by adityamukho · Pull Request #152 · project-minigraf/minigraf · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .github/workflows/wasm-wasi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: WASM WASI

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

env:
CARGO_TERM_COLOR: always
WASMTIME_VERSION: v33.0.0
WASMER_VERSION: v5.0.4

permissions:
contents: read

jobs:
wasm-wasi:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-wasip1

- name: Build (release)
run: cargo build --target wasm32-wasip1 --release --bin minigraf

- name: Install Wasmtime
run: |
curl -sSfL \
"https://github.com/bytecodealliance/wasmtime/releases/download/${WASMTIME_VERSION}/wasmtime-${WASMTIME_VERSION}-x86_64-linux.tar.xz" \
-o /tmp/wasmtime.tar.xz
tar -xf /tmp/wasmtime.tar.xz -C /tmp
echo "/tmp/wasmtime-${WASMTIME_VERSION}-x86_64-linux" >> "$GITHUB_PATH"

- name: Run WASI tests (Wasmtime runner)
run: cargo test --target wasm32-wasip1 --lib
env:
CARGO_TARGET_WASM32_WASIP1_RUNNER: wasmtime run --dir /tmp

- name: Smoke test — Wasmtime transact + query
run: |
out=$(printf '(transact [[:alice :name "Alice"]])\n(query [:find ?e :where [?e :name _]])\n' | \
wasmtime run --dir /tmp \
target/wasm32-wasip1/release/minigraf.wasm)
echo "$out"
echo "$out" | grep -q "result(s) found" || { echo "SMOKE TEST FAILED: no results in output"; exit 1; }

- name: Install Wasmer
run: |
curl -sSfL \
"https://github.com/wasmerio/wasmer/releases/download/${WASMER_VERSION}/wasmer-linux-amd64.tar.gz" \
-o /tmp/wasmer.tar.gz
mkdir -p /tmp/wasmer-install
tar -xf /tmp/wasmer.tar.gz -C /tmp/wasmer-install bin/wasmer
echo "/tmp/wasmer-install/bin" >> "$GITHUB_PATH"

- name: Smoke test — Wasmer transact + query
run: |
out=$(printf '(transact [[:alice :name "Alice"]])\n(query [:find ?e :where [?e :name _]])\n' | \
wasmer run --dir /tmp \
target/wasm32-wasip1/release/minigraf.wasm)
echo "$out"
echo "$out" | grep -q "result(s) found" || { echo "SMOKE TEST FAILED: no results in output"; exit 1; }
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ uuid = { version = "1.0", features = ["v4", "v5", "serde"] }
regex-lite = "0.1.9"
serde_json = { version = "1.0", optional = true }

[target.'cfg(target_arch = "wasm32")'.dependencies]
[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies]
uuid = { version = "1.0", features = ["v4", "v5", "serde", "js"] }
wasm-bindgen = { version = "0.2", optional = true }
wasm-bindgen-futures = { version = "0.4", optional = true }
Expand All @@ -57,7 +57,7 @@ serde_json = "1.0"
tempfile = "3"
criterion = { version = "0.8", features = ["html_reports"] }

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dev-dependencies]
wasm-bindgen-test = "0.3"

[[bench]]
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ No other database offers this combination:
| **Embedded** | ✅ Yes | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes |
| **Graph Native** | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ❌ No |
| **Rust** | ✅ Yes | ❌ Clojure | ✅ Yes | ❌ Java | ❌ C |
| **WASM Ready** | 🎯 Phase 8 | ❌ No | ⚠️ Limited | ❌ No | ✅ Yes |
| **WASM Ready** | ✅ Phase 8.1a/b | ❌ No | ⚠️ Limited | ❌ No | ✅ Yes |

**Embedded graph memory for agents, mobile, and the browser — SQLite's simplicity + Datomic's temporal model.**

Expand All @@ -131,7 +131,7 @@ Offline-first storage with retroactive corrections — the bi-temporal model let

### For WASM / Browser

Phase 8 target: page-granular IndexedDB backend, `wasm-pack` packaging, npm release as `@minigraf/core`. Also supports server-side WASM via WASI.
Phase 8.1a complete: IndexedDB backend, `wasm-pack` packaging. Phase 8.1b complete: server-side WASM via `wasm32-wasip1` / WASI (Wasmtime, Wasmer). npm release as `@minigraf/core` planned for Phase 8.2.

See the [Use Cases](https://github.com/adityamukho/minigraf/wiki/Use-Cases) wiki page for detailed guides on all three targets.

Expand All @@ -140,7 +140,7 @@ See the [Use Cases](https://github.com/adityamukho/minigraf/wiki/Use-Cases) wiki
Minigraf runs as:
- ✅ An embedded library
- ✅ A standalone binary (interactive REPL)
- 🎯 A WebAssembly module (Phase 8)
- ✅ A WebAssembly module — browser (`wasm32-unknown-unknown`) and server-side WASI (`wasm32-wasip1`) (Phase 8.1a/b complete)

Minigraf will **not** be (by design):
- **Distributed** — no clustering, no sharding, no replication; each agent instance owns its own `.graph` file
Expand Down
28 changes: 28 additions & 0 deletions src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1728,3 +1728,31 @@ mod tests {
assert!(result.is_ok(), "Query should succeed with higher limit");
}
}

// ─── WASI smoke test ─────────────────────────────────────────────────────────
// Gated to target_os = "wasi" only. Regular #[test] works here because
// cargo test --target wasm32-wasip1 uses Wasmtime as the runner
// (CARGO_TARGET_WASM32_WASIP1_RUNNER). Not gated on target_arch = "wasm32"
// because the browser target (wasm32-unknown-unknown) requires
// #[wasm_bindgen_test] instead, which is a separate harness.
#[cfg(all(target_os = "wasi", test))]
mod wasi_tests {
use crate::db::Minigraf;
use crate::query::datalog::executor::QueryResult;

#[test]
fn in_memory_smoke() {
let db = Minigraf::in_memory().expect("open in-memory db");
db.execute("(transact [[:e1 :name \"hello\"]])")
.expect("transact");
let r = db
.execute("(query [:find ?e :where [?e :name _]])")
.expect("query");
match r {
QueryResult::QueryResults { results, .. } => {
assert!(!results.is_empty());
}
_ => panic!("expected QueryResults"),
}
}
}
12 changes: 9 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))]
use minigraf::Minigraf;
#[cfg(not(target_arch = "wasm32"))]
use minigraf::OpenOptions;

fn main() -> anyhow::Result<()> {
#[cfg(target_arch = "wasm32")]
#[cfg(target_os = "wasi")]
{
let db = Minigraf::in_memory()?;
db.repl().run();
Ok(())
}
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
{
// The REPL binary is not applicable to WASM targets.
// Browser WASM — entry point is the BrowserDb JS/WASM API, not a REPL binary.
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
Expand Down
1 change: 1 addition & 0 deletions src/storage/btree_v6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1049,6 +1049,7 @@ mod tests {
}

#[test]
#[cfg(not(target_os = "wasi"))]
fn test_concurrent_range_scans_correctness() {
use crate::storage::CommittedIndexReader;
use std::sync::{Arc, Barrier};
Expand Down
1 change: 1 addition & 0 deletions src/storage/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ mod tests {
}

#[test]
#[cfg(not(target_os = "wasi"))]
fn test_concurrent_reads() {
use std::sync::Arc;
use std::thread;
Expand Down
Loading
0