flow
js/ui/web/flow.ts
fino:ui/web/flow — workflow-backed server-driven pages.
flowPage() connects bounded multi-step workflows to the portable UI engine.
A GET without ?run= starts the workflow and redirects to a URL containing
the run id. Later GETs render the persisted WorkflowState as HTML or as a
semantic JSON stream, and the signal the workflow is waiting on is delivered
through an ordinary view action, so it inherits session ownership, origin and
CSRF checks, replay protection, input validation, and the shared safe error
envelope rather than reimplementing them.
The workflow store stays the only durable copy of run state. The view snapshot holds just the run id and the step the rendered form was addressed to; the run itself is a derived signal, reloaded from the store for every render.
webUI() must be installed, because the action and live-stream paths belong
to it. Only a GET route is needed — the action posts back to the same path and
is handled by the middleware.
import { flowPage } from 'fino:ui/web/flow';
import { webUI } from 'fino:ui/web';
import { h } from 'fino:ui';
const ui = app.layer(webUI({ store: views, secret: 'dev-secret' }));
ui.get('/checkout').handle(
flowPage(checkout, {
store,
start: () => ({ cart: [] }),
render: (ctx, state, advance) =>
h('form', { action: advance }, h('button', null, state.waitingOn?.name ?? 'done')),
}),
);Types
type FlowAdvance = unknown
Action descriptor passed to a flow page's render function.
Use it as a form's action prop so the submit is wired to the flow action.
Interfaces
interface FlowPageOptions<In = unknown> {
Properties
store: Store
Durable workflow store containing runs for this page.
start: (ctx: HttpContext) => In | Promise<In>
Input factory used when a GET starts a new run.
render: (ctx: HttpContext, state: WorkflowState, advance: FlowAdvance) => VNode
Render the current workflow state.
advance is the action descriptor for delivering the awaited signal; give it
to a form's action prop. The signal payload is read from the form field
named after the awaited signal.
input?: JsonSchema
Optional schema validating the submitted signal payload object.
id?: string
View definition id. Defaults to fino:flow/<workflow id>.
Set this when one workflow backs more than one page, so each page gets its own view definition.
Functions
function flowPage<In, Out>(workflow: Workflow<In, Out>, opts: FlowPageOptions<In>): Handler
Create a route handler for a bounded workflow-backed page.