@ic-reactor/react 3.12.5 → 3.13.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 (152) hide show
  1. package/README.md +410 -44
  2. package/dist/auth/auth-client-compat.d.ts +122 -0
  3. package/dist/auth/auth-client-compat.d.ts.map +1 -0
  4. package/dist/auth/auth-client-compat.js +162 -0
  5. package/dist/auth/auth-client-compat.js.map +1 -0
  6. package/dist/auth/authentication-manager.d.ts +287 -5
  7. package/dist/auth/authentication-manager.d.ts.map +1 -1
  8. package/dist/auth/authentication-manager.js +920 -150
  9. package/dist/auth/authentication-manager.js.map +1 -1
  10. package/dist/auth/createIdentityAttributeHooks.d.ts.map +1 -1
  11. package/dist/auth/createIdentityAttributeHooks.js +36 -20
  12. package/dist/auth/createIdentityAttributeHooks.js.map +1 -1
  13. package/dist/auth/identity-attributes-manager.d.ts +2 -1
  14. package/dist/auth/identity-attributes-manager.d.ts.map +1 -1
  15. package/dist/auth/identity-attributes-manager.js +90 -6
  16. package/dist/auth/identity-attributes-manager.js.map +1 -1
  17. package/dist/auth/identity-attributes.d.ts.map +1 -1
  18. package/dist/auth/identity-attributes.js +57 -0
  19. package/dist/auth/identity-attributes.js.map +1 -1
  20. package/dist/auth/local-ii-probe.d.ts +12 -1
  21. package/dist/auth/local-ii-probe.d.ts.map +1 -1
  22. package/dist/auth/local-ii-probe.js +22 -3
  23. package/dist/auth/local-ii-probe.js.map +1 -1
  24. package/dist/auth/types.d.ts +48 -5
  25. package/dist/auth/types.d.ts.map +1 -1
  26. package/dist/createActorHooks.d.ts +7 -18
  27. package/dist/createActorHooks.d.ts.map +1 -1
  28. package/dist/createActorHooks.js.map +1 -1
  29. package/dist/createInfiniteQuery.d.ts +51 -10
  30. package/dist/createInfiniteQuery.d.ts.map +1 -1
  31. package/dist/createInfiniteQuery.js +39 -15
  32. package/dist/createInfiniteQuery.js.map +1 -1
  33. package/dist/createMutation.d.ts +3 -0
  34. package/dist/createMutation.d.ts.map +1 -1
  35. package/dist/createMutation.js +87 -80
  36. package/dist/createMutation.js.map +1 -1
  37. package/dist/createQuery.d.ts +35 -2
  38. package/dist/createQuery.d.ts.map +1 -1
  39. package/dist/createQuery.js +104 -17
  40. package/dist/createQuery.js.map +1 -1
  41. package/dist/createReactorProvider.d.ts +158 -0
  42. package/dist/createReactorProvider.d.ts.map +1 -0
  43. package/dist/createReactorProvider.js +256 -0
  44. package/dist/createReactorProvider.js.map +1 -0
  45. package/dist/createSuspenseInfiniteQuery.d.ts +16 -9
  46. package/dist/createSuspenseInfiniteQuery.d.ts.map +1 -1
  47. package/dist/createSuspenseInfiniteQuery.js +59 -27
  48. package/dist/createSuspenseInfiniteQuery.js.map +1 -1
  49. package/dist/createSuspenseQuery.d.ts +23 -2
  50. package/dist/createSuspenseQuery.d.ts.map +1 -1
  51. package/dist/createSuspenseQuery.js +68 -21
  52. package/dist/createSuspenseQuery.js.map +1 -1
  53. package/dist/defineDisplayReactor.d.ts +43 -0
  54. package/dist/defineDisplayReactor.d.ts.map +1 -0
  55. package/dist/defineDisplayReactor.js +42 -0
  56. package/dist/defineDisplayReactor.js.map +1 -0
  57. package/dist/defineReactor.d.ts +46 -72
  58. package/dist/defineReactor.d.ts.map +1 -1
  59. package/dist/defineReactor.js +11 -176
  60. package/dist/defineReactor.js.map +1 -1
  61. package/dist/defineReactorShared.d.ts +84 -0
  62. package/dist/defineReactorShared.d.ts.map +1 -0
  63. package/dist/defineReactorShared.js +139 -0
  64. package/dist/defineReactorShared.js.map +1 -0
  65. package/dist/hooks/createAuthHooks.d.ts +7 -0
  66. package/dist/hooks/createAuthHooks.d.ts.map +1 -1
  67. package/dist/hooks/createAuthHooks.js +180 -20
  68. package/dist/hooks/createAuthHooks.js.map +1 -1
  69. package/dist/hooks/useActorInfiniteQuery.d.ts +34 -6
  70. package/dist/hooks/useActorInfiniteQuery.d.ts.map +1 -1
  71. package/dist/hooks/useActorInfiniteQuery.js +54 -21
  72. package/dist/hooks/useActorInfiniteQuery.js.map +1 -1
  73. package/dist/hooks/useActorMethod.d.ts +37 -4
  74. package/dist/hooks/useActorMethod.d.ts.map +1 -1
  75. package/dist/hooks/useActorMethod.js +201 -57
  76. package/dist/hooks/useActorMethod.js.map +1 -1
  77. package/dist/hooks/useActorMutation.d.ts +10 -7
  78. package/dist/hooks/useActorMutation.d.ts.map +1 -1
  79. package/dist/hooks/useActorMutation.js +14 -13
  80. package/dist/hooks/useActorMutation.js.map +1 -1
  81. package/dist/hooks/useActorQuery.d.ts +17 -4
  82. package/dist/hooks/useActorQuery.d.ts.map +1 -1
  83. package/dist/hooks/useActorQuery.js +30 -9
  84. package/dist/hooks/useActorQuery.js.map +1 -1
  85. package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts +15 -3
  86. package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts.map +1 -1
  87. package/dist/hooks/useActorSuspenseInfiniteQuery.js +37 -17
  88. package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +1 -1
  89. package/dist/hooks/useActorSuspenseQuery.d.ts +2 -2
  90. package/dist/hooks/useActorSuspenseQuery.d.ts.map +1 -1
  91. package/dist/hooks/useActorSuspenseQuery.js +20 -9
  92. package/dist/hooks/useActorSuspenseQuery.js.map +1 -1
  93. package/dist/index.d.ts +4 -0
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +6 -0
  96. package/dist/index.js.map +1 -1
  97. package/dist/ownedAuthentication.d.ts +52 -0
  98. package/dist/ownedAuthentication.d.ts.map +1 -0
  99. package/dist/ownedAuthentication.js +49 -0
  100. package/dist/ownedAuthentication.js.map +1 -0
  101. package/dist/server.d.ts +21 -0
  102. package/dist/server.d.ts.map +1 -0
  103. package/dist/server.js +23 -0
  104. package/dist/server.js.map +1 -0
  105. package/dist/testing.d.ts +19 -0
  106. package/dist/testing.d.ts.map +1 -0
  107. package/dist/testing.js +19 -0
  108. package/dist/testing.js.map +1 -0
  109. package/dist/types.d.ts +416 -15
  110. package/dist/types.d.ts.map +1 -1
  111. package/dist/types.js +1 -1
  112. package/dist/utils.d.ts +159 -3
  113. package/dist/utils.d.ts.map +1 -1
  114. package/dist/utils.js +301 -1
  115. package/dist/utils.js.map +1 -1
  116. package/dist/validation.d.ts +12 -7
  117. package/dist/validation.d.ts.map +1 -1
  118. package/dist/validation.js +34 -15
  119. package/dist/validation.js.map +1 -1
  120. package/llms.txt +259 -33
  121. package/package.json +17 -5
  122. package/src/auth/auth-client-compat.ts +273 -0
  123. package/src/auth/authentication-manager.ts +918 -96
  124. package/src/auth/createIdentityAttributeHooks.ts +47 -21
  125. package/src/auth/identity-attributes-manager.ts +100 -5
  126. package/src/auth/identity-attributes.ts +75 -0
  127. package/src/auth/local-ii-probe.ts +29 -3
  128. package/src/auth/types.ts +49 -6
  129. package/src/createActorHooks.ts +20 -32
  130. package/src/createInfiniteQuery.ts +120 -28
  131. package/src/createMutation.ts +161 -178
  132. package/src/createQuery.ts +164 -32
  133. package/src/createReactorProvider.ts +365 -0
  134. package/src/createSuspenseInfiniteQuery.ts +93 -43
  135. package/src/createSuspenseQuery.ts +102 -32
  136. package/src/defineDisplayReactor.ts +62 -0
  137. package/src/defineReactor.ts +81 -263
  138. package/src/defineReactorShared.ts +268 -0
  139. package/src/hooks/createAuthHooks.ts +206 -24
  140. package/src/hooks/useActorInfiniteQuery.ts +122 -49
  141. package/src/hooks/useActorMethod.ts +295 -92
  142. package/src/hooks/useActorMutation.ts +23 -23
  143. package/src/hooks/useActorQuery.ts +43 -10
  144. package/src/hooks/useActorSuspenseInfiniteQuery.ts +93 -50
  145. package/src/hooks/useActorSuspenseQuery.ts +30 -15
  146. package/src/index.ts +8 -0
  147. package/src/ownedAuthentication.ts +81 -0
  148. package/src/server.ts +23 -0
  149. package/src/testing.ts +18 -0
  150. package/src/types.ts +463 -14
  151. package/src/utils.ts +387 -3
  152. package/src/validation.ts +43 -19
@@ -9,6 +9,9 @@
9
9
  * @example
10
10
  * const transferMutation = createMutation(reactor, {
11
11
  * functionName: "transfer",
12
+ * // Refetched before onSuccess runs: a query factory covers every args
13
+ * // instance, a `{ functionName }` every query of that method
14
+ * invalidateQueries: [getBalance, { functionName: "get_history" }],
12
15
  * onSuccess: () => console.log("Success!"),
13
16
  * })
14
17
  *
@@ -28,6 +31,7 @@
28
31
  import {
29
32
  useMutation,
30
33
  type MutationFunctionContext,
34
+ type UseMutationOptions,
31
35
  } from "@tanstack/react-query"
32
36
  import type {
33
37
  Reactor,
@@ -44,22 +48,7 @@ import type {
44
48
  MutationHookOptions,
45
49
  NoInfer,
46
50
  } from "./types.js"
47
-
48
- // ============================================================================
49
- // Internal helpers
50
- // ============================================================================
51
-
52
- /** Invalidate a list of query keys in parallel, filtering out undefineds. */
53
- async function invalidateAll(
54
- queryClient: Reactor<any, any>["queryClient"],
55
- keys: (import("@tanstack/react-query").QueryKey | undefined)[]
56
- ): Promise<void> {
57
- await Promise.all(
58
- keys.map((queryKey) =>
59
- queryKey ? queryClient.invalidateQueries({ queryKey }) : Promise.resolve()
60
- )
61
- )
62
- }
51
+ import { invalidateTargets, useMountQueryClient } from "./utils.js"
63
52
 
64
53
  // ============================================================================
65
54
  // Internal Implementation
@@ -79,6 +68,10 @@ const createMutationImpl = <
79
68
  TOnMutateResult | undefined
80
69
  >
81
70
  ): MutationResult<Service, Method, Transform> => {
71
+ type TData = ReactorReturnOk<Service, Method, Transform>
72
+ type TError = ReactorReturnErr<Service, Method, Transform>
73
+ type TVariables = ReactorArgs<Service, Method, Transform>
74
+
82
75
  const {
83
76
  functionName,
84
77
  callConfig,
@@ -96,13 +89,11 @@ const createMutationImpl = <
96
89
  * Used as mutationFn so that onSuccess handles all post-mutation work
97
90
  * and there is no double-invalidation.
98
91
  */
99
- const callFn = (
100
- args: ReactorArgs<Service, Method, Transform>
101
- ): Promise<ReactorReturnOk<Service, Method, Transform>> =>
92
+ const callFn = (args: TVariables): Promise<TData> =>
102
93
  reactor.callMethod({ functionName, args, callConfig })
103
94
 
104
95
  /**
105
- * The factory's own `onMutate` result for each call through the hook.
96
+ * The factory's own `onMutate` result for each call.
106
97
  *
107
98
  * TanStack Query stores one `onMutate` result per mutation and hands it to
108
99
  * every callback. That slot holds the hook's result, which the mutation also
@@ -117,9 +108,7 @@ const createMutationImpl = <
117
108
 
118
109
  /**
119
110
  * The `onMutate` result a factory callback receives. A factory without its
120
- * own `onMutate` gets the hook's, as it always has. TanStack Query before
121
- * 5.89 passes no context, so there a factory callback gets the hook's result
122
- * too.
111
+ * own `onMutate` gets the hook's, as it always has.
123
112
  */
124
113
  const factoryOnMutateResult = (
125
114
  onMutateResult: unknown,
@@ -129,73 +118,160 @@ const createMutationImpl = <
129
118
  ? factoryOnMutateResults.get(context)
130
119
  : onMutateResult) as TOnMutateResult | undefined
131
120
 
121
+ /**
122
+ * The options a mutation of this factory runs with, on both call paths: the
123
+ * factory's config, the hook's options on top when there is a hook, and the
124
+ * callbacks of both levels chained, factory first. `useMutation()` hands
125
+ * them to TanStack Query's `useMutation`, and `execute()` builds a mutation
126
+ * from them in the QueryClient's MutationCache.
127
+ */
128
+ const mutationOptions = <THookOnMutateResult = unknown>(
129
+ options?: MutationHookOptions<
130
+ Service,
131
+ Method,
132
+ Transform,
133
+ THookOnMutateResult
134
+ >
135
+ ): UseMutationOptions<TData, TError, TVariables, THookOnMutateResult> => {
136
+ const {
137
+ invalidateQueries: hookInvalidateQueries,
138
+ onCanisterError: hookOnCanisterError,
139
+ ...restOptions
140
+ } = options ?? {}
141
+
142
+ return {
143
+ mutationKey: reactor.getQueryOptions({ functionName }).queryKey,
144
+ ...factoryOptions,
145
+ ...restOptions,
146
+ // Use callFn (not execute) to avoid double-invalidation:
147
+ // factoryInvalidateQueries are handled in onSuccess below.
148
+ mutationFn: callFn,
149
+ onSuccess: async (data, variables, onMutateResult, context) => {
150
+ // 1. Factory-level invalidation
151
+ await invalidateTargets(reactor, factoryInvalidateQueries, callConfig)
152
+ // 2. Hook-level invalidation
153
+ await invalidateTargets(reactor, hookInvalidateQueries, callConfig)
154
+ // 3. Factory onSuccess
155
+ await factoryOnSuccess?.(
156
+ data,
157
+ variables,
158
+ factoryOnMutateResult(onMutateResult, context),
159
+ context
160
+ )
161
+ // 4. Hook onSuccess
162
+ await restOptions.onSuccess?.(data, variables, onMutateResult, context)
163
+ },
164
+ onError: async (error, variables, onMutateResult, context) => {
165
+ if (isCanisterError(error)) {
166
+ factoryOnCanisterError?.(error, variables)
167
+ hookOnCanisterError?.(error, variables)
168
+ }
169
+ // Awaited in order, like `onSuccess`. TanStack Query holds
170
+ // `onSettled` and the settled state until this promise resolves, so
171
+ // an async `onError` must be part of it.
172
+ await factoryOnError?.(
173
+ error,
174
+ variables,
175
+ factoryOnMutateResult(onMutateResult, context),
176
+ context
177
+ )
178
+ await restOptions.onError?.(error, variables, onMutateResult, context)
179
+ },
180
+ // `onMutate` and `onSettled` are composed like `onSuccess`/`onError`
181
+ // above. They used to arrive through the `...restOptions` spread, so a
182
+ // hook-level one silently replaced the factory's — factory teardown,
183
+ // telemetry or logging simply vanished the moment any call site passed
184
+ // its own, with no warning and no type error. Chaining is what a
185
+ // reader who has seen `onSuccess` chain already expects.
186
+ onMutate: async (variables, context) => {
187
+ if (factoryOnMutate) {
188
+ const result = await factoryOnMutate(variables, context)
189
+ if (context) factoryOnMutateResults.set(context, result)
190
+ }
191
+ // Without a hook-level `onMutate` this is `undefined`, and
192
+ // THookOnMutateResult is then `unknown`.
193
+ return (await restOptions.onMutate?.(
194
+ variables,
195
+ context
196
+ )) as THookOnMutateResult
197
+ },
198
+ onSettled: async (data, error, variables, onMutateResult, context) => {
199
+ await factoryOnSettled?.(
200
+ data,
201
+ error,
202
+ variables,
203
+ factoryOnMutateResult(onMutateResult, context),
204
+ context
205
+ )
206
+ await restOptions.onSettled?.(
207
+ data,
208
+ error,
209
+ variables,
210
+ onMutateResult,
211
+ context
212
+ )
213
+ },
214
+ }
215
+ }
216
+
132
217
  /**
133
218
  * Imperative execution for non-React usage.
134
219
  *
135
- * Runs the same factory-level chain the hook does — invalidation, then
136
- * `onSuccess`, or `onCanisterError`/`onError` on failure — so a mutation
137
- * object behaves the same through both call paths. Only hook-level callbacks
138
- * are absent, because there is no hook here to supply them.
220
+ * Builds the mutation in the QueryClient's MutationCache and runs it, as
221
+ * TanStack Query's `useMutation` does, with the options the hook path uses
222
+ * minus the hook's own. It used to call the canister and the factory's
223
+ * callbacks itself, so the MutationCache never saw it: its global
224
+ * `onError`, `onSuccess` and `onSettled` did not run, `useIsMutating` did
225
+ * not count it, and neither the factory's `retry` or `networkMode` nor the
226
+ * same options in the QueryClient's mutation defaults applied to the call.
227
+ * The factory's `onMutate` and `onSettled` did not run either.
228
+ *
229
+ * Now the factory's chain runs as it does through `useMutation()`:
230
+ * `onMutate`, then the factory's invalidation and `onSuccess`, or
231
+ * `onCanisterError` and `onError` on failure, then `onSettled`. Each factory
232
+ * callback gets the result of the factory's `onMutate` for this call. Only
233
+ * hook-level callbacks are absent, because there is no hook here to supply
234
+ * them.
139
235
  *
140
- * The callbacks get the same `{ client, meta, mutationKey }` context
141
- * TanStack Query passes them on the hook path.
236
+ * The QueryClient's mutation defaults apply as well, so a `mutations.retry`
237
+ * there re-sends a failed call here too. Each retry of an update method is
238
+ * a new call the canister runs; set `retry` on the factory (`false`, or
239
+ * `reactorUpdateRetry` to retry only a SysTransient rejection) to decide it
240
+ * per method.
142
241
  *
143
- * The error is rethrown after the callbacks run, so `await execute(...)`
144
- * still rejects for the caller.
242
+ * It resolves with the method's result. On failure it rejects with the
243
+ * call's error once the callbacks have run, so `await execute(...)` still
244
+ * rejects for the caller.
145
245
  *
146
246
  * Use this in route loaders, scripts, or server-side code.
147
247
  */
148
- const execute = async (
149
- args: ReactorArgs<Service, Method, Transform>
150
- ): Promise<ReactorReturnOk<Service, Method, Transform>> => {
151
- // TanStack Query builds this for the hook path, and the callback types
152
- // declare it as always present. None of it is mutation state, so this path
153
- // builds the same object. Passing `undefined` made a callback that reads
154
- // `context.client` throw here and nowhere else.
155
- //
156
- // The hook path takes `meta` and `mutationKey` from the client's defaulted
157
- // options, so this runs the same spread through `defaultMutationOptions`.
158
- // Reading `factoryOptions` alone dropped mutation defaults registered on
159
- // the QueryClient (`defaultOptions.mutations`, `setMutationDefaults`).
160
- const effectiveOptions = reactor.queryClient.defaultMutationOptions({
161
- mutationKey: reactor.getQueryOptions({ functionName }).queryKey,
162
- ...factoryOptions,
163
- })
164
- const context: MutationFunctionContext = {
165
- client: reactor.queryClient,
166
- meta: effectiveOptions.meta,
167
- mutationKey: effectiveOptions.mutationKey,
168
- }
169
-
170
- let result: ReactorReturnOk<Service, Method, Transform>
248
+ const execute = async (args: TVariables): Promise<TData> => {
249
+ const { queryClient } = reactor
250
+ const options = mutationOptions()
251
+ const { onError } = options
252
+ // What the factory's `onError` or `onCanisterError` throws, if either
253
+ // does. execute() has always rejected with it, and a TanStack Query
254
+ // mutation at the 5.90.2 peer floor does too. Later releases reject with
255
+ // the call's error and report the thrown one as an unhandled rejection,
256
+ // which ends a Node script, so it is caught here and rethrown below.
257
+ let thrownByOnError: { error: unknown } | undefined
258
+ const mutation = queryClient
259
+ .getMutationCache()
260
+ .build<TData, TError, TVariables, unknown>(queryClient, {
261
+ ...options,
262
+ onError: async (error, variables, onMutateResult, context) => {
263
+ try {
264
+ await onError?.(error, variables, onMutateResult, context)
265
+ } catch (thrown) {
266
+ thrownByOnError = { error: thrown }
267
+ }
268
+ },
269
+ })
171
270
  try {
172
- result = await callFn(args)
271
+ return await mutation.execute(args)
173
272
  } catch (error) {
174
- if (isCanisterError(error)) {
175
- // `catch` drops the type. `callMethod` rejects with this method's
176
- // CanisterError, the one the hook path narrows to.
177
- factoryOnCanisterError?.(
178
- error as Parameters<NonNullable<typeof factoryOnCanisterError>>[0],
179
- args
180
- )
181
- }
182
- // `onMutate` does not run on this path, so there is no result to pass.
183
- // Awaited like `onSuccess` below and like the hook path, so an async
184
- // rollback or report finishes before `execute()` rejects.
185
- await factoryOnError?.(
186
- error as Parameters<NonNullable<typeof factoryOnError>>[0],
187
- args,
188
- undefined,
189
- context
190
- )
191
- throw error
273
+ throw thrownByOnError ? thrownByOnError.error : error
192
274
  }
193
-
194
- if (factoryInvalidateQueries) {
195
- await invalidateAll(reactor.queryClient, factoryInvalidateQueries)
196
- }
197
- await factoryOnSuccess?.(result, args, undefined, context)
198
- return result
199
275
  }
200
276
 
201
277
  // Hook implementation
@@ -207,103 +283,8 @@ const createMutationImpl = <
207
283
  THookOnMutateResult
208
284
  >
209
285
  ) => {
210
- const baseOptions = reactor.getQueryOptions({ functionName })
211
- const {
212
- invalidateQueries: hookInvalidateQueries,
213
- onCanisterError: hookOnCanisterError,
214
- ...restOptions
215
- } = options ?? {}
216
-
217
- return useMutation<
218
- ReactorReturnOk<Service, Method, Transform>,
219
- ReactorReturnErr<Service, Method, Transform>,
220
- ReactorArgs<Service, Method, Transform>,
221
- THookOnMutateResult
222
- >(
223
- {
224
- mutationKey: baseOptions.queryKey,
225
- ...factoryOptions,
226
- ...restOptions,
227
- // Use callFn (not execute) to avoid double-invalidation:
228
- // factoryInvalidateQueries are handled in onSuccess below.
229
- mutationFn: callFn,
230
- onSuccess: async (data, variables, onMutateResult, context) => {
231
- // 1. Factory-level invalidation
232
- if (factoryInvalidateQueries) {
233
- await invalidateAll(reactor.queryClient, factoryInvalidateQueries)
234
- }
235
- // 2. Hook-level invalidation
236
- if (hookInvalidateQueries) {
237
- await invalidateAll(reactor.queryClient, hookInvalidateQueries)
238
- }
239
- // 3. Factory onSuccess
240
- await factoryOnSuccess?.(
241
- data,
242
- variables,
243
- factoryOnMutateResult(onMutateResult, context),
244
- context
245
- )
246
- // 4. Hook onSuccess
247
- await restOptions.onSuccess?.(
248
- data,
249
- variables,
250
- onMutateResult,
251
- context
252
- )
253
- },
254
- onError: async (error, variables, onMutateResult, context) => {
255
- if (isCanisterError(error)) {
256
- factoryOnCanisterError?.(error, variables)
257
- hookOnCanisterError?.(error, variables)
258
- }
259
- // Awaited in order, like `onSuccess`. TanStack Query holds
260
- // `onSettled` and the settled state until this promise resolves, so
261
- // an async `onError` must be part of it.
262
- await factoryOnError?.(
263
- error,
264
- variables,
265
- factoryOnMutateResult(onMutateResult, context),
266
- context
267
- )
268
- await restOptions.onError?.(error, variables, onMutateResult, context)
269
- },
270
- // `onMutate` and `onSettled` are composed like `onSuccess`/`onError`
271
- // above. They used to arrive through the `...restOptions` spread, so a
272
- // hook-level one silently replaced the factory's — factory teardown,
273
- // telemetry or logging simply vanished the moment any call site passed
274
- // its own, with no warning and no type error. Chaining is what a
275
- // reader who has seen `onSuccess` chain already expects.
276
- onMutate: async (variables, context) => {
277
- if (factoryOnMutate) {
278
- const result = await factoryOnMutate(variables, context)
279
- if (context) factoryOnMutateResults.set(context, result)
280
- }
281
- // Without a hook-level `onMutate` this is `undefined`, and
282
- // THookOnMutateResult is then `unknown`.
283
- return (await restOptions.onMutate?.(
284
- variables,
285
- context
286
- )) as THookOnMutateResult
287
- },
288
- onSettled: async (data, error, variables, onMutateResult, context) => {
289
- await factoryOnSettled?.(
290
- data,
291
- error,
292
- variables,
293
- factoryOnMutateResult(onMutateResult, context),
294
- context
295
- )
296
- await restOptions.onSettled?.(
297
- data,
298
- error,
299
- variables,
300
- onMutateResult,
301
- context
302
- )
303
- },
304
- },
305
- reactor.queryClient
306
- )
286
+ useMountQueryClient(reactor.queryClient)
287
+ return useMutation(mutationOptions(options), reactor.queryClient)
307
288
  }
308
289
 
309
290
  return { useMutation: useMutationHook, execute }
@@ -320,8 +301,10 @@ export function createMutation<
320
301
  TOnMutateResult = unknown,
321
302
  >(
322
303
  reactor: Reactor<Service, Transform>,
323
- // `execute()` runs the factory's `onSuccess` and `onError` without running
324
- // its `onMutate`, so their `onMutate` result can also be `undefined`.
304
+ // A factory `onError` or `onSettled` gets no `onMutate` result when
305
+ // `onMutate` throws, so the result can also be `undefined`. `onSuccess`
306
+ // shares the type argument. It kept the `undefined` from when `execute()`
307
+ // ran no `onMutate`, although it now always gets a result.
325
308
  config: MutationConfig<
326
309
  NoInfer<Service>,
327
310
  Method,