/** * `@nifrajs/web/fn-state` - the pending/error state around a server-function call. * * Calling a server function needs no binding: the client stub is `(input) => Promise`, and a * click handler can await it. What a component usually wants on top is whether a call is in flight, * what came back, and what went wrong - which is state, and state is the part every framework spells * differently. * * So the state machine lives here, framework-agnostic, and each adapter contributes only its * subscription primitive. Five copies of "is it pending" would drift, and the drift would be five * subtly different answers to the same question. * * ## Two behaviours worth stating * * **The last call wins.** Concurrent calls are not queued or cancelled; a response that is no longer * the newest is discarded rather than written. Without that, a slow first call landing after a fast * second one would overwrite fresh data with stale - the classic out-of-order bug, and one that only * appears under load, which is to say in production. * * **`call` still rejects.** The error is recorded for rendering AND the promise rejects, so `await` * behaves the way `await` should and a caller who wants to branch can. A caller who only renders from * state should attach a `.catch(() => {})`, the same as `useFetcher`'s `submit`. */ /** What a component renders from. */ export interface ServerFnState { /** A call is in flight. */ readonly pending: boolean; /** The most recent successful result, kept across a later pending call so the UI need not flicker. */ readonly data: Output | undefined; /** The most recent failure, cleared when a call succeeds. */ readonly error: Error | undefined; } /** A subscribable call site. One per component instance, created by the framework binding. */ export interface ServerFnStore { /** Register a listener; returns the unsubscribe. */ subscribe(listener: () => void): () => void; /** * The current state. The SAME object is returned until something changes, because * `useSyncExternalStore` and its equivalents compare by reference - a fresh object each call is an * infinite render loop. */ snapshot(): ServerFnState; /** Invoke the server function. Records state, and rejects on failure. */ call(input: Input): Promise; /** Back to idle, discarding data and error. */ reset(): void; } /** The idle state, shared so a server render and the first client render agree by reference. */ export declare function idleServerFnState(): ServerFnState; /** * Wrap a server function (or any async function) in a subscribable pending/error state. * * Framework-free by design: the adapters call this and subscribe with their own primitive. */ export declare function createServerFnStore(fn: (input: Input) => Promise | Output): ServerFnStore; //# sourceMappingURL=fn-state.d.ts.map