@tanstack/query-core 5.0.0-alpha.1 → 5.0.0-alpha.3

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 (77) hide show
  1. package/build/lib/index.d.ts +1 -1
  2. package/build/lib/index.esm.js +1 -1
  3. package/build/lib/index.js +1 -0
  4. package/build/lib/index.js.map +1 -1
  5. package/build/lib/index.mjs +1 -1
  6. package/build/lib/infiniteQueryObserver.d.ts +2 -2
  7. package/build/lib/infiniteQueryObserver.esm.js.map +1 -1
  8. package/build/lib/infiniteQueryObserver.js.map +1 -1
  9. package/build/lib/infiniteQueryObserver.mjs.map +1 -1
  10. package/build/lib/mutation.d.ts +5 -4
  11. package/build/lib/mutation.esm.js +16 -2
  12. package/build/lib/mutation.esm.js.map +1 -1
  13. package/build/lib/mutation.js +16 -2
  14. package/build/lib/mutation.js.map +1 -1
  15. package/build/lib/mutation.mjs +16 -2
  16. package/build/lib/mutation.mjs.map +1 -1
  17. package/build/lib/mutationCache.d.ts +5 -4
  18. package/build/lib/mutationCache.esm.js.map +1 -1
  19. package/build/lib/mutationCache.js.map +1 -1
  20. package/build/lib/mutationCache.mjs.map +1 -1
  21. package/build/lib/mutationObserver.d.ts +2 -2
  22. package/build/lib/mutationObserver.esm.js +1 -0
  23. package/build/lib/mutationObserver.esm.js.map +1 -1
  24. package/build/lib/mutationObserver.js +1 -0
  25. package/build/lib/mutationObserver.js.map +1 -1
  26. package/build/lib/mutationObserver.mjs +1 -0
  27. package/build/lib/mutationObserver.mjs.map +1 -1
  28. package/build/lib/queriesObserver.esm.js +1 -5
  29. package/build/lib/queriesObserver.esm.js.map +1 -1
  30. package/build/lib/queriesObserver.js +1 -5
  31. package/build/lib/queriesObserver.js.map +1 -1
  32. package/build/lib/queriesObserver.mjs +1 -5
  33. package/build/lib/queriesObserver.mjs.map +1 -1
  34. package/build/lib/query.d.ts +4 -4
  35. package/build/lib/query.esm.js +2 -0
  36. package/build/lib/query.esm.js.map +1 -1
  37. package/build/lib/query.js +2 -0
  38. package/build/lib/query.js.map +1 -1
  39. package/build/lib/query.mjs +2 -0
  40. package/build/lib/query.mjs.map +1 -1
  41. package/build/lib/queryCache.d.ts +5 -4
  42. package/build/lib/queryCache.esm.js.map +1 -1
  43. package/build/lib/queryCache.js.map +1 -1
  44. package/build/lib/queryCache.mjs.map +1 -1
  45. package/build/lib/queryClient.d.ts +7 -7
  46. package/build/lib/queryClient.esm.js.map +1 -1
  47. package/build/lib/queryClient.js.map +1 -1
  48. package/build/lib/queryClient.mjs.map +1 -1
  49. package/build/lib/queryObserver.d.ts +2 -2
  50. package/build/lib/queryObserver.esm.js.map +1 -1
  51. package/build/lib/queryObserver.js.map +1 -1
  52. package/build/lib/queryObserver.mjs.map +1 -1
  53. package/build/lib/retryer.d.ts +5 -5
  54. package/build/lib/retryer.esm.js.map +1 -1
  55. package/build/lib/retryer.js.map +1 -1
  56. package/build/lib/retryer.mjs.map +1 -1
  57. package/build/lib/types.d.ts +38 -38
  58. package/build/umd/index.development.js +21 -7
  59. package/build/umd/index.development.js.map +1 -1
  60. package/build/umd/index.production.js +1 -1
  61. package/build/umd/index.production.js.map +1 -1
  62. package/package.json +1 -1
  63. package/src/index.ts +7 -1
  64. package/src/infiniteQueryObserver.ts +2 -2
  65. package/src/mutation.ts +34 -7
  66. package/src/mutationCache.ts +11 -4
  67. package/src/mutationObserver.ts +3 -2
  68. package/src/queriesObserver.ts +3 -7
  69. package/src/query.ts +18 -5
  70. package/src/queryCache.ts +12 -4
  71. package/src/queryClient.ts +7 -7
  72. package/src/queryObserver.ts +2 -2
  73. package/src/retryer.ts +5 -5
  74. package/src/tests/mutationCache.test.tsx +54 -10
  75. package/src/tests/mutations.test.tsx +31 -0
  76. package/src/tests/queryCache.test.tsx +18 -6
  77. package/src/types.ts +44 -36
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/query-core",
3
- "version": "5.0.0-alpha.1",
3
+ "version": "5.0.0-alpha.3",
4
4
  "description": "The framework agnostic core that powers TanStack Query",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
package/src/index.ts CHANGED
@@ -11,7 +11,13 @@ export { MutationObserver } from './mutationObserver'
11
11
  export { notifyManager } from './notifyManager'
12
12
  export { focusManager } from './focusManager'
13
13
  export { onlineManager } from './onlineManager'
14
- export { hashKey, replaceEqualDeep, isServer, keepPreviousData } from './utils'
14
+ export {
15
+ hashKey,
16
+ replaceEqualDeep,
17
+ isServer,
18
+ matchQuery,
19
+ keepPreviousData,
20
+ } from './utils'
15
21
  export type { MutationFilters, QueryFilters, Updater } from './utils'
16
22
  export { isCancelledError } from './retryer'
17
23
  export {
@@ -6,7 +6,7 @@ import type {
6
6
  InfiniteQueryObserverOptions,
7
7
  InfiniteQueryObserverResult,
8
8
  QueryKey,
9
- RegisteredError,
9
+ DefaultError,
10
10
  } from './types'
11
11
  import type { QueryClient } from './queryClient'
12
12
  import type { NotifyOptions, ObserverFetchOptions } from './queryObserver'
@@ -24,7 +24,7 @@ type InfiniteQueryObserverListener<TData, TError> = (
24
24
 
25
25
  export class InfiniteQueryObserver<
26
26
  TQueryFnData = unknown,
27
- TError = RegisteredError,
27
+ TError = DefaultError,
28
28
  TData = InfiniteData<TQueryFnData>,
29
29
  TQueryData = TQueryFnData,
30
30
  TQueryKey extends QueryKey = QueryKey,
package/src/mutation.ts CHANGED
@@ -2,7 +2,7 @@ import type {
2
2
  MutationOptions,
3
3
  MutationStatus,
4
4
  MutationMeta,
5
- RegisteredError,
5
+ DefaultError,
6
6
  } from './types'
7
7
  import type { MutationCache } from './mutationCache'
8
8
  import type { MutationObserver } from './mutationObserver'
@@ -23,7 +23,7 @@ interface MutationConfig<TData, TError, TVariables, TContext> {
23
23
 
24
24
  export interface MutationState<
25
25
  TData = unknown,
26
- TError = RegisteredError,
26
+ TError = DefaultError,
27
27
  TVariables = void,
28
28
  TContext = unknown,
29
29
  > {
@@ -80,31 +80,40 @@ export type Action<TData, TError, TVariables, TContext> =
80
80
 
81
81
  export class Mutation<
82
82
  TData = unknown,
83
- TError = RegisteredError,
83
+ TError = DefaultError,
84
84
  TVariables = void,
85
85
  TContext = unknown,
86
86
  > extends Removable {
87
87
  state: MutationState<TData, TError, TVariables, TContext>
88
- readonly options: MutationOptions<TData, TError, TVariables, TContext>
88
+ options!: MutationOptions<TData, TError, TVariables, TContext>
89
89
  readonly mutationId: number
90
90
 
91
91
  #observers: MutationObserver<TData, TError, TVariables, TContext>[]
92
+ #defaultOptions?: MutationOptions<TData, TError, TVariables, TContext>
92
93
  #mutationCache: MutationCache
93
94
  #retryer?: Retryer<TData>
94
95
 
95
96
  constructor(config: MutationConfig<TData, TError, TVariables, TContext>) {
96
97
  super()
97
98
 
98
- this.options = config.options
99
99
  this.mutationId = config.mutationId
100
+ this.#defaultOptions = config.defaultOptions
100
101
  this.#mutationCache = config.mutationCache
101
102
  this.#observers = []
102
103
  this.state = config.state || getDefaultState()
103
104
 
104
- this.updateGcTime(this.options.gcTime)
105
+ this.setOptions(config.options)
105
106
  this.scheduleGc()
106
107
  }
107
108
 
109
+ setOptions(
110
+ options?: MutationOptions<TData, TError, TVariables, TContext>,
111
+ ): void {
112
+ this.options = { ...this.#defaultOptions, ...options }
113
+
114
+ this.updateGcTime(this.options.gcTime)
115
+ }
116
+
108
117
  get meta(): MutationMeta | undefined {
109
118
  return this.options.meta
110
119
  }
@@ -211,6 +220,15 @@ export class Mutation<
211
220
 
212
221
  await this.options.onSuccess?.(data, variables, this.state.context)
213
222
 
223
+ // Notify cache callback
224
+ await this.#mutationCache.config.onSettled?.(
225
+ data,
226
+ null,
227
+ this.state.variables,
228
+ this.state.context,
229
+ this as Mutation<unknown, unknown, unknown, unknown>,
230
+ )
231
+
214
232
  await this.options.onSettled?.(data, null, variables, this.state.context)
215
233
 
216
234
  this.#dispatch({ type: 'success', data })
@@ -219,7 +237,7 @@ export class Mutation<
219
237
  try {
220
238
  // Notify cache callback
221
239
  await this.#mutationCache.config.onError?.(
222
- error,
240
+ error as any,
223
241
  variables,
224
242
  this.state.context,
225
243
  this as Mutation<unknown, unknown, unknown, unknown>,
@@ -231,6 +249,15 @@ export class Mutation<
231
249
  this.state.context,
232
250
  )
233
251
 
252
+ // Notify cache callback
253
+ await this.#mutationCache.config.onSettled?.(
254
+ undefined,
255
+ error as any,
256
+ this.state.variables,
257
+ this.state.context,
258
+ this as Mutation<unknown, unknown, unknown, unknown>,
259
+ )
260
+
234
261
  await this.options.onSettled?.(
235
262
  undefined,
236
263
  error as TError,
@@ -1,5 +1,5 @@
1
1
  import type { MutationObserver } from './mutationObserver'
2
- import type { NotifyEvent, MutationOptions, RegisteredError } from './types'
2
+ import type { NotifyEvent, MutationOptions, DefaultError } from './types'
3
3
  import type { QueryClient } from './queryClient'
4
4
  import { notifyManager } from './notifyManager'
5
5
  import type { Action, MutationState } from './mutation'
@@ -12,7 +12,7 @@ import { Subscribable } from './subscribable'
12
12
 
13
13
  interface MutationCacheConfig {
14
14
  onError?: (
15
- error: unknown,
15
+ error: DefaultError,
16
16
  variables: unknown,
17
17
  context: unknown,
18
18
  mutation: Mutation<unknown, unknown, unknown>,
@@ -25,7 +25,14 @@ interface MutationCacheConfig {
25
25
  ) => Promise<unknown> | unknown
26
26
  onMutate?: (
27
27
  variables: unknown,
28
- mutation: Mutation<unknown, unknown, unknown, unknown>,
28
+ mutation: Mutation<unknown, unknown, unknown>,
29
+ ) => Promise<unknown> | unknown
30
+ onSettled?: (
31
+ data: unknown | undefined,
32
+ error: DefaultError | null,
33
+ variables: unknown,
34
+ context: unknown,
35
+ mutation: Mutation<unknown, unknown, unknown>,
29
36
  ) => Promise<unknown> | unknown
30
37
  }
31
38
 
@@ -126,7 +133,7 @@ export class MutationCache extends Subscribable<MutationCacheListener> {
126
133
 
127
134
  find<
128
135
  TData = unknown,
129
- TError = RegisteredError,
136
+ TError = DefaultError,
130
137
  TVariables = any,
131
138
  TContext = unknown,
132
139
  >(
@@ -7,7 +7,7 @@ import type {
7
7
  MutateOptions,
8
8
  MutationObserverResult,
9
9
  MutationObserverOptions,
10
- RegisteredError,
10
+ DefaultError,
11
11
  } from './types'
12
12
  import { shallowEqualObjects } from './utils'
13
13
 
@@ -21,7 +21,7 @@ type MutationObserverListener<TData, TError, TVariables, TContext> = (
21
21
 
22
22
  export class MutationObserver<
23
23
  TData = unknown,
24
- TError = RegisteredError,
24
+ TError = DefaultError,
25
25
  TVariables = void,
26
26
  TContext = unknown,
27
27
  > extends Subscribable<
@@ -64,6 +64,7 @@ export class MutationObserver<
64
64
  observer: this,
65
65
  })
66
66
  }
67
+ this.#currentMutation?.setOptions(this.options)
67
68
  }
68
69
 
69
70
  protected onUnsubscribe(): void {
@@ -26,7 +26,6 @@ export class QueriesObserver extends Subscribable<QueriesObserverListener> {
26
26
  #result: QueryObserverResult[]
27
27
  #queries: QueryObserverOptions[]
28
28
  #observers: QueryObserver[]
29
- #observersMap: Record<string, QueryObserver>
30
29
 
31
30
  constructor(client: QueryClient, queries?: QueryObserverOptions[]) {
32
31
  super()
@@ -35,7 +34,6 @@ export class QueriesObserver extends Subscribable<QueriesObserverListener> {
35
34
  this.#queries = []
36
35
  this.#result = []
37
36
  this.#observers = []
38
- this.#observersMap = {}
39
37
 
40
38
  if (queries) {
41
39
  this.setQueries(queries)
@@ -82,9 +80,6 @@ export class QueriesObserver extends Subscribable<QueriesObserverListener> {
82
80
  )
83
81
 
84
82
  const newObservers = newObserverMatches.map((match) => match.observer)
85
- const newObserversMap = Object.fromEntries(
86
- newObservers.map((observer) => [observer.options.queryHash, observer]),
87
- )
88
83
  const newResult = newObservers.map((observer) =>
89
84
  observer.getCurrentResult(),
90
85
  )
@@ -97,7 +92,6 @@ export class QueriesObserver extends Subscribable<QueriesObserverListener> {
97
92
  }
98
93
 
99
94
  this.#observers = newObservers
100
- this.#observersMap = newObserversMap
101
95
  this.#result = newResult
102
96
 
103
97
  if (!this.hasListeners()) {
@@ -166,7 +160,9 @@ export class QueriesObserver extends Subscribable<QueriesObserverListener> {
166
160
 
167
161
  const getObserver = (options: QueryObserverOptions): QueryObserver => {
168
162
  const defaultedOptions = this.#client.defaultQueryOptions(options)
169
- const currentObserver = this.#observersMap[defaultedOptions.queryHash!]
163
+ const currentObserver = this.#observers.find(
164
+ (o) => o.options.queryHash === defaultedOptions.queryHash,
165
+ )
170
166
  return (
171
167
  currentObserver ?? new QueryObserver(this.#client, defaultedOptions)
172
168
  )
package/src/query.ts CHANGED
@@ -9,7 +9,7 @@ import type {
9
9
  CancelOptions,
10
10
  SetDataOptions,
11
11
  FetchStatus,
12
- RegisteredError,
12
+ DefaultError,
13
13
  } from './types'
14
14
  import type { QueryCache } from './queryCache'
15
15
  import type { QueryObserver } from './queryObserver'
@@ -34,7 +34,7 @@ interface QueryConfig<
34
34
  state?: QueryState<TData, TError>
35
35
  }
36
36
 
37
- export interface QueryState<TData = unknown, TError = RegisteredError> {
37
+ export interface QueryState<TData = unknown, TError = DefaultError> {
38
38
  data: TData | undefined
39
39
  dataUpdateCount: number
40
40
  dataUpdatedAt: number
@@ -65,7 +65,7 @@ export interface FetchContext<
65
65
 
66
66
  export interface QueryBehavior<
67
67
  TQueryFnData = unknown,
68
- TError = RegisteredError,
68
+ TError = DefaultError,
69
69
  TData = TQueryFnData,
70
70
  TQueryKey extends QueryKey = QueryKey,
71
71
  > {
@@ -142,7 +142,7 @@ export interface SetStateOptions {
142
142
 
143
143
  export class Query<
144
144
  TQueryFnData = unknown,
145
- TError = RegisteredError,
145
+ TError = DefaultError,
146
146
  TData = TQueryFnData,
147
147
  TQueryKey extends QueryKey = QueryKey,
148
148
  > extends Removable {
@@ -433,7 +433,15 @@ export class Query<
433
433
 
434
434
  if (!isCancelledError(error)) {
435
435
  // Notify cache callback
436
- this.#cache.config.onError?.(error, this as Query<any, any, any, any>)
436
+ this.#cache.config.onError?.(
437
+ error as any,
438
+ this as Query<any, any, any, any>,
439
+ )
440
+ this.#cache.config.onSettled?.(
441
+ this.state.data,
442
+ error as any,
443
+ this as Query<any, any, any, any>,
444
+ )
437
445
  }
438
446
 
439
447
  if (!this.isFetchingOptimistic) {
@@ -462,6 +470,11 @@ export class Query<
462
470
 
463
471
  // Notify cache callback
464
472
  this.#cache.config.onSuccess?.(data, this as Query<any, any, any, any>)
473
+ this.#cache.config.onSettled?.(
474
+ data,
475
+ this.state.error as any,
476
+ this as Query<any, any, any, any>,
477
+ )
465
478
 
466
479
  if (!this.isFetchingOptimistic) {
467
480
  // Schedule query gc after fetching
package/src/queryCache.ts CHANGED
@@ -6,7 +6,7 @@ import type {
6
6
  NotifyEvent,
7
7
  QueryKey,
8
8
  QueryOptions,
9
- RegisteredError,
9
+ DefaultError,
10
10
  WithRequired,
11
11
  } from './types'
12
12
  import { notifyManager } from './notifyManager'
@@ -17,8 +17,16 @@ import type { QueryObserver } from './queryObserver'
17
17
  // TYPES
18
18
 
19
19
  interface QueryCacheConfig {
20
- onError?: (error: unknown, query: Query<unknown, unknown, unknown>) => void
20
+ onError?: (
21
+ error: DefaultError,
22
+ query: Query<unknown, unknown, unknown>,
23
+ ) => void
21
24
  onSuccess?: (data: unknown, query: Query<unknown, unknown, unknown>) => void
25
+ onSettled?: (
26
+ data: unknown | undefined,
27
+ error: DefaultError | null,
28
+ query: Query<unknown, unknown, unknown>,
29
+ ) => void
22
30
  createStore?: () => QueryStore
23
31
  }
24
32
 
@@ -150,7 +158,7 @@ export class QueryCache extends Subscribable<QueryCacheListener> {
150
158
 
151
159
  get<
152
160
  TQueryFnData = unknown,
153
- TError = RegisteredError,
161
+ TError = DefaultError,
154
162
  TData = TQueryFnData,
155
163
  TQueryKey extends QueryKey = QueryKey,
156
164
  >(
@@ -165,7 +173,7 @@ export class QueryCache extends Subscribable<QueryCacheListener> {
165
173
  return [...this.#queries.values()]
166
174
  }
167
175
 
168
- find<TQueryFnData = unknown, TError = RegisteredError, TData = TQueryFnData>(
176
+ find<TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData>(
169
177
  filters: WithRequired<QueryFilters, 'queryKey'>,
170
178
  ): Query<TQueryFnData, TError, TData> | undefined {
171
179
  if (typeof filters.exact === 'undefined') {
@@ -24,7 +24,7 @@ import type {
24
24
  RefetchQueryFilters,
25
25
  ResetOptions,
26
26
  SetDataOptions,
27
- RegisteredError,
27
+ DefaultError,
28
28
  } from './types'
29
29
  import type { QueryState } from './query'
30
30
  import { QueryCache } from './queryCache'
@@ -177,7 +177,7 @@ export class QueryClient {
177
177
  )
178
178
  }
179
179
 
180
- getQueryState<TQueryFnData = unknown, TError = RegisteredError>(
180
+ getQueryState<TQueryFnData = unknown, TError = DefaultError>(
181
181
  queryKey: QueryKey,
182
182
  ): QueryState<TQueryFnData, TError> | undefined {
183
183
  return this.#queryCache.find<TQueryFnData, TError>({ queryKey })?.state
@@ -272,7 +272,7 @@ export class QueryClient {
272
272
 
273
273
  fetchQuery<
274
274
  TQueryFnData,
275
- TError = RegisteredError,
275
+ TError = DefaultError,
276
276
  TData = TQueryFnData,
277
277
  TQueryKey extends QueryKey = QueryKey,
278
278
  TPageParam = never,
@@ -301,7 +301,7 @@ export class QueryClient {
301
301
 
302
302
  prefetchQuery<
303
303
  TQueryFnData = unknown,
304
- TError = RegisteredError,
304
+ TError = DefaultError,
305
305
  TData = TQueryFnData,
306
306
  TQueryKey extends QueryKey = QueryKey,
307
307
  >(
@@ -312,7 +312,7 @@ export class QueryClient {
312
312
 
313
313
  fetchInfiniteQuery<
314
314
  TQueryFnData,
315
- TError = RegisteredError,
315
+ TError = DefaultError,
316
316
  TData = TQueryFnData,
317
317
  TQueryKey extends QueryKey = QueryKey,
318
318
  TPageParam = unknown,
@@ -331,7 +331,7 @@ export class QueryClient {
331
331
 
332
332
  prefetchInfiniteQuery<
333
333
  TQueryFnData,
334
- TError = RegisteredError,
334
+ TError = DefaultError,
335
335
  TData = TQueryFnData,
336
336
  TQueryKey extends QueryKey = QueryKey,
337
337
  TPageParam = unknown,
@@ -422,7 +422,7 @@ export class QueryClient {
422
422
 
423
423
  defaultQueryOptions<
424
424
  TQueryFnData = unknown,
425
- TError = RegisteredError,
425
+ TError = DefaultError,
426
426
  TData = TQueryFnData,
427
427
  TQueryData = TQueryFnData,
428
428
  TQueryKey extends QueryKey = QueryKey,
@@ -1,4 +1,4 @@
1
- import type { DefaultedQueryObserverOptions, RegisteredError } from './types'
1
+ import type { DefaultedQueryObserverOptions, DefaultError } from './types'
2
2
  import {
3
3
  isServer,
4
4
  isValidTimeout,
@@ -39,7 +39,7 @@ export interface ObserverFetchOptions extends FetchOptions {
39
39
 
40
40
  export class QueryObserver<
41
41
  TQueryFnData = unknown,
42
- TError = RegisteredError,
42
+ TError = DefaultError,
43
43
  TData = TQueryFnData,
44
44
  TQueryData = TQueryFnData,
45
45
  TQueryKey extends QueryKey = QueryKey,
package/src/retryer.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { focusManager } from './focusManager'
2
2
  import { onlineManager } from './onlineManager'
3
3
  import { sleep } from './utils'
4
- import type { CancelOptions, NetworkMode, RegisteredError } from './types'
4
+ import type { CancelOptions, NetworkMode, DefaultError } from './types'
5
5
 
6
6
  // TYPES
7
7
 
8
- interface RetryerConfig<TData = unknown, TError = RegisteredError> {
8
+ interface RetryerConfig<TData = unknown, TError = DefaultError> {
9
9
  fn: () => TData | Promise<TData>
10
10
  abort?: () => void
11
11
  onError?: (error: TError) => void
@@ -28,14 +28,14 @@ export interface Retryer<TData = unknown> {
28
28
 
29
29
  export type RetryValue<TError> = boolean | number | ShouldRetryFunction<TError>
30
30
 
31
- type ShouldRetryFunction<TError = RegisteredError> = (
31
+ type ShouldRetryFunction<TError = DefaultError> = (
32
32
  failureCount: number,
33
33
  error: TError,
34
34
  ) => boolean
35
35
 
36
36
  export type RetryDelayValue<TError> = number | RetryDelayFunction<TError>
37
37
 
38
- type RetryDelayFunction<TError = RegisteredError> = (
38
+ type RetryDelayFunction<TError = DefaultError> = (
39
39
  failureCount: number,
40
40
  error: TError,
41
41
  ) => number
@@ -63,7 +63,7 @@ export function isCancelledError(value: any): value is CancelledError {
63
63
  return value instanceof CancelledError
64
64
  }
65
65
 
66
- export function createRetryer<TData = unknown, TError = RegisteredError>(
66
+ export function createRetryer<TData = unknown, TError = DefaultError>(
67
67
  config: RetryerConfig<TData, TError>,
68
68
  ): Retryer<TData> {
69
69
  let isRetryCancelled = false
@@ -3,11 +3,13 @@ import { queryKey, sleep, executeMutation, createQueryClient } from './utils'
3
3
  import { MutationCache, MutationObserver } from '..'
4
4
 
5
5
  describe('mutationCache', () => {
6
- describe('MutationCacheConfig.onError', () => {
7
- test('should be called when a mutation errors', async () => {
6
+ describe('MutationCacheConfig error callbacks', () => {
7
+ test('should call onError and onSettled when a mutation errors', async () => {
8
8
  const key = queryKey()
9
9
  const onError = jest.fn()
10
- const testCache = new MutationCache({ onError })
10
+ const onSuccess = jest.fn()
11
+ const onSettled = jest.fn()
12
+ const testCache = new MutationCache({ onError, onSuccess, onSettled })
11
13
  const testClient = createQueryClient({ mutationCache: testCache })
12
14
 
13
15
  try {
@@ -23,12 +25,22 @@ describe('mutationCache', () => {
23
25
  } catch {}
24
26
 
25
27
  const mutation = testCache.getAll()[0]
28
+ expect(onError).toHaveBeenCalledTimes(1)
26
29
  expect(onError).toHaveBeenCalledWith(
27
30
  new Error('error'),
28
31
  'vars',
29
32
  'context',
30
33
  mutation,
31
34
  )
35
+ expect(onSuccess).not.toHaveBeenCalled()
36
+ expect(onSettled).toHaveBeenCalledTimes(1)
37
+ expect(onSettled).toHaveBeenCalledWith(
38
+ undefined,
39
+ new Error('error'),
40
+ 'vars',
41
+ 'context',
42
+ mutation,
43
+ )
32
44
  })
33
45
 
34
46
  test('should be awaited', async () => {
@@ -39,7 +51,12 @@ describe('mutationCache', () => {
39
51
  await sleep(1)
40
52
  states.push(2)
41
53
  }
42
- const testCache = new MutationCache({ onError })
54
+ const onSettled = async () => {
55
+ states.push(5)
56
+ await sleep(1)
57
+ states.push(6)
58
+ }
59
+ const testCache = new MutationCache({ onError, onSettled })
43
60
  const testClient = createQueryClient({ mutationCache: testCache })
44
61
 
45
62
  try {
@@ -53,19 +70,26 @@ describe('mutationCache', () => {
53
70
  await sleep(1)
54
71
  states.push(4)
55
72
  },
73
+ onSettled: async () => {
74
+ states.push(7)
75
+ await sleep(1)
76
+ states.push(8)
77
+ },
56
78
  },
57
79
  'vars',
58
80
  )
59
81
  } catch {}
60
82
 
61
- expect(states).toEqual([1, 2, 3, 4])
83
+ expect(states).toEqual([1, 2, 3, 4, 5, 6, 7, 8])
62
84
  })
63
85
  })
64
- describe('MutationCacheConfig.onSuccess', () => {
65
- test('should be called when a mutation is successful', async () => {
86
+ describe('MutationCacheConfig success callbacks', () => {
87
+ test('should call onSuccess and onSettled when a mutation is successful', async () => {
66
88
  const key = queryKey()
89
+ const onError = jest.fn()
67
90
  const onSuccess = jest.fn()
68
- const testCache = new MutationCache({ onSuccess })
91
+ const onSettled = jest.fn()
92
+ const testCache = new MutationCache({ onError, onSuccess, onSettled })
69
93
  const testClient = createQueryClient({ mutationCache: testCache })
70
94
 
71
95
  try {
@@ -81,12 +105,22 @@ describe('mutationCache', () => {
81
105
  } catch {}
82
106
 
83
107
  const mutation = testCache.getAll()[0]
108
+ expect(onSuccess).toHaveBeenCalledTimes(1)
84
109
  expect(onSuccess).toHaveBeenCalledWith(
85
110
  { data: 5 },
86
111
  'vars',
87
112
  'context',
88
113
  mutation,
89
114
  )
115
+ expect(onError).not.toHaveBeenCalled()
116
+ expect(onSettled).toHaveBeenCalledTimes(1)
117
+ expect(onSettled).toHaveBeenCalledWith(
118
+ { data: 5 },
119
+ null,
120
+ 'vars',
121
+ 'context',
122
+ mutation,
123
+ )
90
124
  })
91
125
  test('should be awaited', async () => {
92
126
  const key = queryKey()
@@ -96,7 +130,12 @@ describe('mutationCache', () => {
96
130
  await sleep(1)
97
131
  states.push(2)
98
132
  }
99
- const testCache = new MutationCache({ onSuccess })
133
+ const onSettled = async () => {
134
+ states.push(5)
135
+ await sleep(1)
136
+ states.push(6)
137
+ }
138
+ const testCache = new MutationCache({ onSuccess, onSettled })
100
139
  const testClient = createQueryClient({ mutationCache: testCache })
101
140
 
102
141
  await executeMutation(
@@ -109,11 +148,16 @@ describe('mutationCache', () => {
109
148
  await sleep(1)
110
149
  states.push(4)
111
150
  },
151
+ onSettled: async () => {
152
+ states.push(7)
153
+ await sleep(1)
154
+ states.push(8)
155
+ },
112
156
  },
113
157
  'vars',
114
158
  )
115
159
 
116
- expect(states).toEqual([1, 2, 3, 4])
160
+ expect(states).toEqual([1, 2, 3, 4, 5, 6, 7, 8])
117
161
  })
118
162
  })
119
163
  describe('MutationCacheConfig.onMutate', () => {
@@ -2,6 +2,7 @@ import type { QueryClient } from '..'
2
2
  import { createQueryClient, executeMutation, queryKey, sleep } from './utils'
3
3
  import type { MutationState } from '../mutation'
4
4
  import { MutationObserver } from '../mutationObserver'
5
+ import { waitFor } from '@testing-library/react'
5
6
 
6
7
  describe('mutations', () => {
7
8
  let queryClient: QueryClient
@@ -377,4 +378,34 @@ describe('mutations', () => {
377
378
  expect(onSuccess).not.toHaveBeenCalled()
378
379
  expect(onSettled).not.toHaveBeenCalled()
379
380
  })
381
+
382
+ test('mutation callbacks should see updated options', async () => {
383
+ const onSuccess = jest.fn()
384
+
385
+ const mutation = new MutationObserver(queryClient, {
386
+ mutationFn: async () => {
387
+ sleep(100)
388
+ return 'update'
389
+ },
390
+ onSuccess: () => {
391
+ onSuccess(1)
392
+ },
393
+ })
394
+
395
+ void mutation.mutate()
396
+
397
+ mutation.setOptions({
398
+ mutationFn: async () => {
399
+ sleep(100)
400
+ return 'update'
401
+ },
402
+ onSuccess: () => {
403
+ onSuccess(2)
404
+ },
405
+ })
406
+
407
+ await waitFor(() => expect(onSuccess).toHaveBeenCalledTimes(1))
408
+
409
+ expect(onSuccess).toHaveBeenCalledWith(2)
410
+ })
380
411
  })