AssemblyScript WASM Nodes
AssemblyScript compiles TypeScript-like syntax directly to compact Core Module WASM binaries. The template uses a class-based SDK with typed getters/setters — no raw JSON wrangling required.
Prerequisites
Section titled “Prerequisites”- Node.js 18+ and npm
- The
@flow-like/wasm-sdk-assemblyscriptSDK (installed via npm)
Project Setup
Section titled “Project Setup”mkdir my-as-node && cd my-as-nodenpm init -ynpm install @flow-like/wasm-sdk-assemblyscriptnpm install --save-dev assemblyscript@^0.28.9Compiler Configuration
Section titled “Compiler Configuration”{ "targets": { "debug": { "outFile": "build/debug.wasm", "textFile": "build/debug.wat", "sourceMap": true, "debug": true }, "release": { "outFile": "build/release.wasm", "textFile": "build/release.wat", "sourceMap": true, "optimizeLevel": 3, "shrinkLevel": 2, "converge": false, "noAssert": false } }, "options": { "bindings": "esm", "exportRuntime": true, "runtime": "stub" }}Build Scripts
Section titled “Build Scripts”{ "scripts": { "build": "asc assembly/index.ts --target release", "build:debug": "asc assembly/index.ts --target debug" }}Template Code
Section titled “Template Code”The SDK provides a FlowNode base class. Extend it, implement define() and execute(), then wire up the required exports:
import { type Context, DataType, type ExecutionResult, FlowNode, NodeDefinition, PinDefinition, runSingle, singleNode,} from "@flow-like/wasm-sdk-assemblyscript/assembly/index";
// Re-export memory management + ABI helpers from the SDKexport { alloc, dealloc, get_abi_version,} from "@flow-like/wasm-sdk-assemblyscript/assembly/index";
class MyCustomNode extends FlowNode { define(): NodeDefinition { const def = new NodeDefinition(); def.name = "my_custom_node_as"; def.friendly_name = "My Custom Node (AS)"; def.description = "A template WASM node built with AssemblyScript"; def.category = "Custom/WASM"; def.addPermission("streaming");
// Input pins def.addPin( PinDefinition.input("exec", "Execute", "Trigger execution", DataType.Exec), ); def.addPin( PinDefinition.input("input_text", "Input Text", "Text to process", DataType.String) .withDefaultString(""), ); def.addPin( PinDefinition.input("multiplier", "Multiplier", "Number of times to repeat", DataType.I64) .withDefaultI64(1), );
// Output pins def.addPin( PinDefinition.output("exec_out", "Done", "Execution complete", DataType.Exec), ); def.addPin( PinDefinition.output("output_text", "Output Text", "Processed text", DataType.String), ); def.addPin( PinDefinition.output("char_count", "Character Count", "Number of characters in output", DataType.I64), );
return def; }
execute(ctx: Context): ExecutionResult { const inputText = ctx.getString("input_text"); const multiplier = ctx.getI64("multiplier", 1);
let outputText = ""; for (let i: i64 = 0; i < multiplier; i++) { outputText += inputText; }
ctx.setString("output_text", outputText); ctx.setI64("char_count", outputText.length); return ctx.success(); }}
// Instantiate and wire up the required exportsconst node = new MyCustomNode();
export function get_node(): i64 { return singleNode(node);}
export function run(ptr: i32, len: i32): i64 { return runSingle(node, ptr, len);}npm run buildOutput: build/release.wasm
For a debug build with source maps:
npm run build:debugRequired Exports
Section titled “Required Exports”Every Core Module WASM node must export these symbols:
| Export | Purpose |
|---|---|
alloc(size) → ptr | Allocate memory for the host to write into |
dealloc(ptr, size) | Free previously allocated memory |
get_node() → ptr | Return the JSON node definition |
get_nodes() → ptr | Return a JSON array of all node definitions |
run(ptr, len) → ptr | Execute the node with the given context |
get_abi_version() → u32 | Return the ABI version (currently 1) |
The SDK re-exports alloc, dealloc, and get_abi_version — you only need to implement get_node and run yourself.
SDK API
Section titled “SDK API”Pin Definitions
Section titled “Pin Definitions”Use PinDefinition static factories to declare inputs and outputs:
PinDefinition.input(name, friendlyName, description, DataType.String)PinDefinition.output(name, friendlyName, description, DataType.I64)Chain defaults with .withDefaultString(""), .withDefaultI64(0), etc.
Context Methods
Section titled “Context Methods”Read input values and write outputs inside execute():
// Gettersctx.getString("pin_name") // stringctx.getI64("pin_name", default) // i64ctx.getF64("pin_name", default) // f64ctx.getBool("pin_name", default) // bool
// Settersctx.setString("pin_name", value)ctx.setI64("pin_name", value)ctx.setF64("pin_name", value)ctx.setBool("pin_name", value)
// Execution controlctx.success() // signals completion, activates exec_outData Types
Section titled “Data Types”DataType | AssemblyScript type |
|---|---|
Exec | — (execution flow) |
String | string |
I64 | i64 |
F64 | f64 |
Bool | bool |
Host FFI
Section titled “Host FFI”Under the hood, the SDK calls flowlike_* functions imported from the env module. You don’t need to use these directly — the Context and FlowNode classes abstract them away.
Install
Section titled “Install”cp build/release.wasm ~/.flow-like/nodes/my-custom-node.wasmRelated
Section titled “Related”- WASM Nodes Overview
- Runtime Models — Core Module vs Component Model
- TypeScript WASM Nodes — alternative TS approaches (Javy)
- Rust Template
- Package Manifest