@tanstack/query-core 5.29.0 → 5.32.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 (116) hide show
  1. package/build/legacy/hydration.cjs +4 -4
  2. package/build/legacy/hydration.cjs.map +1 -1
  3. package/build/legacy/hydration.d.cts +2 -1
  4. package/build/legacy/hydration.d.ts +2 -1
  5. package/build/legacy/hydration.js +4 -4
  6. package/build/legacy/hydration.js.map +1 -1
  7. package/build/legacy/index.d.cts +1 -1
  8. package/build/legacy/index.d.ts +1 -1
  9. package/build/legacy/infiniteQueryBehavior.d.cts +1 -1
  10. package/build/legacy/infiniteQueryBehavior.d.ts +1 -1
  11. package/build/legacy/infiniteQueryObserver.d.cts +1 -1
  12. package/build/legacy/infiniteQueryObserver.d.ts +1 -1
  13. package/build/legacy/mutation.cjs +31 -32
  14. package/build/legacy/mutation.cjs.map +1 -1
  15. package/build/legacy/mutation.d.cts +1 -1
  16. package/build/legacy/mutation.d.ts +1 -1
  17. package/build/legacy/mutation.js +32 -33
  18. package/build/legacy/mutation.js.map +1 -1
  19. package/build/legacy/mutationCache.cjs +43 -25
  20. package/build/legacy/mutationCache.cjs.map +1 -1
  21. package/build/legacy/mutationCache.d.cts +1 -1
  22. package/build/legacy/mutationCache.d.ts +1 -1
  23. package/build/legacy/mutationCache.js +43 -25
  24. package/build/legacy/mutationCache.js.map +1 -1
  25. package/build/legacy/mutationObserver.d.cts +1 -1
  26. package/build/legacy/mutationObserver.d.ts +1 -1
  27. package/build/legacy/queriesObserver.d.cts +1 -1
  28. package/build/legacy/queriesObserver.d.ts +1 -1
  29. package/build/legacy/query.cjs +18 -21
  30. package/build/legacy/query.cjs.map +1 -1
  31. package/build/legacy/query.d.cts +1 -1
  32. package/build/legacy/query.d.ts +1 -1
  33. package/build/legacy/query.js +18 -21
  34. package/build/legacy/query.js.map +1 -1
  35. package/build/legacy/queryCache.d.cts +1 -1
  36. package/build/legacy/queryCache.d.ts +1 -1
  37. package/build/legacy/queryClient.d.cts +1 -1
  38. package/build/legacy/queryClient.d.ts +1 -1
  39. package/build/legacy/queryObserver.d.cts +1 -1
  40. package/build/legacy/queryObserver.d.ts +1 -1
  41. package/build/legacy/retryer.cjs +16 -16
  42. package/build/legacy/retryer.cjs.map +1 -1
  43. package/build/legacy/retryer.d.cts +1 -1
  44. package/build/legacy/retryer.d.ts +1 -1
  45. package/build/legacy/retryer.js +16 -16
  46. package/build/legacy/retryer.js.map +1 -1
  47. package/build/legacy/{queryClient-K0zFyarY.d.ts → types-PcQm32IV.d.ts} +276 -266
  48. package/build/legacy/{queryClient--tFV-sQG.d.cts → types-eJX-7bPI.d.cts} +276 -266
  49. package/build/legacy/types.cjs.map +1 -1
  50. package/build/legacy/types.d.cts +1 -1
  51. package/build/legacy/types.d.ts +1 -1
  52. package/build/legacy/utils.d.cts +1 -1
  53. package/build/legacy/utils.d.ts +1 -1
  54. package/build/modern/hydration.cjs +4 -4
  55. package/build/modern/hydration.cjs.map +1 -1
  56. package/build/modern/hydration.d.cts +2 -1
  57. package/build/modern/hydration.d.ts +2 -1
  58. package/build/modern/hydration.js +4 -4
  59. package/build/modern/hydration.js.map +1 -1
  60. package/build/modern/index.d.cts +1 -1
  61. package/build/modern/index.d.ts +1 -1
  62. package/build/modern/infiniteQueryBehavior.d.cts +1 -1
  63. package/build/modern/infiniteQueryBehavior.d.ts +1 -1
  64. package/build/modern/infiniteQueryObserver.d.cts +1 -1
  65. package/build/modern/infiniteQueryObserver.d.ts +1 -1
  66. package/build/modern/mutation.cjs +30 -30
  67. package/build/modern/mutation.cjs.map +1 -1
  68. package/build/modern/mutation.d.cts +1 -1
  69. package/build/modern/mutation.d.ts +1 -1
  70. package/build/modern/mutation.js +31 -31
  71. package/build/modern/mutation.js.map +1 -1
  72. package/build/modern/mutationCache.cjs +38 -23
  73. package/build/modern/mutationCache.cjs.map +1 -1
  74. package/build/modern/mutationCache.d.cts +1 -1
  75. package/build/modern/mutationCache.d.ts +1 -1
  76. package/build/modern/mutationCache.js +38 -23
  77. package/build/modern/mutationCache.js.map +1 -1
  78. package/build/modern/mutationObserver.d.cts +1 -1
  79. package/build/modern/mutationObserver.d.ts +1 -1
  80. package/build/modern/queriesObserver.d.cts +1 -1
  81. package/build/modern/queriesObserver.d.ts +1 -1
  82. package/build/modern/query.cjs +17 -19
  83. package/build/modern/query.cjs.map +1 -1
  84. package/build/modern/query.d.cts +1 -1
  85. package/build/modern/query.d.ts +1 -1
  86. package/build/modern/query.js +17 -19
  87. package/build/modern/query.js.map +1 -1
  88. package/build/modern/queryCache.d.cts +1 -1
  89. package/build/modern/queryCache.d.ts +1 -1
  90. package/build/modern/queryClient.d.cts +1 -1
  91. package/build/modern/queryClient.d.ts +1 -1
  92. package/build/modern/queryObserver.d.cts +1 -1
  93. package/build/modern/queryObserver.d.ts +1 -1
  94. package/build/modern/retryer.cjs +16 -16
  95. package/build/modern/retryer.cjs.map +1 -1
  96. package/build/modern/retryer.d.cts +1 -1
  97. package/build/modern/retryer.d.ts +1 -1
  98. package/build/modern/retryer.js +16 -16
  99. package/build/modern/retryer.js.map +1 -1
  100. package/build/modern/{queryClient-K0zFyarY.d.ts → types-PcQm32IV.d.ts} +276 -266
  101. package/build/modern/{queryClient--tFV-sQG.d.cts → types-eJX-7bPI.d.cts} +276 -266
  102. package/build/modern/types.cjs.map +1 -1
  103. package/build/modern/types.d.cts +1 -1
  104. package/build/modern/types.d.ts +1 -1
  105. package/build/modern/utils.d.cts +1 -1
  106. package/build/modern/utils.d.ts +1 -1
  107. package/package.json +1 -1
  108. package/src/__tests__/hydration.test.tsx +34 -0
  109. package/src/__tests__/mutations.test.tsx +191 -0
  110. package/src/__tests__/queryClient.test.tsx +96 -4
  111. package/src/hydration.ts +8 -6
  112. package/src/mutation.ts +32 -33
  113. package/src/mutationCache.ts +54 -28
  114. package/src/query.ts +17 -18
  115. package/src/retryer.ts +24 -20
  116. package/src/types.ts +5 -0
package/src/mutation.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { notifyManager } from './notifyManager'
2
2
  import { Removable } from './removable'
3
- import { canFetch, createRetryer } from './retryer'
3
+ import { createRetryer } from './retryer'
4
4
  import type {
5
5
  DefaultError,
6
6
  MutationMeta,
@@ -17,7 +17,6 @@ interface MutationConfig<TData, TError, TVariables, TContext> {
17
17
  mutationId: number
18
18
  mutationCache: MutationCache
19
19
  options: MutationOptions<TData, TError, TVariables, TContext>
20
- defaultOptions?: MutationOptions<TData, TError, TVariables, TContext>
21
20
  state?: MutationState<TData, TError, TVariables, TContext>
22
21
  }
23
22
 
@@ -46,6 +45,7 @@ interface FailedAction<TError> {
46
45
 
47
46
  interface PendingAction<TVariables, TContext> {
48
47
  type: 'pending'
48
+ isPaused: boolean
49
49
  variables?: TVariables
50
50
  context?: TContext
51
51
  }
@@ -89,7 +89,6 @@ export class Mutation<
89
89
  readonly mutationId: number
90
90
 
91
91
  #observers: Array<MutationObserver<TData, TError, TVariables, TContext>>
92
- #defaultOptions?: MutationOptions<TData, TError, TVariables, TContext>
93
92
  #mutationCache: MutationCache
94
93
  #retryer?: Retryer<TData>
95
94
 
@@ -97,7 +96,6 @@ export class Mutation<
97
96
  super()
98
97
 
99
98
  this.mutationId = config.mutationId
100
- this.#defaultOptions = config.defaultOptions
101
99
  this.#mutationCache = config.mutationCache
102
100
  this.#observers = []
103
101
  this.state = config.state || getDefaultState()
@@ -107,9 +105,9 @@ export class Mutation<
107
105
  }
108
106
 
109
107
  setOptions(
110
- options?: MutationOptions<TData, TError, TVariables, TContext>,
108
+ options: MutationOptions<TData, TError, TVariables, TContext>,
111
109
  ): void {
112
- this.options = { ...this.#defaultOptions, ...options }
110
+ this.options = options
113
111
 
114
112
  this.updateGcTime(this.options.gcTime)
115
113
  }
@@ -164,36 +162,34 @@ export class Mutation<
164
162
  }
165
163
 
166
164
  async execute(variables: TVariables): Promise<TData> {
167
- const executeMutation = () => {
168
- this.#retryer = createRetryer({
169
- fn: () => {
170
- if (!this.options.mutationFn) {
171
- return Promise.reject(new Error('No mutationFn found'))
172
- }
173
- return this.options.mutationFn(variables)
174
- },
175
- onFail: (failureCount, error) => {
176
- this.#dispatch({ type: 'failed', failureCount, error })
177
- },
178
- onPause: () => {
179
- this.#dispatch({ type: 'pause' })
180
- },
181
- onContinue: () => {
182
- this.#dispatch({ type: 'continue' })
183
- },
184
- retry: this.options.retry ?? 0,
185
- retryDelay: this.options.retryDelay,
186
- networkMode: this.options.networkMode,
187
- })
188
-
189
- return this.#retryer.promise
190
- }
165
+ this.#retryer = createRetryer({
166
+ fn: () => {
167
+ if (!this.options.mutationFn) {
168
+ return Promise.reject(new Error('No mutationFn found'))
169
+ }
170
+ return this.options.mutationFn(variables)
171
+ },
172
+ onFail: (failureCount, error) => {
173
+ this.#dispatch({ type: 'failed', failureCount, error })
174
+ },
175
+ onPause: () => {
176
+ this.#dispatch({ type: 'pause' })
177
+ },
178
+ onContinue: () => {
179
+ this.#dispatch({ type: 'continue' })
180
+ },
181
+ retry: this.options.retry ?? 0,
182
+ retryDelay: this.options.retryDelay,
183
+ networkMode: this.options.networkMode,
184
+ canRun: () => this.#mutationCache.canRun(this),
185
+ })
191
186
 
192
187
  const restored = this.state.status === 'pending'
188
+ const isPaused = !this.#retryer.canStart()
193
189
 
194
190
  try {
195
191
  if (!restored) {
196
- this.#dispatch({ type: 'pending', variables })
192
+ this.#dispatch({ type: 'pending', variables, isPaused })
197
193
  // Notify cache callback
198
194
  await this.#mutationCache.config.onMutate?.(
199
195
  variables,
@@ -205,10 +201,11 @@ export class Mutation<
205
201
  type: 'pending',
206
202
  context,
207
203
  variables,
204
+ isPaused,
208
205
  })
209
206
  }
210
207
  }
211
- const data = await executeMutation()
208
+ const data = await this.#retryer.start()
212
209
 
213
210
  // Notify cache callback
214
211
  await this.#mutationCache.config.onSuccess?.(
@@ -268,6 +265,8 @@ export class Mutation<
268
265
  } finally {
269
266
  this.#dispatch({ type: 'error', error: error as TError })
270
267
  }
268
+ } finally {
269
+ this.#mutationCache.runNext(this)
271
270
  }
272
271
  }
273
272
 
@@ -300,7 +299,7 @@ export class Mutation<
300
299
  failureCount: 0,
301
300
  failureReason: null,
302
301
  error: null,
303
- isPaused: !canFetch(this.options.networkMode),
302
+ isPaused: action.isPaused,
304
303
  status: 'pending',
305
304
  variables: action.variables,
306
305
  submittedAt: Date.now(),
@@ -82,14 +82,13 @@ type MutationCacheListener = (event: MutationCacheNotifyEvent) => void
82
82
  // CLASS
83
83
 
84
84
  export class MutationCache extends Subscribable<MutationCacheListener> {
85
- #mutations: Array<Mutation<any, any, any, any>>
85
+ #mutations: Map<string, Array<Mutation<any, any, any, any>>>
86
86
  #mutationId: number
87
- #resuming: Promise<unknown> | undefined
88
87
 
89
88
  constructor(public config: MutationCacheConfig = {}) {
90
89
  super()
91
- this.#mutations = []
92
- this.#mutationId = 0
90
+ this.#mutations = new Map()
91
+ this.#mutationId = Date.now()
93
92
  }
94
93
 
95
94
  build<TData, TError, TVariables, TContext>(
@@ -110,25 +109,59 @@ export class MutationCache extends Subscribable<MutationCacheListener> {
110
109
  }
111
110
 
112
111
  add(mutation: Mutation<any, any, any, any>): void {
113
- this.#mutations.push(mutation)
112
+ const scope = scopeFor(mutation)
113
+ const mutations = this.#mutations.get(scope) ?? []
114
+ mutations.push(mutation)
115
+ this.#mutations.set(scope, mutations)
114
116
  this.notify({ type: 'added', mutation })
115
117
  }
116
118
 
117
119
  remove(mutation: Mutation<any, any, any, any>): void {
118
- this.#mutations = this.#mutations.filter((x) => x !== mutation)
120
+ const scope = scopeFor(mutation)
121
+ if (this.#mutations.has(scope)) {
122
+ const mutations = this.#mutations
123
+ .get(scope)
124
+ ?.filter((x) => x !== mutation)
125
+ if (mutations) {
126
+ if (mutations.length === 0) {
127
+ this.#mutations.delete(scope)
128
+ } else {
129
+ this.#mutations.set(scope, mutations)
130
+ }
131
+ }
132
+ }
133
+
119
134
  this.notify({ type: 'removed', mutation })
120
135
  }
121
136
 
137
+ canRun(mutation: Mutation<any, any, any, any>): boolean {
138
+ const firstPendingMutation = this.#mutations
139
+ .get(scopeFor(mutation))
140
+ ?.find((m) => m.state.status === 'pending')
141
+
142
+ // we can run if there is no current pending mutation (start use-case)
143
+ // or if WE are the first pending mutation (continue use-case)
144
+ return !firstPendingMutation || firstPendingMutation === mutation
145
+ }
146
+
147
+ runNext(mutation: Mutation<any, any, any, any>): Promise<unknown> {
148
+ const foundMutation = this.#mutations
149
+ .get(scopeFor(mutation))
150
+ ?.find((m) => m !== mutation && m.state.isPaused)
151
+
152
+ return foundMutation?.continue() ?? Promise.resolve()
153
+ }
154
+
122
155
  clear(): void {
123
156
  notifyManager.batch(() => {
124
- this.#mutations.forEach((mutation) => {
157
+ this.getAll().forEach((mutation) => {
125
158
  this.remove(mutation)
126
159
  })
127
160
  })
128
161
  }
129
162
 
130
163
  getAll(): Array<Mutation> {
131
- return this.#mutations
164
+ return [...this.#mutations.values()].flat()
132
165
  }
133
166
 
134
167
  find<
@@ -141,15 +174,13 @@ export class MutationCache extends Subscribable<MutationCacheListener> {
141
174
  ): Mutation<TData, TError, TVariables, TContext> | undefined {
142
175
  const defaultedFilters = { exact: true, ...filters }
143
176
 
144
- return this.#mutations.find((mutation) =>
177
+ return this.getAll().find((mutation) =>
145
178
  matchMutation(defaultedFilters, mutation),
146
- )
179
+ ) as Mutation<TData, TError, TVariables, TContext> | undefined
147
180
  }
148
181
 
149
182
  findAll(filters: MutationFilters = {}): Array<Mutation> {
150
- return this.#mutations.filter((mutation) =>
151
- matchMutation(filters, mutation),
152
- )
183
+ return this.getAll().filter((mutation) => matchMutation(filters, mutation))
153
184
  }
154
185
 
155
186
  notify(event: MutationCacheNotifyEvent) {
@@ -161,21 +192,16 @@ export class MutationCache extends Subscribable<MutationCacheListener> {
161
192
  }
162
193
 
163
194
  resumePausedMutations(): Promise<unknown> {
164
- this.#resuming = (this.#resuming ?? Promise.resolve())
165
- .then(() => {
166
- const pausedMutations = this.#mutations.filter((x) => x.state.isPaused)
167
- return notifyManager.batch(() =>
168
- pausedMutations.reduce(
169
- (promise, mutation) =>
170
- promise.then(() => mutation.continue().catch(noop)),
171
- Promise.resolve() as Promise<unknown>,
172
- ),
173
- )
174
- })
175
- .then(() => {
176
- this.#resuming = undefined
177
- })
195
+ const pausedMutations = this.getAll().filter((x) => x.state.isPaused)
178
196
 
179
- return this.#resuming
197
+ return notifyManager.batch(() =>
198
+ Promise.all(
199
+ pausedMutations.map((mutation) => mutation.continue().catch(noop)),
200
+ ),
201
+ )
180
202
  }
181
203
  }
204
+
205
+ function scopeFor(mutation: Mutation<any, any, any, any>) {
206
+ return mutation.options.scope?.id ?? String(mutation.mutationId)
207
+ }
package/src/query.ts CHANGED
@@ -160,7 +160,7 @@ export class Query<
160
160
  #revertState?: QueryState<TData, TError>
161
161
  #cache: QueryCache
162
162
  #retryer?: Retryer<TData>
163
- #observers: Array<QueryObserver<any, any, any, any, any>>
163
+ observers: Array<QueryObserver<any, any, any, any, any>>
164
164
  #defaultOptions?: QueryOptions<TQueryFnData, TError, TData, TQueryKey>
165
165
  #abortSignalConsumed: boolean
166
166
 
@@ -170,7 +170,7 @@ export class Query<
170
170
  this.#abortSignalConsumed = false
171
171
  this.#defaultOptions = config.defaultOptions
172
172
  this.setOptions(config.options)
173
- this.#observers = []
173
+ this.observers = []
174
174
  this.#cache = config.cache
175
175
  this.queryKey = config.queryKey
176
176
  this.queryHash = config.queryHash
@@ -191,7 +191,7 @@ export class Query<
191
191
  }
192
192
 
193
193
  protected optionalRemove() {
194
- if (!this.#observers.length && this.state.fetchStatus === 'idle') {
194
+ if (!this.observers.length && this.state.fetchStatus === 'idle') {
195
195
  this.#cache.remove(this)
196
196
  }
197
197
  }
@@ -238,9 +238,7 @@ export class Query<
238
238
  }
239
239
 
240
240
  isActive(): boolean {
241
- return this.#observers.some(
242
- (observer) => observer.options.enabled !== false,
243
- )
241
+ return this.observers.some((observer) => observer.options.enabled !== false)
244
242
  }
245
243
 
246
244
  isDisabled(): boolean {
@@ -253,7 +251,7 @@ export class Query<
253
251
  }
254
252
 
255
253
  if (this.getObserversCount() > 0) {
256
- return this.#observers.some(
254
+ return this.observers.some(
257
255
  (observer) => observer.getCurrentResult().isStale,
258
256
  )
259
257
  }
@@ -270,7 +268,7 @@ export class Query<
270
268
  }
271
269
 
272
270
  onFocus(): void {
273
- const observer = this.#observers.find((x) => x.shouldFetchOnWindowFocus())
271
+ const observer = this.observers.find((x) => x.shouldFetchOnWindowFocus())
274
272
 
275
273
  observer?.refetch({ cancelRefetch: false })
276
274
 
@@ -279,7 +277,7 @@ export class Query<
279
277
  }
280
278
 
281
279
  onOnline(): void {
282
- const observer = this.#observers.find((x) => x.shouldFetchOnReconnect())
280
+ const observer = this.observers.find((x) => x.shouldFetchOnReconnect())
283
281
 
284
282
  observer?.refetch({ cancelRefetch: false })
285
283
 
@@ -288,8 +286,8 @@ export class Query<
288
286
  }
289
287
 
290
288
  addObserver(observer: QueryObserver<any, any, any, any, any>): void {
291
- if (!this.#observers.includes(observer)) {
292
- this.#observers.push(observer)
289
+ if (!this.observers.includes(observer)) {
290
+ this.observers.push(observer)
293
291
 
294
292
  // Stop the query from being garbage collected
295
293
  this.clearGcTimeout()
@@ -299,10 +297,10 @@ export class Query<
299
297
  }
300
298
 
301
299
  removeObserver(observer: QueryObserver<any, any, any, any, any>): void {
302
- if (this.#observers.includes(observer)) {
303
- this.#observers = this.#observers.filter((x) => x !== observer)
300
+ if (this.observers.includes(observer)) {
301
+ this.observers = this.observers.filter((x) => x !== observer)
304
302
 
305
- if (!this.#observers.length) {
303
+ if (!this.observers.length) {
306
304
  // If the transport layer does not support cancellation
307
305
  // we'll let the query continue so the result can be cached
308
306
  if (this.#retryer) {
@@ -321,7 +319,7 @@ export class Query<
321
319
  }
322
320
 
323
321
  getObserversCount(): number {
324
- return this.#observers.length
322
+ return this.observers.length
325
323
  }
326
324
 
327
325
  invalidate(): void {
@@ -354,7 +352,7 @@ export class Query<
354
352
  // Use the options from the first observer with a query function if no function is found.
355
353
  // This can happen when the query is hydrated or created with setQueryData.
356
354
  if (!this.options.queryFn) {
357
- const observer = this.#observers.find((x) => x.options.queryFn)
355
+ const observer = this.observers.find((x) => x.options.queryFn)
358
356
  if (observer) {
359
357
  this.setOptions(observer.options)
360
358
  }
@@ -527,9 +525,10 @@ export class Query<
527
525
  retry: context.options.retry,
528
526
  retryDelay: context.options.retryDelay,
529
527
  networkMode: context.options.networkMode,
528
+ canRun: () => true,
530
529
  })
531
530
 
532
- return this.#retryer.promise
531
+ return this.#retryer.start()
533
532
  }
534
533
 
535
534
  #dispatch(action: Action<TData, TError>): void {
@@ -607,7 +606,7 @@ export class Query<
607
606
  this.state = reducer(this.state)
608
607
 
609
608
  notifyManager.batch(() => {
610
- this.#observers.forEach((observer) => {
609
+ this.observers.forEach((observer) => {
611
610
  observer.onQueryUpdate()
612
611
  })
613
612
 
package/src/retryer.ts CHANGED
@@ -16,6 +16,7 @@ interface RetryerConfig<TData = unknown, TError = DefaultError> {
16
16
  retry?: RetryValue<TError>
17
17
  retryDelay?: RetryDelayValue<TError>
18
18
  networkMode: NetworkMode | undefined
19
+ canRun: () => boolean
19
20
  }
20
21
 
21
22
  export interface Retryer<TData = unknown> {
@@ -24,6 +25,8 @@ export interface Retryer<TData = unknown> {
24
25
  continue: () => Promise<unknown>
25
26
  cancelRetry: () => void
26
27
  continueRetry: () => void
28
+ canStart: () => boolean
29
+ start: () => Promise<TData>
27
30
  }
28
31
 
29
32
  export type RetryValue<TError> = boolean | number | ShouldRetryFunction<TError>
@@ -69,7 +72,7 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
69
72
  let isRetryCancelled = false
70
73
  let failureCount = 0
71
74
  let isResolved = false
72
- let continueFn: ((value?: unknown) => boolean) | undefined
75
+ let continueFn: ((value?: unknown) => void) | undefined
73
76
  let promiseResolve: (data: TData) => void
74
77
  let promiseReject: (error: TError) => void
75
78
 
@@ -93,9 +96,12 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
93
96
  isRetryCancelled = false
94
97
  }
95
98
 
96
- const shouldPause = () =>
97
- !focusManager.isFocused() ||
98
- (config.networkMode !== 'always' && !onlineManager.isOnline())
99
+ const canContinue = () =>
100
+ focusManager.isFocused() &&
101
+ (config.networkMode === 'always' || onlineManager.isOnline()) &&
102
+ config.canRun()
103
+
104
+ const canStart = () => canFetch(config.networkMode) && config.canRun()
99
105
 
100
106
  const resolve = (value: any) => {
101
107
  if (!isResolved) {
@@ -118,11 +124,9 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
118
124
  const pause = () => {
119
125
  return new Promise((continueResolve) => {
120
126
  continueFn = (value) => {
121
- const canContinue = isResolved || !shouldPause()
122
- if (canContinue) {
127
+ if (isResolved || canContinue()) {
123
128
  continueResolve(value)
124
129
  }
125
- return canContinue
126
130
  }
127
131
  config.onPause?.()
128
132
  }).then(() => {
@@ -184,10 +188,7 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
184
188
  sleep(delay)
185
189
  // Pause if the document is not visible or when the device is offline
186
190
  .then(() => {
187
- if (shouldPause()) {
188
- return pause()
189
- }
190
- return
191
+ return canContinue() ? undefined : pause()
191
192
  })
192
193
  .then(() => {
193
194
  if (isRetryCancelled) {
@@ -199,21 +200,24 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
199
200
  })
200
201
  }
201
202
 
202
- // Start loop
203
- if (canFetch(config.networkMode)) {
204
- run()
205
- } else {
206
- pause().then(run)
207
- }
208
-
209
203
  return {
210
204
  promise,
211
205
  cancel,
212
206
  continue: () => {
213
- const didContinue = continueFn?.()
214
- return didContinue ? promise : Promise.resolve()
207
+ continueFn?.()
208
+ return promise
215
209
  },
216
210
  cancelRetry,
217
211
  continueRetry,
212
+ canStart,
213
+ start: () => {
214
+ // Start loop
215
+ if (canStart()) {
216
+ run()
217
+ } else {
218
+ pause().then(run)
219
+ }
220
+ return promise
221
+ },
218
222
  }
219
223
  }
package/src/types.ts CHANGED
@@ -717,6 +717,10 @@ export type MutationKey = ReadonlyArray<unknown>
717
717
 
718
718
  export type MutationStatus = 'idle' | 'pending' | 'success' | 'error'
719
719
 
720
+ export type MutationScope = {
721
+ id: string
722
+ }
723
+
720
724
  export type MutationMeta = Register extends {
721
725
  mutationMeta: infer TMutationMeta
722
726
  }
@@ -762,6 +766,7 @@ export interface MutationOptions<
762
766
  gcTime?: number
763
767
  _defaulted?: boolean
764
768
  meta?: MutationMeta
769
+ scope?: MutationScope
765
770
  }
766
771
 
767
772
  export interface MutationObserverOptions<