Embed Flowscript
Use the compiler and runtime in your own application: declare a host, replay data, step a live run and retain checkpoints.
An application embeds Flowscript by declaring a host, compiling a program against it and supplying the observations that program reads. The Rust library is flowscript-core; @flowscript/core exposes the compiler and evaluator to JavaScript and TypeScript through WebAssembly. The JavaScript package has no runtime dependencies and does not depend on ftw.trade.
Install and load#
There is currently no public package release or public download of @flowscript/core, and it is not published to the npm registry. Embedding it locally requires authorized access to the source repository. These docs describe the library that exists in that checkout; they do not offer a public installation URL.
With Node.js, npm, Rust with the wasm32-unknown-unknown target, and just installed, run this from the checkout's root:
just flowscript-packThe recipe installs the library's development dependencies, type-checks, builds the JavaScript and WebAssembly, runs the tests and packs the result. At the current package version, its output is flowscript/npm/flowscript-core-0.1.0.tgz; the version in flowscript/npm/package.json determines the filename. Packing does not publish the artifact.
Install the resulting tarball from your application directory. Replace the example checkout path with your actual source location:
npm install /path/to/authorized-checkout/flowscript/npm/flowscript-core-0.1.0.tgzIn Node, await loadFlowscript() loads the WebAssembly shipped beside that installed package. In a browser it fetches that file. A bundled application must serve the file itself and pass its bytes or a compiled WebAssembly.Module to loadFlowscript.
Compiler and evaluator calls are synchronous. Run them in a Web Worker when embedding in a browser so evaluation does not block the interface.
Declare your host#
The host determines what a program may read and call:
- Sources declare names, value types, key shapes and available intervals. The application supplies the actual samples.
- Capabilities are manifests declaring action names and arguments. Declaring an action does not implement or authorize an external transaction.
- Libraries supply named source imports. Integrations describe which implementations serve particular interfaces and markets.
An undeclared read or action is a compiler refusal. A host can declare sources without any capabilities, as the quickstart does.
Store the program source, its host and library configuration, and its compiled language version together. A program can pin its version with flowscript "0.3";; a host can record a version for source that omits that line. Restoring a checkpoint requires the same compiled program identity.
Use flowscript.resolveLibraries(source, host) to obtain and persist the exact transitive dependency sources, including reached standard libraries. The extension guide shows how to compose and test user libraries, implement custom tool consumers and record external model predictions.
Replay and checkpoint#
compile returns a program or located problems. replay takes a program and a history with len rows, input columns by source name, and optional evaluation timestamps in seconds. It returns per-bar metrics and decisions, or an error. Inspect unmodelled too: a successful result can identify behavior the selected evaluation configuration did not model.
For batches, { save: true } returns a checkpoint in snapshot. Pass that text as { resume: snapshot } with only the observations after it. This complete example uses illustrated hourly inputs and no actions:
import { loadFlowscript } from '@flowscript/core';
const flowscript = await loadFlowscript();
const host = {
sources: [
{ name: 'bars.close', type: 'number', keyed: true, intervals: ['1h'] },
],
capabilities: [],
};
const source = `flowscript "0.3";
uses { bars: "^1" }
const close = bars.close("DEMO-USD", "1h");
metric("average", sma(close, 3));
`;
const compiled = flowscript.compile(source, host);
if (!compiled.ok) throw new Error(JSON.stringify(compiled.problems));
try {
const firstClose = Date.UTC(2026, 0, 1, 1) / 1000;
const first = flowscript.replay(compiled.program, {
len: 3,
series: { 'bars.close': new Float64Array([100, 102, 104]) },
time: new Float64Array([firstClose, firstClose + 3600, firstClose + 7200]),
}, { save: true });
if (!first.ok) throw new Error(first.error);
if (first.unmodelled.length) throw new Error(first.unmodelled.join('; '));
if (typeof first.snapshot !== 'string') throw new Error('No checkpoint returned');
// Your application persists this text with its input cursor and configuration.
const saved = first.snapshot;
const next = flowscript.replay(compiled.program, {
len: 2,
series: { 'bars.close': new Float64Array([106, 108]) },
time: new Float64Array([firstClose + 10800, firstClose + 14400]),
}, { resume: saved, save: true });
if (!next.ok) throw new Error(next.error);
if (next.unmodelled.length) throw new Error(next.unmodelled.join('; '));
if (typeof next.snapshot !== 'string') throw new Error('No checkpoint returned');
console.log(Array.from(next.metrics.average)); // [104, 106]
} finally {
flowscript.drop(compiled.program);
}Persist a new checkpoint and the cursor naming its consumed inputs as one application-level commit. Flowscript produces checkpoint text; it does not provide a database, subscription cursor or transaction around your storage.
For one bar at a time, flowscript.run(program) creates a run. run.step(values, time) consumes one completed bar; run.snapshot() returns a result containing checkpoint text. Restore with flowscript.run(program, { snapshot }), check each result and call run.drop() when finished. Supply the same paper-broker configuration when restoring a run that used one. Call flowscript.drop(program) after its runs have been dropped.
Each successful step returns structured metrics, text and decisions for your application to consume. Outputs and consumers shows a complete adapter that builds chart rows, unsent action records and application logs from those real results.
Preserve the boundary#
The application owns data collection, bar construction, units, ordering, freshness and durable input cursors. An omitted observation in run.step holds its prior value; null explicitly supplies an unknown observation. Choose that behavior deliberately for your feed.
An action decision records what the program requested. Sending it to a venue, protecting signing keys, enforcing trading permissions and reconciling the actual outcome are application responsibilities. The optional paper broker models explicitly configured orders; it is not a venue depth or partial-fill simulator.
Runtime refusals and unmodelled behavior must reach the caller. Do not replace them with fabricated values or silently restart evaluation. A WebAssembly trap invalidates existing program and run handles; compile explicitly and restore a validated checkpoint before continuing. Checkpoint identity mismatches are refused.
Read the language reference for the complete host contract, runtime semantics and diagnostics. For ftw.trade's own host, modules, accounts and execution workflow, use its platform guide.