# raf-effect-atom

> Render-as-you-fetch for Effect Atom: start atoms from route loaders and stream them into the browser, typed failures intact

```ts
import {
  Hydration,
  Loader,
  RequestRegistry,
  useAtomResult,
  useHandleValue,
  HydrationBoundary,
  RegistryProvider,
  WaterfallDetection,
  useWaterfall,
} from "raf-effect-atom";
```

## Handle

Handles: what a loader or server component hands to the component that
reads an atom. The loader verbs and `useWaterfall` return them, and
`useAtomResult` reads one through the atom family that made it, so a
component can't start a request from an id it was given.

A handle is plain data, `{ args, key }`, so loader data and server
component props carry it as is.

### `Source`

<p class="api-meta"><span class="api-kind">type</span><span>models</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/handle.ts#L25">Source</a></p>

```ts
type Source =
  | Atom.Atom<AsyncResult.AsyncResult<any, any>>
  | ((...args: any) => Atom.Atom<AsyncResult.AsyncResult<any, any>>);
```

What the verbs start and `useAtomResult` reads: an atom family, a function
from arguments to an `AsyncResult` atom such as an `AtomRpc` query, or an
`AsyncResult` atom that takes no arguments.

### `ArgsOf`

<p class="api-meta"><span class="api-kind">type</span><span>utility types</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/handle.ts#L38">Source</a></p>

```ts
type ArgsOf<S> = S extends (...args: infer Args) => unknown ? Args : [];
```

The arguments a source takes: a family's parameters, or none for an atom.

### `SuccessOf`

<p class="api-meta"><span class="api-kind">type</span><span>utility types</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/handle.ts#L47">Source</a></p>

```ts
type SuccessOf<S> =
  ResultOf<S> extends AsyncResult.AsyncResult<infer A, infer _E>
    ? A
    : never;
```

The success type of a source's `AsyncResult`.

### `ErrorOf`

<p class="api-meta"><span class="api-kind">type</span><span>utility types</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/handle.ts#L57">Source</a></p>

```ts
type ErrorOf<S> =
  ResultOf<S> extends AsyncResult.AsyncResult<infer _A, infer E>
    ? E
    : never;
```

The error type of a source's `AsyncResult`.

### `HandleKey`

<p class="api-meta"><span class="api-kind">type</span><span>models</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/handle.ts#L79">Source</a></p>

```ts
type HandleKey<S> = string & {
  /** Type-only: ties the handle to its source. Never present at runtime. */
  readonly [SourceType]?: S;
};
```

A handle's serialization key, branded with its source. The brand sits on
a string, so types that map loader data to what survives serialization,
such as React Router's, keep it as is.

### `Handle`

<p class="api-meta"><span class="api-kind">interface</span><span>models</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/handle.ts#L100">Source</a></p>

```ts
interface Handle<S> {
  readonly args: ArgsOf<S>;
  /**
   * `true` when `Loader.prefetchInBrowser` ran on the server and left the
   * atom to the browser: the browser's read starts it.
   */
  readonly inBrowser?: true;
  /** The atom's serialization key; `undefined` for an atom without one. */
  readonly key: HandleKey<S> | undefined;
}
```

An atom a loader started: its source's arguments, and the atom's
serialization key. Its type names its source, so `useAtomResult` reads it
only through a source with the same arguments and success type.

**Details**

`Loader.prefetch` and `useWaterfall` return it, without waiting: the
component that reads it suspends until the result settles.

**See also**

- [`LoadedHandle`](https://raf-effect-atom.katt.dev/docs/api-raf-effect-atom.md#LoadedHandle) for the handle `ensure` and `fetch` return

### `LoadedHandle`

<p class="api-meta"><span class="api-kind">interface</span><span>models</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/handle.ts#L125">Source</a></p>

```ts
interface LoadedHandle<S> extends Handle<S> {
  readonly loaded: true;
}
```

A handle from `Loader.ensure` or `Loader.fetch`: they waited for the
result, so the component that reads it renders without suspending.

**Details**

A `LoadedHandle` fits a `Handle` prop, and a `Handle` doesn't fit a
`LoadedHandle` prop. In the loader that ran the verb, `Loader.result`
returns the result it waited for.

## `Hydration`

<p class="api-meta"><span class="api-kind">namespace</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/Hydration.ts#L1">Source</a></p>

Streams a server registry into the browser's, typed failures intact, and
hydrates it without requesting anything again.

`dehydrate` encodes the request registry's serializable atoms. Settled
results cross as values; results still loading cross as promises that
resolve when they settle, carried by the framework's own streaming.
`hydrate` applies settled values at once and holds each streamed result
until its promise resolves, so `useAtomResult` waits for it instead of
starting the atom. `hydrated` keeps a hydrated value on the atom's first
read instead of running its effect again.

Typed failures survive because a serializable `AsyncResult` atom, such as
an `AtomRpc` query with a `serializationKey`, encodes its `Cause` with the
procedure's error Schemas: a server-side `IssueNotFound` decodes as an
`IssueNotFound` in the browser.

```ts
import { Hydration } from "raf-effect-atom";
```

### `Hydration.hydrated`

<p class="api-meta"><span class="api-kind">const</span><span>combinators</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/Hydration.ts#L69">Source</a></p>

```ts
const hydrated: <Self extends Atom.Atom<unknown>>(atom: Self) => Self;
```

Returns a serializable atom that keeps its hydrated value on its first
read, instead of running its effect again.

**When to use**

The loader verbs and `useAtomResult` already read through it. Use it when
reading a hydrated atom with `@effect/atom-react`'s own hooks.

**Details**

The copy shares the atom's serialization key, so both read the same
registry node. It sets `skipInitialRun` on the atom's `Serializable`
metadata, and is memoized per atom, so every call returns the same copy.
An atom that isn't serializable, or already skips its initial run, is
returned as it is.

**Gotchas**

The copy keeps the hydrated value only for the first read: a refresh, or a
change to a dependency, runs the effect as usual. An `Initial` value never
skips the effect, which is why a result the server was still loading
crosses as a promise that `useAtomResult` waits for.

**Example**

```ts
import { useAtomValue } from "@effect/atom-react"
import { Hydration } from "raf-effect-atom"

const result = useAtomValue(Hydration.hydrated(issue(1)))
```

**See also**

- `Atom.serializable` for the metadata it copies

### `Hydration.dehydrate`

<p class="api-meta"><span class="api-kind">const</span><span>dehydration</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/Hydration.ts#L119">Source</a></p>

```ts
const dehydrate: (
  registry: AtomRegistry.AtomRegistry,
) => Array<EffectHydration.DehydratedAtom>;
```

Encodes a request registry's serializable atoms for the browser.

**Details**

`Hydration.dehydrate` with `encodeInitialAs: "promise"`: a settled result
crosses as its encoded value, and a result still loading crosses as a
promise of its encoded value, which resolves when it settles. The
framework's serializer carries the promises (TanStack Router's dehydrated
state, React's server component props, React Router's Single Fetch).

**Gotchas**

Only atoms in the registry when it's called are included, so call it once
the loaders have started theirs.

**See also**

- [`hydrate`](https://raf-effect-atom.katt.dev/docs/api-raf-effect-atom.md#Hydration.hydrate) for the browser side

### `Hydration.hydrate`

<p class="api-meta"><span class="api-kind">const</span><span>hydration</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/Hydration.ts#L149">Source</a></p>

```ts
const hydrate: (
  registry: AtomRegistry.AtomRegistry,
  state: Iterable<EffectHydration.DehydratedAtom>,
) => void;
```

Applies dehydrated state to the browser's registry.

**When to use**

Use outside React, such as in a router's `hydrate` hook, before the app
renders. Inside React, use `HydrationBoundary`.

**Details**

Settled values go through `Hydration.hydrate`. A streamed result is held
until its promise resolves, then hydrated; until then `useAtomResult`
suspends on it instead of starting the atom, which would request it again.
A streamed result that can't be decoded, or whose promise rejects, is
dropped, and the atom runs in the browser.

**See also**

- [`dehydrate`](https://raf-effect-atom.katt.dev/docs/api-raf-effect-atom.md#Hydration.dehydrate) for the server side

## `Loader`

<p class="api-meta"><span class="api-kind">namespace</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/Loader.ts#L1">Source</a></p>

Starts atoms from route loaders, before anything renders, so nothing
waterfalls.

Each verb takes a source, an atom family or an `AsyncResult` atom, with its
arguments. It starts the atom in the registry, takes a lease on it, so the
registry's idle TTL can't evict it before the route renders and
`useAtomResult` takes it over, and returns its handle for the component
that reads it:

| Verb       | Starts the atom                 | Waits                         | Has a value already   |
| ---------- | ------------------------------- | ----------------------------- | --------------------- |
| `prefetch` | if it isn't in the registry     | never                         | leaves it alone       |
| `ensure`   | if it isn't in the registry     | only while it's `Initial`     | returns it at once    |
| `fetch`    | if it isn't, or refreshes it    | until the new result settles  | refreshes, then waits |

`prefetchInBrowser` is `prefetch` in the browser. On the server, it
starts nothing, and the browser's read starts the atom.

Typed failures don't reject: `ensure` and `fetch` settle on them, and
`result` returns the settled `AsyncResult`, so a loader can redirect on a
tag and otherwise let a boundary render the failure. Only defects and
interruptions reject.

The verbs are Effects that need the `AtomRegistry`; `make` gives the same
verbs as Promises over one registry, for loaders that are async functions.

```ts
import { Loader } from "raf-effect-atom";
```

### `Loader.SettledResult`

<p class="api-meta"><span class="api-kind">type</span><span>models</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/Loader.ts#L63">Source</a></p>

```ts
type SettledResult<A, E> =
  | AsyncResult.Failure<A, E>
  | AsyncResult.Success<A, E>;
```

A settled `AsyncResult`: what `ensure` and `fetch` return.

### `Loader.LeaseTTL`

<p class="api-meta"><span class="api-kind">const</span><span>references</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/Loader.ts#L83">Source</a></p>

```ts
const LeaseTTL: Context.Reference<Duration.Duration>;
```

How long a loader's lease keeps an atom mounted when no component takes it
over. Default 30 seconds.

**Details**

A lease ends when `useAtomResult` or `useHandleValue` mounts the atom, or
when this time has passed, after which the registry's idle TTL applies as
usual. While a component reads the atom, a verb takes no lease on it, so the
atom goes idle when the last reader unmounts. On a request registry, leases
last until the registry is disposed.

### `Loader.prefetch`

<p class="api-meta"><span class="api-kind">const</span><span>verbs</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/Loader.ts#L204">Source</a></p>

```ts
const prefetch: <S extends Source>(
  source: S,
  ...args: ArgsOf<S>
) => Effect.Effect<Handle<S>, never, AtomRegistry.AtomRegistry>;
```

Starts a source's atom without waiting for it, and returns its handle.

**When to use**

Use for data the route needs but its loader doesn't: it streams in while
the route renders.

**Details**

- Starts the atom if it isn't in the registry, and leaves it alone if it
  is. Takes a lease either way, unless a mounted component reads the
  atom. The component that reads the handle
  suspends until the result settles. On a request registry whose async
  seed is still running, it waits for the seed first.
- A derived atom that isn't serializable starts the first serializable
  atom its `initialValueTarget` chain points at, the one `useAtomResult`
  reads it through: the lease, the handle's key and what the server sends
  are that atom's. `ensure` and `fetch` wait for the derived atom's own
  result.

**Gotchas**

The server sends a result still loading as a promise only when the atom
it sends holds an `AsyncResult`. A derived atom whose serializable atom
holds another value, such as an `Option` snapshot, crosses with the value
it has when the registry is dehydrated: point it at an `AsyncResult`
atom, or `ensure` it.

**Example**

```ts
import { Effect } from "effect"
import { Loader } from "raf-effect-atom"

const program = Loader.prefetch(commentPage, issueId, 0)
```

### `Loader.prefetchInBrowser`

<p class="api-meta"><span class="api-kind">const</span><span>verbs</span><span>since 0.2.6</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/Loader.ts#L256">Source</a></p>

```ts
const prefetchInBrowser: <S extends Source>(
  source: S,
  ...args: ArgsOf<S>
) => Effect.Effect<Handle<S>, never, AtomRegistry.AtomRegistry>;
```

Starts a source's atom in the browser, and returns its handle: on the
server, it starts nothing, and the browser's read starts the atom.

**When to use**

Use for data only the browser can load: an input the server never sees,
such as a token in the URL's fragment or a permission, or a request the
server can't make. The rest of the page still renders on the server.

**Details**

- In the browser, it's `prefetch`: a loader that runs there, such as on a
  client navigation, starts the atom and takes a lease.
- On a request registry, it returns a handle marked `inBrowser` without
  starting the atom, so the atom needs no serialization key. On the
  server, `useAtomResult` throws for that handle, so React renders the
  nearest Suspense boundary's fallback there and renders the boundary in
  the browser. There, the read starts the atom, and the waterfall check
  skips it, since a loader asked for it.

**Gotchas**

- Read the handle under a Suspense boundary: on the server, a read
  outside one fails the whole render. React reports the boundary it
  renders in the browser to `onRecoverableError`.
- On a page the server rendered, the request starts when the browser
  renders the read, after hydration's JavaScript has loaded: start the
  data the server can load with `prefetch` or `ensure`.

**Example**

```ts
import { Loader } from "raf-effect-atom"

const program = Loader.prefetchInBrowser(pushSubscription)
```

### `Loader.ensure`

<p class="api-meta"><span class="api-kind">const</span><span>verbs</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/Loader.ts#L314">Source</a></p>

```ts
const ensure: <S extends Source>(
  source: S,
  ...args: ArgsOf<S>
) => Effect.Effect<LoadedHandle<S>, never, AtomRegistry.AtomRegistry>;
```

Starts a source's atom, waits only while it has no result, and returns a
handle whose reader renders without suspending.

**When to use**

Use when the loader needs the result, such as to redirect on a tag, or
when the route shouldn't render without it.

**Details**

An atom with a result returns at once, without refetching. On a request
registry, it also waits out a refresh in flight, so the server never
renders a result that's about to change. A typed failure settles it like
a success: `result` returns it. A defect or an interruption fails the
Effect.

**Example**

```ts
import { Effect } from "effect"
import { AsyncResult } from "effect/reactivity"
import { Loader } from "raf-effect-atom"

const program = Effect.gen(function* () {
  const handle = yield* Loader.ensure(issue, 1)
  const result = Loader.result(handle)
  return AsyncResult.isSuccess(result) ? result.value.title : "missing"
})
```

### `Loader.fetch`

<p class="api-meta"><span class="api-kind">const</span><span>verbs</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/Loader.ts#L349">Source</a></p>

```ts
const fetch: <S extends Source>(
  source: S,
  ...args: ArgsOf<S>
) => Effect.Effect<LoadedHandle<S>, never, AtomRegistry.AtomRegistry>;
```

Starts a source's atom, or refreshes it if it has a result, waits for the
new result to settle, and returns a handle whose reader renders without
suspending.

**When to use**

Use when the loader must not see a result from before it ran, such as
after a mutation.

**Details**

Waits until the result settles and isn't refreshing. A typed failure
settles it like a success: `result` returns it. A defect or an
interruption fails the Effect.

### `Loader.result`

<p class="api-meta"><span class="api-kind">const</span><span>getters</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/Loader.ts#L401">Source</a></p>

```ts
const result: <S extends Source>(
  handle: LoadedHandle<S>,
) => SettledResult<SuccessOf<S>, ErrorOf<S>>;
```

Returns the settled result `ensure` or `fetch` waited for.

**When to use**

Use in the loader that ran the verb, to branch on the result: redirect on
a tag, or build another atom's arguments from it. Components read the
handle with `useAtomResult`, which follows the registry.

**Gotchas**

Only the handle object the verb returned has it: a handle that crossed
the network, as loader data or a server component's prop, is a copy, and
this throws for it.

**Example**

```ts
import { AsyncResult } from "effect/reactivity"
import { Loader } from "raf-effect-atom"

const handle = await atoms.ensure(issue, id)
const error = AsyncResult.error(Loader.result(handle))
```

### `Loader.Loader`

<p class="api-meta"><span class="api-kind">interface</span><span>models</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/Loader.ts#L422">Source</a></p>

```ts
interface Loader {
  /** {@link ensure}, as a Promise that rejects only on a defect. */
  readonly ensure: <S extends Source>(
    source: S,
    ...args: ArgsOf<S>
  ) => Promise<LoadedHandle<S>>;
  /** {@link fetch}, as a Promise that rejects only on a defect. */
  readonly fetch: <S extends Source>(
    source: S,
    ...args: ArgsOf<S>
  ) => Promise<LoadedHandle<S>>;
  /**
   * {@link prefetch}, run at once. On a request registry whose async seed is
   * still running, it returns the handle at once and starts the atom once
   * the seed resolves.
   */
  readonly prefetch: <S extends Source>(
    source: S,
    ...args: ArgsOf<S>
  ) => Handle<S>;
  /** {@link prefetchInBrowser}, run at once. */
  readonly prefetchInBrowser: <S extends Source>(
    source: S,
    ...args: ArgsOf<S>
  ) => Handle<S>;
  /**
   * Runs an Effect that needs the registry, such as the Effect verbs in an
   * `Effect.gen`, as a Promise that rejects with its squashed cause.
   * Aborting `signal`, or the one the verbs were made with, interrupts it,
   * and the Promise rejects with the abort reason.
   */
  readonly run: <A, E>(
    effect: Effect.Effect<A, E, AtomRegistry.AtomRegistry>,
    options?: {
      readonly signal?: AbortSignal | undefined;
    },
  ) => Promise<A>;
}
```

The loader verbs as Promises over one registry, as the framework
integrations put them on the router context.

### `Loader.MakeOptions`

<p class="api-meta"><span class="api-kind">interface</span><span>models</span><span>since 0.2.2</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/Loader.ts#L466">Source</a></p>

```ts
interface MakeOptions {
  /** How long a lease lasts in the browser. See {@link LeaseTTL}. */
  readonly leaseTTL?: Duration.Input | undefined;
  /**
   * The request's or the navigation's abort signal. Aborting it interrupts
   * the `ensure`, `fetch` and `run` calls still waiting, which reject with
   * its reason. The atoms they started keep loading.
   */
  readonly signal?: AbortSignal | undefined;
}
```

Options for `make`.

### `Loader.make`

<p class="api-meta"><span class="api-kind">const</span><span>constructors</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/Loader.ts#L525">Source</a></p>

```ts
const make: (
  registry: AtomRegistry.AtomRegistry,
  options?: MakeOptions,
) => Loader;
```

Creates the Promise verbs over one registry.

**When to use**

Use in a framework integration, in loaders that are async functions, or
in an event handler that starts an atom before the state change that
renders its reader, for a choice a click or a submit makes that doesn't
belong in the URL. In Effect code, use the verbs directly, and run
the Effect on the registry with `run`.

**Details**

With a `signal`, an aborted request or navigation stops waiting: `ensure`,
`fetch` and `run` reject with the signal's reason, and the atoms they
started keep loading. The React Router and Next.js integrations pass the
request's.

**Example**

```ts
import { Loader, RequestRegistry } from "raf-effect-atom"

const loader = Loader.make(RequestRegistry.make(), { signal: request.signal })
const comments = loader.prefetch(commentPage, 1, 0)
const issueHandle = await loader.ensure(issue, 1)
```

## `RequestRegistry`

<p class="api-meta"><span class="api-kind">namespace</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/RequestRegistry.ts#L1">Source</a></p>

A registry per server request.

`@effect/atom-react`'s default `RegistryContext` is one registry for the
whole module, which on a server would share one user's results with every
other request. Each framework integration makes a request registry with
`make`, provides it through `RegistryContext`, and dehydrates it into the
response.

```ts
import { RequestRegistry } from "raf-effect-atom";
```

### `RequestRegistry.Seed`

<p class="api-meta"><span class="api-kind">type</span><span>models</span><span>since 0.2.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/RequestRegistry.ts#L30">Source</a></p>

```ts
type Seed = Seeds.Seed;
```

Prepares a request registry before the loader verbs start atoms in it,
such as by setting an atom to the request's cookie.

### `RequestRegistry.InitialValues`

<p class="api-meta"><span class="api-kind">type</span><span>models</span><span>since 0.2.4</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/RequestRegistry.ts#L41">Source</a></p>

```ts
type InitialValues = Iterable<
  readonly [Atom.Atom<any>, any, Atom.InitialValueOptions?]
>;
```

Atoms paired with the values a registry starts with, as
`AtomRegistry.make`'s `initialValues` takes them: build each pair with
`Atom.initialValue`.

### `RequestRegistry.Options`

<p class="api-meta"><span class="api-kind">interface</span><span>models</span><span>since 0.2.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/RequestRegistry.ts#L53">Source</a></p>

```ts
interface Options {
  /**
   * Values the registry starts with, as `AtomRegistry.make`'s
   * `initialValues`. Unlike `registry.set` in a seed, they reach readable
   * atoms, such as one that provides a per-request service. The registry
   * keeps each of these atoms mounted until it's disposed, so the value
   * outlives the atoms that read it.
   */
  readonly initialValues?: InitialValues | undefined;
  /**
   * Runs on the new registry before any verb starts an atom in it. When it
   * returns a promise, `ensure` and `fetch` wait for it, and `prefetch`
   * returns its handle at once and starts the atom once it resolves.
   */
  readonly seed?: Seed | undefined;
}
```

Options for `make`.

### `RequestRegistry.make`

<p class="api-meta"><span class="api-kind">const</span><span>constructors</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/RequestRegistry.ts#L112">Source</a></p>

```ts
const make: (options?: Options) => AtomRegistry.AtomRegistry;
```

Creates the registry for one server request.

**Details**

- It's an ordinary `AtomRegistry`, marked as a request registry. On a
  request registry, `ensure` waits like `fetch` does, nothing revalidates
  in the background, and a loader's lease lasts until the registry is
  disposed. In development, the loader verbs refuse an atom that isn't
  serializable, because the browser would request it again.
- `initialValues` are values the request's atoms start with, set on each
  atom, or on the end of its `initialValueTarget` chain, before anything
  reads it. They reach atoms `registry.set` can't, such as a readable atom
  that provides the request's `fetch`, and each stays mounted until the
  registry is disposed.
- `seed` sets what the request's atoms need before they run, such as the
  page request's cookie for an RPC protocol that forwards it. It can be
  async, for request data only a promise gives, such as Next.js's
  `headers()`. It runs after `initialValues` are set.

**Gotchas**

With an async seed, a `prefetch` before it settles starts its atom later,
so `Hydration.dehydrate` would miss it: wait for `settled` first, as the
framework integrations do.

**Example**

```ts
import { Atom } from "effect/reactivity"
import { RequestRegistry } from "raf-effect-atom"

const registry = RequestRegistry.make({
  initialValues: [Atom.initialValue(requestFetch, request.fetch)],
  seed: (registry) => registry.set(requestCookie, request.headers.get("cookie") ?? undefined)
})
```

### `RequestRegistry.settled`

<p class="api-meta"><span class="api-kind">const</span><span>getters</span><span>since 0.2.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/RequestRegistry.ts#L148">Source</a></p>

```ts
const settled: (registry: AtomRegistry.AtomRegistry) => Promise<void>;
```

Waits until a request registry's seed has settled and every atom a verb
started while it ran has started.

**When to use**

Use before `Hydration.dehydrate` on a registry with an async seed, outside
the framework integrations, which call it for you.

**Details**

Resolves at once for a registry without a seed, or whose seed was
synchronous. Rejects with the seed's error.

### `RequestRegistry.layer`

<p class="api-meta"><span class="api-kind">const</span><span>layers</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/RequestRegistry.ts#L166">Source</a></p>

```ts
const layer: Layer.Layer<AtomRegistry.AtomRegistry>;
```

A layer that provides a request registry as the `AtomRegistry`, disposed
when the layer's scope closes.

**When to use**

Use when the request is handled in Effect, around the loaders' verbs and
`dehydrate`.

**See also**

- [`make`](https://raf-effect-atom.katt.dev/docs/api-raf-effect-atom.md#RequestRegistry.make) for the registry it provides

### `RequestRegistry.isRequestRegistry`

<p class="api-meta"><span class="api-kind">const</span><span>guards</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/RequestRegistry.ts#L181">Source</a></p>

```ts
const isRequestRegistry: (
  registry: AtomRegistry.AtomRegistry,
) => boolean;
```

Returns whether a registry was made by `make`, for one server request.

## Use atom result

The read hooks for atoms started by loaders and streamed from the server.

### `useAtomResult`

<p class="api-meta"><span class="api-kind">const</span><span>hooks</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/use-atom-result.ts#L167">Source</a></p>

```ts
const useAtomResult: <S extends Source>(
  source: S,
  handle: NoInfer<Handle<Readable<S>>>,
) => [ErrorOf<S>] extends [never]
  ? AsyncResult.Success<SuccessOf<S>, never>
  : SettledResult<SuccessOf<S>, ErrorOf<S>>;
```

Reads a handle through React Suspense, through the source that made it: a
result streamed from the server, a hydrated one, or one a loader started.

**When to use**

Use in the component a loader, a server component or `useWaterfall` gave
the handle to. The source can be the one the verb started, or one derived
from it whose atoms point at its atoms with `initialValueTarget`. To send
some failures to an error boundary, pass the result to the boundary's hook,
such as `useIssueBoundary(useAtomResult(issue, handle))`.

**Details**

- The atom is `source(...handle.args)`, or `source` itself for an atom.
  In development, the read throws when the atom it hydrates has another
  serialization key than the handle's: the handle came from another
  source.
- A handle from `ensure` or `fetch` renders without suspending, unless
  the registry dropped the result since; one from `prefetch` or
  `useWaterfall` suspends until the result settles. The registry is the
  source of truth either way: the read follows refreshes and mutations.
- A result the server was still loading when it dehydrated is awaited
  instead of started, so the browser doesn't request it a second time.
- The atom is read through `hydrated`, so a hydrated result is kept.
- Once the component mounts, the loader's lease ends and the component
  keeps the atom mounted. A loader that starts the atom again while it's
  mounted takes no lease, so the atom goes idle when the component unmounts.
- A derived atom is read through the first serializable atom its
  `initialValueTarget` chain points at: that one is awaited, hydrated and
  released, so the derived atom doesn't request it again.
- The settled result is returned, typed failures included. A defect is
  thrown to the error boundaries above. With no typed failures, it's typed
  as a success.
- While a failed atom refreshes, such as after a boundary's `reset`, the
  read suspends instead of returning the old failure again.

**Gotchas**

A read of an atom that nothing started, such as one whose loader result the
registry dropped, starts it, and its request waits for the render: a
request waterfall. In development, the read reports it as
`WaterfallDetection` says, by default with a warning. Start the atom in a
loader or a server component, with the state that picks it in the URL,
and pass its handle down. For state that can't be in the URL, start it in
the event handler that changes the state, or make its handle with
`useWaterfall` when only the render knows its input.

Two sources with the same arguments and success type take each other's
handles as far as the types go; the development check catches it.

During server rendering, the registry must be a request registry (see
`RequestRegistry.make`), or a module-level registry would share results
between requests: in development, the hook throws without one.

On the server, the hook throws for a handle `Loader.prefetchInBrowser`
returned, so React renders the nearest Suspense boundary's fallback and
reports the error. With no boundary between the read and the route, the
server can't render the page. `useHandleValue` returns `Initial` there
instead.

`Atom.transform`'s `initialValueTarget` option points at the end of the
target's own chain, which for an AtomRpc query with `reactivityKeys` is an
inner atom with no serialization key. A derived atom read here sets
`initialValueTarget` to its source itself, or it requests the source again
after hydration.

**Example**

```tsx
import { AsyncResult } from "effect/reactivity"
import { type Handle, useAtomResult } from "raf-effect-atom"

function IssueTitle(props: { readonly issue: Handle<typeof issue> }) {
  const result = useAtomResult(issue, props.issue)
  return AsyncResult.isSuccess(result) ? <h1>{result.value.title}</h1> : null
}
```

**See also**

- [`hydrated`](https://raf-effect-atom.katt.dev/docs/api-raf-effect-atom.md#Hydration.hydrated) for reading hydrated atoms with Atom's own hooks

### `useHandleValue`

<p class="api-meta"><span class="api-kind">const</span><span>hooks</span><span>since 0.3.1</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/use-atom-result.ts#L276">Source</a></p>

```ts
const useHandleValue: <S extends Source>(
  source: S,
  handle: NoInfer<Handle<Readable<S>>>,
) => AsyncResult.AsyncResult<SuccessOf<S>, ErrorOf<S>>;
```

Reads a handle without React Suspense, through the source that made it,
and returns the atom's current result as it is.

**When to use**

Use where a component renders every state of the read itself: its loading
state, a failure that stays on the page while the atom refreshes, or a
defect. Otherwise prefer `useAtomResult`, which suspends until the result
settles and sends defects to the error boundaries above.

**Details**

- It reads the atom as `useAtomResult` does: through `hydrated`, through
  the serializable atom a derived source points at, and with the same
  development checks on the handle's key and on request waterfalls.
- It returns `Initial` while the atom loads, and while a result the server
  was still loading streams in. It doesn't read the atom until the stream
  settles, so the browser doesn't request it a second time.
- A failure is returned as it is, `waiting` while the atom refreshes, and
  so is a defect.
- Once the component has subscribed to the atom, the loader's lease ends
  and the component keeps the atom mounted. A loader that starts the atom
  again while it's mounted takes no lease, so the atom goes idle when the
  component unmounts.
- A handle `Loader.prefetchInBrowser` returned on the server reads as
  `Initial` there, without starting the atom; the browser's read starts
  it.

**Gotchas**

The server renders what the read returns, `Initial` for a result still
loading, and the browser's first render has to match it: render the same
loading state for every `Initial`.

During server rendering, the registry must be a request registry (see
`RequestRegistry.make`): in development, the hook throws without one.

**Example**

```tsx
import { AsyncResult } from "effect/reactivity"
import { type Handle, useHandleValue } from "raf-effect-atom"

function IssueTitle(props: { readonly issue: Handle<typeof issue> }) {
  const result = useHandleValue(issue, props.issue)
  return AsyncResult.isSuccess(result) ? <h1>{result.value.title}</h1> : <p>Loading…</p>
}
```

**See also**

- [`useAtomResult`](https://raf-effect-atom.katt.dev/docs/api-raf-effect-atom.md#useAtomResult) for a read that suspends until the result settles

## Hydration boundary

Applies a server registry's dehydrated state to the nearest
`RegistryContext`, while rendering.

### `HydrationBoundaryProps`

<p class="api-meta"><span class="api-kind">type</span><span>models</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/hydration-boundary.tsx#L30">Source</a></p>

```ts
type HydrationBoundaryProps =
  | {
      readonly children?: React.ReactNode | undefined;
      /**
       * The request registry of the server component render, such as
       * `raf-effect-atom/rsc`'s `getRegistry()`. Server components only.
       */
      readonly registry: AtomRegistry.AtomRegistry;
      readonly state?: never;
    }
  | {
      readonly children?: React.ReactNode | undefined;
      readonly registry?: never;
      /** The state `Hydration.dehydrate` returned on the server. */
      readonly state?: Iterable<Hydration.DehydratedAtom> | undefined;
    };
```

Props for `HydrationBoundary`: the request's `registry` in a server
component, or the `state` it dehydrated anywhere else.

### `HydrationBoundary`

<p class="api-meta"><span class="api-kind">const</span><span>components</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/hydration-boundary.tsx#L112">Source</a></p>

```ts
const HydrationBoundary: (
  props: HydrationBoundaryProps,
) => React.ReactNode;
```

Hydrates the state a server registry dehydrated, streamed results
included.

**When to use**

Use where the server's state reaches the React tree through rendering
rather than a router: a server component's registry, or a React Router
loader's data. A router that hydrates before rendering calls
`Hydration.hydrate` instead.

**Details**

In a server component, pass the request's `registry`. Server components
get their own build of `HydrationBoundary` through the package's
`react-server` export condition: it waits for the registry's seed,
dehydrates it, and sends the state with the server component's payload,
so results still loading stream in as they settle.

Anywhere else, pass the `state` a loader dehydrated. Settled values go to
`@effect/atom-react`'s `HydrationBoundary`, which hydrates new atoms
during render and existing ones after commit. Each streamed result is
held until it arrives, so `useAtomResult` waits for it instead of
requesting it again.

During server rendering, when `state` is the array `dehydrate` returned,
as React Router passes a loader's data to the server render, the children
read the registry it came from instead of a copy. The loaders' atoms are
there, so an `effect-atom-boundary` boundary sees their failures before
its children read them and renders its handler on the server.

**Gotchas**

`registry` works only in server components: in a client component it
throws, since a registry can't cross to the browser. In development, it
throws during server rendering when the registry isn't a request
registry, such as `@effect/atom-react`'s default one, which every request
shares: one request's state would reach the others.

**Example**

```tsx
import { HydrationBoundary } from "raf-effect-atom"
import { ensure, getRegistry } from "raf-effect-atom/rsc"

// A server component
export default async function IssueRoute(props: { params: Promise<{ id: string }> }) {
  const handle = await ensure(issue, Number((await props.params).id))
  return (
    <HydrationBoundary registry={getRegistry()}>
      <IssuePage issue={handle} />
    </HydrationBoundary>
  )
}

// A React Router route component
<HydrationBoundary state={loaderData.atoms}>
  <IssuePage issue={loaderData.issue} />
</HydrationBoundary>
```

## Registry provider

The registry provider for apps that render the server's state through
`HydrationBoundary`, such as React Server Components and React Router.

### `RegistryProvider`

<p class="api-meta"><span class="api-kind">const</span><span>components</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/registry-provider.tsx#L53">Source</a></p>

```ts
const RegistryProvider: (props: {
  readonly children?: React.ReactNode | undefined;
  readonly defaultIdleTTL?: number | undefined;
}) => React.ReactNode;
```

Provides the app's registry: a request registry while the framework
renders the app on the server, and one registry for the page's lifetime
in the browser.

**When to use**

Put it in the root layout, around `children`, in place of
`@effect/atom-react`'s `RegistryProvider`, which would share one registry
between requests' server renders.

**Details**

`defaultIdleTTL` is how many milliseconds the browser's registry keeps an
atom no component reads, 400 by default, as `@effect/atom-react`'s default
registry does. An atom's own `Atom.setIdleTTL` or `Atom.keepAlive` wins.
It's read once, when the registry is made. The server's request registry
lives for one request, so it has none.

In the browser, React Router's `clientAtomsMiddleware` gives client
loaders the registry of the mounted provider.

**Example**

```tsx
import { RegistryProvider } from "raf-effect-atom"

export default function RootLayout(props: { children: React.ReactNode }) {
  return (
    <RegistryProvider defaultIdleTTL={60_000}>{props.children}</RegistryProvider>
  )
}
```

## Waterfalls

Request waterfalls: the escape hatch for atoms only a render can start,
and the development check that finds the others.

### `WaterfallMode`

<p class="api-meta"><span class="api-kind">type</span><span>models</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/waterfalls.tsx#L43">Source</a></p>

```ts
type WaterfallMode = Waterfalls.Mode;
```

What `useAtomResult` does in development when a read starts an atom that
no loader, server component, event handler or `useWaterfall` started.

- `"warn"`: `console.warn` once per atom, naming it. The default.
- `"throw"`: throw to the nearest error boundary, so the page can't be
  used until the atom starts before render.
- `"off"`: don't check.

### `WaterfallDetection`

<p class="api-meta"><span class="api-kind">const</span><span>components</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/waterfalls.tsx#L75">Source</a></p>

```ts
const WaterfallDetection: (props: {
  readonly children?: React.ReactNode | undefined;
  readonly mode: WaterfallMode;
}) => React.ReactNode;
```

Sets how `useAtomResult` reports request waterfalls in the tree below.

**When to use**

Put it in the root layout, around the app. `"throw"` turns every atom a
component starts on its own into an error in development, which a person
or a coding agent can't miss.

**Details**

Production builds don't check: the check sits behind
`process.env.NODE_ENV !== "production"`, which bundlers replace at build
time, so it's left out of the bundle, and this component renders only its
children.

**Example**

```tsx
import { WaterfallDetection } from "raf-effect-atom"

function App(props: { readonly children: React.ReactNode }) {
  return <WaterfallDetection mode="throw">{props.children}</WaterfallDetection>
}
```

### `useWaterfall`

<p class="api-meta"><span class="api-kind">const</span><span>hooks</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/waterfalls.tsx#L130">Source</a></p>

```ts
const useWaterfall: <S extends Source>(
  source: S,
  ...args: ArgsOf<S>
) => Handle<S>;
```

Starts a source's atom during render and returns its handle, for an input
only a render knows, such as an id read from another atom's result.

**When to use**

As an escape hatch, when no loader, server component or event handler can
start the atom: each call is a request waterfall, a round trip after the
render that found the input. Prefer putting the state that picks the data
in the URL, where a loader reads it, or a procedure that resolves the input
on the server. Searching a codebase for `useWaterfall` lists its
waterfalls.

**Details**

- Starts the atom like `Loader.prefetch`, with a lease that
  `useAtomResult` takes over when the component mounts, and only if
  nothing started it yet: re-renders start nothing.
- A result the server is still streaming is awaited by `useAtomResult`,
  not started again.
- `useAtomResult` doesn't report an atom started this way.
- The handle is a prefetched one: its reader suspends until the result
  settles, as for a handle from a loader's `prefetch`.
- A derived source starts the serializable atom its `initialValueTarget`
  chain points at, as `Loader.prefetch` does.

**Example**

```tsx
import { useAtomResult, useWaterfall } from "raf-effect-atom"

function Author(props: { readonly userId: UserId }) {
  const result = useAtomResult(user, useWaterfall(user, props.userId))
  // ...
}
```

**See also**

- [`WaterfallDetection`](https://raf-effect-atom.katt.dev/docs/api-raf-effect-atom.md#WaterfallDetection) for the development check it opts out of
