Scaffold a new custom execution module with its HyperBEAM integration test.
The argument $ARGUMENTS should include the module name and optionally --lua or --wasm to specify the type. If no type is given, ask the user.
Steps
- •Check for conflicts:
bash
test -f "custom-lua/$ARGUMENTS.lua" && echo "WARNING: custom-lua/$ARGUMENTS.lua already exists" || echo "OK" test -d "custom-wasm/" && test -f "custom-wasm/src/lib.rs" && echo "WARNING: custom-wasm/src/lib.rs already exists" || echo "OK"
If the file exists, ask the user: overwrite or choose a different name?
For Lua modules (--lua or default)
- •Create
custom-lua/directory if it doesn't exist:
bash
mkdir -p custom-lua
- •Create
custom-lua/{name}.lua:
lua
-- {name}.lua - Custom Lua module for lua@5.3a device
-- Standalone: no AOS boot code, no Handlers framework.
-- Implements a {Name} with basic actions.
local state = {}
local function intstr(n)
if n == math.floor(n) then return string.format("%d", n) end
return tostring(n)
end
function compute(base, req, opts)
local action = nil
if type(req) == "table" then
if type(req.body) == "table" then
action = req.body.Action or req.body.action
end
if not action then
action = req.Action or req.action
end
end
local result = "ok"
if action == "Get" then
-- Return current state
result = "state"
end
base.results = {
outbox = {
["1"] = { data = result }
},
output = ""
}
return base
end
- •Create
test/{name}-module.test.js:
js
import { describe, it, before, after } from "node:test"
import assert from "node:assert"
import { readFileSync } from "node:fs"
import { resolve } from "node:path"
import { HyperBEAM } from "wao/test"
import HB from "wao/hb"
const luaPath = resolve(import.meta.dirname, "../custom-lua/{name}.lua")
async function spawnCustomLua(hb, moduleId) {
return hb.spawn({
"data-protocol": "ao",
variant: "ao.TN.1",
module: moduleId,
"execution-device": "lua@5.3a",
"push-device": "push@1.0",
"patch-from": "/results/outbox",
})
}
describe("{Name} Custom Lua Module", function () {
let hbeam, hb, moduleId
before(async () => {
hbeam = await new HyperBEAM({ reset: true }).ready()
hb = new HB({ url: hbeam.url })
await hb.init(hbeam.jwk)
const luaSrc = readFileSync(luaPath, "utf-8")
moduleId = await hb.cacheScript(luaSrc, "application/lua")
})
after(async () => { if (hbeam) hbeam.kill() })
it("should spawn and get initial state", async () => {
const { pid } = await spawnCustomLua(hb, moduleId)
assert.ok(pid, "should get process ID")
const get = await hb.scheduleLua({ pid, action: "Get" })
const result = await hb.computeLua({ pid, slot: get.slot })
assert.ok(result)
})
})
For WASM64 modules (--wasm)
- •Create the Rust project if it doesn't exist:
bash
test -d custom-wasm || cargo new custom-wasm --lib
- •Write
custom-wasm/Cargo.toml:
toml
[package] name = "custom-wasm" version = "0.1.0" edition = "2021" [lib] crate-type = ["cdylib"] [profile.release] opt-level = "z" lto = true strip = true
- •
Write
custom-wasm/src/lib.rswith the#![no_std]counter template fromdocs/docs/pages/tutorials/rust-wasm64.mdx. - •
Build:
bash
cd custom-wasm && cargo +nightly build --target wasm64-unknown-unknown --release -Zbuild-std=core,panic_abort -Zbuild-std-features=panic_immediate_abort
- •Create
test/{name}-module.test.jsusing the WASM test template (withcacheBinary+spawnAOS+scheduleAOS+computeAOS).
Final steps
- •Run the test:
bash
yarn test test/{name}-module.test.js
- •Report whether the test passes. If it fails, fix and re-run.