Skip to content

Pipeline editor

The pipeline editor is the large code area in the centre of the FlowPad workspace. It is the <pipeline-editor> custom element registered as PipelineEditorShell (projects/matrix-3/packages/flowpad/src/components/PipelineEditor.ts:201-327). Its actor — PipelineEditor — owns the current code string and the current mode (flow or lisp).

Two modes, auto-detected

The editor decides which language you typed by looking at the first non-comment line (PipelineEditor.ts:259-282):

ts
const trimmed = code.trim();
const withoutComments = trimmed
  .split('\n')
  .map(line => line.trim())
  .filter(line => !line.startsWith(';'))
  .join('\n')
  .trim();
const isLisp = withoutComments.startsWith('(');
const newMode = isLisp ? 'lisp' : 'flow';

The header label flips between JavaScript (Flow DSL) and LISP, and the editor border changes colour: blue (var(--mx-color-primary)) for Flow, green (var(--mx-color-success)) for LISP.

Flow DSL

Fluent JavaScript-like syntax built around flow():

js
flow()
  .fromValue({ table: 'products', limit: 10 })
  .viaRemote('./db', 'Query')
  .mapLocal(rows => rows.filter(x => x.price > 50))
  .mapLocal(rows => rows.map(x => ({ name: x.name, price: x.price })))

Each chained call adds one stage to the execution plan:

CallStage kind
.fromValue(seed)source — provides the initial value
.viaRemote(mount, op)remote-rr — request/reply against an actor
.mapLocal(fn)local-map — pure transform inside the page

./db and ./scheme are relative addresses; they resolve to the current authority root's db / scheme mount. $TARGET and $CATALOG_TARGET are placeholder tokens — the runtime substitutes the page's targetMount (default: FLOWPAD-AUTHORITY/system.catalog).

LISP / Scheme

Anything beginning with ( is treated as a LISP expression. The editor wraps it for the VLM (Virtual LISP Machine) before executing:

js
const wrapped = `flow()
  .fromValue({ "eval": null, "expr": "${escapedLispCode}" })
  .viaRemote("${targetMount}", "eval")`;

(PipelineEditor.ts:184-198.) Default target is ./vlm-eval.

The DSL's eval accepts both quoted S-expressions and Scheme-syntax source strings. See dsl/examples.ts for ~80 LISP examples covering arithmetic, lambda/letrec, pattern call/introspect, sandboxing, and live capability mint/revoke.

Run dispatch

Press Ctrl+Enter anywhere in the textarea, or click ▶ RUN in the toolbar above. Both routes call into the shell's _runCode handler:

ts
private _runCode(code: string) {
  this.sendToSelf('requestRun', {
    code,
    mode: this._currentMode,
  });
}

requestRun is one of the editor actor's accepted ops; the actor re-emits it as a runRequested event that the parent shell forwards to FlowpadApp's runFromCode. FlowpadApp then:

  1. Compiles the source to a Canonical IR (compileSourceToCanonicalIR, runtime/queryPipeline.ts).
  2. Emits flowStarted with a generated flowId.
  3. Emits executionPlanned with { language, irKind, adapterPath } so the status pane can render the plan.
  4. Calls executeCanonicalIR(executionContext, ir) against a derived _runner context.
  5. Emits flowCompleted (or flowError) with the final result, which ResultsPanel then turns into a result child.

The executor enforces per-stage timeouts (FlowpadApp.ts:336-340):

Address shapeTimeout
Cross-root (AUTHORITY/mount)30000 ms
Mount contains vlm10000 ms
Anything else5000 ms

Loading examples

Five built-in examples are coded into FlowpadApp and listed first in any introspection (FlowpadApp.ts:131-203): Basic Query, Filter & Map, Query Tree, Scheme Eval, Security Policy.

The much larger gallery comes from two places:

  • dsl/examples.ts — ~85 named DSL examples.
  • src/projects/<id>/manifest.json — six bundled "projects" (database, federation, lisp, security, serialization, vlm), each with its own examples.

Click an example in the sidebar — the FlowpadSidebar emits exampleSelected with the example's endpointMount and source code. The shell forwards that to the editor as setCode, which fills the textarea and triggers mode detection.

Keyboard surface

KeyAction
Ctrl+EnterRun the current code
Type any textSaves to the actor as setCode
Click an exampleReplaces the editor contents
Click Clear in the toolbarEmpties the textarea (sends clear op)

Common DSL recipes

Pulled directly from dsl/examples.ts:

js
// Query and project
flow()
  .fromValue({ table: 'products', limit: 20 })
  .viaRemote('./db', 'Query')
  .mapLocal(rows => rows.filter(x => x.price > 75))
  .mapLocal(rows => rows.sort((a, b) => b.price - a.price))

// Introspect a target component (whatever $TARGET points to)
flow()
  .fromValue({})
  .viaRemote('$TARGET', 'introspect')

// Apply a theme by setting design tokens
flow()
  .fromValue({ tokens: { 'color-surface': '#0f172a', 'color-primary': '#3b82f6' } })
  .viaRemote('flowpad-page', '$tokens')

See also

Source: projects/matrix-3/packages/flowpad/src/components/PipelineEditor.ts and projects/matrix-3/packages/flowpad/src/runtime/queryPipeline.ts.