import { useRef } from "react"; import { QueryClient, QueryClientProvider, mutationOptions, queryOptions, skipToken, useMutation as useTanStackMutation, useQuery as useTanStackQuery, useQueryClient as useTanStackQueryClient, type QueryClient as QueryClientType, type UseMutationOptions, type UseMutationResult, type UseQueryOptions, type UseQueryResult, } from "@tanstack/react-query"; export { QueryClient, QueryClientProvider, mutationOptions, queryOptions, skipToken }; const invalidationScopes: Array>> = []; const trackInvalidation = (promise: Promise) => { for (const scope of invalidationScopes) { scope.push(promise); } return promise; }; const trackMutationCallback = async (callback: () => T | Promise): Promise => { const scope: Array> = []; invalidationScopes.push(scope); try { const result = await callback(); await Promise.allSettled(scope); return result; } finally { invalidationScopes.pop(); } }; const wrapQueryClient = (client: QueryClientType): QueryClientType => new Proxy(client, { get(target, prop, receiver) { const value = Reflect.get(target, prop, receiver) as unknown; if (prop === "invalidateQueries" && typeof value === "function") { return (...args: unknown[]) => trackInvalidation( (value as (...args: unknown[]) => Promise).apply(target, args), ); } return typeof value === "function" ? value.bind(target) : value; }, }) as QueryClientType; export const useQueryClient = (queryClient?: QueryClientType): QueryClientType => { const client = useTanStackQueryClient(queryClient); const wrappedRef = useRef<{ client: QueryClientType; wrapped: QueryClientType } | null>(null); if (wrappedRef.current?.client !== client) { wrappedRef.current = { client, wrapped: wrapQueryClient(client) }; } return wrappedRef.current.wrapped; }; const wrapMutationOptions = ( options: UseMutationOptions, ): UseMutationOptions => ({ ...options, onSuccess: options.onSuccess ? (...args: Parameters>) => trackMutationCallback(() => options.onSuccess?.(...args)) : undefined, onError: options.onError ? (...args: Parameters>) => trackMutationCallback(() => options.onError?.(...args)) : undefined, onSettled: options.onSettled ? (...args: Parameters>) => trackMutationCallback(() => options.onSettled?.(...args)) : undefined, }); export function useQuery< TQueryFnData, TError = Error, TData = TQueryFnData, TQueryKey extends readonly unknown[] = readonly unknown[], >( options: UseQueryOptions, queryClient?: QueryClientType, ): UseQueryResult; export function useQuery< TQueryFnData, TError = Error, TData = TQueryFnData, TQueryKey extends readonly unknown[] = readonly unknown[], >( options: UseQueryOptions, queryClient?: QueryClientType, ): UseQueryResult { return useTanStackQuery(options, queryClient); } export function useMutation( options: UseMutationOptions, queryClient?: QueryClientType, ): UseMutationResult; export function useMutation( options: UseMutationOptions, queryClient?: QueryClientType, ): UseMutationResult { return useTanStackMutation(wrapMutationOptions(options), queryClient); }