import { keepPreviousData, skipToken, useQueries, useQuery, type QueryKey, type UseQueryOptions, type UseQueryResult, } from "@tanstack/react-query"; type QueryFnOption = NonNullable< UseQueryOptions["queryFn"] >; type ReplicaQueryInput = Omit< UseQueryOptions, | "gcTime" | "initialData" | "refetchOnMount" | "refetchOnReconnect" | "refetchOnWindowFocus" | "staleTime" > & { pushEvent: string; }; type FetchQueryInput = Omit< UseQueryOptions, "initialData" | "placeholderData" | "queryFn" | "refetchOnMount" | "staleTime" > & { dataShape: "list" | "value"; queryFn: QueryFnOption; staleTimeMs: number; }; export function useReplicaQuery< TQueryFnData, TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >(input: ReplicaQueryInput): UseQueryResult { return useQuery(replicaQueryOptions(input)); } export function useFetchQuery< TQueryFnData, TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >(input: FetchQueryInput): UseQueryResult { return useQuery(fetchQueryOptions(input)); } export function useFetchQueries( inputs: FetchQueryInput[], ): UseQueryResult[] { return useQueries({ queries: inputs.map((input) => fetchQueryOptions(input)) }); } function replicaQueryOptions< TQueryFnData, TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( input: ReplicaQueryInput, ): UseQueryOptions { const { pushEvent, meta, ...options } = input; return { ...options, gcTime: Infinity, meta: { ...meta, serverDataPolicy: { class: "replica", pushEvent, }, }, queryFn: options.queryFn ?? skipToken, refetchOnMount: false, refetchOnReconnect: false, refetchOnWindowFocus: false, staleTime: Infinity, }; } function fetchQueryOptions< TQueryFnData, TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( input: FetchQueryInput, ): UseQueryOptions { if (!Number.isFinite(input.staleTimeMs)) { throw new Error("Fetch queries must declare a finite staleTimeMs."); } const { dataShape, meta, staleTimeMs, ...options } = input; return { ...options, ...(dataShape === "list" ? { placeholderData: keepPreviousData } : {}), meta: { ...meta, serverDataPolicy: { class: "fetch", dataShape, }, }, refetchOnMount: "always", staleTime: staleTimeMs, }; }