@tanstack/db
Version:
A reactive client store for building super fast apps on sync
98 lines (97 loc) • 4.99 kB
text/typescript
import { Collection } from './collection/index.js';
import { ChangeMessage, CollectionStatus } from './types.js';
/**
* The canonical, adapter-agnostic view of a live query at a point in time.
*
* `getSnapshot()` returns a stable object identity that only changes when the
* query changes, so `useSyncExternalStore`-style consumers can compare by
* reference. Each snapshot owns a captured view of `state`/`data`, so reading
* an older snapshot cannot expose rows from a later revision.
*/
export interface LiveQuerySnapshot<T extends object, TKey extends string | number> {
/** Keyed results, or `undefined` for a disabled query. */
state: ReadonlyMap<TKey, T> | undefined;
/** Ordered results (single row for `findOne`), or `undefined` when disabled. */
data: T | ReadonlyArray<T> | undefined;
/** The underlying collection, or `undefined` when disabled. */
collection: Collection<T, TKey, any> | undefined;
/**
* Monotonic counter bumped whenever the visible layout (the ordered key
* sequence) changes — membership, ordering, or an order-only move. Lets
* consumers detect a reorder that changed no row value (which `data`/`state`
* identity alone can't express once row values are structurally shared).
*
* It is NOT in lockstep with snapshot identity: a value-only update produces a
* new snapshot while `layoutRevision` stays put. A `layoutRevision` change
* always accompanies a new snapshot, but not vice versa.
*/
layoutRevision: number;
status: CollectionStatus | `disabled`;
isLoading: boolean;
isReady: boolean;
isIdle: boolean;
isError: boolean;
isCleanedUp: boolean;
isEnabled: boolean;
}
/**
* Listener payload: changes, `[]` for an internal layout-only publication, or
* `undefined` for a synthetic status/ready notification.
*/
export type LiveQueryObserverListener<T extends object, TKey extends string | number> = (changes: Array<ChangeMessage<T, TKey>> | undefined) => void;
/**
* Wraps a resolved live-query `Collection` (or `null` for a disabled query) with
* the shared lifecycle every framework adapter needs: start sync on first
* subscribe, subscribe to changes and status transitions, expose a stable
* snapshot for wholesale consumers, and deliver the raw change set for
* granular consumers.
*
* Input resolution (query fn / config / collection / disabled) stays in the
* adapter — it is framework-reactive. The observer owns everything after the
* input is resolved to a concrete collection.
*
* @internal Unstable contract for TanStack DB's official framework adapters —
* not a public extension point yet; may change in any release.
*/
export interface LiveQueryObserver<T extends object, TKey extends string | number> {
/** Stable per-revision snapshot for wholesale materialization. */
getSnapshot: () => LiveQuerySnapshot<T, TKey>;
/**
* Subscribe to changes. The listener receives the change set (or `undefined`
* for the synthetic notify a ready collection emits on attach). Granular
* adapters apply the changes; wholesale adapters can ignore them and re-read
* `getSnapshot()`. Returns an unsubscribe function.
*/
subscribe: (listener: LiveQueryObserverListener<T, TKey>) => () => void;
/** Resolve once the collection has loaded its first data. */
preload: () => Promise<void>;
/** Idempotent teardown. */
dispose: () => void;
}
export interface CreateLiveQueryObserverOptions {
/**
* How subscribers consume the observer:
*
* - `granular` (default): subscribers apply the delivered `ChangeMessage[]`
* deltas to their own keyed state (Vue/Svelte/Solid). The observer
* subscribes with initial state and seeds late subscribers, so every
* subscriber converges from deltas alone.
* - `wholesale`: subscribers treat notifications as a wake-up and re-read
* `getSnapshot()` (React/Angular). The observer subscribes WITHOUT initial
* state, preserving those adapters' loading policy — no snapshot request,
* so no unfiltered `loadSubset` against on-demand collections. Nothing is
* delivered synchronously during `subscribe`, which keeps
* `useSyncExternalStore`-style consumers safe by construction.
*/
mode?: `granular` | `wholesale`;
}
/**
* Create a {@link LiveQueryObserver} for a resolved live-query collection, or a
* disabled observer when `collection` is `null`/`undefined`.
*
* @internal This is an unstable contract shared by TanStack DB's official
* framework adapters. It is exported so the adapter packages can use it, but
* it is not a public extension point yet: its API may change in any release
* without a semver major.
*/
export declare function createLiveQueryObserver<T extends object, TKey extends string | number>(collection: Collection<T, TKey, any> | null | undefined, options?: CreateLiveQueryObserverOptions): LiveQueryObserver<T, TKey>;