Installation
Ahoi has two halves. A Rust crate that holds the state, and an npm package that connects it to your framework.
1. The Rust side
Section titled “1. The Rust side”Add the crate. The serde-wasm-bindgen feature gives you a ready-made
converter for values crossing the bridge.
[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.
2. Export the types
Section titled “2. Export the types”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.
#[test]fn generate() { ahoi::js_bridge::TsFile::new() .with::<Hail>() .with::<Tell>() .export("./bindings/Rets.ts");}Run cargo test and you get:
export type HailRets = { Count: number; Doubled: number };export type TellRets = { Increase: number };3. Build the wasm module
Section titled “3. Build the wasm module”wasm-pack build --target webThis 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.
4. The JS side
Section titled “4. The JS side”npm i @acheul/ahoi-jsEvery 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 |
5. Wire it up
Section titled “5. Wire it up”This is the only setup file an app needs. It hands the six wasm exports to the adapter and gives you back the hooks.
// #region setupimport 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// #region setupimport wasmInit, { abi_version, clear, hail, pier, tell, write,} from "../../rust/pkg/ahoi_book_examples";import { createAhoi } from "@acheul/ahoi-js/react";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, useHail, useReadHail, useTell } = 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// #region setupimport wasmInit, { abi_version, clear, hail, pier, tell, write,} from "../../rust/pkg/ahoi_book_examples";import { createAhoi } from "@acheul/ahoi-js/vue";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, useHail, useReadHail, useTell } = 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// #region setupimport wasmInit, { abi_version, clear, hail, pier, tell, write,} from "../../rust/pkg/ahoi_book_examples";import { createAhoi } from "@acheul/ahoi-js/svelte";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();
// Svelte has no provider component — `providePier` uses `setContext` directly.export const { providePier, useHail, useReadHail, useTell } = 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 setupThe 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.