TypeScript WASM Nodes
TypeScript can compile to WASM using AssemblyScript (TypeScript-like syntax) or Javy (full JavaScript/TypeScript).
Option 1: AssemblyScript (Recommended)
Section titled “Option 1: AssemblyScript (Recommended)”AssemblyScript is a TypeScript-like language that compiles directly to WASM with small output sizes.
mkdir my-custom-nodecd my-custom-nodenpm init -ynpm install --save-dev assemblyscriptnpx asinit .Template Code
Section titled “Template Code”// Memory managementlet resultBuffer: ArrayBuffer;
class NodeDefinition { name: string; friendly_name: string; description: string; category: string; icon: string; pins: PinDefinition[];}
class PinDefinition { name: string; friendly_name: string; description: string; pin_type: string; data_type: string; default_value: string | null;}
class ExecutionContext { inputs: Map<string, string>;}
class ExecutionResult { outputs: Map<string, string>; error: string | null;}
// Export: get_nodeexport function get_node(): usize { const json = `{ "name": "wasm_ts_uppercase", "friendly_name": "Uppercase (TS)", "description": "Converts a string to uppercase using TypeScript", "category": "Custom/Text", "icon": "/flow/icons/text.svg", "pins": [ { "name": "exec_in", "friendly_name": "▶", "description": "Trigger execution", "pin_type": "Input", "data_type": "Execution" }, { "name": "exec_out", "friendly_name": "▶", "description": "Continue execution", "pin_type": "Output", "data_type": "Execution" }, { "name": "input", "friendly_name": "Input", "description": "The string to convert", "pin_type": "Input", "data_type": "String", "default_value": "" }, { "name": "output", "friendly_name": "Output", "description": "The uppercase string", "pin_type": "Output", "data_type": "String" } ], "scores": { "privacy": 0, "security": 0, "performance": 1, "governance": 0, "reliability": 0, "cost": 0 } }`;
return changetype<usize>(String.UTF8.encode(json));}
// Export: runexport function run(contextPtr: usize, contextLen: u32): usize { // Read context from memory const contextBytes = new Uint8Array(contextLen); memory.copy( changetype<usize>(contextBytes.buffer), contextPtr, contextLen );
const contextJson = String.UTF8.decode(contextBytes.buffer);
// Simple JSON parsing (AssemblyScript has limited JSON support) // In production, use a JSON library like @assemblyscript/json const inputMatch = contextJson.match(/"input"\s*:\s*"([^"]*)"/); const input = inputMatch ? inputMatch[1] : "";
// Execute logic const output = input.toUpperCase();
// Return result const result = `{"outputs":{"output":"${output}"},"error":null}`; return changetype<usize>(String.UTF8.encode(result));}npm run asbuild:releaseOutput: build/release.wasm
Configuration
Section titled “Configuration”Update asconfig.json for optimized builds:
{ "targets": { "release": { "outFile": "build/release.wasm", "optimizeLevel": 3, "shrinkLevel": 2, "noAssert": true } }}Option 2: Javy (Full TypeScript/JavaScript)
Section titled “Option 2: Javy (Full TypeScript/JavaScript)”Javy compiles full JavaScript to WASM, supporting the entire language but with larger binary sizes.
# Install Javy# macOSbrew install aspect-build/aspect/javy
# Or download from releases# https://github.com/aspect-build/aspect-cli/releasesTemplate Code
Section titled “Template Code”// Node definitionconst nodeDefinition = { name: "wasm_javy_uppercase", friendly_name: "Uppercase (Javy)", description: "Converts a string to uppercase using Javy", category: "Custom/Text", icon: "/flow/icons/text.svg", pins: [ { name: "exec_in", friendly_name: "▶", description: "Trigger execution", pin_type: "Input", data_type: "Execution", }, { name: "exec_out", friendly_name: "▶", description: "Continue execution", pin_type: "Output", data_type: "Execution", }, { name: "input", friendly_name: "Input", description: "The string to convert", pin_type: "Input", data_type: "String", default_value: "", }, { name: "output", friendly_name: "Output", description: "The uppercase string", pin_type: "Output", data_type: "String", }, ], scores: { privacy: 0, security: 0, performance: 2, governance: 0, reliability: 0, cost: 0, },};
// Javy uses stdin/stdout for I/Oconst decoder = new TextDecoder();const encoder = new TextEncoder();
// Read operation from stdinconst input = Javy.IO.readSync(0);const request = JSON.parse(decoder.decode(input));
let response: any;
if (request.operation === "get_node") { response = nodeDefinition;} else if (request.operation === "run") { const context = request.context; const inputValue = context.inputs?.input || "";
response = { outputs: { output: inputValue.toUpperCase(), }, error: null, };} else { response = { error: "Unknown operation" };}
// Write response to stdoutconst outputBytes = encoder.encode(JSON.stringify(response));Javy.IO.writeSync(1, outputBytes);First compile TypeScript:
npx tsc --outDir distThen compile to WASM:
javy build -o my-node.wasm dist/index.jsOptimize with Javy
Section titled “Optimize with Javy”javy build -o my-node.wasm dist/index.js -OSize Comparison
Section titled “Size Comparison”| Compiler | Typical Size | Full TS Support |
|---|---|---|
| AssemblyScript | 10-100 KB | Limited |
| Javy | 500 KB - 2 MB | Full |
When to Use What
Section titled “When to Use What”| Use Case | Recommended |
|---|---|
| Simple transformations | AssemblyScript |
| Complex logic, npm packages | Javy |
| Smallest possible binary | AssemblyScript |
| Rapid prototyping | Javy |
Install
Section titled “Install”cp build/release.wasm ~/.flow-like/nodes/my-custom-node.wasmRelated
Section titled “Related”→ WASM Nodes Overview → Rust Template → Go Template → C/C++ Template