@navios/react-query 0.7.1 → 1.0.0-alpha.1
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/CHANGELOG.md +161 -1
- package/README.md +152 -4
- package/dist/src/__tests__/errorSchema.spec.d.mts +2 -0
- package/dist/src/__tests__/errorSchema.spec.d.mts.map +1 -0
- package/dist/src/client/__type-tests__/from-endpoint.spec-d.d.mts +2 -0
- package/dist/src/client/__type-tests__/from-endpoint.spec-d.d.mts.map +1 -0
- package/dist/src/client/__type-tests__/infinite-query.spec-d.d.mts +2 -0
- package/dist/src/client/__type-tests__/infinite-query.spec-d.d.mts.map +1 -0
- package/dist/src/client/__type-tests__/multipart-mutation.spec-d.d.mts +2 -0
- package/dist/src/client/__type-tests__/multipart-mutation.spec-d.d.mts.map +1 -0
- package/dist/src/client/__type-tests__/mutation.spec-d.d.mts +2 -0
- package/dist/src/client/__type-tests__/mutation.spec-d.d.mts.map +1 -0
- package/dist/src/client/__type-tests__/query.spec-d.d.mts +2 -0
- package/dist/src/client/__type-tests__/query.spec-d.d.mts.map +1 -0
- package/dist/src/client/declare-client.d.mts +15 -8
- package/dist/src/client/declare-client.d.mts.map +1 -1
- package/dist/src/client/types/from-endpoint.d.mts +130 -0
- package/dist/src/client/types/from-endpoint.d.mts.map +1 -0
- package/dist/src/client/types/helpers.d.mts +74 -0
- package/dist/src/client/types/helpers.d.mts.map +1 -0
- package/dist/src/client/types/index.d.mts +21 -0
- package/dist/src/client/types/index.d.mts.map +1 -0
- package/dist/src/client/types/infinite-query.d.mts +61 -0
- package/dist/src/client/types/infinite-query.d.mts.map +1 -0
- package/dist/src/client/types/multipart-mutation.d.mts +98 -0
- package/dist/src/client/types/multipart-mutation.d.mts.map +1 -0
- package/dist/src/client/types/mutation.d.mts +75 -0
- package/dist/src/client/types/mutation.d.mts.map +1 -0
- package/dist/src/client/types/query.d.mts +65 -0
- package/dist/src/client/types/query.d.mts.map +1 -0
- package/dist/src/client/types.d.mts +1 -608
- package/dist/src/client/types.d.mts.map +1 -1
- package/dist/src/common/types.d.mts +30 -3
- package/dist/src/common/types.d.mts.map +1 -1
- package/dist/src/mutation/index.d.mts +1 -0
- package/dist/src/mutation/index.d.mts.map +1 -1
- package/dist/src/mutation/make-hook.d.mts +42 -16
- package/dist/src/mutation/make-hook.d.mts.map +1 -1
- package/dist/src/mutation/optimistic.d.mts +166 -0
- package/dist/src/mutation/optimistic.d.mts.map +1 -0
- package/dist/src/mutation/types.d.mts +51 -19
- package/dist/src/mutation/types.d.mts.map +1 -1
- package/dist/src/query/index.d.mts +1 -0
- package/dist/src/query/index.d.mts.map +1 -1
- package/dist/src/query/key-creator.d.mts.map +1 -1
- package/dist/src/query/make-infinite-options.d.mts +3 -2
- package/dist/src/query/make-infinite-options.d.mts.map +1 -1
- package/dist/src/query/make-options.d.mts +42 -12
- package/dist/src/query/make-options.d.mts.map +1 -1
- package/dist/src/query/prefetch.d.mts +245 -0
- package/dist/src/query/prefetch.d.mts.map +1 -0
- package/dist/src/query/types.d.mts +35 -17
- package/dist/src/query/types.d.mts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/lib/index.cjs +445 -28
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +1022 -599
- package/lib/index.d.cts.map +1 -1
- package/lib/index.d.mts +1019 -596
- package/lib/index.d.mts.map +1 -1
- package/lib/index.mjs +441 -29
- package/lib/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/declare-client.spec.mts +1 -2
- package/src/__tests__/errorSchema.spec.mts +391 -0
- package/src/__tests__/make-mutation.spec.mts +6 -5
- package/src/__tests__/makeDataTag.spec.mts +2 -1
- package/src/__tests__/makeQueryOptions.spec.mts +2 -1
- package/src/client/__type-tests__/from-endpoint.spec-d.mts +550 -0
- package/src/client/__type-tests__/infinite-query.spec-d.mts +648 -0
- package/src/client/__type-tests__/multipart-mutation.spec-d.mts +725 -0
- package/src/client/__type-tests__/mutation.spec-d.mts +757 -0
- package/src/client/__type-tests__/query.spec-d.mts +701 -0
- package/src/client/declare-client.mts +59 -34
- package/src/client/types/from-endpoint.mts +345 -0
- package/src/client/types/helpers.mts +140 -0
- package/src/client/types/index.mts +26 -0
- package/src/client/types/infinite-query.mts +133 -0
- package/src/client/types/multipart-mutation.mts +264 -0
- package/src/client/types/mutation.mts +176 -0
- package/src/client/types/query.mts +132 -0
- package/src/client/types.mts +1 -1935
- package/src/common/types.mts +48 -3
- package/src/mutation/index.mts +1 -0
- package/src/mutation/make-hook.mts +171 -63
- package/src/mutation/optimistic.mts +294 -0
- package/src/mutation/types.mts +102 -29
- package/src/query/index.mts +1 -0
- package/src/query/key-creator.mts +24 -13
- package/src/query/make-infinite-options.mts +53 -10
- package/src/query/make-options.mts +184 -43
- package/src/query/prefetch.mts +326 -0
- package/src/query/types.mts +76 -16
- package/src/client/__type-tests__/client-instance.spec-d.mts +0 -852
package/src/common/types.mts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
BuilderInstance,
|
|
3
|
+
ErrorSchemaRecord,
|
|
4
|
+
InferErrorSchemaOutput,
|
|
5
|
+
} from '@navios/builder'
|
|
6
|
+
import type { z, ZodType } from 'zod/v4'
|
|
2
7
|
|
|
3
8
|
/**
|
|
4
9
|
* Splits a string by a delimiter into a tuple type.
|
|
@@ -21,11 +26,51 @@ export type ProcessResponseFunction<TData = unknown, TVariables = unknown> = (
|
|
|
21
26
|
|
|
22
27
|
/**
|
|
23
28
|
* Options for creating a client instance.
|
|
29
|
+
*
|
|
30
|
+
* @template UseDiscriminator - When `true`, errors are returned as union types.
|
|
31
|
+
* When `false` (default), errors are thrown.
|
|
24
32
|
*/
|
|
25
|
-
export type ClientOptions = {
|
|
26
|
-
api: BuilderInstance
|
|
33
|
+
export type ClientOptions<UseDiscriminator extends boolean = false> = {
|
|
34
|
+
api: BuilderInstance<UseDiscriminator>
|
|
27
35
|
defaults?: {
|
|
28
36
|
keyPrefix?: string[]
|
|
29
37
|
keySuffix?: string[]
|
|
30
38
|
}
|
|
31
39
|
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Infers the full response type from an endpoint configuration.
|
|
43
|
+
* Returns `ResponseType | ErrorTypes` if errorSchema exists,
|
|
44
|
+
* otherwise just `ResponseType`.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* type Response = InferEndpointResponse<{
|
|
49
|
+
* responseSchema: z.ZodObject<{ data: z.ZodString }>,
|
|
50
|
+
* errorSchema: { 400: z.ZodObject<{ error: z.ZodString }> }
|
|
51
|
+
* }>
|
|
52
|
+
* // Result: { data: string } | { error: string }
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export type InferEndpointResponse<
|
|
56
|
+
Config extends {
|
|
57
|
+
responseSchema: ZodType
|
|
58
|
+
errorSchema?: ErrorSchemaRecord
|
|
59
|
+
},
|
|
60
|
+
> = Config['errorSchema'] extends ErrorSchemaRecord
|
|
61
|
+
? z.output<Config['responseSchema']> | InferErrorSchemaOutput<Config['errorSchema']>
|
|
62
|
+
: z.output<Config['responseSchema']>
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Computes the Result type, applying processResponse transformation
|
|
66
|
+
* to the full response (including error union when present).
|
|
67
|
+
*/
|
|
68
|
+
export type ComputeResultType<
|
|
69
|
+
ResponseSchema extends ZodType,
|
|
70
|
+
ErrorSchema extends ErrorSchemaRecord | undefined,
|
|
71
|
+
ProcessedResult,
|
|
72
|
+
> = ProcessedResult extends undefined
|
|
73
|
+
? ErrorSchema extends ErrorSchemaRecord
|
|
74
|
+
? z.output<ResponseSchema> | InferErrorSchemaOutput<ErrorSchema>
|
|
75
|
+
: z.output<ResponseSchema>
|
|
76
|
+
: ProcessedResult
|
package/src/mutation/index.mts
CHANGED
|
@@ -1,22 +1,105 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
AbstractEndpoint,
|
|
3
2
|
AnyEndpointConfig,
|
|
4
|
-
|
|
3
|
+
BaseEndpointConfig,
|
|
4
|
+
ErrorSchemaRecord,
|
|
5
|
+
HttpMethod,
|
|
6
|
+
InferErrorSchemaOutput,
|
|
5
7
|
UrlHasParams,
|
|
6
8
|
UrlParams,
|
|
7
9
|
} from '@navios/builder'
|
|
8
10
|
import type {
|
|
9
11
|
MutationFunctionContext,
|
|
12
|
+
UseMutationOptions,
|
|
10
13
|
UseMutationResult,
|
|
11
14
|
} from '@tanstack/react-query'
|
|
12
|
-
import type { z } from 'zod/v4'
|
|
15
|
+
import type { z, ZodObject, ZodType } from 'zod/v4'
|
|
13
16
|
|
|
14
17
|
import { useIsMutating, useMutation } from '@tanstack/react-query'
|
|
15
18
|
|
|
16
|
-
import type {
|
|
19
|
+
import type { ProcessResponseFunction } from '../common/types.mjs'
|
|
20
|
+
import type { MutationHelpers } from './types.mjs'
|
|
17
21
|
|
|
18
22
|
import { createMutationKey } from './key-creator.mjs'
|
|
19
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Helper type for endpoint with config property
|
|
26
|
+
*/
|
|
27
|
+
type EndpointWithConfig<Config extends AnyEndpointConfig> = ((
|
|
28
|
+
params: any,
|
|
29
|
+
) => Promise<any>) & {
|
|
30
|
+
config: Config
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Helper type for response input when errorSchema is present
|
|
35
|
+
*/
|
|
36
|
+
type ResponseInput<
|
|
37
|
+
ResponseSchema extends ZodType,
|
|
38
|
+
ErrorSchema extends ErrorSchemaRecord | undefined,
|
|
39
|
+
> = ErrorSchema extends ErrorSchemaRecord
|
|
40
|
+
? z.output<ResponseSchema> | InferErrorSchemaOutput<ErrorSchema>
|
|
41
|
+
: z.output<ResponseSchema>
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Options type for makeMutation
|
|
45
|
+
*/
|
|
46
|
+
type MakeMutationParams<
|
|
47
|
+
Config extends AnyEndpointConfig,
|
|
48
|
+
ResponseSchema extends ZodType,
|
|
49
|
+
ErrorSchema extends ErrorSchemaRecord | undefined,
|
|
50
|
+
TData,
|
|
51
|
+
TVariables,
|
|
52
|
+
TOnMutateResult,
|
|
53
|
+
TContext,
|
|
54
|
+
UseKey extends boolean,
|
|
55
|
+
> = Omit<
|
|
56
|
+
UseMutationOptions<TData, Error, TVariables>,
|
|
57
|
+
| 'mutationKey'
|
|
58
|
+
| 'mutationFn'
|
|
59
|
+
| 'onMutate'
|
|
60
|
+
| 'onSuccess'
|
|
61
|
+
| 'onError'
|
|
62
|
+
| 'onSettled'
|
|
63
|
+
| 'scope'
|
|
64
|
+
> & {
|
|
65
|
+
processResponse?: ProcessResponseFunction<TData, ResponseInput<ResponseSchema, ErrorSchema>>
|
|
66
|
+
useContext?: () => TContext
|
|
67
|
+
onSuccess?: (
|
|
68
|
+
data: TData,
|
|
69
|
+
variables: TVariables,
|
|
70
|
+
context: TContext &
|
|
71
|
+
MutationFunctionContext & { onMutateResult: TOnMutateResult | undefined },
|
|
72
|
+
) => void | Promise<void>
|
|
73
|
+
onError?: (
|
|
74
|
+
err: unknown,
|
|
75
|
+
variables: TVariables,
|
|
76
|
+
context: TContext &
|
|
77
|
+
MutationFunctionContext & { onMutateResult: TOnMutateResult | undefined },
|
|
78
|
+
) => void | Promise<void>
|
|
79
|
+
onMutate?: (
|
|
80
|
+
variables: TVariables,
|
|
81
|
+
context: TContext & MutationFunctionContext,
|
|
82
|
+
) => TOnMutateResult | Promise<TOnMutateResult>
|
|
83
|
+
onSettled?: (
|
|
84
|
+
data: TData | undefined,
|
|
85
|
+
error: Error | null,
|
|
86
|
+
variables: TVariables,
|
|
87
|
+
context: TContext &
|
|
88
|
+
MutationFunctionContext & { onMutateResult: TOnMutateResult | undefined },
|
|
89
|
+
) => void | Promise<void>
|
|
90
|
+
useKey?: UseKey
|
|
91
|
+
keyPrefix?: UseKey extends true
|
|
92
|
+
? UrlHasParams<Config['url']> extends true
|
|
93
|
+
? string[]
|
|
94
|
+
: never
|
|
95
|
+
: never
|
|
96
|
+
keySuffix?: UseKey extends true
|
|
97
|
+
? UrlHasParams<Config['url']> extends true
|
|
98
|
+
? string[]
|
|
99
|
+
: never
|
|
100
|
+
: never
|
|
101
|
+
}
|
|
102
|
+
|
|
20
103
|
/**
|
|
21
104
|
* Creates a mutation hook for a given endpoint.
|
|
22
105
|
*
|
|
@@ -27,44 +110,87 @@ import { createMutationKey } from './key-creator.mjs'
|
|
|
27
110
|
* @param options - Mutation configuration including processResponse and callbacks
|
|
28
111
|
* @returns A hook function that returns mutation result with attached helpers
|
|
29
112
|
*/
|
|
113
|
+
// Overload: WITH errorSchema
|
|
30
114
|
export function makeMutation<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
115
|
+
Method extends HttpMethod,
|
|
116
|
+
Url extends string,
|
|
117
|
+
QuerySchema extends ZodObject | undefined,
|
|
118
|
+
ResponseSchema extends ZodType,
|
|
119
|
+
RequestSchema extends ZodType,
|
|
120
|
+
ErrorSchema extends ErrorSchemaRecord,
|
|
121
|
+
TData,
|
|
122
|
+
TOnMutateResult = unknown,
|
|
123
|
+
TContext = unknown,
|
|
124
|
+
UseKey extends boolean = false,
|
|
125
|
+
>(
|
|
126
|
+
endpoint: EndpointWithConfig<
|
|
127
|
+
BaseEndpointConfig<Method, Url, QuerySchema, ResponseSchema, RequestSchema, ErrorSchema>
|
|
128
|
+
>,
|
|
129
|
+
options: MakeMutationParams<
|
|
130
|
+
BaseEndpointConfig<Method, Url, QuerySchema, ResponseSchema, RequestSchema, ErrorSchema>,
|
|
131
|
+
ResponseSchema,
|
|
132
|
+
ErrorSchema,
|
|
133
|
+
TData,
|
|
134
|
+
any,
|
|
135
|
+
TOnMutateResult,
|
|
136
|
+
TContext,
|
|
137
|
+
UseKey
|
|
138
|
+
>,
|
|
139
|
+
): ((
|
|
140
|
+
keyParams: UseKey extends true
|
|
141
|
+
? UrlHasParams<Url> extends true
|
|
142
|
+
? { urlParams: UrlParams<Url> }
|
|
143
|
+
: never
|
|
144
|
+
: never,
|
|
145
|
+
) => UseMutationResult<TData, Error, any, TOnMutateResult>) &
|
|
146
|
+
MutationHelpers<Url, TData>
|
|
147
|
+
|
|
148
|
+
// Overload: WITHOUT errorSchema
|
|
149
|
+
export function makeMutation<
|
|
150
|
+
Method extends HttpMethod,
|
|
151
|
+
Url extends string,
|
|
152
|
+
QuerySchema extends ZodObject | undefined,
|
|
153
|
+
ResponseSchema extends ZodType,
|
|
154
|
+
RequestSchema extends ZodType | undefined,
|
|
155
|
+
TData,
|
|
35
156
|
TOnMutateResult = unknown,
|
|
36
157
|
TContext = unknown,
|
|
37
158
|
UseKey extends boolean = false,
|
|
38
159
|
>(
|
|
39
|
-
endpoint:
|
|
40
|
-
|
|
41
|
-
|
|
160
|
+
endpoint: EndpointWithConfig<
|
|
161
|
+
BaseEndpointConfig<Method, Url, QuerySchema, ResponseSchema, RequestSchema, undefined>
|
|
162
|
+
>,
|
|
163
|
+
options: MakeMutationParams<
|
|
164
|
+
BaseEndpointConfig<Method, Url, QuerySchema, ResponseSchema, RequestSchema, undefined>,
|
|
165
|
+
ResponseSchema,
|
|
166
|
+
undefined,
|
|
42
167
|
TData,
|
|
43
|
-
|
|
44
|
-
TResponse,
|
|
168
|
+
any,
|
|
45
169
|
TOnMutateResult,
|
|
46
170
|
TContext,
|
|
47
171
|
UseKey
|
|
48
172
|
>,
|
|
49
|
-
)
|
|
173
|
+
): ((
|
|
174
|
+
keyParams: UseKey extends true
|
|
175
|
+
? UrlHasParams<Url> extends true
|
|
176
|
+
? { urlParams: UrlParams<Url> }
|
|
177
|
+
: never
|
|
178
|
+
: never,
|
|
179
|
+
) => UseMutationResult<TData, Error, any, TOnMutateResult>) &
|
|
180
|
+
MutationHelpers<Url, TData>
|
|
181
|
+
|
|
182
|
+
// Implementation
|
|
183
|
+
export function makeMutation(
|
|
184
|
+
endpoint: EndpointWithConfig<AnyEndpointConfig>,
|
|
185
|
+
options: any,
|
|
186
|
+
): any {
|
|
50
187
|
const config = endpoint.config
|
|
51
188
|
|
|
52
189
|
const mutationKey = createMutationKey(config, {
|
|
53
190
|
...options,
|
|
54
|
-
processResponse: options.processResponse ?? ((data) => data),
|
|
191
|
+
processResponse: options.processResponse ?? ((data: any) => data),
|
|
55
192
|
})
|
|
56
|
-
const result = (
|
|
57
|
-
keyParams: UseKey extends true
|
|
58
|
-
? UrlHasParams<Config['url']> extends true
|
|
59
|
-
? { urlParams: UrlParams<Config['url']> }
|
|
60
|
-
: never
|
|
61
|
-
: never,
|
|
62
|
-
): UseMutationResult<
|
|
63
|
-
TData,
|
|
64
|
-
Error,
|
|
65
|
-
NaviosZodRequest<Config>,
|
|
66
|
-
TOnMutateResult
|
|
67
|
-
> => {
|
|
193
|
+
const result = (keyParams: any): any => {
|
|
68
194
|
const {
|
|
69
195
|
useKey,
|
|
70
196
|
useContext,
|
|
@@ -78,9 +204,8 @@ export function makeMutation<
|
|
|
78
204
|
...rest
|
|
79
205
|
} = options
|
|
80
206
|
|
|
81
|
-
const ownContext =
|
|
207
|
+
const ownContext = useContext?.() ?? {}
|
|
82
208
|
|
|
83
|
-
// @ts-expect-error The types match
|
|
84
209
|
return useMutation({
|
|
85
210
|
...rest,
|
|
86
211
|
mutationKey: useKey ? mutationKey(keyParams) : undefined,
|
|
@@ -89,89 +214,72 @@ export function makeMutation<
|
|
|
89
214
|
id: JSON.stringify(mutationKey(keyParams)),
|
|
90
215
|
}
|
|
91
216
|
: undefined,
|
|
92
|
-
async mutationFn(params:
|
|
217
|
+
async mutationFn(params: any) {
|
|
93
218
|
const response = await endpoint(params)
|
|
94
219
|
|
|
95
|
-
return
|
|
220
|
+
return processResponse ? processResponse(response) : response
|
|
96
221
|
},
|
|
97
222
|
onSuccess: onSuccess
|
|
98
223
|
? (
|
|
99
|
-
data:
|
|
100
|
-
variables:
|
|
101
|
-
onMutateResult:
|
|
224
|
+
data: any,
|
|
225
|
+
variables: any,
|
|
226
|
+
onMutateResult: any,
|
|
102
227
|
context: MutationFunctionContext,
|
|
103
228
|
) => {
|
|
104
229
|
return onSuccess?.(data, variables, {
|
|
105
230
|
...ownContext,
|
|
106
231
|
...context,
|
|
107
232
|
onMutateResult,
|
|
108
|
-
}
|
|
109
|
-
MutationFunctionContext & {
|
|
110
|
-
onMutateResult: TOnMutateResult | undefined
|
|
111
|
-
})
|
|
233
|
+
})
|
|
112
234
|
}
|
|
113
235
|
: undefined,
|
|
114
236
|
onError: onError
|
|
115
237
|
? (
|
|
116
238
|
err: Error,
|
|
117
|
-
variables:
|
|
118
|
-
onMutateResult:
|
|
239
|
+
variables: any,
|
|
240
|
+
onMutateResult: any,
|
|
119
241
|
context: MutationFunctionContext,
|
|
120
242
|
) => {
|
|
121
243
|
return onError?.(err, variables, {
|
|
122
244
|
onMutateResult,
|
|
123
245
|
...ownContext,
|
|
124
246
|
...context,
|
|
125
|
-
}
|
|
126
|
-
MutationFunctionContext & {
|
|
127
|
-
onMutateResult: TOnMutateResult | undefined
|
|
128
|
-
})
|
|
247
|
+
})
|
|
129
248
|
}
|
|
130
249
|
: undefined,
|
|
131
250
|
onMutate: onMutate
|
|
132
|
-
? (variables:
|
|
251
|
+
? (variables: any, context: MutationFunctionContext) => {
|
|
133
252
|
return onMutate(variables, {
|
|
134
253
|
...ownContext,
|
|
135
254
|
...context,
|
|
136
|
-
}
|
|
255
|
+
})
|
|
137
256
|
}
|
|
138
257
|
: undefined,
|
|
139
258
|
onSettled: onSettled
|
|
140
259
|
? (
|
|
141
|
-
data:
|
|
260
|
+
data: any,
|
|
142
261
|
error: Error | null,
|
|
143
|
-
variables:
|
|
144
|
-
onMutateResult:
|
|
262
|
+
variables: any,
|
|
263
|
+
onMutateResult: any,
|
|
145
264
|
context: MutationFunctionContext,
|
|
146
265
|
) => {
|
|
147
266
|
return onSettled(data, error, variables, {
|
|
148
267
|
...ownContext,
|
|
149
268
|
...context,
|
|
150
269
|
onMutateResult,
|
|
151
|
-
}
|
|
152
|
-
MutationFunctionContext & {
|
|
153
|
-
onMutateResult: TOnMutateResult | undefined
|
|
154
|
-
})
|
|
270
|
+
})
|
|
155
271
|
}
|
|
156
272
|
: undefined,
|
|
157
273
|
})
|
|
158
274
|
}
|
|
159
|
-
result.useIsMutating = (
|
|
160
|
-
keyParams: UseKey extends true
|
|
161
|
-
? UrlHasParams<Config['url']> extends true
|
|
162
|
-
? UrlParams<Config['url']>
|
|
163
|
-
: never
|
|
164
|
-
: never,
|
|
165
|
-
): boolean => {
|
|
275
|
+
result.useIsMutating = (keyParams: any): boolean => {
|
|
166
276
|
if (!options.useKey) {
|
|
167
277
|
throw new Error(
|
|
168
278
|
'useIsMutating can only be used when useKey is set to true',
|
|
169
279
|
)
|
|
170
280
|
}
|
|
171
281
|
const isMutating = useIsMutating({
|
|
172
|
-
mutationKey: mutationKey(
|
|
173
|
-
urlParams: keyParams,
|
|
174
|
-
}),
|
|
282
|
+
mutationKey: mutationKey(keyParams),
|
|
175
283
|
})
|
|
176
284
|
return isMutating > 0
|
|
177
285
|
}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import type { QueryClient } from '@tanstack/react-query'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for creating optimistic update callbacks.
|
|
5
|
+
*
|
|
6
|
+
* @template TData - The mutation response data type
|
|
7
|
+
* @template TVariables - The mutation variables type
|
|
8
|
+
* @template TQueryData - The query cache data type
|
|
9
|
+
*/
|
|
10
|
+
export interface OptimisticUpdateConfig<
|
|
11
|
+
TData,
|
|
12
|
+
TVariables,
|
|
13
|
+
TQueryData,
|
|
14
|
+
> {
|
|
15
|
+
/**
|
|
16
|
+
* The query key to optimistically update.
|
|
17
|
+
* This should match the query key used for the affected query.
|
|
18
|
+
*/
|
|
19
|
+
queryKey: readonly unknown[]
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Function to compute the optimistic cache value.
|
|
23
|
+
* Receives the current cache data and mutation variables.
|
|
24
|
+
*
|
|
25
|
+
* @param oldData - Current data in the cache (may be undefined if not cached)
|
|
26
|
+
* @param variables - The mutation variables being submitted
|
|
27
|
+
* @returns The new optimistic cache value
|
|
28
|
+
*/
|
|
29
|
+
updateFn: (oldData: TQueryData | undefined, variables: TVariables) => TQueryData
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Whether to rollback on error.
|
|
33
|
+
* Defaults to true.
|
|
34
|
+
*/
|
|
35
|
+
rollbackOnError?: boolean
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Whether to invalidate the query on settlement.
|
|
39
|
+
* Defaults to true.
|
|
40
|
+
*/
|
|
41
|
+
invalidateOnSettled?: boolean
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Return type for optimistic update callbacks.
|
|
46
|
+
*/
|
|
47
|
+
export interface OptimisticUpdateCallbacks<TData, TVariables, TQueryData> {
|
|
48
|
+
/**
|
|
49
|
+
* Called before the mutation starts. Cancels outgoing refetches,
|
|
50
|
+
* snapshots the current cache value, and applies the optimistic update.
|
|
51
|
+
*/
|
|
52
|
+
onMutate: (
|
|
53
|
+
variables: TVariables,
|
|
54
|
+
context: { queryClient: QueryClient },
|
|
55
|
+
) => Promise<{ previousData: TQueryData | undefined }>
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Called when the mutation fails. Rolls back the cache to the previous
|
|
59
|
+
* value if rollbackOnError is enabled.
|
|
60
|
+
*/
|
|
61
|
+
onError: (
|
|
62
|
+
err: Error,
|
|
63
|
+
variables: TVariables,
|
|
64
|
+
context: { previousData?: TQueryData; queryClient: QueryClient },
|
|
65
|
+
) => void
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Called when the mutation completes (success or error).
|
|
69
|
+
* Invalidates the query if invalidateOnSettled is enabled.
|
|
70
|
+
*/
|
|
71
|
+
onSettled: (
|
|
72
|
+
data: TData | undefined,
|
|
73
|
+
error: Error | null,
|
|
74
|
+
variables: TVariables,
|
|
75
|
+
context: { queryClient: QueryClient },
|
|
76
|
+
) => void
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Creates type-safe optimistic update callbacks for mutations.
|
|
81
|
+
*
|
|
82
|
+
* This helper generates the onMutate, onError, and onSettled callbacks
|
|
83
|
+
* that implement the standard optimistic update pattern:
|
|
84
|
+
*
|
|
85
|
+
* 1. onMutate: Cancel refetches, snapshot cache, apply optimistic update
|
|
86
|
+
* 2. onError: Rollback cache to previous value on failure
|
|
87
|
+
* 3. onSettled: Invalidate query to refetch fresh data
|
|
88
|
+
*
|
|
89
|
+
* @param config - Configuration for the optimistic update
|
|
90
|
+
* @returns Object containing onMutate, onError, and onSettled callbacks
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* // Create a mutation with optimistic updates
|
|
95
|
+
* const updateUser = client.mutation({
|
|
96
|
+
* method: 'PATCH',
|
|
97
|
+
* url: '/users/$userId',
|
|
98
|
+
* requestSchema: updateUserSchema,
|
|
99
|
+
* responseSchema: userSchema,
|
|
100
|
+
* processResponse: (data) => data,
|
|
101
|
+
* ...createOptimisticUpdate({
|
|
102
|
+
* queryKey: ['users', userId],
|
|
103
|
+
* updateFn: (oldData, variables) => ({
|
|
104
|
+
* ...oldData,
|
|
105
|
+
* ...variables.data,
|
|
106
|
+
* }),
|
|
107
|
+
* }),
|
|
108
|
+
* })
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* // Optimistic update for adding an item to a list
|
|
114
|
+
* const addTodo = client.mutation({
|
|
115
|
+
* method: 'POST',
|
|
116
|
+
* url: '/todos',
|
|
117
|
+
* requestSchema: createTodoSchema,
|
|
118
|
+
* responseSchema: todoSchema,
|
|
119
|
+
* processResponse: (data) => data,
|
|
120
|
+
* ...createOptimisticUpdate({
|
|
121
|
+
* queryKey: ['todos'],
|
|
122
|
+
* updateFn: (oldData, variables) => [
|
|
123
|
+
* ...(oldData ?? []),
|
|
124
|
+
* { id: 'temp-id', ...variables.data, createdAt: new Date() },
|
|
125
|
+
* ],
|
|
126
|
+
* }),
|
|
127
|
+
* })
|
|
128
|
+
* ```
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```ts
|
|
132
|
+
* // Optimistic delete
|
|
133
|
+
* const deleteTodo = client.mutation({
|
|
134
|
+
* method: 'DELETE',
|
|
135
|
+
* url: '/todos/$todoId',
|
|
136
|
+
* responseSchema: z.object({ success: z.boolean() }),
|
|
137
|
+
* processResponse: (data) => data,
|
|
138
|
+
* ...createOptimisticUpdate({
|
|
139
|
+
* queryKey: ['todos'],
|
|
140
|
+
* updateFn: (oldData, variables) =>
|
|
141
|
+
* (oldData ?? []).filter((t) => t.id !== variables.urlParams.todoId),
|
|
142
|
+
* }),
|
|
143
|
+
* })
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
export function createOptimisticUpdate<
|
|
147
|
+
TData,
|
|
148
|
+
TVariables,
|
|
149
|
+
TQueryData,
|
|
150
|
+
>(config: OptimisticUpdateConfig<TData, TVariables, TQueryData>): OptimisticUpdateCallbacks<TData, TVariables, TQueryData> {
|
|
151
|
+
const {
|
|
152
|
+
queryKey,
|
|
153
|
+
updateFn,
|
|
154
|
+
rollbackOnError = true,
|
|
155
|
+
invalidateOnSettled = true,
|
|
156
|
+
} = config
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
onMutate: async (
|
|
160
|
+
variables: TVariables,
|
|
161
|
+
context: { queryClient: QueryClient },
|
|
162
|
+
) => {
|
|
163
|
+
// Cancel any outgoing refetches to prevent overwriting optimistic update
|
|
164
|
+
await context.queryClient.cancelQueries({ queryKey })
|
|
165
|
+
|
|
166
|
+
// Snapshot the previous value
|
|
167
|
+
const previousData = context.queryClient.getQueryData<TQueryData>(queryKey)
|
|
168
|
+
|
|
169
|
+
// Optimistically update the cache
|
|
170
|
+
context.queryClient.setQueryData<TQueryData>(
|
|
171
|
+
queryKey,
|
|
172
|
+
(old) => updateFn(old, variables),
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
// Return context with the previous data for potential rollback
|
|
176
|
+
return { previousData }
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
onError: (
|
|
180
|
+
_err: Error,
|
|
181
|
+
_variables: TVariables,
|
|
182
|
+
context: { previousData?: TQueryData; queryClient: QueryClient },
|
|
183
|
+
) => {
|
|
184
|
+
// Rollback to the previous value on error
|
|
185
|
+
if (rollbackOnError && context?.previousData !== undefined) {
|
|
186
|
+
context.queryClient.setQueryData(queryKey, context.previousData)
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
onSettled: (
|
|
191
|
+
_data: TData | undefined,
|
|
192
|
+
_error: Error | null,
|
|
193
|
+
_variables: TVariables,
|
|
194
|
+
context: { queryClient: QueryClient },
|
|
195
|
+
) => {
|
|
196
|
+
// Always invalidate to ensure we have the correct server state
|
|
197
|
+
if (invalidateOnSettled) {
|
|
198
|
+
void context.queryClient.invalidateQueries({ queryKey })
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Creates optimistic update callbacks that work with multiple query keys.
|
|
206
|
+
*
|
|
207
|
+
* Useful when a mutation affects multiple cached queries.
|
|
208
|
+
*
|
|
209
|
+
* @param configs - Array of optimistic update configurations
|
|
210
|
+
* @returns Combined callbacks that handle all specified queries
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```ts
|
|
214
|
+
* // Updating a user affects both user detail and user list queries
|
|
215
|
+
* const updateUser = client.mutation({
|
|
216
|
+
* method: 'PATCH',
|
|
217
|
+
* url: '/users/$userId',
|
|
218
|
+
* requestSchema: updateUserSchema,
|
|
219
|
+
* responseSchema: userSchema,
|
|
220
|
+
* processResponse: (data) => data,
|
|
221
|
+
* ...createMultiOptimisticUpdate([
|
|
222
|
+
* {
|
|
223
|
+
* queryKey: ['users', userId],
|
|
224
|
+
* updateFn: (oldData, variables) => ({ ...oldData, ...variables.data }),
|
|
225
|
+
* },
|
|
226
|
+
* {
|
|
227
|
+
* queryKey: ['users'],
|
|
228
|
+
* updateFn: (oldList, variables) =>
|
|
229
|
+
* (oldList ?? []).map((u) =>
|
|
230
|
+
* u.id === userId ? { ...u, ...variables.data } : u
|
|
231
|
+
* ),
|
|
232
|
+
* },
|
|
233
|
+
* ]),
|
|
234
|
+
* })
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
export function createMultiOptimisticUpdate<TData, TVariables>(
|
|
238
|
+
configs: Array<OptimisticUpdateConfig<TData, TVariables, unknown>>,
|
|
239
|
+
): OptimisticUpdateCallbacks<TData, TVariables, Map<string, unknown>> {
|
|
240
|
+
return {
|
|
241
|
+
onMutate: async (
|
|
242
|
+
variables: TVariables,
|
|
243
|
+
context: { queryClient: QueryClient },
|
|
244
|
+
) => {
|
|
245
|
+
// Cancel and snapshot all queries
|
|
246
|
+
const previousData = new Map<string, unknown>()
|
|
247
|
+
|
|
248
|
+
for (const config of configs) {
|
|
249
|
+
await context.queryClient.cancelQueries({ queryKey: config.queryKey })
|
|
250
|
+
const key = JSON.stringify(config.queryKey)
|
|
251
|
+
previousData.set(key, context.queryClient.getQueryData(config.queryKey))
|
|
252
|
+
context.queryClient.setQueryData(
|
|
253
|
+
config.queryKey,
|
|
254
|
+
(old: unknown) => config.updateFn(old, variables),
|
|
255
|
+
)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return { previousData }
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
onError: (
|
|
262
|
+
_err: Error,
|
|
263
|
+
_variables: TVariables,
|
|
264
|
+
context: { previousData?: Map<string, unknown>; queryClient: QueryClient },
|
|
265
|
+
) => {
|
|
266
|
+
// Rollback all queries
|
|
267
|
+
if (context?.previousData) {
|
|
268
|
+
for (const config of configs) {
|
|
269
|
+
if (config.rollbackOnError !== false) {
|
|
270
|
+
const key = JSON.stringify(config.queryKey)
|
|
271
|
+
const previous = context.previousData.get(key)
|
|
272
|
+
if (previous !== undefined) {
|
|
273
|
+
context.queryClient.setQueryData(config.queryKey, previous)
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
onSettled: (
|
|
281
|
+
_data: TData | undefined,
|
|
282
|
+
_error: Error | null,
|
|
283
|
+
_variables: TVariables,
|
|
284
|
+
context: { queryClient: QueryClient },
|
|
285
|
+
) => {
|
|
286
|
+
// Invalidate all queries
|
|
287
|
+
for (const config of configs) {
|
|
288
|
+
if (config.invalidateOnSettled !== false) {
|
|
289
|
+
void context.queryClient.invalidateQueries({ queryKey: config.queryKey })
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
}
|
|
294
|
+
}
|