@navios/react-query 0.5.2 → 0.6.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.
@@ -10,6 +10,7 @@ import { declareClient } from '../client/declare-client.mjs'
10
10
 
11
11
  vi.mock('@tanstack/react-query', async (importReal) => {
12
12
  const actual = await importReal<typeof import('@tanstack/react-query')>()
13
+ const mockMutationContext = { mutationId: 1, meta: undefined }
13
14
  return {
14
15
  ...actual,
15
16
  useQueryClient: () => ({
@@ -22,11 +23,27 @@ vi.mock('@tanstack/react-query', async (importReal) => {
22
23
  ...req,
23
24
  mutateAsync: async (data: unknown) => {
24
25
  try {
26
+ const onMutateResult = await req.onMutate?.(data, mockMutationContext)
25
27
  const res = await req.mutationFn(data)
26
- await req.onSuccess?.(res, data)
28
+ await req.onSuccess?.(res, data, onMutateResult, mockMutationContext)
29
+ await req.onSettled?.(
30
+ res,
31
+ null,
32
+ data,
33
+ onMutateResult,
34
+ mockMutationContext,
35
+ )
27
36
  return res
28
37
  } catch (err) {
29
- req.onError?.(err, data)
38
+ const onMutateResult = undefined
39
+ await req.onError?.(err, data, onMutateResult, mockMutationContext)
40
+ await req.onSettled?.(
41
+ undefined,
42
+ err,
43
+ data,
44
+ onMutateResult,
45
+ mockMutationContext,
46
+ )
30
47
  throw err
31
48
  }
32
49
  },
@@ -147,7 +164,7 @@ describe('declareClient', () => {
147
164
  }
148
165
  return data
149
166
  },
150
- onSuccess(queryClient, data, variables) {
167
+ onSuccess(data, variables, context) {
151
168
  expect(data).toMatchObject({
152
169
  success: true,
153
170
  test: 'test',
@@ -161,9 +178,11 @@ describe('declareClient', () => {
161
178
  testId: 'test',
162
179
  },
163
180
  })
181
+ expect(context).toHaveProperty('mutationId')
182
+ expect(context).toHaveProperty('onMutateResult')
164
183
  },
165
- onError: (err) => {
166
- console.log('onError', err)
184
+ onError: (err, _variables, context) => {
185
+ console.log('onError', err, context)
167
186
  },
168
187
  })
169
188
 
@@ -10,6 +10,7 @@ import { makeMutation } from '../mutation/make-hook.mjs'
10
10
 
11
11
  vi.mock('@tanstack/react-query', async (importReal) => {
12
12
  const actual = await importReal<typeof import('@tanstack/react-query')>()
13
+ const mockMutationContext = { mutationId: 1, meta: undefined }
13
14
  return {
14
15
  ...actual,
15
16
  useQueryClient: () => ({
@@ -22,11 +23,27 @@ vi.mock('@tanstack/react-query', async (importReal) => {
22
23
  ...req,
23
24
  mutateAsync: async (data: unknown) => {
24
25
  try {
26
+ const onMutateResult = await req.onMutate?.(data, mockMutationContext)
25
27
  const res = await req.mutationFn(data)
26
- await req.onSuccess?.(res, data)
28
+ await req.onSuccess?.(res, data, onMutateResult, mockMutationContext)
29
+ await req.onSettled?.(
30
+ res,
31
+ null,
32
+ data,
33
+ onMutateResult,
34
+ mockMutationContext,
35
+ )
27
36
  return res
28
37
  } catch (err) {
29
- req.onError?.(err, data)
38
+ const onMutateResult = undefined
39
+ await req.onError?.(err, data, onMutateResult, mockMutationContext)
40
+ await req.onSettled?.(
41
+ undefined,
42
+ err,
43
+ data,
44
+ onMutateResult,
45
+ mockMutationContext,
46
+ )
30
47
  throw err
31
48
  }
32
49
  },
@@ -77,7 +94,7 @@ describe('makeMutation', () => {
77
94
  }
78
95
  return data
79
96
  },
80
- onSuccess: (queryClient, data, variables) => {
97
+ onSuccess: (data, variables, context) => {
81
98
  expect(data).toMatchObject({
82
99
  success: true,
83
100
  test: 'test',
@@ -95,10 +112,12 @@ describe('makeMutation', () => {
95
112
  foo: 'bar',
96
113
  },
97
114
  })
115
+ expect(context).toHaveProperty('mutationId')
116
+ expect(context).toHaveProperty('onMutateResult')
98
117
  },
99
118
 
100
- onError: (err) => {
101
- console.log('onError', err)
119
+ onError: (err, _variables, context) => {
120
+ console.log('onError', err, context)
102
121
  },
103
122
  })
104
123
  // @ts-expect-error internal type
@@ -184,7 +203,7 @@ describe('makeMutation', () => {
184
203
  expect(result).toContain('blob:')
185
204
  })
186
205
 
187
- it('should call onSuccess with Blob data', async () => {
206
+ it('should call onSuccess with Blob data and context', async () => {
188
207
  const onSuccess = vi.fn()
189
208
  // @ts-expect-error stream endpoint type differs from regular endpoint
190
209
  const mutation = makeMutation(streamEndpoint, {
@@ -198,10 +217,133 @@ describe('makeMutation', () => {
198
217
  })
199
218
 
200
219
  expect(onSuccess).toHaveBeenCalledTimes(1)
201
- expect(onSuccess.mock.calls[0][1]).toBeInstanceOf(Blob)
202
- expect(onSuccess.mock.calls[0][2]).toMatchObject({
220
+ expect(onSuccess.mock.calls[0][0]).toBeInstanceOf(Blob)
221
+ expect(onSuccess.mock.calls[0][1]).toMatchObject({
203
222
  urlParams: { fileId: '123' },
204
223
  })
224
+ // Third argument should be context with onMutateResult and mutationId
225
+ expect(onSuccess.mock.calls[0][2]).toHaveProperty('onMutateResult')
226
+ expect(onSuccess.mock.calls[0][2]).toHaveProperty('mutationId')
227
+ })
228
+ })
229
+
230
+ describe('mutation callbacks', () => {
231
+ it('should call onMutate before mutation and pass result to other callbacks', async () => {
232
+ const callOrder: string[] = []
233
+ const onMutate = vi.fn((_variables, _context) => {
234
+ callOrder.push('onMutate')
235
+ return { optimisticId: 'temp-123' }
236
+ })
237
+ const onSuccess = vi.fn((_data, _variables, context) => {
238
+ callOrder.push('onSuccess')
239
+ expect(context.onMutateResult).toEqual({ optimisticId: 'temp-123' })
240
+ })
241
+ const onSettled = vi.fn((_data, _error, _variables, context) => {
242
+ callOrder.push('onSettled')
243
+ expect(context.onMutateResult).toEqual({ optimisticId: 'temp-123' })
244
+ })
245
+
246
+ const mutation = makeMutation(endpoint, {
247
+ processResponse: (data) => {
248
+ if (!data.success) throw new Error(data.message)
249
+ return data
250
+ },
251
+ onMutate,
252
+ onSuccess,
253
+ onSettled,
254
+ })
255
+
256
+ // @ts-expect-error internal type
257
+ const mutationResult = mutation()
258
+ await mutationResult.mutateAsync({
259
+ urlParams: { testId: '1', fooId: '2' },
260
+ data: { testId: '1', fooId: '2' },
261
+ params: { foo: 'bar' },
262
+ })
263
+
264
+ expect(callOrder).toEqual(['onMutate', 'onSuccess', 'onSettled'])
265
+ expect(onMutate).toHaveBeenCalledTimes(1)
266
+ expect(onSuccess).toHaveBeenCalledTimes(1)
267
+ expect(onSettled).toHaveBeenCalledTimes(1)
268
+ })
269
+
270
+ it('should call onError and onSettled on failure', async () => {
271
+ adapter.mock('/test/1/foo/2', 'POST', () => {
272
+ return new Response(
273
+ JSON.stringify({ success: false, message: 'Test error' }),
274
+ { status: 200, headers: { 'content-type': 'application/json' } },
275
+ )
276
+ })
277
+
278
+ const onError = vi.fn()
279
+ const onSettled = vi.fn()
280
+
281
+ const mutation = makeMutation(endpoint, {
282
+ processResponse: (data) => {
283
+ if (!data.success) throw new Error(data.message)
284
+ return data
285
+ },
286
+ onError,
287
+ onSettled,
288
+ })
289
+
290
+ // @ts-expect-error internal type
291
+ const mutationResult = mutation()
292
+
293
+ await expect(
294
+ mutationResult.mutateAsync({
295
+ urlParams: { testId: '1', fooId: '2' },
296
+ data: { testId: '1', fooId: '2' },
297
+ params: { foo: 'bar' },
298
+ }),
299
+ ).rejects.toThrow('Test error')
300
+
301
+ expect(onError).toHaveBeenCalledTimes(1)
302
+ expect(onError.mock.calls[0][0]).toBeInstanceOf(Error)
303
+ expect(onError.mock.calls[0][0].message).toBe('Test error')
304
+ expect(onSettled).toHaveBeenCalledTimes(1)
305
+ expect(onSettled.mock.calls[0][1]).toBeInstanceOf(Error)
306
+
307
+ // Restore the mock
308
+ adapter.mock('/test/1/foo/2', 'POST', () => {
309
+ return new Response(
310
+ JSON.stringify({ success: true, test: 'test' }),
311
+ { status: 200, headers: { 'content-type': 'application/json' } },
312
+ )
313
+ })
314
+ })
315
+
316
+ it('should merge useContext result with MutationFunctionContext', async () => {
317
+ const useContext = () => ({
318
+ queryClient: { invalidate: vi.fn() },
319
+ customValue: 'test',
320
+ })
321
+
322
+ const onSuccess = vi.fn()
323
+
324
+ const mutation = makeMutation(endpoint, {
325
+ processResponse: (data) => {
326
+ if (!data.success) throw new Error(data.message)
327
+ return data
328
+ },
329
+ useContext,
330
+ onSuccess,
331
+ })
332
+
333
+ // @ts-expect-error internal type
334
+ const mutationResult = mutation()
335
+ await mutationResult.mutateAsync({
336
+ urlParams: { testId: '1', fooId: '2' },
337
+ data: { testId: '1', fooId: '2' },
338
+ params: { foo: 'bar' },
339
+ })
340
+
341
+ expect(onSuccess).toHaveBeenCalledTimes(1)
342
+ const context = onSuccess.mock.calls[0][2]
343
+ expect(context).toHaveProperty('queryClient')
344
+ expect(context).toHaveProperty('customValue', 'test')
345
+ expect(context).toHaveProperty('mutationId')
346
+ expect(context).toHaveProperty('onMutateResult')
205
347
  })
206
348
  })
207
349
  })
@@ -758,7 +758,7 @@ describe('mutationFromEndpoint() with stream endpoints', () => {
758
758
 
759
759
  test('stream mutation with onSuccess callback', () => {
760
760
  const mutation = client.mutationFromEndpoint(streamEndpointGet, {
761
- onSuccess: (_queryClient, data, variables) => {
761
+ onSuccess: (data, variables) => {
762
762
  // data should be Blob
763
763
  assertType<Blob>(data)
764
764
  // variables should have urlParams
@@ -778,7 +778,7 @@ describe('mutationFromEndpoint() with stream endpoints', () => {
778
778
  test('stream mutation with custom processResponse and onSuccess', () => {
779
779
  const mutation = client.mutationFromEndpoint(streamEndpointGet, {
780
780
  processResponse: (blob) => ({ url: URL.createObjectURL(blob), size: blob.size }),
781
- onSuccess: (_queryClient, data) => {
781
+ onSuccess: (data) => {
782
782
  // data should be the transformed type
783
783
  assertType<{ url: string; size: number }>(data)
784
784
  },
@@ -5,10 +5,17 @@ import type {
5
5
  AnyStreamConfig,
6
6
  HttpMethod,
7
7
  } from '@navios/builder'
8
- import type { InfiniteData, QueryClient } from '@tanstack/react-query'
8
+ import type {
9
+ InfiniteData,
10
+ MutationFunctionContext,
11
+ QueryClient,
12
+ } from '@tanstack/react-query'
9
13
  import type { z, ZodObject, ZodType } from 'zod/v4'
10
14
 
11
- import type { ClientOptions, ProcessResponseFunction } from '../common/types.mjs'
15
+ import type {
16
+ ClientOptions,
17
+ ProcessResponseFunction,
18
+ } from '../common/types.mjs'
12
19
  import type { MutationArgs } from '../mutation/types.mjs'
13
20
  import type { ClientInstance } from './types.mjs'
14
21
 
@@ -84,6 +91,7 @@ export interface MutationConfig<
84
91
  Response extends ZodType = ZodType,
85
92
  ReqResult = z.output<Response>,
86
93
  Result = unknown,
94
+ TOnMutateResult = unknown,
87
95
  Context = unknown,
88
96
  UseKey extends boolean = false,
89
97
  > {
@@ -95,18 +103,30 @@ export interface MutationConfig<
95
103
  processResponse: ProcessResponseFunction<Result, ReqResult>
96
104
  useContext?: () => Context
97
105
  onSuccess?: (
98
- queryClient: QueryClient,
99
- data: NoInfer<Result>,
106
+ data: Result,
100
107
  variables: MutationArgs<Url, RequestSchema, QuerySchema>,
101
- context: Context,
108
+ context: Context &
109
+ MutationFunctionContext & { onMutateResult: TOnMutateResult | undefined },
102
110
  ) => void | Promise<void>
103
111
  onError?: (
104
- queryClient: QueryClient,
105
- error: Error,
112
+ err: unknown,
106
113
  variables: MutationArgs<Url, RequestSchema, QuerySchema>,
107
- context: Context,
114
+ context: Context &
115
+ MutationFunctionContext & { onMutateResult: TOnMutateResult | undefined },
116
+ ) => void | Promise<void>
117
+ onMutate?: (
118
+ variables: MutationArgs<Url, RequestSchema, QuerySchema>,
119
+ context: Context & MutationFunctionContext,
120
+ ) => TOnMutateResult | Promise<TOnMutateResult>
121
+ onSettled?: (
122
+ data: Result | undefined,
123
+ error: Error | null,
124
+ variables: MutationArgs<Url, RequestSchema, QuerySchema>,
125
+ context: Context &
126
+ MutationFunctionContext & { onMutateResult: TOnMutateResult | undefined },
108
127
  ) => void | Promise<void>
109
128
  useKey?: UseKey
129
+ meta?: Record<string, unknown>
110
130
  }
111
131
 
112
132
  /**
@@ -237,6 +257,7 @@ export function declareClient<Options extends ClientOptions>({
237
257
  // @ts-expect-error We forgot about the DELETE method in original makeMutation
238
258
  onError: config.onError,
239
259
  useKey: config.useKey,
260
+ meta: config.meta,
240
261
  ...defaults,
241
262
  })
242
263
 
@@ -293,6 +314,10 @@ export function declareClient<Options extends ClientOptions>({
293
314
  onSuccess: config.onSuccess,
294
315
  // @ts-expect-error We forgot about the DELETE method in original makeMutation
295
316
  onError: config.onError,
317
+ // @ts-expect-error We forgot about the DELETE method in original makeMutation
318
+ onMutate: config.onMutate,
319
+ // @ts-expect-error We forgot about the DELETE method in original makeMutation
320
+ onSettled: config.onSettled,
296
321
  useKey: config.useKey,
297
322
  ...defaults,
298
323
  })