state

js/ui/web/state.ts

fino:ui/web/state — view-snapshot codecs over a generic atomic store.

Applications pass the same AtomicStore used by other subsystems. These functions own only the UI record layout: one current head per viewId, retained history, JSON validation, optimistic saves, and expiry sweeps. The store provider owns memory, persistence, serialization, and lifecycle.

Snapshot values are cloned at this domain boundary, so request-local mutation cannot leak through providers such as memoryStore() that retain values by reference. saveViewState() can require the current snapshot version and throws ViewVersionConflictError on mismatch.

import { sqliteStore } from 'fino:store';
import { loadViewState, saveViewState } from 'fino:ui/web/state';

const store = await sqliteStore({ path: './ui.db' });
await saveViewState(store, {
  viewId: 'todos-1', view: 'todos', version: 0,
  data: { items: [] }, regions: {}, applied: [],
  createdAt: Date.now(), updatedAt: Date.now(), expiresAt: Date.now() + 60_000,
});
const head = await loadViewState(store, 'todos-1');

Interfaces

interface ViewSnapshot {

Durable state for one mounted view instance.

Properties

viewId: string

Random or keyed view instance id embedded in forms and live channels.

view: string

Stable view definition id.

version: number

Monotonic application-level version and SSE event id.

sessionId?: string

Optional owning browser session id.

data: Record<string, unknown>

JSON-serializable server-owned signal values.

regions: Record<string, string>

Last-rendered HTML hashes keyed by region element id.

applied: Array<{ rid: string; action: string }>

Recent action nonces used to avoid double-submit replays.

createdAt: number

Creation time in Unix milliseconds.

updatedAt: number

Last update time in Unix milliseconds.

expiresAt: number

Expiration time in Unix milliseconds.

Classes

class ViewVersionConflictError extends Error {

Error raised when a guarded snapshot save observes another version.

Constructors

constructor(viewId: string, expected: number, actual: number | null)

Create a conflict describing expected and observed application versions.

Functions

async function loadViewState( store: AtomicStore, viewId: string, ): Promise<ViewSnapshot | null>

Load a detached view head, or null when it is absent.

async function saveViewState( store: AtomicStore, snapshot: ViewSnapshot, options: { expectVersion?: number } = {}, ): Promise<void>

Atomically replace a view head and append the same snapshot to history.

async function viewStateHistory( store: AtomicStore, viewId: string, options: { limit?: number } = {}, ): Promise<ViewSnapshot[]>

Return retained snapshots newest first, optionally limited.

async function deleteViewState(store: AtomicStore, viewId: string): Promise<void>

Delete a view head and all retained history.

async function sweepViewState( store: AtomicStore, now: number = Date.now(), ): Promise<number>

Delete expired view heads and return the number removed.