@tanstack/query-core 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/build/cjs/focusManager.js +101 -0
  2. package/build/cjs/focusManager.js.map +1 -0
  3. package/build/cjs/hydration.js +112 -0
  4. package/build/cjs/hydration.js.map +1 -0
  5. package/build/cjs/index.js +51 -0
  6. package/build/cjs/index.js.map +1 -0
  7. package/build/cjs/infiniteQueryBehavior.js +160 -0
  8. package/build/cjs/infiniteQueryBehavior.js.map +1 -0
  9. package/build/cjs/infiniteQueryObserver.js +92 -0
  10. package/build/cjs/infiniteQueryObserver.js.map +1 -0
  11. package/build/cjs/logger.js +18 -0
  12. package/build/cjs/logger.js.map +1 -0
  13. package/build/cjs/mutation.js +258 -0
  14. package/build/cjs/mutation.js.map +1 -0
  15. package/build/cjs/mutationCache.js +99 -0
  16. package/build/cjs/mutationCache.js.map +1 -0
  17. package/build/cjs/mutationObserver.js +130 -0
  18. package/build/cjs/mutationObserver.js.map +1 -0
  19. package/build/cjs/notifyManager.js +114 -0
  20. package/build/cjs/notifyManager.js.map +1 -0
  21. package/build/cjs/onlineManager.js +100 -0
  22. package/build/cjs/onlineManager.js.map +1 -0
  23. package/build/cjs/queriesObserver.js +170 -0
  24. package/build/cjs/queriesObserver.js.map +1 -0
  25. package/build/cjs/query.js +474 -0
  26. package/build/cjs/query.js.map +1 -0
  27. package/build/cjs/queryCache.js +140 -0
  28. package/build/cjs/queryCache.js.map +1 -0
  29. package/build/cjs/queryClient.js +357 -0
  30. package/build/cjs/queryClient.js.map +1 -0
  31. package/build/cjs/queryObserver.js +521 -0
  32. package/build/cjs/queryObserver.js.map +1 -0
  33. package/build/cjs/removable.js +47 -0
  34. package/build/cjs/removable.js.map +1 -0
  35. package/build/cjs/retryer.js +177 -0
  36. package/build/cjs/retryer.js.map +1 -0
  37. package/build/cjs/subscribable.js +43 -0
  38. package/build/cjs/subscribable.js.map +1 -0
  39. package/build/cjs/utils.js +356 -0
  40. package/build/cjs/utils.js.map +1 -0
  41. package/build/esm/index.js +3077 -0
  42. package/build/esm/index.js.map +1 -0
  43. package/build/stats-html.html +2689 -0
  44. package/build/umd/index.development.js +3106 -0
  45. package/build/umd/index.development.js.map +1 -0
  46. package/build/umd/index.production.js +12 -0
  47. package/build/umd/index.production.js.map +1 -0
  48. package/package.json +25 -0
  49. package/src/focusManager.ts +89 -0
  50. package/src/hydration.ts +164 -0
  51. package/src/index.ts +35 -0
  52. package/src/infiniteQueryBehavior.ts +214 -0
  53. package/src/infiniteQueryObserver.ts +159 -0
  54. package/src/logger.native.ts +11 -0
  55. package/src/logger.ts +9 -0
  56. package/src/mutation.ts +349 -0
  57. package/src/mutationCache.ts +157 -0
  58. package/src/mutationObserver.ts +195 -0
  59. package/src/notifyManager.ts +96 -0
  60. package/src/onlineManager.ts +89 -0
  61. package/src/queriesObserver.ts +211 -0
  62. package/src/query.ts +612 -0
  63. package/src/queryCache.ts +206 -0
  64. package/src/queryClient.ts +716 -0
  65. package/src/queryObserver.ts +748 -0
  66. package/src/removable.ts +37 -0
  67. package/src/retryer.ts +215 -0
  68. package/src/subscribable.ts +33 -0
  69. package/src/tests/focusManager.test.tsx +155 -0
  70. package/src/tests/hydration.test.tsx +429 -0
  71. package/src/tests/infiniteQueryBehavior.test.tsx +124 -0
  72. package/src/tests/infiniteQueryObserver.test.tsx +64 -0
  73. package/src/tests/mutationCache.test.tsx +260 -0
  74. package/src/tests/mutationObserver.test.tsx +75 -0
  75. package/src/tests/mutations.test.tsx +363 -0
  76. package/src/tests/notifyManager.test.tsx +51 -0
  77. package/src/tests/onlineManager.test.tsx +148 -0
  78. package/src/tests/queriesObserver.test.tsx +330 -0
  79. package/src/tests/query.test.tsx +888 -0
  80. package/src/tests/queryCache.test.tsx +236 -0
  81. package/src/tests/queryClient.test.tsx +1435 -0
  82. package/src/tests/queryObserver.test.tsx +802 -0
  83. package/src/tests/utils.test.tsx +360 -0
  84. package/src/types.ts +705 -0
  85. package/src/utils.ts +435 -0
package/src/utils.ts ADDED
@@ -0,0 +1,435 @@
1
+ import type { Mutation } from './mutation'
2
+ import type { Query } from './query'
3
+ import type {
4
+ FetchStatus,
5
+ MutationFunction,
6
+ MutationKey,
7
+ MutationOptions,
8
+ QueryFunction,
9
+ QueryKey,
10
+ QueryOptions,
11
+ } from './types'
12
+
13
+ // TYPES
14
+
15
+ export interface QueryFilters {
16
+ /**
17
+ * Filter to active queries, inactive queries or all queries
18
+ */
19
+ type?: QueryTypeFilter
20
+ /**
21
+ * Match query key exactly
22
+ */
23
+ exact?: boolean
24
+ /**
25
+ * Include queries matching this predicate function
26
+ */
27
+ predicate?: (query: Query) => boolean
28
+ /**
29
+ * Include queries matching this query key
30
+ */
31
+ queryKey?: QueryKey
32
+ /**
33
+ * Include or exclude stale queries
34
+ */
35
+ stale?: boolean
36
+ /**
37
+ * Include queries matching their fetchStatus
38
+ */
39
+ fetchStatus?: FetchStatus
40
+ }
41
+
42
+ export interface MutationFilters {
43
+ /**
44
+ * Match mutation key exactly
45
+ */
46
+ exact?: boolean
47
+ /**
48
+ * Include mutations matching this predicate function
49
+ */
50
+ predicate?: (mutation: Mutation<any, any, any>) => boolean
51
+ /**
52
+ * Include mutations matching this mutation key
53
+ */
54
+ mutationKey?: MutationKey
55
+ /**
56
+ * Include or exclude fetching mutations
57
+ */
58
+ fetching?: boolean
59
+ }
60
+
61
+ export type DataUpdateFunction<TInput, TOutput> = (input: TInput) => TOutput
62
+
63
+ export type Updater<TInput, TOutput> =
64
+ | TOutput
65
+ | DataUpdateFunction<TInput, TOutput>
66
+
67
+ export type QueryTypeFilter = 'all' | 'active' | 'inactive'
68
+
69
+ // UTILS
70
+
71
+ export const isServer = typeof window === 'undefined'
72
+
73
+ export function noop(): undefined {
74
+ return undefined
75
+ }
76
+
77
+ export function functionalUpdate<TInput, TOutput>(
78
+ updater: Updater<TInput, TOutput>,
79
+ input: TInput,
80
+ ): TOutput {
81
+ return typeof updater === 'function'
82
+ ? (updater as DataUpdateFunction<TInput, TOutput>)(input)
83
+ : updater
84
+ }
85
+
86
+ export function isValidTimeout(value: unknown): value is number {
87
+ return typeof value === 'number' && value >= 0 && value !== Infinity
88
+ }
89
+
90
+ export function difference<T>(array1: T[], array2: T[]): T[] {
91
+ return array1.filter((x) => array2.indexOf(x) === -1)
92
+ }
93
+
94
+ export function replaceAt<T>(array: T[], index: number, value: T): T[] {
95
+ const copy = array.slice(0)
96
+ copy[index] = value
97
+ return copy
98
+ }
99
+
100
+ export function timeUntilStale(updatedAt: number, staleTime?: number): number {
101
+ return Math.max(updatedAt + (staleTime || 0) - Date.now(), 0)
102
+ }
103
+
104
+ export function parseQueryArgs<
105
+ TOptions extends QueryOptions<any, any, any, TQueryKey>,
106
+ TQueryKey extends QueryKey = QueryKey,
107
+ >(
108
+ arg1: TQueryKey | TOptions,
109
+ arg2?: QueryFunction<any, TQueryKey> | TOptions,
110
+ arg3?: TOptions,
111
+ ): TOptions {
112
+ if (!isQueryKey(arg1)) {
113
+ return arg1 as TOptions
114
+ }
115
+
116
+ if (typeof arg2 === 'function') {
117
+ return { ...arg3, queryKey: arg1, queryFn: arg2 } as TOptions
118
+ }
119
+
120
+ return { ...arg2, queryKey: arg1 } as TOptions
121
+ }
122
+
123
+ export function parseMutationArgs<
124
+ TOptions extends MutationOptions<any, any, any, any>,
125
+ >(
126
+ arg1: MutationKey | MutationFunction<any, any> | TOptions,
127
+ arg2?: MutationFunction<any, any> | TOptions,
128
+ arg3?: TOptions,
129
+ ): TOptions {
130
+ if (isQueryKey(arg1)) {
131
+ if (typeof arg2 === 'function') {
132
+ return { ...arg3, mutationKey: arg1, mutationFn: arg2 } as TOptions
133
+ }
134
+ return { ...arg2, mutationKey: arg1 } as TOptions
135
+ }
136
+
137
+ if (typeof arg1 === 'function') {
138
+ return { ...arg2, mutationFn: arg1 } as TOptions
139
+ }
140
+
141
+ return { ...arg1 } as TOptions
142
+ }
143
+
144
+ export function parseFilterArgs<
145
+ TFilters extends QueryFilters,
146
+ TOptions = unknown,
147
+ >(
148
+ arg1?: QueryKey | TFilters,
149
+ arg2?: TFilters | TOptions,
150
+ arg3?: TOptions,
151
+ ): [TFilters, TOptions | undefined] {
152
+ return (
153
+ isQueryKey(arg1) ? [{ ...arg2, queryKey: arg1 }, arg3] : [arg1 || {}, arg2]
154
+ ) as [TFilters, TOptions]
155
+ }
156
+
157
+ export function parseMutationFilterArgs<
158
+ TFilters extends MutationFilters,
159
+ TOptions = unknown,
160
+ >(
161
+ arg1?: QueryKey | TFilters,
162
+ arg2?: TFilters | TOptions,
163
+ arg3?: TOptions,
164
+ ): [TFilters, TOptions | undefined] {
165
+ return (
166
+ isQueryKey(arg1)
167
+ ? [{ ...arg2, mutationKey: arg1 }, arg3]
168
+ : [arg1 || {}, arg2]
169
+ ) as [TFilters, TOptions]
170
+ }
171
+
172
+ export function matchQuery(
173
+ filters: QueryFilters,
174
+ query: Query<any, any, any, any>,
175
+ ): boolean {
176
+ const {
177
+ type = 'all',
178
+ exact,
179
+ fetchStatus,
180
+ predicate,
181
+ queryKey,
182
+ stale,
183
+ } = filters
184
+
185
+ if (isQueryKey(queryKey)) {
186
+ if (exact) {
187
+ if (query.queryHash !== hashQueryKeyByOptions(queryKey, query.options)) {
188
+ return false
189
+ }
190
+ } else if (!partialMatchKey(query.queryKey, queryKey)) {
191
+ return false
192
+ }
193
+ }
194
+
195
+ if (type !== 'all') {
196
+ const isActive = query.isActive()
197
+ if (type === 'active' && !isActive) {
198
+ return false
199
+ }
200
+ if (type === 'inactive' && isActive) {
201
+ return false
202
+ }
203
+ }
204
+
205
+ if (typeof stale === 'boolean' && query.isStale() !== stale) {
206
+ return false
207
+ }
208
+
209
+ if (
210
+ typeof fetchStatus !== 'undefined' &&
211
+ fetchStatus !== query.state.fetchStatus
212
+ ) {
213
+ return false
214
+ }
215
+
216
+ if (predicate && !predicate(query)) {
217
+ return false
218
+ }
219
+
220
+ return true
221
+ }
222
+
223
+ export function matchMutation(
224
+ filters: MutationFilters,
225
+ mutation: Mutation<any, any>,
226
+ ): boolean {
227
+ const { exact, fetching, predicate, mutationKey } = filters
228
+ if (isQueryKey(mutationKey)) {
229
+ if (!mutation.options.mutationKey) {
230
+ return false
231
+ }
232
+ if (exact) {
233
+ if (
234
+ hashQueryKey(mutation.options.mutationKey) !== hashQueryKey(mutationKey)
235
+ ) {
236
+ return false
237
+ }
238
+ } else if (!partialMatchKey(mutation.options.mutationKey, mutationKey)) {
239
+ return false
240
+ }
241
+ }
242
+
243
+ if (
244
+ typeof fetching === 'boolean' &&
245
+ (mutation.state.status === 'loading') !== fetching
246
+ ) {
247
+ return false
248
+ }
249
+
250
+ if (predicate && !predicate(mutation)) {
251
+ return false
252
+ }
253
+
254
+ return true
255
+ }
256
+
257
+ export function hashQueryKeyByOptions<TQueryKey extends QueryKey = QueryKey>(
258
+ queryKey: TQueryKey,
259
+ options?: QueryOptions<any, any, any, TQueryKey>,
260
+ ): string {
261
+ const hashFn = options?.queryKeyHashFn || hashQueryKey
262
+ return hashFn(queryKey)
263
+ }
264
+
265
+ /**
266
+ * Default query keys hash function.
267
+ * Hashes the value into a stable hash.
268
+ */
269
+ export function hashQueryKey(queryKey: QueryKey): string {
270
+ return JSON.stringify(queryKey, (_, val) =>
271
+ isPlainObject(val)
272
+ ? Object.keys(val)
273
+ .sort()
274
+ .reduce((result, key) => {
275
+ result[key] = val[key]
276
+ return result
277
+ }, {} as any)
278
+ : val,
279
+ )
280
+ }
281
+
282
+ /**
283
+ * Checks if key `b` partially matches with key `a`.
284
+ */
285
+ export function partialMatchKey(a: QueryKey, b: QueryKey): boolean {
286
+ return partialDeepEqual(a, b)
287
+ }
288
+
289
+ /**
290
+ * Checks if `b` partially matches with `a`.
291
+ */
292
+ export function partialDeepEqual(a: any, b: any): boolean {
293
+ if (a === b) {
294
+ return true
295
+ }
296
+
297
+ if (typeof a !== typeof b) {
298
+ return false
299
+ }
300
+
301
+ if (a && b && typeof a === 'object' && typeof b === 'object') {
302
+ return !Object.keys(b).some((key) => !partialDeepEqual(a[key], b[key]))
303
+ }
304
+
305
+ return false
306
+ }
307
+
308
+ /**
309
+ * This function returns `a` if `b` is deeply equal.
310
+ * If not, it will replace any deeply equal children of `b` with those of `a`.
311
+ * This can be used for structural sharing between JSON values for example.
312
+ */
313
+ export function replaceEqualDeep<T>(a: unknown, b: T): T
314
+ export function replaceEqualDeep(a: any, b: any): any {
315
+ if (a === b) {
316
+ return a
317
+ }
318
+
319
+ const array = isPlainArray(a) && isPlainArray(b)
320
+
321
+ if (array || (isPlainObject(a) && isPlainObject(b))) {
322
+ const aSize = array ? a.length : Object.keys(a).length
323
+ const bItems = array ? b : Object.keys(b)
324
+ const bSize = bItems.length
325
+ const copy: any = array ? [] : {}
326
+
327
+ let equalItems = 0
328
+
329
+ for (let i = 0; i < bSize; i++) {
330
+ const key = array ? i : bItems[i]
331
+ copy[key] = replaceEqualDeep(a[key], b[key])
332
+ if (copy[key] === a[key]) {
333
+ equalItems++
334
+ }
335
+ }
336
+
337
+ return aSize === bSize && equalItems === aSize ? a : copy
338
+ }
339
+
340
+ return b
341
+ }
342
+
343
+ /**
344
+ * Shallow compare objects. Only works with objects that always have the same properties.
345
+ */
346
+ export function shallowEqualObjects<T>(a: T, b: T): boolean {
347
+ if ((a && !b) || (b && !a)) {
348
+ return false
349
+ }
350
+
351
+ for (const key in a) {
352
+ if (a[key] !== b[key]) {
353
+ return false
354
+ }
355
+ }
356
+
357
+ return true
358
+ }
359
+
360
+ export function isPlainArray(value: unknown) {
361
+ return Array.isArray(value) && value.length === Object.keys(value).length
362
+ }
363
+
364
+ // Copied from: https://github.com/jonschlinkert/is-plain-object
365
+ export function isPlainObject(o: any): o is Object {
366
+ if (!hasObjectPrototype(o)) {
367
+ return false
368
+ }
369
+
370
+ // If has modified constructor
371
+ const ctor = o.constructor
372
+ if (typeof ctor === 'undefined') {
373
+ return true
374
+ }
375
+
376
+ // If has modified prototype
377
+ const prot = ctor.prototype
378
+ if (!hasObjectPrototype(prot)) {
379
+ return false
380
+ }
381
+
382
+ // If constructor does not have an Object-specific method
383
+ if (!prot.hasOwnProperty('isPrototypeOf')) {
384
+ return false
385
+ }
386
+
387
+ // Most likely a plain Object
388
+ return true
389
+ }
390
+
391
+ function hasObjectPrototype(o: any): boolean {
392
+ return Object.prototype.toString.call(o) === '[object Object]'
393
+ }
394
+
395
+ export function isQueryKey(value: unknown): value is QueryKey {
396
+ return Array.isArray(value)
397
+ }
398
+
399
+ export function isError(value: any): value is Error {
400
+ return value instanceof Error
401
+ }
402
+
403
+ export function sleep(timeout: number): Promise<void> {
404
+ return new Promise((resolve) => {
405
+ setTimeout(resolve, timeout)
406
+ })
407
+ }
408
+
409
+ /**
410
+ * Schedules a microtask.
411
+ * This can be useful to schedule state updates after rendering.
412
+ */
413
+ export function scheduleMicrotask(callback: () => void) {
414
+ sleep(0).then(callback)
415
+ }
416
+
417
+ export function getAbortController(): AbortController | undefined {
418
+ if (typeof AbortController === 'function') {
419
+ return new AbortController()
420
+ }
421
+ }
422
+
423
+ export function replaceData<
424
+ TData,
425
+ TOptions extends QueryOptions<any, any, any, any>,
426
+ >(prevData: TData | undefined, data: TData, options: TOptions): TData {
427
+ // Use prev data if an isDataEqual function is defined and returns `true`
428
+ if (options.isDataEqual?.(prevData, data)) {
429
+ return prevData as TData
430
+ } else if (options.structuralSharing !== false) {
431
+ // Structurally share data between prev and new data if needed
432
+ return replaceEqualDeep(prevData, data)
433
+ }
434
+ return data
435
+ }