On this pageFunctions
AsyncData
/**
* Builds the six-state `AsyncData` Schema for the given data and error
* Schemas (value-first). Put `schema` in your Model; use the returned
* constructors when you want ones typed to this instance's `A` and `E`.
*/
<A, AI, E, EI>(
dataSchema: Codec<A, AI>,
errorSchema: Codec<E, EI>
): AsyncDataSchema<A, AI, E, EI>/** Bare-value alias for `Failure({ error })`, mirroring `Result.fail`. */
<E, A = never>(error: E): AsyncData<A, E>/**
* Returns the data as an `Option`, spanning all three data-bearing states:
* `Some` for `Success`, `Refreshing`, and `Stale`, `None` otherwise.
* Payload-named because it deliberately spans three tags.
*/
<A, E>(self: AsyncData<A, E>): Option<A>/**
* Returns the error as an `Option`, spanning both error-bearing states:
* `Some` for `Failure` and `Stale`, `None` otherwise. Symmetric with
* `getData`.
*/
<A, E>(self: AsyncData<A, E>): Option<E>/**
* Returns `true` when the state holds data: `Success`, `Refreshing`, or
* `Stale`.
*/
<A, E>(self: AsyncData<A, E>): boolean/**
* Returns `true` when the state carries an error: `Failure` or `Stale`.
* The error-channel twin of `hasData`.
*/
<A, E>(self: AsyncData<A, E>): boolean/**
* Type guard on `unknown`: checks that the value has a `_tag` belonging to
* the six `AsyncData` states.
*/
(input: unknown): unknown/** Returns `true` only for the `Failure` state, not `Stale`. Refinement. */
<A, E>(self: AsyncData<A, E>): unknown/**
* Returns `true` only for the empty `Loading` state, not `Refreshing`.
* Refinement.
*/
<A, E>(self: AsyncData<A, E>): unknown/**
* Returns `true` when a request is in flight: `Loading` or `Refreshing`
* only. `Stale` is not pending because its fetch already failed. Use for a
* spinner regardless of held data.
*/
<A, E>(self: AsyncData<A, E>): boolean/** Returns `true` only for the `Refreshing` state. Refinement. */
<A, E>(self: AsyncData<A, E>): unknown/**
* Returns `true` only for the `Stale` state: a failed refresh holding the
* last good data. Refinement.
*/
<A, E>(self: AsyncData<A, E>): unknown/**
* Returns `true` only for the `Success` state, not `Refreshing` or
* `Stale`. Refinement.
*/
<A, E>(self: AsyncData<A, E>): unknown/**
* The loaded-only revalidation transition: `Success` and `Stale` move to
* `Refreshing`, every other state yields `None`. Unlike
* `revalidateOrLoad` there is no cold-start `Loading`, so only caches
* that actually hold data revalidate; this is the generic refresher path
* after a mutation.
*/
<A, E>(self: AsyncData<A, E>): Option<AsyncData<A, E>>/**
* The revalidate-on-entry transition: revalidates loaded data and loads
* cold data. The data-bearing loaded states (`Success`, `Stale`) move to
* `Refreshing`; the cold no-data states (`Idle`, `Failure`) start a fresh
* `Loading`; the already-pending states (`Loading`, `Refreshing`) yield
* `None` so the request in flight is not restarted. `None` means no
* transition, and no load Command, is needed.
*/
<A, E>(self: AsyncData<A, E>): Option<AsyncData<A, E>>/** Bare-value alias for `Success({ data })`, mirroring `Result.succeed`. */
<A, E = never>(data: A): AsyncData<A, E>/**
* The six-state union for an asynchronously loaded value: `Idle | Loading
* | Refreshing(data) | Failure(error) | Stale(error, data) | Success(data)`.
* Value-first type parameters, matching `Result<A, E>` and `Exit<A, E>`.
*
* `Refreshing` and `Stale` make stale-while-revalidate and
* stale-on-failure first-class states: both hold the previous good `data`,
* `Refreshing` while a reload is in flight, `Stale` after a reload failed.
*
* Data-presence classification, used throughout the module:
*
* - "has data" states: `Success`, `Refreshing`, `Stale`.
* - "no data" states: `Idle`, `Loading`, `Failure`.
* - "pending" states (a request in flight): `Loading`, `Refreshing` only.
* `Stale` is not pending; its fetch already failed.
*
* Naming rule: predicates are tag-named (`isSuccess`, `isFailure`) but
* getters are payload-named (`getData`, `getError`) because `getData` spans
* three tags (`Success`, `Refreshing`, `Stale`) and `getError` spans two
* (`Failure`, `Stale`). Handler keys are tag-named where dispatch is per tag
* (`match`) and channel-named where one handler covers multiple tags
* (`matchData`, `mapBoth`).
*/
type AsyncData = Idle | Loading | Refreshing<A> | Failure<E> | Stale<A, E> | Success<A>/**
* The encoded (wire) form of a `AsyncData<A, E>` whose data encodes to
* `AI` and whose error encodes to `EI`.
*/
type AsyncDataEncoded = Readonly<{
_tag: "Idle"
}> | Readonly<{
_tag: "Loading"
}> | Readonly<{
_tag: "Refreshing"
data: AI
}> | Readonly<{
_tag: "Failure"
error: EI
}> | Readonly<{
_tag: "Stale"
data: AI
error: EI
}> | Readonly<{
_tag: "Success"
data: AI
}>/**
* What the `Schema` factory returns: the six-state Union codec to embed in
* a Model plus the Schema-bound constructors for the four parameterized
* states. Combinators are not part of this bag; they are module-level free
* functions over any `AsyncData<A, E>` value.
*/
type AsyncDataSchema = Readonly<{
Failure: (payload: Readonly<{
error: E
}>) => AsyncData<A, E>
Idle: typeof Idle
Loading: typeof Loading
Refreshing: (payload: Readonly<{
data: A
}>) => AsyncData<A, E>
schema: S.Codec<AsyncData<A, E>, AsyncDataEncoded<AI, EI>>
Stale: (payload: Readonly<{
data: A
error: E
}>) => AsyncData<A, E>
Success: (payload: Readonly<{
data: A
}>) => AsyncData<A, E>
}>/** The `Failure` state: request failed, showing the failure. */
type Failure = Readonly<{
_tag: "Failure"
error: E
}>/** The `Loading` state: first request in flight, no prior data. */
type Loading = Readonly<{
_tag: "Loading"
}>/** The `Refreshing` state: reloading while holding the previous good data. */
type Refreshing = Readonly<{
_tag: "Refreshing"
data: A
}>/**
* The `Stale` state: the last refresh failed, still holding the previous
* good data. The failed-refresh mirror of `Refreshing`.
*/
type Stale = Readonly<{
_tag: "Stale"
data: A
error: E
}>/** The `Success` state: request succeeded, data present. */
type Success = Readonly<{
_tag: "Success"
data: A
}>/**
* Constructs a `Failure` state carrying the error only. Plain value
* builder, generic in `E`; use the `Schema` factory's `Failure` for a
* Schema-bound constructor.
*/
const Failure: (payload: Readonly<{
error: E
}>) => AsyncData<never, E>/**
* Constructs the `Idle` state. A parameter-free callable Schema, so it can
* also serve as a Union member.
*/
const Idle: CallableTaggedStruct<"Idle", {}>/**
* Constructs the `Loading` state. A parameter-free callable Schema, so it
* can also serve as a Union member.
*/
const Loading: CallableTaggedStruct<"Loading", {}>/**
* Constructs a `Refreshing` state holding the previous good data. Plain
* value builder, generic in `A`; use the `Schema` factory's `Refreshing`
* for a Schema-bound constructor.
*/
const Refreshing: (payload: Readonly<{
data: A
}>) => AsyncData<A, never>/**
* Constructs a `Stale` state carrying both the refresh error and the
* last good data. Plain value builder, generic in `A` and `E`; use the
* `Schema` factory's `Stale` for a Schema-bound constructor.
*/
const Stale: (payload: Readonly<{
data: A
error: E
}>) => AsyncData<A, E>/**
* Constructs a `Success` state holding the data. Plain value builder,
* generic in `A`; use the `Schema` factory's `Success` for a Schema-bound
* constructor.
*/
const Success: (payload: Readonly<{
data: A
}>) => AsyncData<A, never>/**
* Combines an iterable or record of values under the `zipWith` lattice.
* An iterable collapses to an in-order array of data (empty input yields
* `Success({ data: [] })`); a record builds a struct (empty input yields
* `Success({ data: {} })`). The highest-ranked no-data state blocks, the
* leftmost `Failure` error wins, and any `Stale` in an all-data set makes
* the result `Stale` with the leftmost `Stale` error. All inputs must share
* one error type; unify with `mapError` first.
*
* The record form is the multi-resource screen:
* `all({ user, orders, prefs })` combines into one value whose data is the
* struct of all datas.
*/
const all: (inputs: Inputs) => [Inputs] extends [ReadonlyArray<AsyncData<any, any>>]
? AsyncData<{
[K in keyof Inputs]: [Inputs[K]] extends [AsyncData<infer A, any>]
? A
: never
}, Inputs[number] extends never
? never
: [Inputs[number]] extends [AsyncData<any, infer E>]
? E
: never>
: [Inputs] extends [Iterable<AsyncData<infer A, infer E>>]
? AsyncData<Array<A>, E>
: AsyncData<{
[K in keyof Inputs]: [Inputs[K]] extends [AsyncData<infer A, any>]
? A
: never
}, Inputs[keyof Inputs] extends never
? never
: [Inputs[keyof Inputs]] extends [AsyncData<any, infer E>]
? E
: never>/**
* Treats every data-bearing state (`Success`, `Refreshing`, `Stale`)
* exactly like `Success(data)`: returns `f(data)` unchanged, dropping the
* tag and any `Stale` error, so a caller can settle in-flight or stale data
* into `Success`. The no-data states pass through. Widens the error channel
* to `E | E2`, matching `Result.flatMap`.
*/
const flatMap: (f: (data: A) => AsyncData<B, E2>) => (self: AsyncData<A, E>) => AsyncData<B, E2 | E>/**
* Returns the data of any data-bearing state, or `onEmpty()` for the three
* no-data states. The fallback is a nullary thunk, mirroring
* `Option.getOrElse`, because the no-data states collapse to empty with no
* single payload.
*/
const getOrElse: (onEmpty: LazyArg<B>) => (self: AsyncData<A, E>) => B | A/**
* Maps the data of all three data-bearing states, preserving each tag so
* the `Refreshing` and `Stale` signals survive a pure transform. `Stale`
* maps only its `data`, keeping its `error`. The no-data states pass
* through unchanged.
*/
const map: (f: (data: A) => B) => (self: AsyncData<A, E>) => AsyncData<B, E>/**
* Maps both channels with channel-named handlers: `onData` spans `Success`,
* `Refreshing`, and `Stale`; `onError` spans `Failure` and `Stale`. For
* `Stale`, both handlers apply. Tags are preserved.
*/
const mapBoth: (handlers: {
onData: (data: A) => B
onError: (error: E) => E2
}) => (self: AsyncData<A, E>) => AsyncData<B, E2>/**
* Maps the error of the two error-bearing states: `Failure` and `Stale`
* transform (`Stale` keeps its `data`), everything else passes through.
* Use it to unify heterogeneous error types before a combine.
*/
const mapError: (f: (error: E) => E2) => (self: AsyncData<A, E>) => AsyncData<A, E2>/**
* Handles all six states exhaustively, passing each handler its unwrapped
* payload. `onStale` alone receives the whole `{ error, data }` payload
* object because `Stale` carries two fields. Use `matchData` when the view
* does not care which of the data-bearing states it is rendering.
*/
const match: (handlers: Readonly<{
onFailure: (error: E) => F
onIdle: Function.LazyArg<B>
onLoading: Function.LazyArg<C>
onRefreshing: (data: A) => D
onStale: (payload: Readonly<{
data: A
error: E
}>) => G
onSuccess: (data: A) => H
}>) => (self: AsyncData<A, E>) => B | C | D | F | G | H/**
* Collapses the six states to the three channels a view usually renders:
* `onData` spans the data-bearing states (`Success`, `Refreshing`,
* `Stale`), `onFailure` receives the `Failure` error, and `onEmpty` covers
* `Idle` and `Loading` together. A `Stale` renders through `onData` so its
* data stays on screen. Use `matchDataSplit` when `Idle` and `Loading`
* render differently, and `match` when the stale error or the `Refreshing`
* signal matters.
*/
const matchData: (handlers: {
onData: (data: A) => D
onEmpty: Function.LazyArg<B>
onFailure: (error: E) => C
}) => (self: AsyncData<A, E>) => B | C | D/**
* Like `matchData`, but the two cold states are split: `onIdle` handles
* `Idle` and `onLoading` handles `Loading`, for views that render nothing
* requested yet differently from a request in flight. `onData` and
* `onFailure` behave exactly as in `matchData`.
*/
const matchDataSplit: (handlers: {
onData: (data: A) => F
onFailure: (error: E) => D
onIdle: Function.LazyArg<B>
onLoading: Function.LazyArg<C>
}) => (self: AsyncData<A, E>) => B | C | D | F/**
* Returns `self` when it holds data (`Success`, `Refreshing`, or `Stale`),
* otherwise `that()`. The recovery and cache-fallback combinator: recover
* an `Idle`, `Loading`, or `Failure` into a secondary source without
* `match`.
*/
const orElse: (that: LazyArg<AsyncData<A, E>>) => (self: AsyncData<A, E>) => AsyncData<A, E>/**
* Folds a settled `Result` into the previous state, keeping the last good
* data: a `Result` success becomes `Success`, and a `Result` failure
* becomes `Stale({ error, data })` when `self` holds data, else a bare
* `Failure`. The primary way to fold a fetch back into the Model, and the
* reason a failed refresh keeps data on screen: without `settle`, every
* fetch needs a success arm and a failure arm that hand-assemble
* `Success`, `Stale`, and `Failure` from the previous state. When a
* failure should deliberately drop the previous data, match on the
* `Result` directly and build the `Failure` explicitly.
*/
const settle: (result: Result<A, E>) => (self: AsyncData<A, E>) => AsyncData<A, E>/**
* Combines two values under the two-tier lattice
* `Failure > Loading > Idle > Stale > Refreshing > Success`. If either
* input is a no-data state, the highest-ranked such state wins with no
* combination (`self`'s error wins when both are `Failure`). Otherwise both
* hold data: combine with `f` and tag the result with the highest-ranked
* data state present (`self`'s error wins when both are `Stale`).
*
* Because the combined value needs both inputs' data, a single no-data
* input collapses the whole combine to that state:
* `zipWith(Idle(), Success({ data }), f)` yields `Idle`.
*/
const zipWith: (that: AsyncData<B, E>, f: (selfData: A, thatData: B) => C) => (self: AsyncData<A, E>) => AsyncData<C, E>