@ic-reactor/react 3.12.4 → 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.
- package/README.md +410 -44
- package/dist/auth/auth-client-compat.d.ts +122 -0
- package/dist/auth/auth-client-compat.d.ts.map +1 -0
- package/dist/auth/auth-client-compat.js +162 -0
- package/dist/auth/auth-client-compat.js.map +1 -0
- package/dist/auth/authentication-manager.d.ts +287 -5
- package/dist/auth/authentication-manager.d.ts.map +1 -1
- package/dist/auth/authentication-manager.js +920 -150
- package/dist/auth/authentication-manager.js.map +1 -1
- package/dist/auth/createIdentityAttributeHooks.d.ts.map +1 -1
- package/dist/auth/createIdentityAttributeHooks.js +36 -20
- package/dist/auth/createIdentityAttributeHooks.js.map +1 -1
- package/dist/auth/identity-attributes-manager.d.ts +2 -1
- package/dist/auth/identity-attributes-manager.d.ts.map +1 -1
- package/dist/auth/identity-attributes-manager.js +90 -6
- package/dist/auth/identity-attributes-manager.js.map +1 -1
- package/dist/auth/identity-attributes.d.ts.map +1 -1
- package/dist/auth/identity-attributes.js +57 -0
- package/dist/auth/identity-attributes.js.map +1 -1
- package/dist/auth/local-ii-probe.d.ts +12 -1
- package/dist/auth/local-ii-probe.d.ts.map +1 -1
- package/dist/auth/local-ii-probe.js +22 -3
- package/dist/auth/local-ii-probe.js.map +1 -1
- package/dist/auth/types.d.ts +48 -5
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/createActorHooks.d.ts +9 -20
- package/dist/createActorHooks.d.ts.map +1 -1
- package/dist/createActorHooks.js.map +1 -1
- package/dist/createInfiniteQuery.d.ts +51 -10
- package/dist/createInfiniteQuery.d.ts.map +1 -1
- package/dist/createInfiniteQuery.js +39 -15
- package/dist/createInfiniteQuery.js.map +1 -1
- package/dist/createMutation.d.ts +4 -1
- package/dist/createMutation.d.ts.map +1 -1
- package/dist/createMutation.js +121 -84
- package/dist/createMutation.js.map +1 -1
- package/dist/createQuery.d.ts +35 -2
- package/dist/createQuery.d.ts.map +1 -1
- package/dist/createQuery.js +104 -17
- package/dist/createQuery.js.map +1 -1
- package/dist/createReactorProvider.d.ts +158 -0
- package/dist/createReactorProvider.d.ts.map +1 -0
- package/dist/createReactorProvider.js +256 -0
- package/dist/createReactorProvider.js.map +1 -0
- package/dist/createSuspenseInfiniteQuery.d.ts +16 -9
- package/dist/createSuspenseInfiniteQuery.d.ts.map +1 -1
- package/dist/createSuspenseInfiniteQuery.js +59 -27
- package/dist/createSuspenseInfiniteQuery.js.map +1 -1
- package/dist/createSuspenseQuery.d.ts +23 -2
- package/dist/createSuspenseQuery.d.ts.map +1 -1
- package/dist/createSuspenseQuery.js +68 -21
- package/dist/createSuspenseQuery.js.map +1 -1
- package/dist/defineDisplayReactor.d.ts +43 -0
- package/dist/defineDisplayReactor.d.ts.map +1 -0
- package/dist/defineDisplayReactor.js +42 -0
- package/dist/defineDisplayReactor.js.map +1 -0
- package/dist/defineReactor.d.ts +46 -72
- package/dist/defineReactor.d.ts.map +1 -1
- package/dist/defineReactor.js +11 -176
- package/dist/defineReactor.js.map +1 -1
- package/dist/defineReactorShared.d.ts +84 -0
- package/dist/defineReactorShared.d.ts.map +1 -0
- package/dist/defineReactorShared.js +139 -0
- package/dist/defineReactorShared.js.map +1 -0
- package/dist/hooks/createAuthHooks.d.ts +9 -2
- package/dist/hooks/createAuthHooks.d.ts.map +1 -1
- package/dist/hooks/createAuthHooks.js +184 -24
- package/dist/hooks/createAuthHooks.js.map +1 -1
- package/dist/hooks/useActorInfiniteQuery.d.ts +36 -8
- package/dist/hooks/useActorInfiniteQuery.d.ts.map +1 -1
- package/dist/hooks/useActorInfiniteQuery.js +54 -21
- package/dist/hooks/useActorInfiniteQuery.js.map +1 -1
- package/dist/hooks/useActorMethod.d.ts +37 -4
- package/dist/hooks/useActorMethod.d.ts.map +1 -1
- package/dist/hooks/useActorMethod.js +201 -57
- package/dist/hooks/useActorMethod.js.map +1 -1
- package/dist/hooks/useActorMutation.d.ts +15 -12
- package/dist/hooks/useActorMutation.d.ts.map +1 -1
- package/dist/hooks/useActorMutation.js +14 -13
- package/dist/hooks/useActorMutation.js.map +1 -1
- package/dist/hooks/useActorQuery.d.ts +17 -4
- package/dist/hooks/useActorQuery.d.ts.map +1 -1
- package/dist/hooks/useActorQuery.js +30 -9
- package/dist/hooks/useActorQuery.js.map +1 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts +17 -5
- package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts.map +1 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.js +37 -17
- package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +1 -1
- package/dist/hooks/useActorSuspenseQuery.d.ts +2 -2
- package/dist/hooks/useActorSuspenseQuery.d.ts.map +1 -1
- package/dist/hooks/useActorSuspenseQuery.js +20 -9
- package/dist/hooks/useActorSuspenseQuery.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/ownedAuthentication.d.ts +52 -0
- package/dist/ownedAuthentication.d.ts.map +1 -0
- package/dist/ownedAuthentication.js +49 -0
- package/dist/ownedAuthentication.js.map +1 -0
- package/dist/server.d.ts +21 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +23 -0
- package/dist/server.js.map +1 -0
- package/dist/testing.d.ts +19 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +19 -0
- package/dist/testing.js.map +1 -0
- package/dist/types.d.ts +428 -21
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/utils.d.ts +159 -3
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +301 -1
- package/dist/utils.js.map +1 -1
- package/dist/validation.d.ts +12 -7
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +34 -15
- package/dist/validation.js.map +1 -1
- package/llms.txt +259 -33
- package/package.json +17 -5
- package/src/auth/auth-client-compat.ts +273 -0
- package/src/auth/authentication-manager.ts +918 -96
- package/src/auth/createIdentityAttributeHooks.ts +47 -21
- package/src/auth/identity-attributes-manager.ts +100 -5
- package/src/auth/identity-attributes.ts +75 -0
- package/src/auth/local-ii-probe.ts +29 -3
- package/src/auth/types.ts +49 -6
- package/src/createActorHooks.ts +50 -42
- package/src/createInfiniteQuery.ts +120 -28
- package/src/createMutation.ts +213 -132
- package/src/createQuery.ts +164 -32
- package/src/createReactorProvider.ts +365 -0
- package/src/createSuspenseInfiniteQuery.ts +93 -43
- package/src/createSuspenseQuery.ts +102 -32
- package/src/defineDisplayReactor.ts +62 -0
- package/src/defineReactor.ts +81 -263
- package/src/defineReactorShared.ts +268 -0
- package/src/hooks/createAuthHooks.ts +210 -28
- package/src/hooks/useActorInfiniteQuery.ts +156 -55
- package/src/hooks/useActorMethod.ts +295 -92
- package/src/hooks/useActorMutation.ts +42 -30
- package/src/hooks/useActorQuery.ts +43 -10
- package/src/hooks/useActorSuspenseInfiniteQuery.ts +110 -54
- package/src/hooks/useActorSuspenseQuery.ts +30 -15
- package/src/index.ts +8 -0
- package/src/ownedAuthentication.ts +81 -0
- package/src/server.ts +23 -0
- package/src/testing.ts +18 -0
- package/src/types.ts +492 -22
- package/src/utils.ts +387 -3
- package/src/validation.ts +43 -19
package/src/createMutation.ts
CHANGED
|
@@ -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,
|
|
@@ -35,6 +39,7 @@ import type {
|
|
|
35
39
|
ReactorArgs,
|
|
36
40
|
TransformKey,
|
|
37
41
|
ReactorReturnOk,
|
|
42
|
+
ReactorReturnErr,
|
|
38
43
|
} from "@ic-reactor/core"
|
|
39
44
|
import { isCanisterError } from "@ic-reactor/core"
|
|
40
45
|
import type {
|
|
@@ -43,22 +48,7 @@ import type {
|
|
|
43
48
|
MutationHookOptions,
|
|
44
49
|
NoInfer,
|
|
45
50
|
} from "./types.js"
|
|
46
|
-
|
|
47
|
-
// ============================================================================
|
|
48
|
-
// Internal helpers
|
|
49
|
-
// ============================================================================
|
|
50
|
-
|
|
51
|
-
/** Invalidate a list of query keys in parallel, filtering out undefineds. */
|
|
52
|
-
async function invalidateAll(
|
|
53
|
-
queryClient: Reactor<any, any>["queryClient"],
|
|
54
|
-
keys: (import("@tanstack/react-query").QueryKey | undefined)[]
|
|
55
|
-
): Promise<void> {
|
|
56
|
-
await Promise.all(
|
|
57
|
-
keys.map((queryKey) =>
|
|
58
|
-
queryKey ? queryClient.invalidateQueries({ queryKey }) : Promise.resolve()
|
|
59
|
-
)
|
|
60
|
-
)
|
|
61
|
-
}
|
|
51
|
+
import { invalidateTargets, useMountQueryClient } from "./utils.js"
|
|
62
52
|
|
|
63
53
|
// ============================================================================
|
|
64
54
|
// Internal Implementation
|
|
@@ -68,10 +58,20 @@ const createMutationImpl = <
|
|
|
68
58
|
Service,
|
|
69
59
|
Method extends FunctionName<Service> = FunctionName<Service>,
|
|
70
60
|
Transform extends TransformKey = "candid",
|
|
61
|
+
TOnMutateResult = unknown,
|
|
71
62
|
>(
|
|
72
63
|
reactor: Reactor<Service, Transform>,
|
|
73
|
-
config: MutationConfig<
|
|
64
|
+
config: MutationConfig<
|
|
65
|
+
Service,
|
|
66
|
+
Method,
|
|
67
|
+
Transform,
|
|
68
|
+
TOnMutateResult | undefined
|
|
69
|
+
>
|
|
74
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
|
+
|
|
75
75
|
const {
|
|
76
76
|
functionName,
|
|
77
77
|
callConfig,
|
|
@@ -89,136 +89,202 @@ const createMutationImpl = <
|
|
|
89
89
|
* Used as mutationFn so that onSuccess handles all post-mutation work
|
|
90
90
|
* and there is no double-invalidation.
|
|
91
91
|
*/
|
|
92
|
-
const callFn = (
|
|
93
|
-
args: ReactorArgs<Service, Method, Transform>
|
|
94
|
-
): Promise<ReactorReturnOk<Service, Method, Transform>> =>
|
|
92
|
+
const callFn = (args: TVariables): Promise<TData> =>
|
|
95
93
|
reactor.callMethod({ functionName, args, callConfig })
|
|
96
94
|
|
|
95
|
+
/**
|
|
96
|
+
* The factory's own `onMutate` result for each call.
|
|
97
|
+
*
|
|
98
|
+
* TanStack Query stores one `onMutate` result per mutation and hands it to
|
|
99
|
+
* every callback. That slot holds the hook's result, which the mutation also
|
|
100
|
+
* exposes as `context`, so this map holds the factory's. Each key is the
|
|
101
|
+
* context object TanStack Query creates for a call and passes to every
|
|
102
|
+
* callback of that call.
|
|
103
|
+
*/
|
|
104
|
+
const factoryOnMutateResults = new WeakMap<
|
|
105
|
+
MutationFunctionContext,
|
|
106
|
+
TOnMutateResult | undefined
|
|
107
|
+
>()
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The `onMutate` result a factory callback receives. A factory without its
|
|
111
|
+
* own `onMutate` gets the hook's, as it always has.
|
|
112
|
+
*/
|
|
113
|
+
const factoryOnMutateResult = (
|
|
114
|
+
onMutateResult: unknown,
|
|
115
|
+
context: MutationFunctionContext | undefined
|
|
116
|
+
) =>
|
|
117
|
+
(factoryOnMutate && context
|
|
118
|
+
? factoryOnMutateResults.get(context)
|
|
119
|
+
: onMutateResult) as TOnMutateResult | undefined
|
|
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
|
+
|
|
97
217
|
/**
|
|
98
218
|
* Imperative execution for non-React usage.
|
|
99
219
|
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
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.
|
|
104
228
|
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
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.
|
|
107
235
|
*
|
|
108
|
-
* The
|
|
109
|
-
*
|
|
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.
|
|
241
|
+
*
|
|
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.
|
|
110
245
|
*
|
|
111
246
|
* Use this in route loaders, scripts, or server-side code.
|
|
112
247
|
*/
|
|
113
|
-
const execute = async (
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
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
|
+
})
|
|
136
270
|
try {
|
|
137
|
-
|
|
271
|
+
return await mutation.execute(args)
|
|
138
272
|
} catch (error) {
|
|
139
|
-
|
|
140
|
-
factoryOnCanisterError?.(error, args)
|
|
141
|
-
}
|
|
142
|
-
// `onMutate` does not run on this path, so there is no result to pass.
|
|
143
|
-
// Awaited like `onSuccess` below and like the hook path, so an async
|
|
144
|
-
// rollback or report finishes before `execute()` rejects.
|
|
145
|
-
await factoryOnError?.(
|
|
146
|
-
error as Parameters<NonNullable<typeof factoryOnError>>[0],
|
|
147
|
-
args,
|
|
148
|
-
undefined,
|
|
149
|
-
context
|
|
150
|
-
)
|
|
151
|
-
throw error
|
|
273
|
+
throw thrownByOnError ? thrownByOnError.error : error
|
|
152
274
|
}
|
|
153
|
-
|
|
154
|
-
if (factoryInvalidateQueries) {
|
|
155
|
-
await invalidateAll(reactor.queryClient, factoryInvalidateQueries)
|
|
156
|
-
}
|
|
157
|
-
await factoryOnSuccess?.(result, args, undefined, context)
|
|
158
|
-
return result
|
|
159
275
|
}
|
|
160
276
|
|
|
161
277
|
// Hook implementation
|
|
162
|
-
const useMutationHook = (
|
|
163
|
-
options?: MutationHookOptions<
|
|
278
|
+
const useMutationHook = <THookOnMutateResult = unknown>(
|
|
279
|
+
options?: MutationHookOptions<
|
|
280
|
+
Service,
|
|
281
|
+
Method,
|
|
282
|
+
Transform,
|
|
283
|
+
THookOnMutateResult
|
|
284
|
+
>
|
|
164
285
|
) => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
invalidateQueries: hookInvalidateQueries,
|
|
168
|
-
onCanisterError: hookOnCanisterError,
|
|
169
|
-
...restOptions
|
|
170
|
-
} = options ?? {}
|
|
171
|
-
|
|
172
|
-
return useMutation(
|
|
173
|
-
{
|
|
174
|
-
mutationKey: baseOptions.queryKey,
|
|
175
|
-
...factoryOptions,
|
|
176
|
-
...restOptions,
|
|
177
|
-
// Use callFn (not execute) to avoid double-invalidation:
|
|
178
|
-
// factoryInvalidateQueries are handled in onSuccess below.
|
|
179
|
-
mutationFn: callFn,
|
|
180
|
-
onSuccess: async (...args) => {
|
|
181
|
-
// 1. Factory-level invalidation
|
|
182
|
-
if (factoryInvalidateQueries) {
|
|
183
|
-
await invalidateAll(reactor.queryClient, factoryInvalidateQueries)
|
|
184
|
-
}
|
|
185
|
-
// 2. Hook-level invalidation
|
|
186
|
-
if (hookInvalidateQueries) {
|
|
187
|
-
await invalidateAll(reactor.queryClient, hookInvalidateQueries)
|
|
188
|
-
}
|
|
189
|
-
// 3. Factory onSuccess
|
|
190
|
-
await factoryOnSuccess?.(...args)
|
|
191
|
-
// 4. Hook onSuccess
|
|
192
|
-
await restOptions.onSuccess?.(...args)
|
|
193
|
-
},
|
|
194
|
-
onError: async (error, variables, context, mutation) => {
|
|
195
|
-
if (isCanisterError(error)) {
|
|
196
|
-
factoryOnCanisterError?.(error, variables)
|
|
197
|
-
hookOnCanisterError?.(error, variables)
|
|
198
|
-
}
|
|
199
|
-
// Awaited in order, like `onSuccess`. TanStack Query holds
|
|
200
|
-
// `onSettled` and the settled state until this promise resolves, so
|
|
201
|
-
// an async `onError` must be part of it.
|
|
202
|
-
await factoryOnError?.(error, variables, context, mutation)
|
|
203
|
-
await restOptions.onError?.(error, variables, context, mutation)
|
|
204
|
-
},
|
|
205
|
-
// `onMutate` and `onSettled` are composed like `onSuccess`/`onError`
|
|
206
|
-
// above. They used to arrive through the `...restOptions` spread, so a
|
|
207
|
-
// hook-level one silently replaced the factory's — factory teardown,
|
|
208
|
-
// telemetry or logging simply vanished the moment any call site passed
|
|
209
|
-
// its own, with no warning and no type error. Chaining is what a
|
|
210
|
-
// reader who has seen `onSuccess` chain already expects.
|
|
211
|
-
onMutate: async (...params) => {
|
|
212
|
-
await factoryOnMutate?.(...params)
|
|
213
|
-
return await restOptions.onMutate?.(...params)
|
|
214
|
-
},
|
|
215
|
-
onSettled: async (...params) => {
|
|
216
|
-
await factoryOnSettled?.(...params)
|
|
217
|
-
await restOptions.onSettled?.(...params)
|
|
218
|
-
},
|
|
219
|
-
},
|
|
220
|
-
reactor.queryClient
|
|
221
|
-
)
|
|
286
|
+
useMountQueryClient(reactor.queryClient)
|
|
287
|
+
return useMutation(mutationOptions(options), reactor.queryClient)
|
|
222
288
|
}
|
|
223
289
|
|
|
224
290
|
return { useMutation: useMutationHook, execute }
|
|
@@ -232,12 +298,27 @@ export function createMutation<
|
|
|
232
298
|
Service,
|
|
233
299
|
Transform extends TransformKey,
|
|
234
300
|
Method extends FunctionName<Service> = FunctionName<Service>,
|
|
301
|
+
TOnMutateResult = unknown,
|
|
235
302
|
>(
|
|
236
303
|
reactor: Reactor<Service, Transform>,
|
|
237
|
-
|
|
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.
|
|
308
|
+
config: MutationConfig<
|
|
309
|
+
NoInfer<Service>,
|
|
310
|
+
Method,
|
|
311
|
+
Transform,
|
|
312
|
+
TOnMutateResult | undefined
|
|
313
|
+
>
|
|
238
314
|
): MutationResult<Service, Method, Transform> {
|
|
239
315
|
return createMutationImpl(
|
|
240
316
|
reactor,
|
|
241
|
-
config as MutationConfig<
|
|
317
|
+
config as MutationConfig<
|
|
318
|
+
Service,
|
|
319
|
+
Method,
|
|
320
|
+
Transform,
|
|
321
|
+
TOnMutateResult | undefined
|
|
322
|
+
>
|
|
242
323
|
)
|
|
243
324
|
}
|