/** * The agnostic keyed query-cache - a `query(key, fn)` primitive with in-flight dedup, `staleTime` * freshness, prefix `invalidateQueries`, imperative cache read/write, prefetch, SSR dehydrate/hydrate, * infinite (paged) queries, and standalone mutations. Pure logic, no `window`, no framework, no * `Date.now`/timers (the clock is injected) - so it unit-tests deterministically and is safe to import * from the SSR core's main entry. A per-adapter binding (`useQuery`/`useMutation`/`createQuery`) * subscribes to a query/mutation's `subscribe`/`snapshot` store (the same shape the router + fetchers * use). * * This is a SECOND keyed cache, distinct from the router's path-keyed route cache (F16): route loaders * + navigation use the path cache; component-level interactive data (search, infinite scroll, polling) * uses this arbitrary-keyed query cache. The two coexist. */ /** A query's lifecycle status. `pending` = no data yet; `success`/`error` once it has settled once. */ export type QueryStatus = "pending" | "success" | "error"; /** A query's observable state - what a binding renders. A new (frozen) object per transition, so a * `useSyncExternalStore`/signal binding can compare by reference. */ export interface QueryState { readonly status: QueryStatus; readonly data: T | undefined; readonly error: unknown; /** True while a fetch is in flight - the initial load OR a background refetch (data may still show). */ readonly isFetching: boolean; /** When `data` was last set (via the injected clock); drives `staleTime`. `-Infinity` until first set. */ readonly updatedAt: number; } /** A stable per-key handle: subscribe to its state, read a snapshot, trigger a fetch/refetch. */ export interface QueryHandle { snapshot: () => QueryState; subscribe: (listener: () => void) => () => void; /** Fetch when stale or absent (fresh ⇒ no-op); joins an in-flight fetch (dedup). Resolves to the * data, or rejects if the fetch threw. */ fetch: () => Promise; /** Refetch regardless of staleness (still joins an in-flight fetch). */ refetch: () => Promise; } /** Per-query overrides passed alongside the fetcher. */ export interface QueryOptions { /** Data stays fresh this long (ms) after a fetch; older ⇒ refetch on the next `fetch()`. Overrides * the client default for this key. */ readonly staleTime?: number; } export interface QueryClientOptions { /** Monotonic clock in ms - injected so the core stays deterministic + testable; the adapter binding * passes `() => Date.now()`. */ readonly now: () => number; /** Data stays fresh this long after a fetch; older ⇒ refetch on the next `fetch()` (default `0`). */ readonly staleTime?: number; /** Evict an entry this long (ms) after its last subscriber leaves (default 5 min). */ readonly gcTime?: number; /** Hard cap on cached entries - LRU-evict the oldest *unsubscribed* entry past it (default 1000). */ readonly max?: number; } /** An infinite (paged) query's accumulated data: the fetched `pages` in order + the `pageParam` each * was fetched with (so the next/previous param can be derived). */ export interface InfiniteData { readonly pages: readonly T[]; readonly pageParams: readonly P[]; } /** Options for an {@link InfiniteQueryHandle}. `getNextPageParam` (required) derives the param for the * next page from the last page - return `undefined`/`null` to signal there is no next page. */ export interface InfiniteQueryOptions extends QueryOptions { /** The param the FIRST page is fetched with. */ readonly initialPageParam: P; /** Derive the next page's param from the last fetched page (and all pages/params). `undefined`/`null` * ⇒ no next page (`hasNextPage` is false). */ readonly getNextPageParam: (lastPage: T, allPages: readonly T[], lastPageParam: P, allPageParams: readonly P[]) => P | undefined | null; /** Derive the previous page's param (for bidirectional infinite lists). Omit ⇒ no previous page. */ readonly getPreviousPageParam?: (firstPage: T, allPages: readonly T[], firstPageParam: P, allPageParams: readonly P[]) => P | undefined | null; } /** A stable per-key handle for an infinite (paged) query. */ export interface InfiniteQueryHandle { snapshot: () => QueryState>; subscribe: (listener: () => void) => () => void; /** Fetch the first page when absent/stale (fresh ⇒ no-op). */ fetch: () => Promise>; /** Refetch every currently-loaded page (in order), replacing the data. */ refetch: () => Promise>; /** Append the next page (a no-op when `hasNextPage()` is false or a fetch is already in flight). */ fetchNextPage: () => Promise>; /** Prepend the previous page (a no-op without `getPreviousPageParam` or when there is none). */ fetchPreviousPage: () => Promise>; /** Whether another page can be appended (derived from `getNextPageParam` of the last page). */ hasNextPage: () => boolean; /** Whether a page can be prepended (derived from `getPreviousPageParam` of the first page). */ hasPreviousPage: () => boolean; } /** A serializable snapshot of the cache's successful queries - the SSR→client bridge payload. */ export interface DehydratedState { readonly queries: ReadonlyArray<{ readonly key: unknown; readonly data: unknown; readonly updatedAt: number; }>; } /** The keyed query cache. One per app (a binding registers it like the router). */ export interface QueryClient { /** Get the stable {@link QueryHandle} for `key`, fetched via `fn`. Re-binds the latest `fn` (and * `options`) each call (closures change between renders); returns the same handle for the same * (hashed) key. */ query: (key: unknown, fn: () => Promise, options?: QueryOptions) => QueryHandle; /** Get the stable {@link InfiniteQueryHandle} for `key`, whose pages are fetched via `fn(pageParam)`. */ infiniteQuery: (key: unknown, fn: (pageParam: P) => Promise, options: InfiniteQueryOptions) => InfiniteQueryHandle; /** Mark matching cached queries stale and refetch the **mounted** ones (subscribers > 0). Array * keys match by **prefix** (`["todo"]` ⇒ every `["todo", …]`); other keys match exactly. */ invalidateQueries: (keyOrPrefix: unknown) => void; /** Read a cached query's data (`undefined` if absent or not yet successful). Synchronous, no fetch. */ getQueryData: (key: unknown) => T | undefined; /** Write a query's data directly (optimistic updates, or seeding). Creates the entry if absent; marks * it fresh (`updatedAt = now`). `updater` may be a value or a function of the previous data. */ setQueryData: (key: unknown, updater: T | ((prev: T | undefined) => T)) => void; /** Fetch + cache a query's data WITHOUT subscribing (SSR prefetch, hover warm). Resolves when cached; * a fresh entry is a no-op. Rejects if the fetch throws (caller may swallow). */ prefetchQuery: (key: unknown, fn: () => Promise, options?: QueryOptions) => Promise; /** Serialize every successful query to a transferable snapshot (server → client SSR bridge). */ dehydrate: () => DehydratedState; /** Seed the cache from a {@link dehydrate} snapshot (client boot). Existing fresher entries win. */ hydrate: (state: DehydratedState) => void; } /** * Hash a query key to a stable cache string. Object keys are sorted (so `{a,b}` ≡ `{b,a}`); arrays * keep order. Keys must be serializable - a function/symbol in the key throws (it can't be a stable * identity). Mirrors TanStack Query's structural hashing. */ export declare function hashQueryKey(key: unknown): string; export declare function createQueryClient(options: QueryClientOptions): QueryClient; /** A mutation's lifecycle status. */ export type MutationStatus = "idle" | "pending" | "success" | "error"; /** A mutation's observable state. A new (frozen) object per transition (reference-comparable). */ export interface MutationState { readonly status: MutationStatus; readonly data: TData | undefined; readonly error: unknown; /** The variables of the in-flight / last mutation (`undefined` before the first call). */ readonly variables: TVariables | undefined; } /** Lifecycle callbacks for a mutation. All optional; `onSettled` runs after success OR error. */ export interface MutationCallbacks { readonly onMutate?: (variables: TVariables) => void | Promise; readonly onSuccess?: (data: TData, variables: TVariables) => void | Promise; readonly onError?: (error: unknown, variables: TVariables) => void | Promise; readonly onSettled?: (data: TData | undefined, error: unknown, variables: TVariables) => void | Promise; } /** A standalone mutation store: subscribe to its state, fire `mutate`, `reset` back to idle. */ export interface MutationHandle { snapshot: () => MutationState; subscribe: (listener: () => void) => () => void; /** Run the mutation. Resolves to the data (and runs onSuccess/onSettled) or rejects (onError/onSettled). * The latest concurrent call wins the published state (older results are dropped). */ mutate: (variables: TVariables) => Promise; /** Rebind the latest `fn`/callbacks (closures change between renders) without losing state. */ rebind: (fn: (variables: TVariables) => Promise, callbacks: MutationCallbacks) => void; /** Reset to the idle state (clears data/error/variables). */ reset: () => void; } /** * Create a standalone mutation state machine - framework-agnostic, so a per-adapter `useMutation` * binding just subscribes to it. Single-flight by a monotonic token: overlapping `mutate` calls each * run their `fn`, but only the latest publishes state (an older, slower response can't clobber a newer * one). The callbacks fire in TanStack order: `onMutate` (before), then `onSuccess`/`onError`, then * `onSettled`. */ export declare function createMutation(fn: (variables: TVariables) => Promise, callbacks?: MutationCallbacks): MutationHandle; //# sourceMappingURL=query.d.ts.map