Skip to main content
Version: 0.3.36

WebAssembly: AssemblyScript

TypeScript-shaped syntax compiled ahead of time to a WebAssembly component. No JavaScript engine is embedded, so the artifacts stay small.

Prerequisites

brew install wasm-tools     # or: cargo install wasm-tools

wasm-tools performs the last two build steps (see How the build works). The AssemblyScript compiler is installed per project as a dev dependency, so wasm-tools is the only global addition.

Scaffold

raisindb create function greet --lang assemblyscript --ns demo
cd wasm/demo/greet && npm install

Write the handler

import { run, log, nodes, unknownHandler, cabi_realloc }
from "../node_modules/@raisindb/function-assemblyscript/assembly/index";

function greet(input: string): string {
log.info("greeting");

// Every raisin.* method is available and returns raw JSON text.
const children = nodes.getChildren("content", "/pages", 50);

return '{"greeting":"hello","children":' + children + '}';
}

// The component exports ONE function; the node's `entry_file` suffix picks the
// handler, so routing is an ordinary comparison.
function route(name: string, input: string): string {
if (name == "default") return greet(input);
return unknownHandler(name, "default");
}

export function handler(np: i32, nl: i32, ip: i32, il: i32): i32 {
return run(np, nl, ip, il, route);
}
export { cabi_realloc };

assembly/index.ts exports two things: your handler, and cabi_realloc, which lets the host allocate inside the guest's memory. wasm-tools component new resolves both by name, so keep the names as the scaffold writes them.

The SDK is imported by path into node_modules because asc does not resolve scoped package names. The scaffold writes the import for you.

Working with JSON

Handlers take and return JSON as text, and every raisin.* method returns the raw JSON the server produced. For many functions that is all you need: compose the response directly, as above. Optional numeric arguments take -1 to mean absent.

When you want typed objects, add a JSON library and decode explicitly:

npm install json-as
import { JSON } from "json-as";

@json class Input { name!: string; }

const parsed = JSON.parse<Input>(input);
log.info("greeting " + parsed.name);

AssemblyScript's standard library does not include JSON, and the SDK does not bundle one, so a function that never parses anything stays at its smallest.

Testing

A scaffolded project comes with unit tests that run with no server:

raisindb function test wasm/demo/greet      # npm test

The SDK's mock host loads your compiled core module and answers raisin.* calls from JavaScript, so a handler is exercised the way the server would call it:

import { loadGuest } from "@raisindb/function-assemblyscript/testing";

const guest = await loadGuest(CORE, {
call(method, args) {
if (method === "nodes_getChildren") return [{ id: "a", node_type: "raisin:Page" }];
throw new Error(`unexpected ${method}`);
},
});

const out = guest.invoke("default", { name: "Ada" });
assert.equal(out.greeting, "hello");
assert.deepEqual(guest.calls.map((c) => c.method), ["nodes_getChildren"]);

call returns the value for a successful host call or throws for a failed one. guest.invoke returns the parsed output and throws if the handler returned an error. guest.calls ({ method, args }, with args already parsed) and guest.logs ({ level, message }) record what the handler did. A call you have not scripted throws, so a handler that starts reaching for something new fails the test rather than passing quietly.

To run the scenarios in tests/server.json against a real server:

raisindb function test wasm/demo/greet --server --repo myapp

Build, run, deploy

raisindb function doctor wasm/demo/greet   # checks asc + wasm-tools
raisindb function build wasm/demo/greet
raisindb function run wasm/demo/greet --input '{"name":"Ada"}' --repo myapp
raisindb deploy . --repo myapp --install

How the build works

asc compiles to a core WebAssembly module, and the server runs Component Model components, so the build has three steps, which raisindb function build runs for you:

asc assembly/index.ts -o build/guest.core.wasm --runtime stub --exportRuntime --optimize --use abort=
wasm-tools component embed wit build/guest.core.wasm -o build/guest.embed.wasm --world function
wasm-tools component new build/guest.embed.wasm -o main.wasm

embed attaches the WIT interface from wit/ to the module, and new wraps the result as a component.

Inside the SDK, assembly/abi.ts implements the Component Model's canonical ABI (how a string or a result is laid out in memory) and is the only file that works with pointers. The typed raisin.* surface above it is generated from the server's binding registry, the same source the Rust and Go SDKs are generated from.

Depending on the SDK directly

npm install @raisindb/function-assemblyscript

Limits

AssemblyScript functions run in the same sandbox as every other wasm guest: network access goes through raisin.http.* and is governed by the function's network_policy. See WebAssembly Functions for timeouts, memory and the artifact size cap.