@tanstack/db
Version:
A reactive client store for building super fast apps on sync
95 lines (94 loc) • 4.14 kB
TypeScript
import { InitialQueryBuilder, QueryBuilder } from './builder/index.js';
import { Context } from './builder/types.js';
/** Event types for query result deltas */
export type DeltaType = 'enter' | 'exit' | 'update';
/** Delta event emitted when a row enters, exits, or updates within a query result */
export type DeltaEvent<TRow extends object = Record<string, unknown>, TKey extends string | number = string | number> = {
type: 'enter';
key: TKey;
/** Current value for the entering row */
value: TRow;
metadata?: Record<string, unknown>;
} | {
type: 'exit';
key: TKey;
/** Current value for the exiting row */
value: TRow;
metadata?: Record<string, unknown>;
} | {
type: 'update';
key: TKey;
/** Current value after the update */
value: TRow;
/** Previous value before the batch */
previousValue: TRow;
metadata?: Record<string, unknown>;
};
/** Context passed to effect handlers */
export interface EffectContext {
/** ID of this effect (auto-generated if not provided) */
effectId: string;
/** Aborted when effect.dispose() is called */
signal: AbortSignal;
}
/** Query input - can be a builder function or a prebuilt query */
export type EffectQueryInput<TContext extends Context> = ((q: InitialQueryBuilder) => QueryBuilder<TContext>) | QueryBuilder<TContext>;
type EffectEventHandler<TRow extends object = Record<string, unknown>, TKey extends string | number = string | number> = (event: DeltaEvent<TRow, TKey>, ctx: EffectContext) => void | Promise<void>;
type EffectBatchHandler<TRow extends object = Record<string, unknown>, TKey extends string | number = string | number> = (events: Array<DeltaEvent<TRow, TKey>>, ctx: EffectContext) => void | Promise<void>;
/** Effect configuration */
export interface EffectConfig<TRow extends object = Record<string, unknown>, TKey extends string | number = string | number> {
/** Optional ID for debugging/tracing */
id?: string;
/** Query to watch for deltas */
query: EffectQueryInput<any>;
/** Called once for each row entering the query result */
onEnter?: EffectEventHandler<TRow, TKey>;
/** Called once for each row updating within the query result */
onUpdate?: EffectEventHandler<TRow, TKey>;
/** Called once for each row exiting the query result */
onExit?: EffectEventHandler<TRow, TKey>;
/** Called once per graph run with all delta events from that batch */
onBatch?: EffectBatchHandler<TRow, TKey>;
/** Error handler for exceptions thrown by effect callbacks */
onError?: (error: Error, event: DeltaEvent<TRow, TKey>) => void;
/**
* Called when a source collection enters an error or cleaned-up state.
* The effect is automatically disposed after this callback fires.
* If not provided, the error is logged to console.error.
*/
onSourceError?: (error: Error) => void;
/**
* Skip deltas during initial collection load.
* Defaults to false (process all deltas including initial sync).
* Set to true for effects that should only process new changes.
*/
skipInitial?: boolean;
}
/** Handle returned by createEffect */
export interface Effect {
/** Dispose the effect. Returns a promise that resolves when in-flight handlers complete. */
dispose: () => Promise<void>;
/** Whether this effect has been disposed */
readonly disposed: boolean;
}
/**
* Creates a reactive effect that fires handlers when rows enter, exit, or
* update within a query result. Effects process deltas only — they do not
* maintain or require the full materialised query result.
*
* @example
* ```typescript
* const effect = createEffect({
* query: (q) => q.from({ msg: messagesCollection })
* .where(({ msg }) => eq(msg.role, 'user')),
* onEnter: async (event) => {
* await generateResponse(event.value)
* },
* })
*
* // Later: stop the effect
* await effect.dispose()
* ```
*/
export declare function createEffect<TRow extends object = Record<string, unknown>, TKey extends string | number = string | number>(config: EffectConfig<TRow, TKey>): Effect;
export {};