@tanstack/query-core 5.83.1 → 5.87.4
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.
- package/build/legacy/{hydration-Cvr-9VdO.d.ts → hydration-BYonJkjc.d.ts} +8 -6
- package/build/legacy/{hydration-CADtEOkK.d.cts → hydration-_hO-y142.d.cts} +8 -6
- package/build/legacy/hydration.d.cts +1 -1
- package/build/legacy/hydration.d.ts +1 -1
- package/build/legacy/index.cjs +14 -12
- package/build/legacy/index.cjs.map +1 -1
- package/build/legacy/index.d.cts +5 -4
- package/build/legacy/index.d.ts +5 -4
- package/build/legacy/index.js +26 -23
- package/build/legacy/index.js.map +1 -1
- package/build/legacy/infiniteQueryBehavior.d.cts +1 -1
- package/build/legacy/infiniteQueryBehavior.d.ts +1 -1
- package/build/legacy/infiniteQueryObserver.d.cts +1 -1
- package/build/legacy/infiniteQueryObserver.d.ts +1 -1
- package/build/legacy/mutation.d.cts +1 -1
- package/build/legacy/mutation.d.ts +1 -1
- package/build/legacy/mutationCache.d.cts +1 -1
- package/build/legacy/mutationCache.d.ts +1 -1
- package/build/legacy/mutationObserver.d.cts +1 -1
- package/build/legacy/mutationObserver.d.ts +1 -1
- package/build/legacy/notifyManager.cjs +2 -1
- package/build/legacy/notifyManager.cjs.map +1 -1
- package/build/legacy/notifyManager.js +2 -1
- package/build/legacy/notifyManager.js.map +1 -1
- package/build/legacy/queriesObserver.cjs +19 -13
- package/build/legacy/queriesObserver.cjs.map +1 -1
- package/build/legacy/queriesObserver.d.cts +1 -1
- package/build/legacy/queriesObserver.d.ts +1 -1
- package/build/legacy/queriesObserver.js +20 -14
- package/build/legacy/queriesObserver.js.map +1 -1
- package/build/legacy/query.cjs +78 -61
- package/build/legacy/query.cjs.map +1 -1
- package/build/legacy/query.d.cts +1 -1
- package/build/legacy/query.d.ts +1 -1
- package/build/legacy/query.js +79 -62
- package/build/legacy/query.js.map +1 -1
- package/build/legacy/queryCache.d.cts +1 -1
- package/build/legacy/queryCache.d.ts +1 -1
- package/build/legacy/queryClient.d.cts +1 -1
- package/build/legacy/queryClient.d.ts +1 -1
- package/build/legacy/queryObserver.cjs +12 -9
- package/build/legacy/queryObserver.cjs.map +1 -1
- package/build/legacy/queryObserver.d.cts +1 -1
- package/build/legacy/queryObserver.d.ts +1 -1
- package/build/legacy/queryObserver.js +12 -9
- package/build/legacy/queryObserver.js.map +1 -1
- package/build/legacy/removable.cjs +3 -2
- package/build/legacy/removable.cjs.map +1 -1
- package/build/legacy/removable.js +3 -2
- package/build/legacy/removable.js.map +1 -1
- package/build/legacy/retryer.cjs +12 -16
- package/build/legacy/retryer.cjs.map +1 -1
- package/build/legacy/retryer.d.cts +1 -1
- package/build/legacy/retryer.d.ts +1 -1
- package/build/legacy/retryer.js +12 -16
- package/build/legacy/retryer.js.map +1 -1
- package/build/legacy/streamedQuery.cjs +7 -8
- package/build/legacy/streamedQuery.cjs.map +1 -1
- package/build/legacy/streamedQuery.d.cts +18 -10
- package/build/legacy/streamedQuery.d.ts +18 -10
- package/build/legacy/streamedQuery.js +7 -8
- package/build/legacy/streamedQuery.js.map +1 -1
- package/build/legacy/timeoutManager.cjs +110 -0
- package/build/legacy/timeoutManager.cjs.map +1 -0
- package/build/legacy/timeoutManager.d.cts +58 -0
- package/build/legacy/timeoutManager.d.ts +58 -0
- package/build/legacy/timeoutManager.js +81 -0
- package/build/legacy/timeoutManager.js.map +1 -0
- package/build/legacy/types.d.cts +1 -1
- package/build/legacy/types.d.ts +1 -1
- package/build/legacy/utils.cjs +26 -22
- package/build/legacy/utils.cjs.map +1 -1
- package/build/legacy/utils.d.cts +1 -1
- package/build/legacy/utils.d.ts +1 -1
- package/build/legacy/utils.js +26 -22
- package/build/legacy/utils.js.map +1 -1
- package/build/modern/{hydration-Cvr-9VdO.d.ts → hydration-BYonJkjc.d.ts} +8 -6
- package/build/modern/{hydration-CADtEOkK.d.cts → hydration-_hO-y142.d.cts} +8 -6
- package/build/modern/hydration.d.cts +1 -1
- package/build/modern/hydration.d.ts +1 -1
- package/build/modern/index.cjs +14 -12
- package/build/modern/index.cjs.map +1 -1
- package/build/modern/index.d.cts +5 -4
- package/build/modern/index.d.ts +5 -4
- package/build/modern/index.js +26 -23
- package/build/modern/index.js.map +1 -1
- package/build/modern/infiniteQueryBehavior.d.cts +1 -1
- package/build/modern/infiniteQueryBehavior.d.ts +1 -1
- package/build/modern/infiniteQueryObserver.d.cts +1 -1
- package/build/modern/infiniteQueryObserver.d.ts +1 -1
- package/build/modern/mutation.d.cts +1 -1
- package/build/modern/mutation.d.ts +1 -1
- package/build/modern/mutationCache.d.cts +1 -1
- package/build/modern/mutationCache.d.ts +1 -1
- package/build/modern/mutationObserver.d.cts +1 -1
- package/build/modern/mutationObserver.d.ts +1 -1
- package/build/modern/notifyManager.cjs +2 -1
- package/build/modern/notifyManager.cjs.map +1 -1
- package/build/modern/notifyManager.js +2 -1
- package/build/modern/notifyManager.js.map +1 -1
- package/build/modern/queriesObserver.cjs +19 -13
- package/build/modern/queriesObserver.cjs.map +1 -1
- package/build/modern/queriesObserver.d.cts +1 -1
- package/build/modern/queriesObserver.d.ts +1 -1
- package/build/modern/queriesObserver.js +20 -14
- package/build/modern/queriesObserver.js.map +1 -1
- package/build/modern/query.cjs +71 -52
- package/build/modern/query.cjs.map +1 -1
- package/build/modern/query.d.cts +1 -1
- package/build/modern/query.d.ts +1 -1
- package/build/modern/query.js +72 -53
- package/build/modern/query.js.map +1 -1
- package/build/modern/queryCache.d.cts +1 -1
- package/build/modern/queryCache.d.ts +1 -1
- package/build/modern/queryClient.d.cts +1 -1
- package/build/modern/queryClient.d.ts +1 -1
- package/build/modern/queryObserver.cjs +12 -9
- package/build/modern/queryObserver.cjs.map +1 -1
- package/build/modern/queryObserver.d.cts +1 -1
- package/build/modern/queryObserver.d.ts +1 -1
- package/build/modern/queryObserver.js +12 -9
- package/build/modern/queryObserver.js.map +1 -1
- package/build/modern/removable.cjs +3 -2
- package/build/modern/removable.cjs.map +1 -1
- package/build/modern/removable.js +3 -2
- package/build/modern/removable.js.map +1 -1
- package/build/modern/retryer.cjs +12 -14
- package/build/modern/retryer.cjs.map +1 -1
- package/build/modern/retryer.d.cts +1 -1
- package/build/modern/retryer.d.ts +1 -1
- package/build/modern/retryer.js +12 -14
- package/build/modern/retryer.js.map +1 -1
- package/build/modern/streamedQuery.cjs +7 -8
- package/build/modern/streamedQuery.cjs.map +1 -1
- package/build/modern/streamedQuery.d.cts +18 -10
- package/build/modern/streamedQuery.d.ts +18 -10
- package/build/modern/streamedQuery.js +7 -8
- package/build/modern/streamedQuery.js.map +1 -1
- package/build/modern/timeoutManager.cjs +98 -0
- package/build/modern/timeoutManager.cjs.map +1 -0
- package/build/modern/timeoutManager.d.cts +58 -0
- package/build/modern/timeoutManager.d.ts +58 -0
- package/build/modern/timeoutManager.js +70 -0
- package/build/modern/timeoutManager.js.map +1 -0
- package/build/modern/types.d.cts +1 -1
- package/build/modern/types.d.ts +1 -1
- package/build/modern/utils.cjs +26 -22
- package/build/modern/utils.cjs.map +1 -1
- package/build/modern/utils.d.cts +1 -1
- package/build/modern/utils.d.ts +1 -1
- package/build/modern/utils.js +26 -22
- package/build/modern/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +32 -27
- package/src/notifyManager.ts +3 -1
- package/src/queriesObserver.ts +24 -15
- package/src/query.ts +96 -69
- package/src/queryObserver.ts +19 -11
- package/src/removable.ts +5 -3
- package/src/retryer.ts +20 -17
- package/src/streamedQuery.ts +47 -23
- package/src/timeoutManager.ts +135 -0
- package/src/utils.ts +39 -31
package/src/queriesObserver.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { notifyManager } from './notifyManager'
|
|
2
2
|
import { QueryObserver } from './queryObserver'
|
|
3
3
|
import { Subscribable } from './subscribable'
|
|
4
|
-
import { replaceEqualDeep } from './utils'
|
|
4
|
+
import { replaceEqualDeep, shallowEqualObjects } from './utils'
|
|
5
5
|
import type {
|
|
6
6
|
DefaultedQueryObserverOptions,
|
|
7
7
|
QueryObserverOptions,
|
|
@@ -118,30 +118,39 @@ export class QueriesObserver<
|
|
|
118
118
|
observer.getCurrentResult(),
|
|
119
119
|
)
|
|
120
120
|
|
|
121
|
+
const hasLengthChange = prevObservers.length !== newObservers.length
|
|
121
122
|
const hasIndexChange = newObservers.some(
|
|
122
123
|
(observer, index) => observer !== prevObservers[index],
|
|
123
124
|
)
|
|
125
|
+
const hasStructuralChange = hasLengthChange || hasIndexChange
|
|
124
126
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
127
|
+
const hasResultChange = hasStructuralChange
|
|
128
|
+
? true
|
|
129
|
+
: newResult.some((result, index) => {
|
|
130
|
+
const prev = this.#result[index]
|
|
131
|
+
return !prev || !shallowEqualObjects(result, prev)
|
|
132
|
+
})
|
|
128
133
|
|
|
129
|
-
|
|
130
|
-
this.#result = newResult
|
|
134
|
+
if (!hasStructuralChange && !hasResultChange) return
|
|
131
135
|
|
|
132
|
-
if (
|
|
133
|
-
|
|
136
|
+
if (hasStructuralChange) {
|
|
137
|
+
this.#observers = newObservers
|
|
134
138
|
}
|
|
135
139
|
|
|
136
|
-
|
|
137
|
-
observer.destroy()
|
|
138
|
-
})
|
|
140
|
+
this.#result = newResult
|
|
139
141
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
142
|
+
if (!this.hasListeners()) return
|
|
143
|
+
|
|
144
|
+
if (hasStructuralChange) {
|
|
145
|
+
difference(prevObservers, newObservers).forEach((observer) => {
|
|
146
|
+
observer.destroy()
|
|
143
147
|
})
|
|
144
|
-
|
|
148
|
+
difference(newObservers, prevObservers).forEach((observer) => {
|
|
149
|
+
observer.subscribe((result) => {
|
|
150
|
+
this.#onUpdate(observer, result)
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
}
|
|
145
154
|
|
|
146
155
|
this.#notify()
|
|
147
156
|
})
|
package/src/query.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
timeUntilStale,
|
|
9
9
|
} from './utils'
|
|
10
10
|
import { notifyManager } from './notifyManager'
|
|
11
|
-
import { canFetch, createRetryer
|
|
11
|
+
import { CancelledError, canFetch, createRetryer } from './retryer'
|
|
12
12
|
import { Removable } from './removable'
|
|
13
13
|
import type { QueryCache } from './queryCache'
|
|
14
14
|
import type { QueryClient } from './queryClient'
|
|
@@ -205,6 +205,18 @@ export class Query<
|
|
|
205
205
|
this.options = { ...this.#defaultOptions, ...options }
|
|
206
206
|
|
|
207
207
|
this.updateGcTime(this.options.gcTime)
|
|
208
|
+
|
|
209
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
210
|
+
if (this.state && this.state.data === undefined) {
|
|
211
|
+
const defaultState = getDefaultState(this.options)
|
|
212
|
+
if (defaultState.data !== undefined) {
|
|
213
|
+
this.setData(defaultState.data, {
|
|
214
|
+
updatedAt: defaultState.dataUpdatedAt,
|
|
215
|
+
manual: true,
|
|
216
|
+
})
|
|
217
|
+
this.#initialState = defaultState
|
|
218
|
+
}
|
|
219
|
+
}
|
|
208
220
|
}
|
|
209
221
|
|
|
210
222
|
protected optionalRemove() {
|
|
@@ -372,11 +384,17 @@ export class Query<
|
|
|
372
384
|
}
|
|
373
385
|
}
|
|
374
386
|
|
|
375
|
-
fetch(
|
|
387
|
+
async fetch(
|
|
376
388
|
options?: QueryOptions<TQueryFnData, TError, TData, TQueryKey>,
|
|
377
389
|
fetchOptions?: FetchOptions<TQueryFnData>,
|
|
378
390
|
): Promise<TData> {
|
|
379
|
-
if (
|
|
391
|
+
if (
|
|
392
|
+
this.state.fetchStatus !== 'idle' &&
|
|
393
|
+
// If the promise in the retyer is already rejected, we have to definitely
|
|
394
|
+
// re-start the fetch; there is a chance that the query is still in a
|
|
395
|
+
// pending state when that happens
|
|
396
|
+
this.#retryer?.status() !== 'rejected'
|
|
397
|
+
) {
|
|
380
398
|
if (this.state.data !== undefined && fetchOptions?.cancelRefetch) {
|
|
381
399
|
// Silently cancel current fetch if the user wants to cancel refetch
|
|
382
400
|
this.cancel({ silent: true })
|
|
@@ -495,69 +513,21 @@ export class Query<
|
|
|
495
513
|
this.#dispatch({ type: 'fetch', meta: context.fetchOptions?.meta })
|
|
496
514
|
}
|
|
497
515
|
|
|
498
|
-
const onError = (error: TError | { silent?: boolean }) => {
|
|
499
|
-
// Optimistically update state if needed
|
|
500
|
-
if (!(isCancelledError(error) && error.silent)) {
|
|
501
|
-
this.#dispatch({
|
|
502
|
-
type: 'error',
|
|
503
|
-
error: error as TError,
|
|
504
|
-
})
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
if (!isCancelledError(error)) {
|
|
508
|
-
// Notify cache callback
|
|
509
|
-
this.#cache.config.onError?.(
|
|
510
|
-
error as any,
|
|
511
|
-
this as Query<any, any, any, any>,
|
|
512
|
-
)
|
|
513
|
-
this.#cache.config.onSettled?.(
|
|
514
|
-
this.state.data,
|
|
515
|
-
error as any,
|
|
516
|
-
this as Query<any, any, any, any>,
|
|
517
|
-
)
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
// Schedule query gc after fetching
|
|
521
|
-
this.scheduleGc()
|
|
522
|
-
}
|
|
523
|
-
|
|
524
516
|
// Try to fetch the data
|
|
525
517
|
this.#retryer = createRetryer({
|
|
526
518
|
initialPromise: fetchOptions?.initialPromise as
|
|
527
519
|
| Promise<TData>
|
|
528
520
|
| undefined,
|
|
529
521
|
fn: context.fetchFn as () => Promise<TData>,
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
)
|
|
537
|
-
}
|
|
538
|
-
onError(new Error(`${this.queryHash} data is undefined`) as any)
|
|
539
|
-
return
|
|
522
|
+
onCancel: (error) => {
|
|
523
|
+
if (error instanceof CancelledError && error.revert) {
|
|
524
|
+
this.setState({
|
|
525
|
+
...this.#revertState,
|
|
526
|
+
fetchStatus: 'idle' as const,
|
|
527
|
+
})
|
|
540
528
|
}
|
|
541
|
-
|
|
542
|
-
try {
|
|
543
|
-
this.setData(data)
|
|
544
|
-
} catch (error) {
|
|
545
|
-
onError(error as TError)
|
|
546
|
-
return
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// Notify cache callback
|
|
550
|
-
this.#cache.config.onSuccess?.(data, this as Query<any, any, any, any>)
|
|
551
|
-
this.#cache.config.onSettled?.(
|
|
552
|
-
data,
|
|
553
|
-
this.state.error as any,
|
|
554
|
-
this as Query<any, any, any, any>,
|
|
555
|
-
)
|
|
556
|
-
|
|
557
|
-
// Schedule query gc after fetching
|
|
558
|
-
this.scheduleGc()
|
|
529
|
+
abortController.abort()
|
|
559
530
|
},
|
|
560
|
-
onError,
|
|
561
531
|
onFail: (failureCount, error) => {
|
|
562
532
|
this.#dispatch({ type: 'failed', failureCount, error })
|
|
563
533
|
},
|
|
@@ -573,7 +543,66 @@ export class Query<
|
|
|
573
543
|
canRun: () => true,
|
|
574
544
|
})
|
|
575
545
|
|
|
576
|
-
|
|
546
|
+
try {
|
|
547
|
+
const data = await this.#retryer.start()
|
|
548
|
+
// this is more of a runtime guard
|
|
549
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
550
|
+
if (data === undefined) {
|
|
551
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
552
|
+
console.error(
|
|
553
|
+
`Query data cannot be undefined. Please make sure to return a value other than undefined from your query function. Affected query key: ${this.queryHash}`,
|
|
554
|
+
)
|
|
555
|
+
}
|
|
556
|
+
throw new Error(`${this.queryHash} data is undefined`)
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
this.setData(data)
|
|
560
|
+
|
|
561
|
+
// Notify cache callback
|
|
562
|
+
this.#cache.config.onSuccess?.(data, this as Query<any, any, any, any>)
|
|
563
|
+
this.#cache.config.onSettled?.(
|
|
564
|
+
data,
|
|
565
|
+
this.state.error as any,
|
|
566
|
+
this as Query<any, any, any, any>,
|
|
567
|
+
)
|
|
568
|
+
return data
|
|
569
|
+
} catch (error) {
|
|
570
|
+
if (error instanceof CancelledError) {
|
|
571
|
+
if (error.silent) {
|
|
572
|
+
// silent cancellation implies a new fetch is going to be started,
|
|
573
|
+
// so we piggyback onto that promise
|
|
574
|
+
return this.#retryer.promise
|
|
575
|
+
} else if (error.revert) {
|
|
576
|
+
// transform error into reverted state data
|
|
577
|
+
// if the initial fetch was cancelled, we have no data, so we have
|
|
578
|
+
// to get reject with a CancelledError
|
|
579
|
+
if (this.state.data === undefined) {
|
|
580
|
+
throw error
|
|
581
|
+
}
|
|
582
|
+
return this.state.data
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
this.#dispatch({
|
|
586
|
+
type: 'error',
|
|
587
|
+
error: error as TError,
|
|
588
|
+
})
|
|
589
|
+
|
|
590
|
+
// Notify cache callback
|
|
591
|
+
this.#cache.config.onError?.(
|
|
592
|
+
error as any,
|
|
593
|
+
this as Query<any, any, any, any>,
|
|
594
|
+
)
|
|
595
|
+
this.#cache.config.onSettled?.(
|
|
596
|
+
this.state.data,
|
|
597
|
+
error as any,
|
|
598
|
+
this as Query<any, any, any, any>,
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
throw error // rethrow the error for further handling
|
|
602
|
+
} finally {
|
|
603
|
+
// Schedule query gc after fetching
|
|
604
|
+
this.scheduleGc()
|
|
605
|
+
}
|
|
577
606
|
}
|
|
578
607
|
|
|
579
608
|
#dispatch(action: Action<TData, TError>): void {
|
|
@@ -604,29 +633,27 @@ export class Query<
|
|
|
604
633
|
fetchMeta: action.meta ?? null,
|
|
605
634
|
}
|
|
606
635
|
case 'success':
|
|
607
|
-
|
|
608
|
-
this.#revertState = undefined
|
|
609
|
-
return {
|
|
636
|
+
const newState = {
|
|
610
637
|
...state,
|
|
611
638
|
data: action.data,
|
|
612
639
|
dataUpdateCount: state.dataUpdateCount + 1,
|
|
613
640
|
dataUpdatedAt: action.dataUpdatedAt ?? Date.now(),
|
|
614
641
|
error: null,
|
|
615
642
|
isInvalidated: false,
|
|
616
|
-
status: 'success',
|
|
643
|
+
status: 'success' as const,
|
|
617
644
|
...(!action.manual && {
|
|
618
|
-
fetchStatus: 'idle',
|
|
645
|
+
fetchStatus: 'idle' as const,
|
|
619
646
|
fetchFailureCount: 0,
|
|
620
647
|
fetchFailureReason: null,
|
|
621
648
|
}),
|
|
622
649
|
}
|
|
650
|
+
// If fetching ends successfully, we don't need revertState as a fallback anymore.
|
|
651
|
+
// For manual updates, capture the state to revert to it in case of a cancellation.
|
|
652
|
+
this.#revertState = action.manual ? newState : undefined
|
|
653
|
+
|
|
654
|
+
return newState
|
|
623
655
|
case 'error':
|
|
624
656
|
const error = action.error
|
|
625
|
-
|
|
626
|
-
if (isCancelledError(error) && error.revert && this.#revertState) {
|
|
627
|
-
return { ...this.#revertState, fetchStatus: 'idle' }
|
|
628
|
-
}
|
|
629
|
-
|
|
630
657
|
return {
|
|
631
658
|
...state,
|
|
632
659
|
error,
|
package/src/queryObserver.ts
CHANGED
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
shallowEqualObjects,
|
|
14
14
|
timeUntilStale,
|
|
15
15
|
} from './utils'
|
|
16
|
+
import { timeoutManager } from './timeoutManager'
|
|
17
|
+
import type { ManagedTimerId } from './timeoutManager'
|
|
16
18
|
import type { FetchOptions, Query, QueryState } from './query'
|
|
17
19
|
import type { QueryClient } from './queryClient'
|
|
18
20
|
import type { PendingThenable, Thenable } from './thenable'
|
|
@@ -62,8 +64,8 @@ export class QueryObserver<
|
|
|
62
64
|
// This property keeps track of the last query with defined data.
|
|
63
65
|
// It will be used to pass the previous data and query to the placeholder function between renders.
|
|
64
66
|
#lastQueryWithDefinedData?: Query<TQueryFnData, TError, TQueryData, TQueryKey>
|
|
65
|
-
#staleTimeoutId?:
|
|
66
|
-
#refetchIntervalId?:
|
|
67
|
+
#staleTimeoutId?: ManagedTimerId
|
|
68
|
+
#refetchIntervalId?: ManagedTimerId
|
|
67
69
|
#currentRefetchInterval?: number | false
|
|
68
70
|
#trackedProps = new Set<keyof QueryObserverResult>()
|
|
69
71
|
|
|
@@ -82,11 +84,6 @@ export class QueryObserver<
|
|
|
82
84
|
this.#client = client
|
|
83
85
|
this.#selectError = null
|
|
84
86
|
this.#currentThenable = pendingThenable()
|
|
85
|
-
if (!this.options.experimental_prefetchInRender) {
|
|
86
|
-
this.#currentThenable.reject(
|
|
87
|
-
new Error('experimental_prefetchInRender feature flag is not enabled'),
|
|
88
|
-
)
|
|
89
|
-
}
|
|
90
87
|
|
|
91
88
|
this.bindMethods()
|
|
92
89
|
this.setOptions(options)
|
|
@@ -272,6 +269,17 @@ export class QueryObserver<
|
|
|
272
269
|
get: (target, key) => {
|
|
273
270
|
this.trackProp(key as keyof QueryObserverResult)
|
|
274
271
|
onPropTracked?.(key as keyof QueryObserverResult)
|
|
272
|
+
if (
|
|
273
|
+
key === 'promise' &&
|
|
274
|
+
!this.options.experimental_prefetchInRender &&
|
|
275
|
+
this.#currentThenable.status === 'pending'
|
|
276
|
+
) {
|
|
277
|
+
this.#currentThenable.reject(
|
|
278
|
+
new Error(
|
|
279
|
+
'experimental_prefetchInRender feature flag is not enabled',
|
|
280
|
+
),
|
|
281
|
+
)
|
|
282
|
+
}
|
|
275
283
|
return Reflect.get(target, key)
|
|
276
284
|
},
|
|
277
285
|
})
|
|
@@ -359,7 +367,7 @@ export class QueryObserver<
|
|
|
359
367
|
// To mitigate this issue we always add 1 ms to the timeout.
|
|
360
368
|
const timeout = time + 1
|
|
361
369
|
|
|
362
|
-
this.#staleTimeoutId = setTimeout(() => {
|
|
370
|
+
this.#staleTimeoutId = timeoutManager.setTimeout(() => {
|
|
363
371
|
if (!this.#currentResult.isStale) {
|
|
364
372
|
this.updateResult()
|
|
365
373
|
}
|
|
@@ -388,7 +396,7 @@ export class QueryObserver<
|
|
|
388
396
|
return
|
|
389
397
|
}
|
|
390
398
|
|
|
391
|
-
this.#refetchIntervalId = setInterval(() => {
|
|
399
|
+
this.#refetchIntervalId = timeoutManager.setInterval(() => {
|
|
392
400
|
if (
|
|
393
401
|
this.options.refetchIntervalInBackground ||
|
|
394
402
|
focusManager.isFocused()
|
|
@@ -405,14 +413,14 @@ export class QueryObserver<
|
|
|
405
413
|
|
|
406
414
|
#clearStaleTimeout(): void {
|
|
407
415
|
if (this.#staleTimeoutId) {
|
|
408
|
-
clearTimeout(this.#staleTimeoutId)
|
|
416
|
+
timeoutManager.clearTimeout(this.#staleTimeoutId)
|
|
409
417
|
this.#staleTimeoutId = undefined
|
|
410
418
|
}
|
|
411
419
|
}
|
|
412
420
|
|
|
413
421
|
#clearRefetchInterval(): void {
|
|
414
422
|
if (this.#refetchIntervalId) {
|
|
415
|
-
clearInterval(this.#refetchIntervalId)
|
|
423
|
+
timeoutManager.clearInterval(this.#refetchIntervalId)
|
|
416
424
|
this.#refetchIntervalId = undefined
|
|
417
425
|
}
|
|
418
426
|
}
|
package/src/removable.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { timeoutManager } from './timeoutManager'
|
|
1
2
|
import { isServer, isValidTimeout } from './utils'
|
|
3
|
+
import type { ManagedTimerId } from './timeoutManager'
|
|
2
4
|
|
|
3
5
|
export abstract class Removable {
|
|
4
6
|
gcTime!: number
|
|
5
|
-
#gcTimeout?:
|
|
7
|
+
#gcTimeout?: ManagedTimerId
|
|
6
8
|
|
|
7
9
|
destroy(): void {
|
|
8
10
|
this.clearGcTimeout()
|
|
@@ -12,7 +14,7 @@ export abstract class Removable {
|
|
|
12
14
|
this.clearGcTimeout()
|
|
13
15
|
|
|
14
16
|
if (isValidTimeout(this.gcTime)) {
|
|
15
|
-
this.#gcTimeout = setTimeout(() => {
|
|
17
|
+
this.#gcTimeout = timeoutManager.setTimeout(() => {
|
|
16
18
|
this.optionalRemove()
|
|
17
19
|
}, this.gcTime)
|
|
18
20
|
}
|
|
@@ -28,7 +30,7 @@ export abstract class Removable {
|
|
|
28
30
|
|
|
29
31
|
protected clearGcTimeout() {
|
|
30
32
|
if (this.#gcTimeout) {
|
|
31
|
-
clearTimeout(this.#gcTimeout)
|
|
33
|
+
timeoutManager.clearTimeout(this.#gcTimeout)
|
|
32
34
|
this.#gcTimeout = undefined
|
|
33
35
|
}
|
|
34
36
|
}
|
package/src/retryer.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { focusManager } from './focusManager'
|
|
|
2
2
|
import { onlineManager } from './onlineManager'
|
|
3
3
|
import { pendingThenable } from './thenable'
|
|
4
4
|
import { isServer, sleep } from './utils'
|
|
5
|
+
import type { Thenable } from './thenable'
|
|
5
6
|
import type { CancelOptions, DefaultError, NetworkMode } from './types'
|
|
6
7
|
|
|
7
8
|
// TYPES
|
|
@@ -9,9 +10,7 @@ import type { CancelOptions, DefaultError, NetworkMode } from './types'
|
|
|
9
10
|
interface RetryerConfig<TData = unknown, TError = DefaultError> {
|
|
10
11
|
fn: () => TData | Promise<TData>
|
|
11
12
|
initialPromise?: Promise<TData>
|
|
12
|
-
|
|
13
|
-
onError?: (error: TError) => void
|
|
14
|
-
onSuccess?: (data: TData) => void
|
|
13
|
+
onCancel?: (error: TError) => void
|
|
15
14
|
onFail?: (failureCount: number, error: TError) => void
|
|
16
15
|
onPause?: () => void
|
|
17
16
|
onContinue?: () => void
|
|
@@ -29,6 +28,7 @@ export interface Retryer<TData = unknown> {
|
|
|
29
28
|
continueRetry: () => void
|
|
30
29
|
canStart: () => boolean
|
|
31
30
|
start: () => Promise<TData>
|
|
31
|
+
status: () => 'pending' | 'resolved' | 'rejected'
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export type RetryValue<TError> = boolean | number | ShouldRetryFunction<TError>
|
|
@@ -65,6 +65,9 @@ export class CancelledError extends Error {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* @deprecated Use instanceof `CancelledError` instead.
|
|
70
|
+
*/
|
|
68
71
|
export function isCancelledError(value: any): value is CancelledError {
|
|
69
72
|
return value instanceof CancelledError
|
|
70
73
|
}
|
|
@@ -74,16 +77,19 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
|
|
|
74
77
|
): Retryer<TData> {
|
|
75
78
|
let isRetryCancelled = false
|
|
76
79
|
let failureCount = 0
|
|
77
|
-
let isResolved = false
|
|
78
80
|
let continueFn: ((value?: unknown) => void) | undefined
|
|
79
81
|
|
|
80
82
|
const thenable = pendingThenable<TData>()
|
|
81
83
|
|
|
84
|
+
const isResolved = () =>
|
|
85
|
+
(thenable.status as Thenable<TData>['status']) !== 'pending'
|
|
86
|
+
|
|
82
87
|
const cancel = (cancelOptions?: CancelOptions): void => {
|
|
83
|
-
if (!isResolved) {
|
|
84
|
-
|
|
88
|
+
if (!isResolved()) {
|
|
89
|
+
const error = new CancelledError(cancelOptions) as TError
|
|
90
|
+
reject(error)
|
|
85
91
|
|
|
86
|
-
config.
|
|
92
|
+
config.onCancel?.(error)
|
|
87
93
|
}
|
|
88
94
|
}
|
|
89
95
|
const cancelRetry = () => {
|
|
@@ -102,18 +108,14 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
|
|
|
102
108
|
const canStart = () => canFetch(config.networkMode) && config.canRun()
|
|
103
109
|
|
|
104
110
|
const resolve = (value: any) => {
|
|
105
|
-
if (!isResolved) {
|
|
106
|
-
isResolved = true
|
|
107
|
-
config.onSuccess?.(value)
|
|
111
|
+
if (!isResolved()) {
|
|
108
112
|
continueFn?.()
|
|
109
113
|
thenable.resolve(value)
|
|
110
114
|
}
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
const reject = (value: any) => {
|
|
114
|
-
if (!isResolved) {
|
|
115
|
-
isResolved = true
|
|
116
|
-
config.onError?.(value)
|
|
118
|
+
if (!isResolved()) {
|
|
117
119
|
continueFn?.()
|
|
118
120
|
thenable.reject(value)
|
|
119
121
|
}
|
|
@@ -122,14 +124,14 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
|
|
|
122
124
|
const pause = () => {
|
|
123
125
|
return new Promise((continueResolve) => {
|
|
124
126
|
continueFn = (value) => {
|
|
125
|
-
if (isResolved || canContinue()) {
|
|
127
|
+
if (isResolved() || canContinue()) {
|
|
126
128
|
continueResolve(value)
|
|
127
129
|
}
|
|
128
130
|
}
|
|
129
131
|
config.onPause?.()
|
|
130
132
|
}).then(() => {
|
|
131
133
|
continueFn = undefined
|
|
132
|
-
if (!isResolved) {
|
|
134
|
+
if (!isResolved()) {
|
|
133
135
|
config.onContinue?.()
|
|
134
136
|
}
|
|
135
137
|
})
|
|
@@ -138,7 +140,7 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
|
|
|
138
140
|
// Create loop function
|
|
139
141
|
const run = () => {
|
|
140
142
|
// Do nothing if already resolved
|
|
141
|
-
if (isResolved) {
|
|
143
|
+
if (isResolved()) {
|
|
142
144
|
return
|
|
143
145
|
}
|
|
144
146
|
|
|
@@ -159,7 +161,7 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
|
|
|
159
161
|
.then(resolve)
|
|
160
162
|
.catch((error) => {
|
|
161
163
|
// Stop if the fetch is already resolved
|
|
162
|
-
if (isResolved) {
|
|
164
|
+
if (isResolved()) {
|
|
163
165
|
return
|
|
164
166
|
}
|
|
165
167
|
|
|
@@ -204,6 +206,7 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
|
|
|
204
206
|
|
|
205
207
|
return {
|
|
206
208
|
promise: thenable,
|
|
209
|
+
status: () => thenable.status,
|
|
207
210
|
cancel,
|
|
208
211
|
continue: () => {
|
|
209
212
|
continueFn?.()
|
package/src/streamedQuery.ts
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
import { addToEnd } from './utils'
|
|
2
2
|
import type { QueryFunction, QueryFunctionContext, QueryKey } from './types'
|
|
3
3
|
|
|
4
|
+
type BaseStreamedQueryParams<TQueryFnData, TQueryKey extends QueryKey> = {
|
|
5
|
+
streamFn: (
|
|
6
|
+
context: QueryFunctionContext<TQueryKey>,
|
|
7
|
+
) => AsyncIterable<TQueryFnData> | Promise<AsyncIterable<TQueryFnData>>
|
|
8
|
+
refetchMode?: 'append' | 'reset' | 'replace'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type SimpleStreamedQueryParams<
|
|
12
|
+
TQueryFnData,
|
|
13
|
+
TQueryKey extends QueryKey,
|
|
14
|
+
> = BaseStreamedQueryParams<TQueryFnData, TQueryKey> & {
|
|
15
|
+
reducer?: never
|
|
16
|
+
initialValue?: never
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type ReducibleStreamedQueryParams<
|
|
20
|
+
TQueryFnData,
|
|
21
|
+
TData,
|
|
22
|
+
TQueryKey extends QueryKey,
|
|
23
|
+
> = BaseStreamedQueryParams<TQueryFnData, TQueryKey> & {
|
|
24
|
+
reducer: (acc: TData, chunk: TQueryFnData) => TData
|
|
25
|
+
initialValue: TData
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type StreamedQueryParams<TQueryFnData, TData, TQueryKey extends QueryKey> =
|
|
29
|
+
| SimpleStreamedQueryParams<TQueryFnData, TQueryKey>
|
|
30
|
+
| ReducibleStreamedQueryParams<TQueryFnData, TData, TQueryKey>
|
|
31
|
+
|
|
4
32
|
/**
|
|
5
33
|
* This is a helper function to create a query function that streams data from an AsyncIterable.
|
|
6
34
|
* Data will be an Array of all the chunks received.
|
|
@@ -11,31 +39,29 @@ import type { QueryFunction, QueryFunctionContext, QueryKey } from './types'
|
|
|
11
39
|
* Defaults to `'reset'`, erases all data and puts the query back into `pending` state.
|
|
12
40
|
* Set to `'append'` to append new data to the existing data.
|
|
13
41
|
* Set to `'replace'` to write all data to the cache once the stream ends.
|
|
14
|
-
* @param
|
|
15
|
-
* Defaults to
|
|
16
|
-
*
|
|
17
|
-
* If the number of chunks exceeds this number, the oldest chunk will be removed.
|
|
42
|
+
* @param reducer - A function to reduce the streamed chunks into the final data.
|
|
43
|
+
* Defaults to a function that appends chunks to the end of the array.
|
|
44
|
+
* @param initialValue - Initial value to be used while the first chunk is being fetched.
|
|
18
45
|
*/
|
|
19
46
|
export function streamedQuery<
|
|
20
47
|
TQueryFnData = unknown,
|
|
48
|
+
TData = Array<TQueryFnData>,
|
|
21
49
|
TQueryKey extends QueryKey = QueryKey,
|
|
22
50
|
>({
|
|
23
|
-
|
|
51
|
+
streamFn,
|
|
24
52
|
refetchMode = 'reset',
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}): QueryFunction<Array<TQueryFnData>, TQueryKey> {
|
|
53
|
+
reducer = (items, chunk) =>
|
|
54
|
+
addToEnd(items as Array<TQueryFnData>, chunk) as TData,
|
|
55
|
+
initialValue = [] as TData,
|
|
56
|
+
}: StreamedQueryParams<TQueryFnData, TData, TQueryKey>): QueryFunction<
|
|
57
|
+
TData,
|
|
58
|
+
TQueryKey
|
|
59
|
+
> {
|
|
33
60
|
return async (context) => {
|
|
34
61
|
const query = context.client
|
|
35
62
|
.getQueryCache()
|
|
36
63
|
.find({ queryKey: context.queryKey, exact: true })
|
|
37
64
|
const isRefetch = !!query && query.state.data !== undefined
|
|
38
|
-
|
|
39
65
|
if (isRefetch && refetchMode === 'reset') {
|
|
40
66
|
query.setState({
|
|
41
67
|
status: 'pending',
|
|
@@ -45,8 +71,9 @@ export function streamedQuery<
|
|
|
45
71
|
})
|
|
46
72
|
}
|
|
47
73
|
|
|
48
|
-
let result
|
|
49
|
-
|
|
74
|
+
let result = initialValue
|
|
75
|
+
|
|
76
|
+
const stream = await streamFn(context)
|
|
50
77
|
|
|
51
78
|
for await (const chunk of stream) {
|
|
52
79
|
if (context.signal.aborted) {
|
|
@@ -55,19 +82,16 @@ export function streamedQuery<
|
|
|
55
82
|
|
|
56
83
|
// don't append to the cache directly when replace-refetching
|
|
57
84
|
if (!isRefetch || refetchMode !== 'replace') {
|
|
58
|
-
context.client.setQueryData<
|
|
59
|
-
|
|
60
|
-
(prev = []) => {
|
|
61
|
-
return addToEnd(prev, chunk, maxChunks)
|
|
62
|
-
},
|
|
85
|
+
context.client.setQueryData<TData>(context.queryKey, (prev) =>
|
|
86
|
+
reducer(prev === undefined ? initialValue : prev, chunk),
|
|
63
87
|
)
|
|
64
88
|
}
|
|
65
|
-
result =
|
|
89
|
+
result = reducer(result, chunk)
|
|
66
90
|
}
|
|
67
91
|
|
|
68
92
|
// finalize result: replace-refetching needs to write to the cache
|
|
69
93
|
if (isRefetch && refetchMode === 'replace' && !context.signal.aborted) {
|
|
70
|
-
context.client.setQueryData<
|
|
94
|
+
context.client.setQueryData<TData>(context.queryKey, result)
|
|
71
95
|
}
|
|
72
96
|
|
|
73
97
|
return context.client.getQueryData(context.queryKey)!
|