Skip to content

Installation

Ahoi has two halves. A Rust crate that holds the state, and an npm package that connects it to your framework.

Add the crate. The serde-wasm-bindgen feature gives you a ready-made converter for values crossing the bridge.

Cargo.toml
[dependencies]
ahoi = { version = "0.1", features = ["serde-wasm-bindgen"] }
serde = { version = "1", features = ["derive"] }
serde-wasm-bindgen = "0.6"
wasm-bindgen = "0.2"
[lib]
crate-type = ["cdylib", "rlib"]

cdylib is what produces the wasm module. rlib keeps cargo test working.

Ahoi does not convert Rust types to TypeScript. Pick an exporter you like (ts-rs or Tsify) and use it for your key and data types.

Ahoi adds the one thing those tools cannot know: what each key returns. Write a test that generates the map.

src/lib.rs
#[test]
fn generate() {
ahoi::js_bridge::TsFile::new()
.with::<Hail>()
.with::<Tell>()
.export("./bindings/Rets.ts");
}

Run cargo test and you get:

bindings/Rets.ts
export type HailRets = { Count: number; Doubled: number };
export type TellRets = { Increase: number };
Terminal window
wasm-pack build --target web

This writes a pkg/ directory. Your JS imports the module from there.

Re-run it whenever the Rust changes. Wasm cannot hot-reload, so a dev server needs a full page refresh to pick up a new build.

Terminal window
npm i @acheul/ahoi-js

Every adapter is a subpath of that one package. The framework itself is an optional peer dependency, so you only pull in what you use.

Import For
@acheul/ahoi-js/solid Solid
@acheul/ahoi-js/react React, and Preact via preact/compat
@acheul/ahoi-js/vue Vue
@acheul/ahoi-js/svelte Svelte
@acheul/ahoi-js The framework-agnostic core

This is the only setup file an app needs. It hands the six wasm exports to the adapter and gives you back the hooks.

bridge.ts
// #region setup
import wasmInit, {
abi_version,
clear,
hail,
pier,
tell,
write,
} from "../../rust/pkg/ahoi_book_examples";
import { createAhoi } from "@acheul/ahoi-js/solid";
import type { Pier } from "../../rust/bindings/Pier";
import type { Hail } from "../../rust/bindings/Hail";
import type { Tell } from "../../rust/bindings/Tell";
import type { HailRets, TellRets } from "../../rust/bindings/Rets";
await wasmInit();
export const { PierProvider, usePier } = createAhoi<Pier, Hail, Tell, HailRets, TellRets>({
_enrol_pier: pier,
_enrol_hail: (p, k) => hail(p, k) as [number, any],
_clear_sphere: clear,
_write_hail: write,
_tell: tell,
_abi_version: abi_version,
});
// #endregion setup

The generic parameters are your key types and the generated ret maps. They are what make useHail("Count") a number instead of unknown.

Build a counter and see the round trip.