Self-documenting pipelines
Every stage writes its own narrative as it runs — no post-processing, no
log parsing. chart.recorder(narrative()).run() tells you exactly what
happened and why.
Self-documenting pipelines
Every stage writes its own narrative as it runs — no post-processing, no
log parsing. chart.recorder(narrative()).run() tells you exactly what
happened and why.
Typed from the start
Define your state as a TypeScript interface. TypedScope<T> gives you
full type inference inside every stage — no casts, no getValue() as string.
Decision evidence capture
decide() and select() auto-capture which values matched which rules.
Narrative: “creditScore 750 gt 700 ✓ → chose approved.”
Composable by design
Subflows compose like functions. getSubtreeSnapshot() gives you
time-travel debugging at any depth. Works with MCP and OpenAPI out of the box.
import { flowChart, decide, narrative } from 'footprintjs';
// 1. Define your stateinterface OrderState { orderId: string; total: number; tier?: string; status?: string;}
// 2. Build your flowchartconst chart = flowChart<OrderState>('Validate', async (scope) => { scope.tier = scope.total > 100 ? 'premium' : 'standard';}, 'validate') .addDeciderFunction('Route', (scope) => { return decide(scope, [ { when: { tier: { eq: 'premium' } }, then: 'express', label: 'Premium order' }, ], 'standard'); }, 'route') .addFunctionBranch('express', 'ExpressShip', async (scope) => { scope.status = 'express-shipping'; }) .addFunctionBranch('standard', 'StandardShip', async (scope) => { scope.status = 'standard-shipping'; }) .setDefault('standard') .end() .build();
// 3. Run and observeconst rec = narrative();await chart.recorder(rec).run({ input: { orderId: 'ORD-001', total: 149 } });
console.log(rec.lines().join('\n'));// Stage 1: The process began with Validate.// Stage 2: Next, it moved on to Route.// [Condition]: It evaluated Rule 0 "Premium order": tier "premium" eq "premium" ✓, and chose express.// Stage 3: Next, it moved on to ExpressShip.