1
- /* eslint-disable ts/no-unsafe-argument, ts/no-unsafe-assignment, ts/no-unsafe-return, ts/no-unsafe-member-access, ts/no-unsafe-call */
2
- import { type QueryClient , type QueryKey , useMutation , useQuery } from '@tanstack/vue-query'
1
+ /* eslint-disable ts/no-unsafe-argument */
2
+ /* eslint-disable ts/no-unsafe-assignment */
3
+
4
+ import {
5
+ type InfiniteQueryPageParamsOptions ,
6
+ type QueryClient ,
7
+ skipToken ,
8
+ useInfiniteQuery ,
9
+ useMutation ,
10
+ useQuery ,
11
+ } from '@tanstack/vue-query'
3
12
import {
4
13
type CreateTRPCClientOptions ,
5
- createTRPCClient ,
6
- type inferRouterClient ,
14
+ type TRPCUntypedClient ,
15
+ createTRPCUntypedClient ,
7
16
} from '@trpc/client'
8
17
import { createTRPCFlatProxy } from '@trpc/server'
9
18
import { createRecursiveProxy } from '@trpc/server/unstable-core-do-not-import'
10
19
import { toRef , toRefs , toValue } from '@vueuse/core'
11
- import { computed , isReactive } from 'vue'
20
+ import { computed , isReactive , onScopeDispose , shallowRef , watch } from 'vue'
12
21
13
- import type { DecoratedProcedureRecord } from './types'
22
+ import type { DecorateProcedure , DecoratedProcedureRecord } from './types'
14
23
import type { AnyTRPCRouter } from '@trpc/server'
15
24
import type { MaybeRefOrGetter } from '@vueuse/core'
25
+ import type { UnionToIntersection } from 'type-fest'
26
+
27
+ type QueryType = 'query' | 'infinite'
28
+ export type TRPCQueryKey = [ readonly string [ ] , { input ?: unknown ; type ?: QueryType } ?]
29
+
30
+ function getQueryKey ( path : string [ ] , input : unknown , type ?: QueryType ) : TRPCQueryKey {
31
+ const splitPath = path . flatMap ( ( part ) => part . split ( '.' ) )
16
32
17
- function getQueryKey ( path : string [ ] , input : unknown ) : QueryKey {
18
- return input === undefined ? path : [ ...path , input ]
33
+ if ( input === undefined && ! type ) {
34
+ return splitPath . length > 0 ? [ splitPath ] : ( [ ] as unknown as TRPCQueryKey )
35
+ }
36
+
37
+ return [
38
+ splitPath ,
39
+ {
40
+ ...( input !== undefined && input !== skipToken && { input } ) ,
41
+ ...( type && { type } ) ,
42
+ } ,
43
+ ]
19
44
}
20
45
21
46
function maybeToRefs ( obj : MaybeRefOrGetter < Record < string , unknown > > ) {
@@ -25,78 +50,135 @@ function maybeToRefs(obj: MaybeRefOrGetter<Record<string, unknown>>) {
25
50
26
51
function createVueQueryProxyDecoration < TRouter extends AnyTRPCRouter > (
27
52
name : string ,
28
- client : inferRouterClient < TRouter > ,
53
+ trpc : TRPCUntypedClient < TRouter > ,
29
54
queryClient : QueryClient ,
30
55
) {
31
- return createRecursiveProxy ( ( opts ) => {
32
- const args = opts . args
33
-
34
- const path = [ name , ...opts . path ]
56
+ return createRecursiveProxy ( ( { args, path : _path } ) => {
57
+ const path = [ name , ..._path ]
35
58
36
59
// The last arg is for instance `.useMutation` or `.useQuery`
37
- const lastProperty = path . pop ( ) !
60
+ const prop = path . pop ( ) ! as keyof UnionToIntersection < DecorateProcedure < any , TRouter > > | '_def'
38
61
39
62
const joinedPath = path . join ( '.' )
40
- const [ firstParam , secondParam ] = args
63
+ const [ firstArg , ...rest ] = args
64
+ const opts = rest [ 0 ] || ( { } as any )
41
65
42
- if ( lastProperty === '_def' ) {
66
+ if ( prop === '_def' ) {
43
67
return { path }
44
68
}
45
69
46
- if ( lastProperty === 'useQuery' ) {
47
- const { trpc, ...queryOptions } = secondParam || ( { } as any )
70
+ if ( prop === 'query' ) {
71
+ return trpc . query ( joinedPath , firstArg , opts )
72
+ }
73
+ if ( prop === 'useQuery' ) {
74
+ const { trpc : trpcOptions , ...queryOptions } = opts
48
75
49
76
return useQuery ( {
50
- queryKey : computed ( ( ) => getQueryKey ( path , toValue ( firstParam ) ) ) ,
51
- queryFn : ( { queryKey, signal } ) =>
52
- ( client as any ) [ joinedPath ] . query ( queryKey . at ( - 1 ) , {
77
+ queryKey : computed ( ( ) => getQueryKey ( path , toValue ( firstArg ) , 'query' ) ) ,
78
+ queryFn : async ( { queryKey, signal } ) =>
79
+ trpc . query ( joinedPath , queryKey [ 1 ] ?. input , {
53
80
signal,
54
- ...trpc ,
81
+ ...trpcOptions ,
55
82
} ) ,
56
83
...maybeToRefs ( queryOptions ) ,
57
84
} )
58
85
}
59
86
60
- if ( lastProperty === 'invalidate' ) {
87
+ if ( prop === 'invalidate' ) {
61
88
return queryClient . invalidateQueries ( {
62
- queryKey : getQueryKey ( path , toValue ( firstParam ) ) ,
89
+ queryKey : getQueryKey ( path , toValue ( firstArg ) , 'query' ) ,
63
90
} )
64
91
}
65
92
66
- if ( lastProperty === 'setQueryData' ) {
67
- return queryClient . setQueryData ( getQueryKey ( path , toValue ( secondParam ) ) , firstParam )
93
+ if ( prop === 'setQueryData' ) {
94
+ return queryClient . setQueryData ( getQueryKey ( path , toValue ( opts ) , 'query' ) , firstArg )
68
95
}
69
96
70
- if ( lastProperty === 'key' ) {
71
- return getQueryKey ( path , toValue ( firstParam ) )
97
+ if ( prop === 'key' ) {
98
+ return getQueryKey ( path , toValue ( firstArg ) , 'query' )
72
99
}
73
100
74
- if ( lastProperty === 'useMutation' ) {
75
- const { trpc, ...mutationOptions } = firstParam || ( { } as any )
101
+ if ( prop === 'mutate' ) {
102
+ return trpc . mutation ( joinedPath , firstArg , opts )
103
+ }
104
+ if ( prop === 'useMutation' ) {
105
+ const { trpc : trpcOptions , ...mutationOptions } = firstArg || ( { } as any )
76
106
77
107
return useMutation ( {
78
- mutationFn : ( payload ) =>
79
- ( client as any ) [ joinedPath ] . mutate ( payload , {
80
- ...trpc ,
108
+ mutationKey : computed ( ( ) => getQueryKey ( path , undefined ) ) ,
109
+ mutationFn : async ( payload ) =>
110
+ trpc . mutation ( joinedPath , payload , {
111
+ ...trpcOptions ,
81
112
} ) ,
82
113
...maybeToRefs ( mutationOptions ) ,
83
114
} )
84
115
}
85
116
86
- return ( client as any ) [ joinedPath ] [ lastProperty ] ( ...args )
117
+ if ( prop === 'subscribe' ) {
118
+ return trpc . subscription ( joinedPath , firstArg , opts )
119
+ }
120
+ if ( prop === 'useSubscription' ) {
121
+ const inputData = toRef ( firstArg )
122
+
123
+ const subscription = shallowRef < ReturnType < ( typeof trpc ) [ 'subscription' ] > > ( )
124
+ watch (
125
+ inputData ,
126
+ ( ) => {
127
+ subscription . value ?. unsubscribe ( )
128
+
129
+ subscription . value = trpc . subscription ( joinedPath , inputData . value , {
130
+ ...opts ,
131
+ } )
132
+ } ,
133
+ { immediate : true } ,
134
+ )
135
+
136
+ onScopeDispose ( ( ) => {
137
+ subscription . value ?. unsubscribe ( )
138
+ } , true )
139
+
140
+ return subscription . value !
141
+ }
142
+
143
+ if ( prop === 'useInfiniteQuery' ) {
144
+ const { trpc : trpcOptions , ...queryOptions } = opts
145
+
146
+ return useInfiniteQuery ( {
147
+ queryKey : computed ( ( ) => getQueryKey ( path , toValue ( firstArg ) , 'infinite' ) ) ,
148
+ queryFn : async ( { queryKey, pageParam, signal } ) =>
149
+ trpc . query (
150
+ joinedPath ,
151
+ {
152
+ ...( queryKey [ 1 ] ?. input as object ) ,
153
+ cursor : pageParam ,
154
+ } ,
155
+ {
156
+ signal,
157
+ ...trpcOptions ,
158
+ } ,
159
+ ) ,
160
+ ...( maybeToRefs ( queryOptions ) as InfiniteQueryPageParamsOptions ) ,
161
+ } )
162
+ }
163
+
164
+ // return (trpc as any)[joinedPath][prop](...args)
165
+ throw new Error ( `Method '.${ prop as string } ()' not supported` )
87
166
} )
88
167
}
89
168
90
- export function createTRPCVueQueryClient < TRouter extends AnyTRPCRouter > ( opts : {
169
+ export function createTRPCVueQueryClient < TRouter extends AnyTRPCRouter > ( {
170
+ trpc,
171
+ queryClient,
172
+ } : {
91
173
queryClient : QueryClient
92
174
trpc : CreateTRPCClientOptions < TRouter >
93
175
} ) {
94
- const client = createTRPCClient < TRouter > ( opts . trpc )
176
+ const client = createTRPCUntypedClient < TRouter > ( trpc )
95
177
96
178
const decoratedClient = createTRPCFlatProxy <
97
179
DecoratedProcedureRecord < TRouter [ '_def' ] [ 'record' ] , TRouter >
98
180
> ( ( key ) => {
99
- return createVueQueryProxyDecoration ( key , client as any , opts . queryClient )
181
+ return createVueQueryProxyDecoration ( key , client , queryClient )
100
182
} )
101
183
102
184
return decoratedClient
0 commit comments