import type { CancelOptions, InfiniteData, InvalidateOptions, InvalidateQueryFilters, Query, QueryFilters, QueryKey, RefetchOptions, RefetchQueryFilters, ResetOptions, SetDataOptions, SkipToken, Updater, } from '@tanstack/react-query'; import type { TRPCClientError } from '@trpc/client'; import { createTRPCClientProxy } from '@trpc/client'; import type { AnyMutationProcedure, AnyQueryProcedure, AnyRootTypes, AnyRouter, DeepPartial, inferProcedureInput, inferProcedureOutput, inferTransformedProcedureOutput, ProtectedIntersection, RouterRecord, } from '@trpc/server/unstable-core-do-not-import'; import { createFlatProxy, createRecursiveProxy, } from '@trpc/server/unstable-core-do-not-import'; import type { DecoratedTRPCContextProps, TRPCContextState, TRPCFetchInfiniteQueryOptions, TRPCFetchQueryOptions, TRPCQueryUtils, } from '../../internals/context'; import { contextProps } from '../../internals/context'; import type { QueryKeyKnown, QueryType } from '../../internals/getQueryKey'; import { getMutationKeyInternal, getQueryKeyInternal, } from '../../internals/getQueryKey'; import type { InferMutationOptions } from '../../utils/inferReactQueryProcedure'; import type { ExtractCursorType } from '../hooks/types'; import type { DefinedTRPCInfiniteQueryOptionsIn, DefinedTRPCInfiniteQueryOptionsOut, DefinedTRPCQueryOptionsIn, DefinedTRPCQueryOptionsOut, UndefinedTRPCInfiniteQueryOptionsIn, UndefinedTRPCInfiniteQueryOptionsOut, UndefinedTRPCQueryOptionsIn, UndefinedTRPCQueryOptionsOut, UnusedSkipTokenTRPCInfiniteQueryOptionsIn, UnusedSkipTokenTRPCInfiniteQueryOptionsOut, UnusedSkipTokenTRPCQueryOptionsIn, UnusedSkipTokenTRPCQueryOptionsOut, } from '../types'; export type DecorateQueryProcedure< TRoot extends AnyRootTypes, TProcedure extends AnyQueryProcedure, > = { /** * @see https://tanstack.com/query/latest/docs/framework/react/reference/queryOptions#queryoptions */ queryOptions< TQueryFnData extends inferTransformedProcedureOutput, TData = TQueryFnData, >( input: inferProcedureInput | SkipToken, opts: DefinedTRPCQueryOptionsIn< TQueryFnData, TData, TRPCClientError >, ): DefinedTRPCQueryOptionsOut>; /** * @see https://tanstack.com/query/latest/docs/framework/react/reference/queryOptions#queryoptions */ queryOptions< TQueryFnData extends inferTransformedProcedureOutput, TData = TQueryFnData, >( input: inferProcedureInput | SkipToken, opts?: UnusedSkipTokenTRPCQueryOptionsIn< TQueryFnData, TData, TRPCClientError >, ): UnusedSkipTokenTRPCQueryOptionsOut< TQueryFnData, TData, TRPCClientError >; /** * @see https://tanstack.com/query/latest/docs/framework/react/reference/queryOptions#queryoptions */ queryOptions< TQueryFnData extends inferTransformedProcedureOutput, TData = TQueryFnData, >( input: inferProcedureInput | SkipToken, opts?: UndefinedTRPCQueryOptionsIn< TQueryFnData, TData, TRPCClientError >, ): UndefinedTRPCQueryOptionsOut>; /** * @see https://tanstack.com/query/latest/docs/framework/react/reference/infiniteQueryOptions#infinitequeryoptions */ infiniteQueryOptions< TQueryFnData extends inferTransformedProcedureOutput, TData = TQueryFnData, >( input: inferProcedureInput | SkipToken, opts: DefinedTRPCInfiniteQueryOptionsIn< inferProcedureInput, TQueryFnData, TData, TRPCClientError >, ): DefinedTRPCInfiniteQueryOptionsOut< inferProcedureInput, TQueryFnData, TData, TRPCClientError >; /** * @see https://tanstack.com/query/latest/docs/framework/react/reference/infiniteQueryOptions#infinitequeryoptions */ infiniteQueryOptions< TQueryFnData extends inferTransformedProcedureOutput, TData = TQueryFnData, >( input: inferProcedureInput, opts: UnusedSkipTokenTRPCInfiniteQueryOptionsIn< inferProcedureInput, TQueryFnData, TData, TRPCClientError >, ): UnusedSkipTokenTRPCInfiniteQueryOptionsOut< inferProcedureInput, TQueryFnData, TData, TRPCClientError >; /** * @see https://tanstack.com/query/latest/docs/framework/react/reference/infiniteQueryOptions#infinitequeryoptions */ infiniteQueryOptions< TQueryFnData extends inferTransformedProcedureOutput, TData = TQueryFnData, >( input: inferProcedureInput | SkipToken, opts?: UndefinedTRPCInfiniteQueryOptionsIn< inferProcedureInput, TQueryFnData, TData, TRPCClientError >, ): UndefinedTRPCInfiniteQueryOptionsOut< inferProcedureInput, TQueryFnData, TData, TRPCClientError >; /** * @see https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientfetchquery */ fetch( input: inferProcedureInput, opts?: TRPCFetchQueryOptions< inferTransformedProcedureOutput, TRPCClientError >, ): Promise>; /** * @see https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientfetchinfinitequery */ fetchInfinite( input: inferProcedureInput, opts?: TRPCFetchInfiniteQueryOptions< inferProcedureInput, inferTransformedProcedureOutput, TRPCClientError >, ): Promise< InfiniteData< inferTransformedProcedureOutput, NonNullable>> | null > >; /** * @see https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientprefetchquery */ prefetch( input: inferProcedureInput, opts?: TRPCFetchQueryOptions< inferTransformedProcedureOutput, TRPCClientError >, ): Promise; /** * @see https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientprefetchinfinitequery */ prefetchInfinite( input: inferProcedureInput, opts?: TRPCFetchInfiniteQueryOptions< inferProcedureInput, inferTransformedProcedureOutput, TRPCClientError >, ): Promise; /** * @see https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientensurequerydata */ ensureData( input: inferProcedureInput, opts?: TRPCFetchQueryOptions< inferTransformedProcedureOutput, TRPCClientError >, ): Promise>; /** * @see https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientinvalidatequeries */ invalidate( input?: DeepPartial>, filters?: Omit & { predicate?: ( query: Query< inferProcedureOutput, TRPCClientError, inferTransformedProcedureOutput, QueryKeyKnown< inferProcedureInput, inferProcedureInput extends { cursor?: any } | void ? 'infinite' : 'query' > >, ) => boolean; }, options?: InvalidateOptions, ): Promise; /** * @see https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientrefetchqueries */ refetch( input?: inferProcedureInput, filters?: RefetchQueryFilters, options?: RefetchOptions, ): Promise; /** * @see https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientcancelqueries */ cancel( input?: inferProcedureInput, options?: CancelOptions, ): Promise; /** * @see https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientresetqueries */ reset( input?: inferProcedureInput, options?: ResetOptions, ): Promise; /** * @see https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientsetquerydata */ setData( /** * The input of the procedure */ input: inferProcedureInput, updater: Updater< inferTransformedProcedureOutput | undefined, inferTransformedProcedureOutput | undefined >, options?: SetDataOptions, ): void; /** * @see https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientsetquerydata */ setQueriesData( /** * The input of the procedure */ input: inferProcedureInput, filters: QueryFilters, updater: Updater< inferTransformedProcedureOutput | undefined, inferTransformedProcedureOutput | undefined >, options?: SetDataOptions, ): [QueryKey, inferTransformedProcedureOutput]; /** * @see https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientsetquerydata */ setInfiniteData( input: inferProcedureInput, updater: Updater< | InfiniteData< inferTransformedProcedureOutput, NonNullable>> | null > | undefined, | InfiniteData< inferTransformedProcedureOutput, NonNullable>> | null > | undefined >, options?: SetDataOptions, ): void; /** * @see https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientgetquerydata */ getData( input?: inferProcedureInput, ): inferTransformedProcedureOutput | undefined; /** * @see https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientgetquerydata */ getInfiniteData( input?: inferProcedureInput, ): | InfiniteData< inferTransformedProcedureOutput, NonNullable>> | null > | undefined; }; type DecorateMutationProcedure< TRoot extends AnyRootTypes, TProcedure extends AnyMutationProcedure, > = { setMutationDefaults( options: | InferMutationOptions | ((args: { canonicalMutationFn: NonNullable< InferMutationOptions['mutationFn'] >; }) => InferMutationOptions), ): void; getMutationDefaults(): InferMutationOptions | undefined; isMutating(): number; }; /** * this is the type that is used to add in procedures that can be used on * an entire router */ type DecorateRouter = { /** * Invalidate the full router * @see https://trpc.io/docs/v10/useContext#query-invalidation * @see https://tanstack.com/query/v5/docs/framework/react/guides/query-invalidation */ invalidate( input?: undefined, filters?: InvalidateQueryFilters, options?: InvalidateOptions, ): Promise; }; /** * @internal */ export type DecoratedProcedureUtilsRecord< TRoot extends AnyRootTypes, TRecord extends RouterRecord, > = DecorateRouter & { [TKey in keyof TRecord]: TRecord[TKey] extends infer $Value ? $Value extends AnyQueryProcedure ? DecorateQueryProcedure : $Value extends AnyMutationProcedure ? DecorateMutationProcedure : $Value extends RouterRecord ? DecoratedProcedureUtilsRecord & DecorateRouter : never : never; }; // Add functions that should be available at utils root type AnyDecoratedProcedure = DecorateQueryProcedure & DecorateMutationProcedure; export type CreateReactUtils< TRouter extends AnyRouter, TSSRContext, > = ProtectedIntersection< DecoratedTRPCContextProps, DecoratedProcedureUtilsRecord< TRouter['_def']['_config']['$types'], TRouter['_def']['record'] > >; export type CreateQueryUtils = DecoratedProcedureUtilsRecord< TRouter['_def']['_config']['$types'], TRouter['_def']['record'] >; export const getQueryType = ( utilName: keyof AnyDecoratedProcedure, ): QueryType => { switch (utilName) { case 'queryOptions': case 'fetch': case 'ensureData': case 'prefetch': case 'getData': case 'setData': case 'setQueriesData': return 'query'; case 'infiniteQueryOptions': case 'fetchInfinite': case 'prefetchInfinite': case 'getInfiniteData': case 'setInfiniteData': return 'infinite'; case 'setMutationDefaults': case 'getMutationDefaults': case 'isMutating': case 'cancel': case 'invalidate': case 'refetch': case 'reset': return 'any'; } }; /** * @internal */ function createRecursiveUtilsProxy( context: TRPCQueryUtils, ) { return createRecursiveProxy>((opts) => { const path = [...opts.path]; const utilName = path.pop() as keyof AnyDecoratedProcedure; const args = [...opts.args] as Parameters< AnyDecoratedProcedure[typeof utilName] >; const input = args.shift(); // args can now be spread when input removed const queryType = getQueryType(utilName); const queryKey = getQueryKeyInternal(path, input, queryType); const contextMap: Record unknown> = { infiniteQueryOptions: () => context.infiniteQueryOptions(path, queryKey, args[0]), queryOptions: () => context.queryOptions(path, queryKey, ...args), /** * DecorateQueryProcedure */ fetch: () => context.fetchQuery(queryKey, ...args), fetchInfinite: () => context.fetchInfiniteQuery(queryKey, args[0]), prefetch: () => context.prefetchQuery(queryKey, ...args), prefetchInfinite: () => context.prefetchInfiniteQuery(queryKey, args[0]), ensureData: () => context.ensureQueryData(queryKey, ...args), invalidate: () => context.invalidateQueries(queryKey, ...args), reset: () => context.resetQueries(queryKey, ...args), refetch: () => context.refetchQueries(queryKey, ...args), cancel: () => context.cancelQuery(queryKey, ...args), setData: () => { context.setQueryData(queryKey, args[0], args[1]); }, setQueriesData: () => context.setQueriesData(queryKey, args[0], args[1], args[2]), setInfiniteData: () => { context.setInfiniteQueryData(queryKey, args[0], args[1]); }, getData: () => context.getQueryData(queryKey), getInfiniteData: () => context.getInfiniteQueryData(queryKey), /** * DecorateMutationProcedure */ setMutationDefaults: () => context.setMutationDefaults(getMutationKeyInternal(path), input), getMutationDefaults: () => context.getMutationDefaults(getMutationKeyInternal(path)), isMutating: () => context.isMutating({ mutationKey: getMutationKeyInternal(path) }), }; return contextMap[utilName](); }); } /** * @internal */ export function createReactQueryUtils( context: TRPCContextState, ) { type CreateReactUtilsReturnType = CreateReactUtils; const clientProxy = createTRPCClientProxy(context.client); const proxy = createRecursiveUtilsProxy( context, ) as CreateReactUtilsReturnType; return createFlatProxy((key) => { const contextName = key as (typeof contextProps)[number]; if (contextName === 'client') { return clientProxy; } if (contextProps.includes(contextName)) { return context[contextName]; } return proxy[key]; }); } /** * @internal */ export function createQueryUtilsProxy( context: TRPCQueryUtils, ): CreateQueryUtils { return createRecursiveUtilsProxy(context); }