@tanstack/query-core 4.24.4 → 4.24.9

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 (42) hide show
  1. package/build/lib/mutation.d.ts +1 -1
  2. package/build/lib/mutation.esm.js +2 -5
  3. package/build/lib/mutation.esm.js.map +1 -1
  4. package/build/lib/mutation.js +2 -5
  5. package/build/lib/mutation.js.map +1 -1
  6. package/build/lib/mutation.mjs +2 -5
  7. package/build/lib/mutation.mjs.map +1 -1
  8. package/build/lib/mutationCache.d.ts +9 -8
  9. package/build/lib/mutationCache.esm.js +9 -2
  10. package/build/lib/mutationCache.esm.js.map +1 -1
  11. package/build/lib/mutationCache.js +9 -2
  12. package/build/lib/mutationCache.js.map +1 -1
  13. package/build/lib/mutationCache.mjs +9 -2
  14. package/build/lib/mutationCache.mjs.map +1 -1
  15. package/build/lib/queryCache.d.ts +9 -9
  16. package/build/lib/queryCache.esm.js.map +1 -1
  17. package/build/lib/queryCache.js.map +1 -1
  18. package/build/lib/queryCache.mjs.map +1 -1
  19. package/build/lib/queryClient.d.ts +1 -1
  20. package/build/lib/queryClient.esm.js.map +1 -1
  21. package/build/lib/queryClient.js.map +1 -1
  22. package/build/lib/queryClient.mjs.map +1 -1
  23. package/build/lib/retryer.d.ts +1 -1
  24. package/build/lib/retryer.esm.js +8 -3
  25. package/build/lib/retryer.esm.js.map +1 -1
  26. package/build/lib/retryer.js +8 -3
  27. package/build/lib/retryer.js.map +1 -1
  28. package/build/lib/retryer.mjs +8 -3
  29. package/build/lib/retryer.mjs.map +1 -1
  30. package/build/lib/types.d.ts +4 -0
  31. package/build/umd/index.development.js +19 -10
  32. package/build/umd/index.development.js.map +1 -1
  33. package/build/umd/index.production.js +1 -1
  34. package/build/umd/index.production.js.map +1 -1
  35. package/package.json +1 -1
  36. package/src/mutation.ts +2 -6
  37. package/src/mutationCache.ts +25 -16
  38. package/src/queryCache.ts +10 -10
  39. package/src/queryClient.ts +1 -1
  40. package/src/retryer.ts +8 -5
  41. package/src/tests/queryClient.test.tsx +77 -1
  42. package/src/types.ts +13 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/query-core",
3
- "version": "4.24.4",
3
+ "version": "4.24.9",
4
4
  "description": "The framework agnostic core that powers TanStack Query",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
package/src/mutation.ts CHANGED
@@ -159,12 +159,8 @@ export class Mutation<
159
159
  }
160
160
  }
161
161
 
162
- continue(): Promise<TData> {
163
- if (this.retryer) {
164
- this.retryer.continue()
165
- return this.retryer.promise
166
- }
167
- return this.execute()
162
+ continue(): Promise<unknown> {
163
+ return this.retryer?.continue() ?? this.execute()
168
164
  }
169
165
 
170
166
  async execute(): Promise<TData> {
@@ -1,5 +1,5 @@
1
1
  import type { MutationObserver } from './mutationObserver'
2
- import type { MutationOptions } from './types'
2
+ import type { MutationOptions, NotifyEvent } from './types'
3
3
  import type { QueryClient } from './queryClient'
4
4
  import { notifyManager } from './notifyManager'
5
5
  import type { Action, MutationState } from './mutation'
@@ -29,34 +29,34 @@ interface MutationCacheConfig {
29
29
  ) => Promise<unknown> | unknown
30
30
  }
31
31
 
32
- interface NotifyEventMutationAdded {
32
+ interface NotifyEventMutationAdded extends NotifyEvent {
33
33
  type: 'added'
34
34
  mutation: Mutation<any, any, any, any>
35
35
  }
36
- interface NotifyEventMutationRemoved {
36
+ interface NotifyEventMutationRemoved extends NotifyEvent {
37
37
  type: 'removed'
38
38
  mutation: Mutation<any, any, any, any>
39
39
  }
40
40
 
41
- interface NotifyEventMutationObserverAdded {
41
+ interface NotifyEventMutationObserverAdded extends NotifyEvent {
42
42
  type: 'observerAdded'
43
43
  mutation: Mutation<any, any, any, any>
44
44
  observer: MutationObserver<any, any, any>
45
45
  }
46
46
 
47
- interface NotifyEventMutationObserverRemoved {
47
+ interface NotifyEventMutationObserverRemoved extends NotifyEvent {
48
48
  type: 'observerRemoved'
49
49
  mutation: Mutation<any, any, any, any>
50
50
  observer: MutationObserver<any, any, any>
51
51
  }
52
52
 
53
- interface NotifyEventMutationObserverOptionsUpdated {
53
+ interface NotifyEventMutationObserverOptionsUpdated extends NotifyEvent {
54
54
  type: 'observerOptionsUpdated'
55
55
  mutation?: Mutation<any, any, any, any>
56
56
  observer: MutationObserver<any, any, any, any>
57
57
  }
58
58
 
59
- interface NotifyEventMutationUpdated {
59
+ interface NotifyEventMutationUpdated extends NotifyEvent {
60
60
  type: 'updated'
61
61
  mutation: Mutation<any, any, any, any>
62
62
  action: Action<any, any, any, any>
@@ -79,6 +79,7 @@ export class MutationCache extends Subscribable<MutationCacheListener> {
79
79
 
80
80
  private mutations: Mutation<any, any, any, any>[]
81
81
  private mutationId: number
82
+ private resuming: Promise<unknown> | undefined
82
83
 
83
84
  constructor(config?: MutationCacheConfig) {
84
85
  super()
@@ -152,14 +153,22 @@ export class MutationCache extends Subscribable<MutationCacheListener> {
152
153
  })
153
154
  }
154
155
 
155
- resumePausedMutations(): Promise<void> {
156
- const pausedMutations = this.mutations.filter((x) => x.state.isPaused)
157
- return notifyManager.batch(() =>
158
- pausedMutations.reduce(
159
- (promise, mutation) =>
160
- promise.then(() => mutation.continue().catch(noop)),
161
- Promise.resolve(),
162
- ),
163
- )
156
+ resumePausedMutations(): Promise<unknown> {
157
+ this.resuming = (this.resuming ?? Promise.resolve())
158
+ .then(() => {
159
+ const pausedMutations = this.mutations.filter((x) => x.state.isPaused)
160
+ return notifyManager.batch(() =>
161
+ pausedMutations.reduce(
162
+ (promise, mutation) =>
163
+ promise.then(() => mutation.continue().catch(noop)),
164
+ Promise.resolve() as Promise<unknown>,
165
+ ),
166
+ )
167
+ })
168
+ .then(() => {
169
+ this.resuming = undefined
170
+ })
171
+
172
+ return this.resuming
164
173
  }
165
174
  }
package/src/queryCache.ts CHANGED
@@ -2,7 +2,7 @@ import type { QueryFilters } from './utils'
2
2
  import { hashQueryKeyByOptions, matchQuery, parseFilterArgs } from './utils'
3
3
  import type { Action, QueryState } from './query'
4
4
  import { Query } from './query'
5
- import type { QueryKey, QueryOptions } from './types'
5
+ import type { NotifyEvent, QueryKey, QueryOptions } from './types'
6
6
  import { notifyManager } from './notifyManager'
7
7
  import type { QueryClient } from './queryClient'
8
8
  import { Subscribable } from './subscribable'
@@ -19,40 +19,40 @@ interface QueryHashMap {
19
19
  [hash: string]: Query<any, any, any, any>
20
20
  }
21
21
 
22
- interface NotifyEventQueryAdded {
22
+ interface NotifyEventQueryAdded extends NotifyEvent {
23
23
  type: 'added'
24
24
  query: Query<any, any, any, any>
25
25
  }
26
26
 
27
- interface NotifyEventQueryRemoved {
27
+ interface NotifyEventQueryRemoved extends NotifyEvent {
28
28
  type: 'removed'
29
29
  query: Query<any, any, any, any>
30
30
  }
31
31
 
32
- interface NotifyEventQueryUpdated {
32
+ interface NotifyEventQueryUpdated extends NotifyEvent {
33
33
  type: 'updated'
34
34
  query: Query<any, any, any, any>
35
35
  action: Action<any, any>
36
36
  }
37
37
 
38
- interface NotifyEventQueryObserverAdded {
38
+ interface NotifyEventQueryObserverAdded extends NotifyEvent {
39
39
  type: 'observerAdded'
40
40
  query: Query<any, any, any, any>
41
41
  observer: QueryObserver<any, any, any, any, any>
42
42
  }
43
43
 
44
- interface NotifyEventQueryObserverRemoved {
44
+ interface NotifyEventQueryObserverRemoved extends NotifyEvent {
45
45
  type: 'observerRemoved'
46
46
  query: Query<any, any, any, any>
47
47
  observer: QueryObserver<any, any, any, any, any>
48
48
  }
49
49
 
50
- interface NotifyEventQueryObserverResultsUpdated {
50
+ interface NotifyEventQueryObserverResultsUpdated extends NotifyEvent {
51
51
  type: 'observerResultsUpdated'
52
52
  query: Query<any, any, any, any>
53
53
  }
54
54
 
55
- interface NotifyEventQueryObserverOptionsUpdated {
55
+ interface NotifyEventQueryObserverOptionsUpdated extends NotifyEvent {
56
56
  type: 'observerOptionsUpdated'
57
57
  query: Query<any, any, any, any>
58
58
  observer: QueryObserver<any, any, any, any, any>
@@ -149,10 +149,10 @@ export class QueryCache extends Subscribable<QueryCacheListener> {
149
149
  TQueryFnData = unknown,
150
150
  TError = unknown,
151
151
  TData = TQueryFnData,
152
- TQueyKey extends QueryKey = QueryKey,
152
+ TQueryKey extends QueryKey = QueryKey,
153
153
  >(
154
154
  queryHash: string,
155
- ): Query<TQueryFnData, TError, TData, TQueyKey> | undefined {
155
+ ): Query<TQueryFnData, TError, TData, TQueryKey> | undefined {
156
156
  return this.queriesMap[queryHash]
157
157
  }
158
158
 
@@ -594,7 +594,7 @@ export class QueryClient {
594
594
  .catch(noop)
595
595
  }
596
596
 
597
- resumePausedMutations(): Promise<void> {
597
+ resumePausedMutations(): Promise<unknown> {
598
598
  return this.mutationCache.resumePausedMutations()
599
599
  }
600
600
 
package/src/retryer.ts CHANGED
@@ -21,7 +21,7 @@ interface RetryerConfig<TData = unknown, TError = unknown> {
21
21
  export interface Retryer<TData = unknown> {
22
22
  promise: Promise<TData>
23
23
  cancel: (cancelOptions?: CancelOptions) => void
24
- continue: () => void
24
+ continue: () => Promise<unknown>
25
25
  cancelRetry: () => void
26
26
  continueRetry: () => void
27
27
  }
@@ -69,7 +69,7 @@ export function createRetryer<TData = unknown, TError = unknown>(
69
69
  let isRetryCancelled = false
70
70
  let failureCount = 0
71
71
  let isResolved = false
72
- let continueFn: ((value?: unknown) => void) | undefined
72
+ let continueFn: ((value?: unknown) => boolean) | undefined
73
73
  let promiseResolve: (data: TData) => void
74
74
  let promiseReject: (error: TError) => void
75
75
 
@@ -118,9 +118,11 @@ export function createRetryer<TData = unknown, TError = unknown>(
118
118
  const pause = () => {
119
119
  return new Promise((continueResolve) => {
120
120
  continueFn = (value) => {
121
- if (isResolved || !shouldPause()) {
122
- return continueResolve(value)
121
+ const canContinue = isResolved || !shouldPause()
122
+ if (canContinue) {
123
+ continueResolve(value)
123
124
  }
125
+ return canContinue
124
126
  }
125
127
  config.onPause?.()
126
128
  }).then(() => {
@@ -208,7 +210,8 @@ export function createRetryer<TData = unknown, TError = unknown>(
208
210
  promise,
209
211
  cancel,
210
212
  continue: () => {
211
- continueFn?.()
213
+ const didContinue = continueFn?.()
214
+ return didContinue ? promise : Promise.resolve()
212
215
  },
213
216
  cancelRetry,
214
217
  continueRetry,
@@ -8,8 +8,9 @@ import type {
8
8
  QueryFunction,
9
9
  QueryObserverOptions,
10
10
  } from '..'
11
- import { InfiniteQueryObserver, QueryObserver } from '..'
11
+ import { InfiniteQueryObserver, MutationObserver, QueryObserver } from '..'
12
12
  import { focusManager, onlineManager } from '..'
13
+ import { noop } from '../utils'
13
14
 
14
15
  describe('queryClient', () => {
15
16
  let queryClient: QueryClient
@@ -1468,6 +1469,81 @@ describe('queryClient', () => {
1468
1469
  onlineManager.setOnline(undefined)
1469
1470
  })
1470
1471
 
1472
+ test('should resume paused mutations when coming online', async () => {
1473
+ const consoleMock = jest.spyOn(console, 'error')
1474
+ consoleMock.mockImplementation(() => undefined)
1475
+ onlineManager.setOnline(false)
1476
+
1477
+ const observer1 = new MutationObserver(queryClient, {
1478
+ mutationFn: async () => 1,
1479
+ })
1480
+
1481
+ const observer2 = new MutationObserver(queryClient, {
1482
+ mutationFn: async () => 2,
1483
+ })
1484
+ void observer1.mutate().catch(noop)
1485
+ void observer2.mutate().catch(noop)
1486
+
1487
+ await waitFor(() => {
1488
+ expect(observer1.getCurrentResult().isPaused).toBeTruthy()
1489
+ expect(observer2.getCurrentResult().isPaused).toBeTruthy()
1490
+ })
1491
+
1492
+ onlineManager.setOnline(true)
1493
+
1494
+ await waitFor(() => {
1495
+ expect(observer1.getCurrentResult().status).toBe('success')
1496
+ expect(observer1.getCurrentResult().status).toBe('success')
1497
+ })
1498
+
1499
+ onlineManager.setOnline(undefined)
1500
+ })
1501
+
1502
+ test('should resume paused mutations one after the other when invoked manually at the same time', async () => {
1503
+ const consoleMock = jest.spyOn(console, 'error')
1504
+ consoleMock.mockImplementation(() => undefined)
1505
+ onlineManager.setOnline(false)
1506
+
1507
+ const orders: Array<string> = []
1508
+
1509
+ const observer1 = new MutationObserver(queryClient, {
1510
+ mutationFn: async () => {
1511
+ orders.push('1start')
1512
+ await sleep(50)
1513
+ orders.push('1end')
1514
+ return 1
1515
+ },
1516
+ })
1517
+
1518
+ const observer2 = new MutationObserver(queryClient, {
1519
+ mutationFn: async () => {
1520
+ orders.push('2start')
1521
+ await sleep(20)
1522
+ orders.push('2end')
1523
+ return 2
1524
+ },
1525
+ })
1526
+ void observer1.mutate().catch(noop)
1527
+ void observer2.mutate().catch(noop)
1528
+
1529
+ await waitFor(() => {
1530
+ expect(observer1.getCurrentResult().isPaused).toBeTruthy()
1531
+ expect(observer2.getCurrentResult().isPaused).toBeTruthy()
1532
+ })
1533
+
1534
+ onlineManager.setOnline(undefined)
1535
+ void queryClient.resumePausedMutations()
1536
+ await sleep(5)
1537
+ await queryClient.resumePausedMutations()
1538
+
1539
+ await waitFor(() => {
1540
+ expect(observer1.getCurrentResult().status).toBe('success')
1541
+ expect(observer2.getCurrentResult().status).toBe('success')
1542
+ })
1543
+
1544
+ expect(orders).toEqual(['1start', '1end', '2start', '2end'])
1545
+ })
1546
+
1471
1547
  test('should notify queryCache and mutationCache after multiple mounts and single unmount', async () => {
1472
1548
  const testClient = createQueryClient()
1473
1549
  testClient.mount()
package/src/types.ts CHANGED
@@ -726,3 +726,16 @@ export interface CancelOptions {
726
726
  export interface SetDataOptions {
727
727
  updatedAt?: number
728
728
  }
729
+
730
+ export type NotifyEventType =
731
+ | 'added'
732
+ | 'removed'
733
+ | 'updated'
734
+ | 'observerAdded'
735
+ | 'observerRemoved'
736
+ | 'observerResultsUpdated'
737
+ | 'observerOptionsUpdated'
738
+
739
+ export interface NotifyEvent {
740
+ type: NotifyEventType
741
+ }