@tanstack/query-core 5.27.5 → 5.28.2

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 (88) hide show
  1. package/build/legacy/hydration.d.cts +1 -1
  2. package/build/legacy/hydration.d.ts +1 -1
  3. package/build/legacy/index.d.cts +1 -1
  4. package/build/legacy/index.d.ts +1 -1
  5. package/build/legacy/infiniteQueryBehavior.d.cts +1 -1
  6. package/build/legacy/infiniteQueryBehavior.d.ts +1 -1
  7. package/build/legacy/infiniteQueryObserver.d.cts +1 -1
  8. package/build/legacy/infiniteQueryObserver.d.ts +1 -1
  9. package/build/legacy/mutation.d.cts +1 -1
  10. package/build/legacy/mutation.d.ts +1 -1
  11. package/build/legacy/mutationCache.d.cts +1 -1
  12. package/build/legacy/mutationCache.d.ts +1 -1
  13. package/build/legacy/mutationObserver.cjs +2 -2
  14. package/build/legacy/mutationObserver.cjs.map +1 -1
  15. package/build/legacy/mutationObserver.d.cts +1 -1
  16. package/build/legacy/mutationObserver.d.ts +1 -1
  17. package/build/legacy/mutationObserver.js +2 -2
  18. package/build/legacy/mutationObserver.js.map +1 -1
  19. package/build/legacy/queriesObserver.d.cts +1 -1
  20. package/build/legacy/queriesObserver.d.ts +1 -1
  21. package/build/legacy/query.d.cts +1 -1
  22. package/build/legacy/query.d.ts +1 -1
  23. package/build/legacy/queryCache.d.cts +1 -1
  24. package/build/legacy/queryCache.d.ts +1 -1
  25. package/build/legacy/{queryClient-WUwa1bEQ.d.ts → queryClient-UWB6EkqM.d.ts} +5 -2
  26. package/build/legacy/{queryClient-8vXd-6hw.d.cts → queryClient-WxLAyIG_.d.cts} +5 -2
  27. package/build/legacy/queryClient.cjs +10 -1
  28. package/build/legacy/queryClient.cjs.map +1 -1
  29. package/build/legacy/queryClient.d.cts +1 -1
  30. package/build/legacy/queryClient.d.ts +1 -1
  31. package/build/legacy/queryClient.js +10 -1
  32. package/build/legacy/queryClient.js.map +1 -1
  33. package/build/legacy/queryObserver.d.cts +1 -1
  34. package/build/legacy/queryObserver.d.ts +1 -1
  35. package/build/legacy/retryer.d.cts +1 -1
  36. package/build/legacy/retryer.d.ts +1 -1
  37. package/build/legacy/types.cjs.map +1 -1
  38. package/build/legacy/types.d.cts +1 -1
  39. package/build/legacy/types.d.ts +1 -1
  40. package/build/legacy/utils.d.cts +1 -1
  41. package/build/legacy/utils.d.ts +1 -1
  42. package/build/modern/hydration.d.cts +1 -1
  43. package/build/modern/hydration.d.ts +1 -1
  44. package/build/modern/index.d.cts +1 -1
  45. package/build/modern/index.d.ts +1 -1
  46. package/build/modern/infiniteQueryBehavior.d.cts +1 -1
  47. package/build/modern/infiniteQueryBehavior.d.ts +1 -1
  48. package/build/modern/infiniteQueryObserver.d.cts +1 -1
  49. package/build/modern/infiniteQueryObserver.d.ts +1 -1
  50. package/build/modern/mutation.d.cts +1 -1
  51. package/build/modern/mutation.d.ts +1 -1
  52. package/build/modern/mutationCache.d.cts +1 -1
  53. package/build/modern/mutationCache.d.ts +1 -1
  54. package/build/modern/mutationObserver.cjs +2 -2
  55. package/build/modern/mutationObserver.cjs.map +1 -1
  56. package/build/modern/mutationObserver.d.cts +1 -1
  57. package/build/modern/mutationObserver.d.ts +1 -1
  58. package/build/modern/mutationObserver.js +2 -2
  59. package/build/modern/mutationObserver.js.map +1 -1
  60. package/build/modern/queriesObserver.d.cts +1 -1
  61. package/build/modern/queriesObserver.d.ts +1 -1
  62. package/build/modern/query.d.cts +1 -1
  63. package/build/modern/query.d.ts +1 -1
  64. package/build/modern/queryCache.d.cts +1 -1
  65. package/build/modern/queryCache.d.ts +1 -1
  66. package/build/modern/{queryClient-WUwa1bEQ.d.ts → queryClient-UWB6EkqM.d.ts} +5 -2
  67. package/build/modern/{queryClient-8vXd-6hw.d.cts → queryClient-WxLAyIG_.d.cts} +5 -2
  68. package/build/modern/queryClient.cjs +10 -1
  69. package/build/modern/queryClient.cjs.map +1 -1
  70. package/build/modern/queryClient.d.cts +1 -1
  71. package/build/modern/queryClient.d.ts +1 -1
  72. package/build/modern/queryClient.js +10 -1
  73. package/build/modern/queryClient.js.map +1 -1
  74. package/build/modern/queryObserver.d.cts +1 -1
  75. package/build/modern/queryObserver.d.ts +1 -1
  76. package/build/modern/retryer.d.cts +1 -1
  77. package/build/modern/retryer.d.ts +1 -1
  78. package/build/modern/types.cjs.map +1 -1
  79. package/build/modern/types.d.cts +1 -1
  80. package/build/modern/types.d.ts +1 -1
  81. package/build/modern/utils.d.cts +1 -1
  82. package/build/modern/utils.d.ts +1 -1
  83. package/package.json +1 -1
  84. package/src/mutationObserver.ts +2 -2
  85. package/src/queryClient.ts +16 -4
  86. package/src/tests/mutationObserver.test.tsx +154 -0
  87. package/src/tests/queryClient.test.tsx +27 -0
  88. package/src/types.ts +16 -0
@@ -73,8 +73,8 @@ export class MutationObserver<
73
73
  hashKey(prevOptions.mutationKey) !== hashKey(this.options.mutationKey)
74
74
  ) {
75
75
  this.reset()
76
- } else {
77
- this.#currentMutation?.setOptions(this.options)
76
+ } else if (this.#currentMutation?.state.status === 'pending') {
77
+ this.#currentMutation.setOptions(this.options)
78
78
  }
79
79
  }
80
80
 
@@ -19,6 +19,7 @@ import type {
19
19
  DefaultError,
20
20
  DefaultOptions,
21
21
  DefaultedQueryObserverOptions,
22
+ EnsureQueryDataOptions,
22
23
  FetchInfiniteQueryOptions,
23
24
  FetchQueryOptions,
24
25
  InfiniteData,
@@ -130,13 +131,24 @@ export class QueryClient {
130
131
  TData = TQueryFnData,
131
132
  TQueryKey extends QueryKey = QueryKey,
132
133
  >(
133
- options: FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
134
+ options: EnsureQueryDataOptions<TQueryFnData, TError, TData, TQueryKey>,
134
135
  ): Promise<TData> {
135
136
  const cachedData = this.getQueryData<TData>(options.queryKey)
136
137
 
137
- return cachedData !== undefined
138
- ? Promise.resolve(cachedData)
139
- : this.fetchQuery(options)
138
+ if (cachedData === undefined) return this.fetchQuery(options)
139
+ else {
140
+ const defaultedOptions = this.defaultQueryOptions(options)
141
+ const query = this.#queryCache.build(this, defaultedOptions)
142
+
143
+ if (
144
+ options.revalidateIfStale &&
145
+ query.isStaleByTime(defaultedOptions.staleTime)
146
+ ) {
147
+ void this.prefetchQuery(defaultedOptions)
148
+ }
149
+
150
+ return Promise.resolve(cachedData)
151
+ }
140
152
  }
141
153
 
142
154
  getQueriesData<TQueryFnData = unknown>(
@@ -169,4 +169,158 @@ describe('mutationObserver', () => {
169
169
 
170
170
  unsubscribe()
171
171
  })
172
+
173
+ test('changing mutation meta should not affect successful mutations', async () => {
174
+ const mutationObserver = new MutationObserver(queryClient, {
175
+ meta: { a: 1 },
176
+ mutationFn: async (text: string) => {
177
+ await sleep(5)
178
+ return text
179
+ },
180
+ })
181
+
182
+ const subscriptionHandler = vi.fn()
183
+
184
+ const unsubscribe = mutationObserver.subscribe(subscriptionHandler)
185
+
186
+ await mutationObserver.mutate('input')
187
+
188
+ expect(queryClient.getMutationCache().find({})).toMatchObject({
189
+ options: { meta: { a: 1 } },
190
+ state: {
191
+ status: 'success',
192
+ data: 'input',
193
+ },
194
+ })
195
+
196
+ mutationObserver.setOptions({
197
+ meta: { a: 2 },
198
+ })
199
+
200
+ expect(queryClient.getMutationCache().find({})).toMatchObject({
201
+ options: { meta: { a: 1 } },
202
+ state: {
203
+ status: 'success',
204
+ data: 'input',
205
+ },
206
+ })
207
+
208
+ unsubscribe()
209
+ })
210
+
211
+ test('mutation cache should have different meta when updated between mutations', async () => {
212
+ const mutationFn = async (text: string) => {
213
+ await sleep(5)
214
+ return text
215
+ }
216
+ const mutationObserver = new MutationObserver(queryClient, {
217
+ meta: { a: 1 },
218
+ mutationFn,
219
+ })
220
+
221
+ const subscriptionHandler = vi.fn()
222
+
223
+ const unsubscribe = mutationObserver.subscribe(subscriptionHandler)
224
+
225
+ await mutationObserver.mutate('input')
226
+
227
+ mutationObserver.setOptions({
228
+ meta: { a: 2 },
229
+ mutationFn,
230
+ })
231
+
232
+ await mutationObserver.mutate('input')
233
+
234
+ const mutations = queryClient.getMutationCache().findAll()
235
+ expect(mutations[0]).toMatchObject({
236
+ options: { meta: { a: 1 } },
237
+ state: {
238
+ status: 'success',
239
+ data: 'input',
240
+ },
241
+ })
242
+ expect(mutations[1]).toMatchObject({
243
+ options: { meta: { a: 2 } },
244
+ state: {
245
+ status: 'success',
246
+ data: 'input',
247
+ },
248
+ })
249
+
250
+ unsubscribe()
251
+ })
252
+
253
+ test('changing mutation meta should not affect rejected mutations', async () => {
254
+ const mutationObserver = new MutationObserver(queryClient, {
255
+ meta: { a: 1 },
256
+ mutationFn: async (_: string) => {
257
+ await sleep(5)
258
+ return Promise.reject(new Error('err'))
259
+ },
260
+ })
261
+
262
+ const subscriptionHandler = vi.fn()
263
+
264
+ const unsubscribe = mutationObserver.subscribe(subscriptionHandler)
265
+
266
+ await mutationObserver.mutate('input').catch(() => undefined)
267
+
268
+ expect(queryClient.getMutationCache().find({})).toMatchObject({
269
+ options: { meta: { a: 1 } },
270
+ state: {
271
+ status: 'error',
272
+ },
273
+ })
274
+
275
+ mutationObserver.setOptions({
276
+ meta: { a: 2 },
277
+ })
278
+
279
+ expect(queryClient.getMutationCache().find({})).toMatchObject({
280
+ options: { meta: { a: 1 } },
281
+ state: {
282
+ status: 'error',
283
+ },
284
+ })
285
+
286
+ unsubscribe()
287
+ })
288
+
289
+ test('changing mutation meta should affect pending mutations', async () => {
290
+ const mutationObserver = new MutationObserver(queryClient, {
291
+ meta: { a: 1 },
292
+ mutationFn: async (text: string) => {
293
+ await sleep(20)
294
+ return text
295
+ },
296
+ })
297
+
298
+ const subscriptionHandler = vi.fn()
299
+
300
+ const unsubscribe = mutationObserver.subscribe(subscriptionHandler)
301
+
302
+ mutationObserver.mutate('input')
303
+
304
+ await sleep(0)
305
+
306
+ expect(queryClient.getMutationCache().find({})).toMatchObject({
307
+ options: { meta: { a: 1 } },
308
+ state: {
309
+ status: 'pending',
310
+ },
311
+ })
312
+
313
+ mutationObserver.setOptions({
314
+ meta: { a: 2 },
315
+ })
316
+
317
+ expect(queryClient.getMutationCache().find({})).toMatchObject({
318
+ options: { meta: { a: 2 } },
319
+ state: {
320
+ status: 'pending',
321
+ },
322
+ })
323
+
324
+ unsubscribe()
325
+ })
172
326
  })
@@ -450,6 +450,33 @@ describe('queryClient', () => {
450
450
  queryClient.ensureQueryData({ queryKey: [key], queryFn }),
451
451
  ).resolves.toEqual('data')
452
452
  })
453
+
454
+ test('should return the cached query data if the query is found and preFetchQuery in the background when revalidateIfStale is set', async () => {
455
+ const TIMEOUT = 10
456
+ const key = queryKey()
457
+ queryClient.setQueryData([key, 'id'], 'old')
458
+
459
+ const queryFn = () =>
460
+ new Promise((resolve) => {
461
+ setTimeout(() => resolve('new'), TIMEOUT)
462
+ })
463
+
464
+ await expect(
465
+ queryClient.ensureQueryData({
466
+ queryKey: [key, 'id'],
467
+ queryFn,
468
+ revalidateIfStale: true,
469
+ }),
470
+ ).resolves.toEqual('old')
471
+ await sleep(TIMEOUT + 10)
472
+ await expect(
473
+ queryClient.ensureQueryData({
474
+ queryKey: [key, 'id'],
475
+ queryFn,
476
+ revalidateIfStale: true,
477
+ }),
478
+ ).resolves.toEqual('new')
479
+ })
453
480
  })
454
481
 
455
482
  describe('getQueriesData', () => {
package/src/types.ts CHANGED
@@ -407,6 +407,22 @@ export interface FetchQueryOptions<
407
407
  staleTime?: number
408
408
  }
409
409
 
410
+ export interface EnsureQueryDataOptions<
411
+ TQueryFnData = unknown,
412
+ TError = DefaultError,
413
+ TData = TQueryFnData,
414
+ TQueryKey extends QueryKey = QueryKey,
415
+ TPageParam = never,
416
+ > extends FetchQueryOptions<
417
+ TQueryFnData,
418
+ TError,
419
+ TData,
420
+ TQueryKey,
421
+ TPageParam
422
+ > {
423
+ revalidateIfStale?: boolean
424
+ }
425
+
410
426
  type FetchInfiniteQueryPages<TQueryFnData = unknown, TPageParam = unknown> =
411
427
  | { pages?: never }
412
428
  | {