UNPKG

@stainless-code/persist

Version:

Hydration-aware persistence middleware for reactive stores (storage × codec seams, TanStack Store adapters, React hydration hook)

55 lines 2.77 kB
//#region src/hydration.d.ts /** * Reactive hydration signal — the framework-agnostic subscribe target an * adapter mounts into its external-store mechanism (React * `useSyncExternalStore` via `useHydrated`, Svelte `createSubscriber` / * readable stores, Solid `from`, Vue `shallowRef` + watch). Non-generic: * state reads stay on the store (`useSelector`); the signal only exposes * hydration. * * ADAPTER CONTRACT — what an adapter may rely on and must do: * - `subscribeHydrated(listener)` supports multiple concurrent subscribers * and returns an idempotent unsubscribe. Each call is an independent * subscription, even for the same listener function. * - Listeners are NOT invoked on subscribe (no initial notification) and * carry NO payload — this is a pull model: (re)read `isHydrated()` after * attaching, and on every notification. * - Transitions that happen while nothing is subscribed are not replayed; * the snapshot re-read on attach recovers the current state. * - SSR: render `hydrated = true` on the server (no storage server-side, * nothing to gate) — this policy lives in each adapter, not in the signal; * see `useHydrated`'s server snapshot for the reference implementation. * - A `null` signal means "no persistence" and must render hydrated: `true`. */ interface HydrationSignal { subscribeHydrated: (listener: () => void) => () => void; isHydrated: () => boolean; } /** * Minimal structural source `toHydrationSignal` reads from — `PersistApi` * satisfies it, and so does any object exposing a hydration lifecycle * (the signal layer has no dependency on persist types). */ interface HydrationSource { hasHydrated: () => boolean; onHydrate: (listener: () => void) => () => void; onFinishHydration: (listener: () => void) => () => void; } /** * Derive a `HydrationSignal` from a `HydrationSource` (e.g. a `PersistApi`), * bridging `onHydrate` / `onFinishHydration` into one external-store * subscribe target that notifies on either transition. Null-tolerant: * `null` / `undefined` in → `null` out, so a conditional-persist consumer * drops its hydration ternary (`persist ? toHydrationSignal(persist) : null` * → `toHydrationSignal(persist)`). */ declare function toHydrationSignal(source: HydrationSource | null | undefined): HydrationSignal | null; /** * Always-hydrated `HydrationSignal` for the no-persist path — a uniform * handle instead of a `null` branch at the call site. Solves null-tolerance * once in the core so framework adapters stay dumb (subscribe + snapshot, * nothing else). */ declare function alwaysHydratedSignal(): HydrationSignal; //#endregion export { toHydrationSignal as i, HydrationSource as n, alwaysHydratedSignal as r, HydrationSignal as t };