web
js/ui/web.ts
fino:ui/web — server-driven HTML and semantic UI for
fino:net/http/app.
This module renders fino:ui VNodes into real HTML pages and handles form
actions by rehydrating durable view snapshots. It is intentionally
hypermedia-first: forms keep real action and method attributes, enhanced
requests receive short-lived JSON UI streams, and plain browser submits use
POST-redirect-GET.
Every SSE response uses the same JSON ui events and host-neutral VNode
structure. A client can open a page route directly as an EventSource and
receive the current snapshot before subsequent updates. Component type names
are routed by the client through its own host adapter. The server owns view
state and action execution, but it does not register platform component
implementations or receive client-local interaction state.
import { App } from 'fino:net/http/app';
import { h, Signal } from 'fino:ui';
import { page, view, webUI } from 'fino:ui/web';
import { memoryStore } from 'fino:store';
const app = new App();
const ui = app.layer(webUI({ store: memoryStore(), secret: 'dev-secret' }));
ui.get('/').handle(page(() => h('main', null, 'Hello')));Interfaces
interface ViewActionContext {
Context supplied to a server-driven view action.
Properties
state: StateRecord
Mutable signals restored from the durable snapshot.
snapshot: ViewSnapshot
Snapshot from which this action started.
http: HttpContext
HTTP request context for the action.
Methods
checkpoint(): Promise<void>
Persist the current signals and publish a live render before the action finishes. Await checkpoints to preserve render order.
interface PortableActionRef {
Serializable action descriptor embedded in component props.
Send these fields back in a PortableActionRequest; do not construct action
URLs or revision tokens in the client.
Properties
action: string
Action name within the mounted view.
url: string
Relative HTTP endpoint for the action request.
view: string
Mounted view instance id.
revision: number
Snapshot revision from which the action was rendered.
request: string
Single-use request nonce for replay protection.
confirm?: string
Message a client should confirm before sending the request.
interface PortableActionRequest {
JSON body posted by a client to a PortableActionRef.url.
Properties
version: 1
UI protocol version. Version 1 is currently supported.
view: string
Mounted view instance id from the action descriptor.
revision: number
Snapshot revision from the action descriptor.
request: string
Request nonce from the action descriptor.
input?: Record<string, PortableValue>
Optional JSON object validated by the action's input schema.
interface PortableRenderEvent {
Complete semantic render of one mounted view.
Clients reconcile tree against the previous tree using component names and
keys. The server does not keep or receive a registry of client
implementations.
Properties
version: 1
UI protocol version.
kind: 'render'
Render event discriminator.
view: string
Stable server view definition id.
viewId: string
Mounted view instance id.
revision: number
Monotonic snapshot revision, also sent as the SSE event id.
tree: PortableVNode
Host-neutral component tree.
interface WebUIOptions {
Properties
store: AtomicStore
Generic atomic store used for durable view snapshots.
secret: string
Secret used to seal CSRF tokens and sealed embedded state.
ttlMs?: number
Snapshot lifetime in milliseconds. Defaults to one hour.
maxActionBytes?: number
Maximum JSON action body size in bytes. Defaults to 64 KiB.
sweepIntervalMs?: number | false
Minimum time between opportunistic snapshot sweeps.
Sweeps run before requests handled by this middleware. The default is one
minute. Set to false to operate cleanup from an external scheduler, or
0 to sweep on every request (primarily useful in tests).
interface ViewDefinition {
Definition passed to view().
Properties
id: string
Stable id used in action URLs and stored snapshots.
state: () => StateRecord
Factory called for each render or action event to create fresh signals.
embed?: EmbedSpec[]
Signal keys carried in HTML instead of the snapshot.
actions?: Record<
string,
{ handler: ActionHandler; input?: JsonSchema; stale?: 'reject' | 'rebase'; confirm?: string }
>
Server actions addressable from rendered forms.
derived?: string[]
Signal keys projected from an authoritative external store.
Derived signals are rendered but never written to the view snapshot, so a
store that already owns the data stays the only durable copy. Seed them
through derive rather than expecting hydration to restore them.
derive?: (args: {
state: StateRecord;
http: HttpContext;
snapshot: ViewSnapshot;
}) => void | Promise<void>
Populate derived signals before a render web.ts owns.
This runs on the action and live-stream paths, which are already async. The
initial mount() render is synchronous, so a caller that mounts a view with
derived signals must seed them before calling mount().
render: ViewRender
Render function for the view's current state.
Types
type PortableUIEvent =
| PortableRenderEvent
| { version: 1; kind: 'heartbeat' }
| { version: 1; kind: 'navigate'; url: string; replace: boolean }
| { version: 1; kind: 'error'; code: string; recoverable: boolean }
| { version: 1; kind: 'close' }
Event carried under the SSE ui event name.
A live stream starts with render (or navigate when its view expired).
render replaces the current semantic tree, heartbeat can confirm a live
stream, navigate requests a page transition, error reports a safe
protocol failure, and close ends a short-lived action or error stream.
Classes
class ViewActionError extends Error {
Action failure that carries a stable, publicly safe error code.
An action handler throws this when the caller should learn why the request
failed. Any other thrown value is reported as action_failed with its details
published only to the internal diagnostics topic, so a handler must opt in
before anything reaches the client.
import { ViewActionError } from 'fino:ui/web';
throw new ViewActionError('flow_stale_step', { status: 409, recoverable: true });Readonly Properties
readonly code: string
Stable code sent to clients as the SSE error code.
readonly status: number
HTTP status for the action response.
readonly recoverable: boolean
Whether a client may retry after resynchronizing.
Constructors
constructor(
code: string,
options: { status?: number; recoverable?: boolean; message?: string } = {},
)
Functions
function view(def: ViewDefinition): ServerView
Create a server-driven view definition.
function clientScriptPath(): string
Return the content-hashed browser runtime path served by webUI().
function webUI(options: WebUIOptions): LayerMiddleware
Middleware that installs server-driven UI handling for an App.
function page(render: (ctx: HttpContext) => VNode): Handler
Create a page route handler that returns HTML or a live JSON UI stream.