/** * Query key type - can be static string or reactive function * * @example * ```typescript * // Static key * const key = 'users'; * * // Reactive key * const userId = State(1); * const key = () => `user-${userId.value}`; * ``` */ export type TQueryKey = string | (() => string); /** * Function that fetches the data * * @param signal AbortSignal to cancel the request * @returns Promise with the fetched data * * @example * ```typescript * const fetcher: TQueryFetcher = async (signal) => { * const res = await fetch('/api/users', { signal }); * return res.json(); * }; * ``` */ export type TQueryFetcher = (signal: AbortSignal) => Promise; /** * Query configuration options */ export type TQueryOptions = { /** * Whether the query should run automatically * @default true */ enabled?: boolean; /** * Time in ms before data is considered stale * @default 0 */ staleTime?: number; /** * Time in ms before inactive queries are garbage collected * @default 300000 (5 minutes) */ cacheTime?: number; /** * Refetch when window regains focus * @default false */ refetchOnFocus?: boolean; /** * Refetch when network reconnects * @default false */ refetchOnReconnect?: boolean; /** * Interval in ms to automatically refetch, or false to disable * @default false */ refetchInterval?: number | false; /** * Number of retry attempts or false to disable * @default 3 */ retry?: number | boolean; /** * Delay between retries in ms or function to calculate delay * @default (attempt) => Math.min(1000 * 2 ** attempt, 30000) */ retryDelay?: number | ((attempt: number) => number); /** * Callback when query succeeds */ onSuccess?: (data: TData) => void; /** * Callback when query fails */ onError?: (error: Error) => void; /** * Initial data before first fetch */ initialData?: TData; }; /** * Query status */ export type TQueryStatus = "idle" | "loading" | "success" | "error"; /** * Query store returned by query() * * @example * ```typescript * const usersQuery = query('users', fetchUsers); * * // Access reactive states * Effect(() => { * if (usersQuery.isLoading.value) { * console.log('Loading...'); * } * if (usersQuery.data.value) { * console.log('Data:', usersQuery.data.value); * } * }); * * // Control methods * usersQuery.refetch(); * usersQuery.invalidate(); * ``` */ export type TQueryStore = { /** * Query data state */ data: TData | null; /** * Query error state */ error: TError | null; /** * Whether the query is loading for the first time */ isLoading: boolean; /** * Whether the query is currently fetching (including background refetches) */ isFetching: boolean; /** * Whether the query is in error state */ isError: boolean; /** * Whether the query is in success state */ isSuccess: boolean; /** * Current query status */ status: TQueryStatus; /** * Manually trigger a refetch */ refetch: () => Promise; /** * Invalidate the query and trigger refetch */ invalidate: () => void; /** * Reset query to initial state */ reset: () => void; /** * Cancel ongoing request */ cancel: () => void; /** * Dispose query and cleanup all subscriptions */ dispose: () => void; }; /** * Internal cache entry * @internal */ export type TCacheEntry = { data: TData; timestamp: number; subscribers: number; }; /** * Internal in-flight request tracking * @internal */ export type TInflightRequest = { promise: Promise; controller: AbortController; }; /** * Mutation status */ export type TMutationStatus = "idle" | "loading" | "success" | "error"; /** * Function that performs the mutation * * @param variables Variables to pass to the mutation * @param signal AbortSignal to cancel the request * @returns Promise with the mutation result * * @example * ```typescript * const fetcher: TMutationFetcher = async (input, signal) => { * const res = await fetch(`/api/users/${input.id}`, { * method: 'PUT', * body: JSON.stringify(input), * signal, * }); * return res.json(); * }; * ``` */ export type TMutationFetcher = (variables: TVariables, signal: AbortSignal) => Promise; /** * Mutation configuration options */ export type TMutationOptions = { /** * Callback before mutation executes * Can return context for rollback */ onMutate?: (variables: TVariables) => TContext | Promise; /** * Callback when mutation succeeds */ onSuccess?: (data: TData, variables: TVariables, context: TContext | undefined) => void | Promise; /** * Callback when mutation fails */ onError?: (error: TError, variables: TVariables, context: TContext | undefined) => void | Promise; /** * Callback when mutation settles (success or error) */ onSettled?: (data: TData | undefined, error: TError | null, variables: TVariables, context: TContext | undefined) => void | Promise; /** * Number of retry attempts or false to disable * @default false */ retry?: number | boolean; /** * Delay between retries in ms or function to calculate delay * @default (attempt) => Math.min(1000 * 2 ** attempt, 30000) */ retryDelay?: number | ((attempt: number) => number); /** * Query keys to invalidate on success */ invalidateQueries?: string[]; /** * Pattern to invalidate queries on success * Supports glob patterns (* and ?) or RegExp * * @example * ```typescript * // Glob pattern * invalidatePattern: 'user-*' * * // RegExp * invalidatePattern: /^user-\d+$/ * ``` */ invalidatePattern?: string | RegExp; /** * Predicate function to invalidate queries on success * * @example * ```typescript * invalidateIf: (key, entry) => { * const age = Date.now() - entry.timestamp; * return age > 60000; // Invalidate queries older than 1 minute * } * ``` */ invalidateIf?: TCacheInvalidationPredicate; }; /** * Mutation store returned by mutation() * * @example * ```typescript * const createUser = mutation( * async (user: User, signal) => { * const res = await fetch('/api/users', { * method: 'POST', * body: JSON.stringify(user), * signal, * }); * return res.json(); * } * ); * * // Execute mutation * await createUser.mutate({ name: 'John', email: 'john@example.com' }); * ``` */ export type TMutationStore = { /** * Mutation data state */ data: TData | null; /** * Mutation error state */ error: TError | null; /** * Whether the mutation is currently executing */ isLoading: boolean; /** * Whether the mutation is in error state */ isError: boolean; /** * Whether the mutation is in success state */ isSuccess: boolean; /** * Whether the mutation is idle (never executed) */ isIdle: boolean; /** * Current mutation status */ status: TMutationStatus; /** * Execute the mutation with variables */ mutate: (variables: TVariables) => Promise; /** * Execute the mutation without throwing errors */ mutateAsync: (variables: TVariables) => Promise; /** * Reset mutation to initial state */ reset: () => void; /** * Cancel ongoing mutation */ cancel: () => void; }; /** * Prefetch options */ export type TPrefetchOptions = { /** * Time in ms before data is considered stale * @default 0 */ staleTime?: number; /** * Time in ms before inactive queries are garbage collected * @default 300000 (5 minutes) */ cacheTime?: number; /** * Force refetch even if data exists in cache * @default false */ force?: boolean; }; /** * Cache invalidation predicate function * Used to selectively invalidate cache entries based on custom logic * * @param key Cache entry key * @param entry Cache entry with data and metadata * @returns True if the entry should be invalidated * * @example * ```typescript * // Invalidate all stale entries older than 1 minute * queryCache.invalidateQueries((key, entry) => { * const age = Date.now() - entry.timestamp; * return age > 60000; * }); * ``` */ export type TCacheInvalidationPredicate = (key: string, entry: TCacheEntry) => boolean;