forked from inshared/query
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcreateQueries.ts
203 lines (184 loc) · 7.08 KB
/
createQueries.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
import { createComputed, onCleanup, onMount } from 'solid-js'
import { QueriesObserver } from '@tanstack/query-core'
import { createStore, unwrap } from 'solid-js/store'
import { useQueryClient } from './QueryClientProvider'
import { scheduleMicrotask } from './utils'
import type {
CreateQueryOptions,
CreateQueryResult,
SolidQueryKey,
} from './types'
import type { QueryFunction } from '@tanstack/query-core'
// This defines the `UseQueryOptions` that are accepted in `QueriesOptions` & `GetOptions`.
// - `context` is omitted as it is passed as a root-level option to `useQueries` instead.
type CreateQueryOptionsForCreateQueries<
TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData,
TQueryKey extends SolidQueryKey = SolidQueryKey,
> = Omit<CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>, 'context'>
// Avoid TS depth-limit error in case of large array literal
type MAXIMUM_DEPTH = 20
type GetOptions<T> =
// Part 1: responsible for applying explicit type parameter to function arguments, if object { queryFnData: TQueryFnData, error: TError, data: TData }
T extends {
queryFnData: infer TQueryFnData
error?: infer TError
data: infer TData
}
? CreateQueryOptionsForCreateQueries<TQueryFnData, TError, TData>
: T extends { queryFnData: infer TQueryFnData; error?: infer TError }
? CreateQueryOptionsForCreateQueries<TQueryFnData, TError>
: T extends { data: infer TData; error?: infer TError }
? CreateQueryOptionsForCreateQueries<unknown, TError, TData>
: // Part 2: responsible for applying explicit type parameter to function arguments, if tuple [TQueryFnData, TError, TData]
T extends [infer TQueryFnData, infer TError, infer TData]
? CreateQueryOptionsForCreateQueries<TQueryFnData, TError, TData>
: T extends [infer TQueryFnData, infer TError]
? CreateQueryOptionsForCreateQueries<TQueryFnData, TError>
: T extends [infer TQueryFnData]
? CreateQueryOptionsForCreateQueries<TQueryFnData>
: // Part 3: responsible for inferring and enforcing type if no explicit parameter was provided
T extends {
queryFn?: QueryFunction<infer TQueryFnData, infer TQueryKey>
select: (data: any) => infer TData
}
? CreateQueryOptionsForCreateQueries<
TQueryFnData,
unknown,
TData,
() => TQueryKey
>
: T extends { queryFn?: QueryFunction<infer TQueryFnData, infer TQueryKey> }
? CreateQueryOptionsForCreateQueries<
TQueryFnData,
unknown,
TQueryFnData,
() => TQueryKey
>
: // Fallback
CreateQueryOptionsForCreateQueries
type GetResults<T> =
// Part 1: responsible for mapping explicit type parameter to function result, if object
T extends { queryFnData: any; error?: infer TError; data: infer TData }
? CreateQueryResult<TData, TError>
: T extends { queryFnData: infer TQueryFnData; error?: infer TError }
? CreateQueryResult<TQueryFnData, TError>
: T extends { data: infer TData; error?: infer TError }
? CreateQueryResult<TData, TError>
: // Part 2: responsible for mapping explicit type parameter to function result, if tuple
T extends [any, infer TError, infer TData]
? CreateQueryResult<TData, TError>
: T extends [infer TQueryFnData, infer TError]
? CreateQueryResult<TQueryFnData, TError>
: T extends [infer TQueryFnData]
? CreateQueryResult<TQueryFnData>
: // Part 3: responsible for mapping inferred type to results, if no explicit parameter was provided
T extends {
queryFn?: QueryFunction<unknown, any>
select: (data: any) => infer TData
}
? CreateQueryResult<TData>
: T extends { queryFn?: QueryFunction<infer TQueryFnData, any> }
? CreateQueryResult<TQueryFnData>
: // Fallback
CreateQueryResult
/**
* QueriesOptions reducer recursively unwraps function arguments to infer/enforce type param
*/
export type QueriesOptions<
T extends any[],
Result extends any[] = [],
Depth extends ReadonlyArray<number> = [],
> = Depth['length'] extends MAXIMUM_DEPTH
? CreateQueryOptionsForCreateQueries[]
: T extends []
? []
: T extends [infer Head]
? [...Result, GetOptions<Head>]
: T extends [infer Head, ...infer Tail]
? QueriesOptions<[...Tail], [...Result, GetOptions<Head>], [...Depth, 1]>
: unknown[] extends T
? T
: // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogenous type!
// use this to infer the param types in the case of Array.map() argument
T extends CreateQueryOptionsForCreateQueries<
infer TQueryFnData,
infer TError,
infer TData,
infer TQueryKey
>[]
? CreateQueryOptionsForCreateQueries<TQueryFnData, TError, TData, TQueryKey>[]
: // Fallback
CreateQueryOptionsForCreateQueries[]
/**
* QueriesResults reducer recursively maps type param to results
*/
export type QueriesResults<
T extends any[],
Result extends any[] = [],
Depth extends ReadonlyArray<number> = [],
> = Depth['length'] extends MAXIMUM_DEPTH
? CreateQueryResult[]
: T extends []
? []
: T extends [infer Head]
? [...Result, GetResults<Head>]
: T extends [infer Head, ...infer Tail]
? QueriesResults<[...Tail], [...Result, GetResults<Head>], [...Depth, 1]>
: T extends CreateQueryOptionsForCreateQueries<
infer TQueryFnData,
infer TError,
infer TData,
any
>[]
? // Dynamic-size (homogenous) UseQueryOptions array: map directly to array of results
CreateQueryResult<unknown extends TData ? TQueryFnData : TData, TError>[]
: // Fallback
CreateQueryResult[]
type ArrType<T> = T extends (infer U)[] ? U : never
export function createQueries<T extends any[]>(queriesOptions: {
queries: readonly [...QueriesOptions<T>]
context?: CreateQueryOptions['context']
}): QueriesResults<T> {
const queryClient = useQueryClient({ context: queriesOptions.context })
const normalizeOptions = (
options: ArrType<typeof queriesOptions.queries>,
) => {
const normalizedOptions = { ...options, queryKey: options.queryKey?.() }
const defaultedOptions = queryClient.defaultQueryOptions(normalizedOptions)
defaultedOptions._optimisticResults = 'optimistic'
return defaultedOptions
}
const defaultedQueries = queriesOptions.queries.map((options) =>
normalizeOptions(options),
)
const observer = new QueriesObserver(queryClient, defaultedQueries)
const [state, setState] = createStore(
observer.getOptimisticResult(defaultedQueries),
)
const taskQueue: Array<() => void> = []
const unsubscribe = observer.subscribe((result) => {
taskQueue.push(() => {
setState(unwrap(result))
})
scheduleMicrotask(() => {
const taskToRun = taskQueue.pop()
if (taskToRun) {
taskToRun()
taskQueue.splice(0, taskQueue.length)
}
})
})
onCleanup(unsubscribe)
onMount(() => {
observer.setQueries(defaultedQueries, { listeners: false })
})
createComputed(() => {
const updateDefaultedQueries = queriesOptions.queries.map((options) =>
normalizeOptions(options),
)
observer.setQueries(updateDefaultedQueries)
})
return state as QueriesResults<T>
}