import type { Ref } from '@stacksjs/stx'; export declare function createQueryClient(options?: CreateQueryClientOptions): QueryClient; /** * Reactive query with cache + dedup + stale-while-revalidate. * * @example * ```ts * const judges = useQuery({ * queryKey: ['judges', { practiceArea: filter() }], * queryFn: async ({ signal }) => { * const res = await fetch('/api/judges', { signal }) * return res.json() as Promise * }, * staleTime: 30_000, * }) * ``` */ export declare function useQuery(opts: UseQueryOptions): UseQueryResult; /** * Reactive write-side primitive. Pairs with `useQuery` via * `queryClient.invalidate(...)` inside `onSettled` for the canonical * optimistic-with-rollback pattern. */ export declare function useMutation(opts: UseMutationOptions): UseMutationResult; /** Default app-wide client. Tests should pass their own via `client:`. */ export declare const queryClient: QueryClient; declare interface CacheEntry { key: QueryKey hash: string data: T | undefined error: Error | null updatedAt: number status: 'idle' | 'fetching' | 'success' | 'error' inflight: Promise | null invalidated: boolean subscribers: Set<() => unknown> } export declare interface CreateQueryClientOptions { gcTime?: number } /** * Central cache. Hold one per app (the default `queryClient` export) * or create scoped ones for tests. */ export declare interface QueryClient { get: (key: QueryKey) => T | undefined set: (key: QueryKey, value: T | ((old: T | undefined) => T)) => void invalidate: (matcher: QueryMatcher) => Promise clear: () => void gc: () => number _subscribe: (hash: string, fn: () => unknown) => () => void _entry: (key: QueryKey) => CacheEntry _notify: (hash: string) => void } export declare interface UseQueryOptions { queryKey: QueryKey queryFn: (ctx: { signal: AbortSignal }) => Promise staleTime?: number enabled?: boolean refetchOnFocus?: boolean refetchOnReconnect?: boolean window?: EventTarget client?: QueryClient } export declare interface UseQueryResult { data: Ref error: Ref isLoading: Ref isFetching: Ref refetch: () => Promise unsubscribe: () => void } export declare interface UseMutationOptions { mutationFn: (vars: TVars) => Promise onMutate?: (vars: TVars) => Promise | TCtx onSuccess?: (data: TData, vars: TVars, ctx: TCtx | undefined) => void | Promise onError?: (error: Error, vars: TVars, ctx: TCtx | undefined) => void | Promise onSettled?: (data: TData | undefined, error: Error | null, vars: TVars, ctx: TCtx | undefined) => void | Promise } export declare interface UseMutationResult { data: Ref error: Ref isPending: Ref mutate: (vars: TVars) => void mutateAsync: (vars: TVars) => Promise reset: () => void } /** * useQuery / useMutation / queryClient (stacksjs/stacks#1939 — Phase A). * * Cache + dedup + invalidation primitives. Mirrors TanStack Query shape * so adopters transfer immediately. Phase A intentionally omits: * - `refetchOnFocus` / `refetchOnReconnect` (DOM listener coupling) * - automatic GC of unsubscribed cache entries * - query key partial-match invalidation via predicate functions * Those land in Phase B. Prefix-match invalidation IS supported * (`invalidate(['judges'])` matches `['judges', filterObj]` etc). */ export type QueryKey = readonly unknown[]; /** * Matcher for `invalidate`. Either a `QueryKey` (prefix match, the common * case) or a predicate run against every cached key (stacksjs/stacks#1939 * Phase B) — e.g. invalidate everything tagged a given entity: * `invalidate({ predicate: k => k.includes('judges') })`. */ export type QueryMatcher = QueryKey | { predicate: (key: QueryKey) => boolean }