WASM Workflows
Run Blazen workflows entirely in WebAssembly
Overview
Blazen workflows run natively inside the WASM module. Steps, events, and the context store all execute locally — no server round-trips for orchestration. Step handlers are plain JavaScript async functions that the WASM runtime calls back into.
Creating a workflow
import init, { Workflow, ChatMessage, CompletionModel } from '@blazen/sdk';
await init();
const wf = new Workflow('summarizer');
wf.addStep('fetch_text', ['blazen::StartEvent'], async (event, ctx) => {
ctx.set('source', event.url);
const text = await fetch(event.url).then(r => r.text());
return { type: 'SummarizeEvent', text };
});
wf.addStep('summarize', ['SummarizeEvent'], async (event, ctx) => {
const model = CompletionModel.openrouter(event.apiKey);
const response = await model.complete([
ChatMessage.system('Summarize the following text in 2-3 sentences.'),
ChatMessage.user(event.text),
]);
return {
type: 'blazen::StopEvent',
result: { summary: response.content },
};
});
const result = await wf.run({ url: 'https://example.com/article', apiKey: 'your-key' });
console.log(result.data.summary);
Event-driven architecture
Events are plain objects with a type field. The WASM event router dispatches each event to the step whose eventTypes list includes that type — identical to the Rust and Node.js SDKs.
Built-in event types:
"blazen::StartEvent"— emitted when the workflow begins. The object passed towf.run()is merged onto it."blazen::StopEvent"— returning this from a step ends the workflow. Attach your output to theresultproperty.
Context
The ctx parameter is a WasmContext instance — a real object with methods for sharing state, emitting events, and inspecting the current run. All methods are synchronous (unlike the Node.js SDK, which is async).
Storing and retrieving values
wf.addStep('store', ['blazen::StartEvent'], async (event, ctx) => {
ctx.set('user', event.name);
ctx.set('count', 42);
ctx.set('tags', ['intro', 'demo']);
return { type: 'NextEvent' };
});
wf.addStep('read', ['NextEvent'], async (event, ctx) => {
const user = ctx.get('user'); // 'Alice'
const count = ctx.get('count'); // 42
const missing = ctx.get('nope'); // null
return { type: 'blazen::StopEvent', result: { user, count } };
});
ctx.set(key, value) auto-detects Uint8Array values and stores them as binary. Everything else is stored as-is. ctx.get(key) returns the original value, or null if the key is missing.
Values can be any StateValue:
type StateValue = string | number | boolean | null | Uint8Array | StateValue[] | { [key: string]: StateValue };
Binary data
For explicit binary storage, use ctx.setBytes() and ctx.getBytes():
wf.addStep('binary', ['blazen::StartEvent'], async (event, ctx) => {
const data = new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f]);
// Either approach works for storing binary:
ctx.set('via_set', data); // auto-detected as binary
ctx.setBytes('via_explicit', data); // explicit binary storage
// Both return Uint8Array on read:
const a = ctx.get('via_set'); // Uint8Array
const b = ctx.getBytes('via_explicit'); // Uint8Array | null
return { type: 'blazen::StopEvent', result: { ok: true } };
});
Emitting events
ctx.sendEvent(event) queues an event into the workflow’s event loop, allowing a step to trigger other steps mid-execution:
wf.addStep('kickoff', ['blazen::StartEvent'], async (event, ctx) => {
ctx.sendEvent({ type: 'SideTask', payload: 'extra work' });
return { type: 'MainTask' };
});
Run ID
Each workflow run is assigned a unique UUID v4. Access it with ctx.runId():
wf.addStep('log', ['blazen::StartEvent'], async (event, ctx) => {
console.log('Run:', ctx.runId());
return { type: 'blazen::StopEvent', result: {} };
});
Workflow name
The workflow name is available as a getter property:
console.log(ctx.workflowName); // 'summarizer'
Method summary
| Method | Return type | Description |
|---|---|---|
ctx.set(key, value) | void | Store a value; auto-detects Uint8Array for binary |
ctx.get(key) | StateValue | null | Retrieve a value, or null if missing |
ctx.setBytes(key, data) | void | Explicitly store binary data (Uint8Array) |
ctx.getBytes(key) | Uint8Array | null | Retrieve binary data |
ctx.sendEvent(event) | void | Queue an event into the workflow event loop |
ctx.writeEventToStream(event) | void | No-op in WASM (present for API compatibility) |
ctx.runId() | string | Unique UUID v4 for the current run |
ctx.workflowName | string | Getter property for the workflow name |
Streaming events
In the WASM SDK, ctx.writeEventToStream() is a no-op — it exists for API compatibility with the Node.js and Rust SDKs but does not emit events to an external stream. You can still use wf.runStreaming() to receive events routed via ctx.sendEvent():
wf.addStep('process', ['blazen::StartEvent'], async (event, ctx) => {
for (let i = 0; i < 5; i++) {
ctx.sendEvent({ type: 'Progress', step: i });
}
return { type: 'blazen::StopEvent', result: { done: true } };
});
const result = await wf.runStreaming({}, (event) => {
console.log(`Progress: step ${event.step}`);
});
Branching
Return an array of events to fan out to multiple steps:
wf.addStep('classify', ['blazen::StartEvent'], async (event, ctx) => {
return [
{ type: 'PositiveEvent', text: event.text },
{ type: 'NegativeEvent', text: event.text },
];
});
Timeouts
Set a maximum execution time with setTimeout():
wf.setTimeout(30); // 30 seconds
Next steps
- Add tool-calling agents to your workflows with the WASM Agent guide.
- Deploy workflows to the edge with the Edge Deployment guide.