v0 · Developer Preview Frond is under active development. APIs may change between releases.

Lifecycle

A node has two layers of state. Readiness is whether the node has loaded data. Operations (action, refresh, args reconciliation) run on a ready node without changing its readiness. The runtime owns every transition; callers request them through the handle.

handle.read() returns the current state as a tagged union:

const read = runtime.client.node(ProfileNode, { userId: "u_42" }).read();
read._tag; // "Unwired" | "Idle" | "Pending" | "Ready" | "Error"

States

_tagMeaningHas result
UnwiredNot in the graph.No
IdleWired, no readiness attempt in flight, no ready data.No
PendingReadiness attempt in flight.No
ReadyResult and deps available for consumer reads.Yes
ErrorReadiness attempt failed.No

Idle, Pending, Ready, and Error also carry operation, busy, and operationFailure. Ready carries resultValidity.

Internal states such as Booting, Expired, Invalid, and Unavailable are kept on raw diagnostic surfaces: unsafe reads, snapshots, diagnostics, tests, and devtools. Product code should use the smaller public read union above.

Note: A result of undefined is valid data. Readiness is determined by the state tag, not by whether result is present.

Readiness

ensureReady() crosses the readiness barrier. It resolves with the ready read when the node becomes Ready, and rejects when readiness fails.

await handle.ensureReady();
Idle      + ensureReady -> Pending -> success: Ready
                                   -> failure: Error
Pending   + ensureReady -> joins the in-flight attempt
Error     + ensureReady -> Pending (retry)

Readiness waits for dependency readiness first. A node cannot become Ready until every node it depends on is Ready. If a dependency fails, the dependent enters Error with that failure. See Dependencies.

Operations

Operations run against a Ready node and keep the current result visible while they run. While an operation runs, busy is true and operation is { _tag: "Running", kind }.

OperationTriggerKind
Actionhandle.runAction(action, input)"action"
Refreshhandle.refresh()"refresh"
Args reconciliationhandle.updateArgs(nextArgs)"args"
Ready + operation -> running (result stays visible)
                  -> success: Ready with committed result
                  -> failure: Ready with operationFailure

Operation failure is not readiness failure. A failed action or refresh leaves the node Ready with operationFailure set; it does not move the node to Error.

refresh() requires displayable ready data. It returns a Failure result when the node is idle, pending, errored, invalid, or expired. On failure it rolls back to the previous result. A runtime-managed action result commit is applied only when the action succeeds; direct MobX mutations inside a failing action are author-owned.

updateArgs() reconciles args only when they resolve to the same node id. Args that change the id are rejected; request the other node instead. See Identity and keys.

Expiry

Result validity is Current, Stale, or Expired, set by the node’s validity policy.

ValidityMeaning
CurrentWithin the fresh window.
StalePast the stale threshold, still displayable.
ExpiredPast the expiry threshold; not exposed as an ordinary result.

Stale data still reads as Ready. Expired is a validity state, not a renderable Ready result. Expired data is hidden from ordinary reads and re-acquires when an active consumer needs the node. Expiry does not remove the node from the graph. See Result validity.

Release and eviction

ActionMethodEffect
Releasehandle.releaseResources(reason)Stops resources, runs disposers, returns to Idle. Keeps graph wiring.
Evictionhandle.evict(mode?, reason?)Interrupts active work, cleans up, removes the graph record and edges.

Release keeps the node in the graph; eviction removes it. See Eviction and release.

Observing in React

useNodeState exposes the same fields for render:

const state = FrondReact.useNodeState(ProfileNode, { userId });
state.busy;            // operation in flight
state.operation;       // { _tag: "Idle" } | { _tag: "Running", kind }
state.operationFailure; // last operation failure, if any
state.resultValidity;  // { _tag: "Current" | "Stale" }

See Wire to React.


Next: Dependencies — how readiness flows across edges.