@navios/react-query 0.7.1 → 1.0.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 (117) hide show
  1. package/CHANGELOG.md +171 -1
  2. package/README.md +152 -4
  3. package/coverage/base.css +224 -0
  4. package/coverage/block-navigation.js +87 -0
  5. package/coverage/client/declare-client.mts.html +1264 -0
  6. package/coverage/client/index.html +116 -0
  7. package/coverage/clover.xml +160 -0
  8. package/coverage/coverage-final.json +8 -0
  9. package/coverage/favicon.png +0 -0
  10. package/coverage/index.html +146 -0
  11. package/coverage/mutation/index.html +131 -0
  12. package/coverage/mutation/key-creator.mts.html +277 -0
  13. package/coverage/mutation/make-hook.mts.html +952 -0
  14. package/coverage/prettify.css +1 -0
  15. package/coverage/prettify.js +2 -0
  16. package/coverage/query/index.html +161 -0
  17. package/coverage/query/key-creator.mts.html +415 -0
  18. package/coverage/query/make-infinite-options.mts.html +601 -0
  19. package/coverage/query/make-options.mts.html +838 -0
  20. package/coverage/query/prefetch.mts.html +1063 -0
  21. package/coverage/sort-arrow-sprite.png +0 -0
  22. package/coverage/sorter.js +210 -0
  23. package/dist/src/__tests__/errorSchema.spec.d.mts +2 -0
  24. package/dist/src/__tests__/errorSchema.spec.d.mts.map +1 -0
  25. package/dist/src/__tests__/prefetch.spec.d.mts +2 -0
  26. package/dist/src/__tests__/prefetch.spec.d.mts.map +1 -0
  27. package/dist/src/client/__type-tests__/from-endpoint.spec-d.d.mts +2 -0
  28. package/dist/src/client/__type-tests__/from-endpoint.spec-d.d.mts.map +1 -0
  29. package/dist/src/client/__type-tests__/infinite-query.spec-d.d.mts +2 -0
  30. package/dist/src/client/__type-tests__/infinite-query.spec-d.d.mts.map +1 -0
  31. package/dist/src/client/__type-tests__/multipart-mutation.spec-d.d.mts +2 -0
  32. package/dist/src/client/__type-tests__/multipart-mutation.spec-d.d.mts.map +1 -0
  33. package/dist/src/client/__type-tests__/mutation.spec-d.d.mts +2 -0
  34. package/dist/src/client/__type-tests__/mutation.spec-d.d.mts.map +1 -0
  35. package/dist/src/client/__type-tests__/query.spec-d.d.mts +2 -0
  36. package/dist/src/client/__type-tests__/query.spec-d.d.mts.map +1 -0
  37. package/dist/src/client/declare-client.d.mts +15 -8
  38. package/dist/src/client/declare-client.d.mts.map +1 -1
  39. package/dist/src/client/types/from-endpoint.d.mts +130 -0
  40. package/dist/src/client/types/from-endpoint.d.mts.map +1 -0
  41. package/dist/src/client/types/helpers.d.mts +74 -0
  42. package/dist/src/client/types/helpers.d.mts.map +1 -0
  43. package/dist/src/client/types/index.d.mts +21 -0
  44. package/dist/src/client/types/index.d.mts.map +1 -0
  45. package/dist/src/client/types/infinite-query.d.mts +61 -0
  46. package/dist/src/client/types/infinite-query.d.mts.map +1 -0
  47. package/dist/src/client/types/multipart-mutation.d.mts +98 -0
  48. package/dist/src/client/types/multipart-mutation.d.mts.map +1 -0
  49. package/dist/src/client/types/mutation.d.mts +75 -0
  50. package/dist/src/client/types/mutation.d.mts.map +1 -0
  51. package/dist/src/client/types/query.d.mts +65 -0
  52. package/dist/src/client/types/query.d.mts.map +1 -0
  53. package/dist/src/client/types.d.mts +1 -608
  54. package/dist/src/client/types.d.mts.map +1 -1
  55. package/dist/src/common/types.d.mts +40 -3
  56. package/dist/src/common/types.d.mts.map +1 -1
  57. package/dist/src/mutation/index.d.mts +1 -0
  58. package/dist/src/mutation/index.d.mts.map +1 -1
  59. package/dist/src/mutation/make-hook.d.mts +42 -16
  60. package/dist/src/mutation/make-hook.d.mts.map +1 -1
  61. package/dist/src/mutation/optimistic.d.mts +172 -0
  62. package/dist/src/mutation/optimistic.d.mts.map +1 -0
  63. package/dist/src/mutation/types.d.mts +41 -20
  64. package/dist/src/mutation/types.d.mts.map +1 -1
  65. package/dist/src/query/index.d.mts +1 -0
  66. package/dist/src/query/index.d.mts.map +1 -1
  67. package/dist/src/query/key-creator.d.mts.map +1 -1
  68. package/dist/src/query/make-infinite-options.d.mts +3 -2
  69. package/dist/src/query/make-infinite-options.d.mts.map +1 -1
  70. package/dist/src/query/make-options.d.mts +42 -12
  71. package/dist/src/query/make-options.d.mts.map +1 -1
  72. package/dist/src/query/prefetch.d.mts +245 -0
  73. package/dist/src/query/prefetch.d.mts.map +1 -0
  74. package/dist/src/query/types.d.mts +25 -18
  75. package/dist/src/query/types.d.mts.map +1 -1
  76. package/dist/tsconfig.tsbuildinfo +1 -1
  77. package/lib/index.cjs +451 -28
  78. package/lib/index.cjs.map +1 -1
  79. package/lib/index.d.cts +1019 -600
  80. package/lib/index.d.cts.map +1 -1
  81. package/lib/index.d.mts +1016 -597
  82. package/lib/index.d.mts.map +1 -1
  83. package/lib/index.mjs +447 -29
  84. package/lib/index.mjs.map +1 -1
  85. package/package.json +3 -3
  86. package/src/__tests__/declare-client.spec.mts +229 -2
  87. package/src/__tests__/errorSchema.spec.mts +391 -0
  88. package/src/__tests__/make-mutation.spec.mts +6 -5
  89. package/src/__tests__/makeDataTag.spec.mts +2 -1
  90. package/src/__tests__/makeQueryOptions.spec.mts +2 -1
  91. package/src/__tests__/prefetch.spec.mts +310 -0
  92. package/src/client/__type-tests__/from-endpoint.spec-d.mts +550 -0
  93. package/src/client/__type-tests__/infinite-query.spec-d.mts +648 -0
  94. package/src/client/__type-tests__/multipart-mutation.spec-d.mts +725 -0
  95. package/src/client/__type-tests__/mutation.spec-d.mts +757 -0
  96. package/src/client/__type-tests__/query.spec-d.mts +701 -0
  97. package/src/client/declare-client.mts +59 -34
  98. package/src/client/types/from-endpoint.mts +344 -0
  99. package/src/client/types/helpers.mts +140 -0
  100. package/src/client/types/index.mts +26 -0
  101. package/src/client/types/infinite-query.mts +133 -0
  102. package/src/client/types/multipart-mutation.mts +264 -0
  103. package/src/client/types/mutation.mts +176 -0
  104. package/src/client/types/query.mts +132 -0
  105. package/src/client/types.mts +1 -1935
  106. package/src/common/types.mts +67 -3
  107. package/src/mutation/index.mts +1 -0
  108. package/src/mutation/make-hook.mts +171 -63
  109. package/src/mutation/optimistic.mts +300 -0
  110. package/src/mutation/types.mts +87 -30
  111. package/src/query/index.mts +1 -0
  112. package/src/query/key-creator.mts +24 -13
  113. package/src/query/make-infinite-options.mts +53 -10
  114. package/src/query/make-options.mts +184 -43
  115. package/src/query/prefetch.mts +326 -0
  116. package/src/query/types.mts +56 -17
  117. package/src/client/__type-tests__/client-instance.spec-d.mts +0 -852
package/lib/index.mjs CHANGED
@@ -15,27 +15,28 @@ import { infiniteQueryOptions, queryOptions, useInfiniteQuery, useIsMutating, us
15
15
  */
16
16
  function createQueryKey(config, options, _isInfinite) {
17
17
  const url = config.url;
18
- const urlParts = url.split("/").filter(Boolean);
19
18
  return {
20
- template: urlParts,
19
+ template: url.split("/").filter(Boolean),
21
20
  dataTag: (params) => {
22
21
  const queryParams = params && "querySchema" in config && "params" in params ? config.querySchema?.parse(params.params) : [];
22
+ const boundUrlParts = bindUrlParams(url, params && "urlParams" in params ? params : {}, config.urlParamsSchema).split("/").filter(Boolean);
23
23
  return [
24
24
  ...options.keyPrefix ?? [],
25
- ...urlParts.map((part) => part.startsWith("$") ? params.urlParams[part.slice(1)].toString() : part),
25
+ ...boundUrlParts,
26
26
  ...options.keySuffix ?? [],
27
27
  queryParams ?? []
28
28
  ];
29
29
  },
30
30
  filterKey: (params) => {
31
+ const boundUrlParts = bindUrlParams(url, params && "urlParams" in params ? params : {}, config.urlParamsSchema).split("/").filter(Boolean);
31
32
  return [
32
33
  ...options.keyPrefix ?? [],
33
- ...urlParts.map((part) => part.startsWith("$") ? params.urlParams[part.slice(1)].toString() : part),
34
+ ...boundUrlParts,
34
35
  ...options.keySuffix ?? []
35
36
  ];
36
37
  },
37
38
  bindToUrl: (params) => {
38
- return bindUrlParams(url, params ?? {});
39
+ return bindUrlParams(url, params && "urlParams" in params ? params : {}, config.urlParamsSchema);
39
40
  }
40
41
  };
41
42
  }
@@ -50,12 +51,29 @@ const queryKeyCreator = createQueryKey;
50
51
  * Returns a function that generates TanStack Query options when called with params.
51
52
  * The returned function also has helper methods attached (use, useSuspense, invalidate, etc.)
52
53
  *
53
- * @param endpoint - The navios endpoint to create query options for
54
+ * Uses const generics pattern to automatically infer types from the endpoint configuration.
55
+ *
56
+ * @param endpoint - The navios endpoint handler (from builder's declareEndpoint)
54
57
  * @param options - Query configuration including processResponse
55
58
  * @param baseQuery - Optional base query options to merge
56
59
  * @returns A function that generates query options with attached helpers
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * const getUser = api.declareEndpoint({
64
+ * method: 'GET',
65
+ * url: '/users/$userId',
66
+ * responseSchema: userSchema,
67
+ * })
68
+ *
69
+ * const queryOptions = makeQueryOptions(getUser, {
70
+ * processResponse: (data) => data,
71
+ * })
72
+ *
73
+ * const { data } = queryOptions.useSuspense({ urlParams: { userId: '123' } })
74
+ * ```
57
75
  */
58
- function makeQueryOptions(endpoint, options, baseQuery = {}) {
76
+ function makeQueryOptions(endpoint, options, baseQuery) {
59
77
  const config = endpoint.config;
60
78
  const queryKey = createQueryKey(config, options, false);
61
79
  const processResponse = options.processResponse;
@@ -78,18 +96,62 @@ function makeQueryOptions(endpoint, options, baseQuery = {}) {
78
96
  ...baseQuery
79
97
  });
80
98
  };
99
+ /** The query key creator for this endpoint */
81
100
  result.queryKey = queryKey;
101
+ /**
102
+ * React hook that executes the query.
103
+ * Uses `useQuery` from TanStack Query internally.
104
+ *
105
+ * @param params - URL parameters, query parameters, and request body
106
+ * @returns Query result with data, isLoading, error, etc.
107
+ */
82
108
  result.use = (params) => {
83
109
  return useQuery(result(params));
84
110
  };
111
+ /**
112
+ * React hook that executes the query with Suspense support.
113
+ * Uses `useSuspenseQuery` from TanStack Query internally.
114
+ * The component will suspend while loading and throw on error.
115
+ *
116
+ * @param params - URL parameters, query parameters, and request body
117
+ * @returns Query result with data guaranteed to be defined
118
+ */
85
119
  result.useSuspense = (params) => {
86
120
  return useSuspenseQuery(result(params));
87
121
  };
122
+ /**
123
+ * Creates a function that invalidates a specific query in the cache.
124
+ * Call the returned function to trigger the invalidation.
125
+ *
126
+ * @param queryClient - The TanStack Query client instance
127
+ * @param params - The exact parameters used for this query
128
+ * @returns A function that when called invalidates the query
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * const invalidate = getUser.invalidate(queryClient, { urlParams: { userId: '123' } })
133
+ * await invalidate() // Invalidates this specific query
134
+ * ```
135
+ */
88
136
  result.invalidate = (queryClient, params) => {
89
- return queryClient.invalidateQueries({ queryKey: result.queryKey.dataTag(params) });
137
+ return () => queryClient.invalidateQueries({ queryKey: result.queryKey.dataTag(params) });
90
138
  };
139
+ /**
140
+ * Creates a function that invalidates all queries matching the URL pattern.
141
+ * Useful for invalidating all queries for a resource regardless of query params.
142
+ *
143
+ * @param queryClient - The TanStack Query client instance
144
+ * @param params - URL parameters only (query params are ignored for matching)
145
+ * @returns A function that when called invalidates all matching queries
146
+ *
147
+ * @example
148
+ * ```ts
149
+ * const invalidateAll = getUserPosts.invalidateAll(queryClient, { urlParams: { userId: '123' } })
150
+ * await invalidateAll() // Invalidates all getUserPosts queries for user 123
151
+ * ```
152
+ */
91
153
  result.invalidateAll = (queryClient, params) => {
92
- return queryClient.invalidateQueries({
154
+ return () => queryClient.invalidateQueries({
93
155
  queryKey: result.queryKey.filterKey(params),
94
156
  exact: false
95
157
  });
@@ -136,22 +198,54 @@ function makeInfiniteQueryOptions(endpoint, options, baseQuery = {}) {
136
198
  },
137
199
  getNextPageParam: options.getNextPageParam,
138
200
  getPreviousPageParam: options.getPreviousPageParam,
139
- initialPageParam: options.initialPageParam ?? config.querySchema.parse("params" in params ? params.params : {}),
201
+ initialPageParam: options.initialPageParam ?? config.querySchema?.parse("params" in params ? params.params : {}) ?? ("params" in params ? params.params : {}),
140
202
  ...baseQuery
141
203
  });
142
204
  };
205
+ /** The query key creator for this infinite query endpoint */
143
206
  res.queryKey = queryKey;
207
+ /**
208
+ * React hook that executes the infinite query.
209
+ * Uses `useInfiniteQuery` from TanStack Query internally.
210
+ *
211
+ * @param params - URL parameters and initial query parameters
212
+ * @returns Infinite query result with pages, fetchNextPage, etc.
213
+ */
144
214
  res.use = (params) => {
145
215
  return useInfiniteQuery(res(params));
146
216
  };
217
+ /**
218
+ * React hook that executes the infinite query with Suspense support.
219
+ * Uses `useSuspenseInfiniteQuery` from TanStack Query internally.
220
+ * The component will suspend while loading and throw on error.
221
+ *
222
+ * @param params - URL parameters and initial query parameters
223
+ * @returns Infinite query result with pages guaranteed to be defined
224
+ */
147
225
  res.useSuspense = (params) => {
148
226
  return useSuspenseInfiniteQuery(res(params));
149
227
  };
228
+ /**
229
+ * Creates a function that invalidates this specific infinite query in the cache.
230
+ * Call the returned function to trigger the invalidation.
231
+ *
232
+ * @param queryClient - The TanStack Query client instance
233
+ * @param params - The exact parameters used for this query
234
+ * @returns A function that when called invalidates the query
235
+ */
150
236
  res.invalidate = (queryClient, params) => {
151
- return queryClient.invalidateQueries({ queryKey: res.queryKey.dataTag(params) });
237
+ return () => queryClient.invalidateQueries({ queryKey: res.queryKey.dataTag(params) });
152
238
  };
239
+ /**
240
+ * Creates a function that invalidates all infinite queries matching the URL pattern.
241
+ * Useful for invalidating all queries for a resource regardless of query params.
242
+ *
243
+ * @param queryClient - The TanStack Query client instance
244
+ * @param params - URL parameters only (query params are ignored for matching)
245
+ * @returns A function that when called invalidates all matching queries
246
+ */
153
247
  res.invalidateAll = (queryClient, params) => {
154
- return queryClient.invalidateQueries({
248
+ return () => queryClient.invalidateQueries({
155
249
  queryKey: res.queryKey.filterKey(params),
156
250
  exact: false
157
251
  });
@@ -159,6 +253,179 @@ function makeInfiniteQueryOptions(endpoint, options, baseQuery = {}) {
159
253
  return res;
160
254
  }
161
255
 
256
+ //#endregion
257
+ //#region src/query/prefetch.mts
258
+ /**
259
+ * Creates a type-safe prefetch helper for SSR/RSC.
260
+ *
261
+ * This utility wraps a query options creator to provide convenient
262
+ * methods for server-side data fetching and hydration.
263
+ *
264
+ * @param queryOptionsCreator - A function that creates query options (from client.query())
265
+ * @returns A prefetch helper object with prefetch, ensureData, and getQueryOptions methods
266
+ *
267
+ * @example
268
+ * ```tsx
269
+ * // 1. Create your query
270
+ * const getUserQuery = client.query({
271
+ * method: 'GET',
272
+ * url: '/users/$userId',
273
+ * responseSchema: userSchema,
274
+ * })
275
+ *
276
+ * // 2. Create prefetch helper
277
+ * const userPrefetch = createPrefetchHelper(getUserQuery)
278
+ *
279
+ * // 3. Use in server component
280
+ * async function UserPage({ userId }: { userId: string }) {
281
+ * const queryClient = new QueryClient()
282
+ *
283
+ * await userPrefetch.prefetch(queryClient, {
284
+ * urlParams: { userId },
285
+ * })
286
+ *
287
+ * return (
288
+ * <HydrationBoundary state={dehydrate(queryClient)}>
289
+ * <UserProfile userId={userId} />
290
+ * </HydrationBoundary>
291
+ * )
292
+ * }
293
+ * ```
294
+ *
295
+ * @example
296
+ * ```tsx
297
+ * // With Next.js App Router
298
+ * import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query'
299
+ * import { createPrefetchHelper } from '@navios/react-query'
300
+ *
301
+ * // Define queries
302
+ * const getPostsQuery = client.query({
303
+ * method: 'GET',
304
+ * url: '/posts',
305
+ * querySchema: z.object({ page: z.number() }),
306
+ * responseSchema: postsSchema,
307
+ * })
308
+ *
309
+ * const postsPrefetch = createPrefetchHelper(getPostsQuery)
310
+ *
311
+ * // Server Component
312
+ * export default async function PostsPage() {
313
+ * const queryClient = new QueryClient()
314
+ *
315
+ * await postsPrefetch.prefetch(queryClient, {
316
+ * params: { page: 1 },
317
+ * })
318
+ *
319
+ * return (
320
+ * <HydrationBoundary state={dehydrate(queryClient)}>
321
+ * <PostsList />
322
+ * </HydrationBoundary>
323
+ * )
324
+ * }
325
+ * ```
326
+ */
327
+ function createPrefetchHelper(queryOptionsCreator) {
328
+ return {
329
+ prefetch: async (queryClient, params) => {
330
+ const options = queryOptionsCreator(params);
331
+ await queryClient.prefetchQuery(options);
332
+ },
333
+ ensureData: async (queryClient, params) => {
334
+ const options = queryOptionsCreator(params);
335
+ return queryClient.ensureQueryData(options);
336
+ },
337
+ getQueryOptions: (params) => {
338
+ return queryOptionsCreator(params);
339
+ },
340
+ prefetchMany: async (queryClient, paramsList) => {
341
+ await Promise.all(paramsList.map((params) => {
342
+ const options = queryOptionsCreator(params);
343
+ return queryClient.prefetchQuery(options);
344
+ }));
345
+ }
346
+ };
347
+ }
348
+ /**
349
+ * Creates multiple prefetch helpers from a record of query options creators.
350
+ *
351
+ * Useful when you have multiple queries that need to be prefetched together.
352
+ *
353
+ * @param queries - Record of query options creator functions
354
+ * @returns Record of prefetch helpers with the same keys
355
+ *
356
+ * @example
357
+ * ```tsx
358
+ * // Define all your queries
359
+ * const queries = {
360
+ * user: client.query({
361
+ * method: 'GET',
362
+ * url: '/users/$userId',
363
+ * responseSchema: userSchema,
364
+ * }),
365
+ * posts: client.query({
366
+ * method: 'GET',
367
+ * url: '/users/$userId/posts',
368
+ * responseSchema: postsSchema,
369
+ * }),
370
+ * }
371
+ *
372
+ * // Create all prefetch helpers at once
373
+ * const prefetchers = createPrefetchHelpers(queries)
374
+ *
375
+ * // Use in server component
376
+ * async function UserPage({ userId }: { userId: string }) {
377
+ * const queryClient = new QueryClient()
378
+ *
379
+ * await Promise.all([
380
+ * prefetchers.user.prefetch(queryClient, { urlParams: { userId } }),
381
+ * prefetchers.posts.prefetch(queryClient, { urlParams: { userId } }),
382
+ * ])
383
+ *
384
+ * return (
385
+ * <HydrationBoundary state={dehydrate(queryClient)}>
386
+ * <UserProfileWithPosts userId={userId} />
387
+ * </HydrationBoundary>
388
+ * )
389
+ * }
390
+ * ```
391
+ */
392
+ function createPrefetchHelpers(queries) {
393
+ const result = {};
394
+ for (const key of Object.keys(queries)) result[key] = createPrefetchHelper(queries[key]);
395
+ return result;
396
+ }
397
+ /**
398
+ * Prefetch multiple queries from different query creators in parallel.
399
+ *
400
+ * @param queryClient - The QueryClient instance
401
+ * @param prefetches - Array of { helper, params } objects
402
+ * @returns Promise that resolves when all prefetches complete
403
+ *
404
+ * @example
405
+ * ```tsx
406
+ * const userPrefetch = createPrefetchHelper(getUserQuery)
407
+ * const postsPrefetch = createPrefetchHelper(getPostsQuery)
408
+ *
409
+ * async function DashboardPage({ userId }: { userId: string }) {
410
+ * const queryClient = new QueryClient()
411
+ *
412
+ * await prefetchAll(queryClient, [
413
+ * { helper: userPrefetch, params: { urlParams: { userId } } },
414
+ * { helper: postsPrefetch, params: { urlParams: { userId }, params: { limit: 10 } } },
415
+ * ])
416
+ *
417
+ * return (
418
+ * <HydrationBoundary state={dehydrate(queryClient)}>
419
+ * <Dashboard userId={userId} />
420
+ * </HydrationBoundary>
421
+ * )
422
+ * }
423
+ * ```
424
+ */
425
+ async function prefetchAll(queryClient, prefetches) {
426
+ await Promise.all(prefetches.map(({ helper, params }) => helper.prefetch(queryClient, params)));
427
+ }
428
+
162
429
  //#endregion
163
430
  //#region src/mutation/key-creator.mts
164
431
  /**
@@ -199,16 +466,6 @@ const mutationKeyCreator = createMutationKey;
199
466
 
200
467
  //#endregion
201
468
  //#region src/mutation/make-hook.mts
202
- /**
203
- * Creates a mutation hook for a given endpoint.
204
- *
205
- * Returns a function that when called returns a TanStack Query mutation result.
206
- * The returned function also has helper methods attached (mutationKey, useIsMutating).
207
- *
208
- * @param endpoint - The navios endpoint to create a mutation hook for
209
- * @param options - Mutation configuration including processResponse and callbacks
210
- * @returns A hook function that returns mutation result with attached helpers
211
- */
212
469
  function makeMutation(endpoint, options) {
213
470
  const config = endpoint.config;
214
471
  const mutationKey = createMutationKey(config, {
@@ -257,23 +514,178 @@ function makeMutation(endpoint, options) {
257
514
  };
258
515
  result.useIsMutating = (keyParams) => {
259
516
  if (!options.useKey) throw new Error("useIsMutating can only be used when useKey is set to true");
260
- return useIsMutating({ mutationKey: mutationKey({ urlParams: keyParams }) }) > 0;
517
+ return useIsMutating({ mutationKey: mutationKey(keyParams) }) > 0;
261
518
  };
262
519
  result.mutationKey = mutationKey;
263
520
  return result;
264
521
  }
265
522
 
523
+ //#endregion
524
+ //#region src/mutation/optimistic.mts
525
+ /**
526
+ * Creates type-safe optimistic update callbacks for mutations.
527
+ *
528
+ * This helper generates the onMutate, onError, and onSettled callbacks
529
+ * that implement the standard optimistic update pattern:
530
+ *
531
+ * 1. onMutate: Cancel refetches, snapshot cache, apply optimistic update
532
+ * 2. onError: Rollback cache to previous value on failure
533
+ * 3. onSettled: Invalidate query to refetch fresh data
534
+ *
535
+ * @experimental This API is experimental and may change in future versions.
536
+ * Use with caution in production code.
537
+ *
538
+ * @param config - Configuration for the optimistic update
539
+ * @returns Object containing onMutate, onError, and onSettled callbacks
540
+ *
541
+ * @example
542
+ * ```ts
543
+ * // Create a mutation with optimistic updates
544
+ * const updateUser = client.mutation({
545
+ * method: 'PATCH',
546
+ * url: '/users/$userId',
547
+ * requestSchema: updateUserSchema,
548
+ * responseSchema: userSchema,
549
+ * processResponse: (data) => data,
550
+ * ...createOptimisticUpdate({
551
+ * queryKey: ['users', userId],
552
+ * updateFn: (oldData, variables) => ({
553
+ * ...oldData,
554
+ * ...variables.data,
555
+ * }),
556
+ * }),
557
+ * })
558
+ * ```
559
+ *
560
+ * @example
561
+ * ```ts
562
+ * // Optimistic update for adding an item to a list
563
+ * const addTodo = client.mutation({
564
+ * method: 'POST',
565
+ * url: '/todos',
566
+ * requestSchema: createTodoSchema,
567
+ * responseSchema: todoSchema,
568
+ * processResponse: (data) => data,
569
+ * ...createOptimisticUpdate({
570
+ * queryKey: ['todos'],
571
+ * updateFn: (oldData, variables) => [
572
+ * ...(oldData ?? []),
573
+ * { id: 'temp-id', ...variables.data, createdAt: new Date() },
574
+ * ],
575
+ * }),
576
+ * })
577
+ * ```
578
+ *
579
+ * @example
580
+ * ```ts
581
+ * // Optimistic delete
582
+ * const deleteTodo = client.mutation({
583
+ * method: 'DELETE',
584
+ * url: '/todos/$todoId',
585
+ * responseSchema: z.object({ success: z.boolean() }),
586
+ * processResponse: (data) => data,
587
+ * ...createOptimisticUpdate({
588
+ * queryKey: ['todos'],
589
+ * updateFn: (oldData, variables) =>
590
+ * (oldData ?? []).filter((t) => t.id !== variables.urlParams.todoId),
591
+ * }),
592
+ * })
593
+ * ```
594
+ */
595
+ function createOptimisticUpdate(config) {
596
+ const { queryKey, updateFn, rollbackOnError = true, invalidateOnSettled = true } = config;
597
+ return {
598
+ onMutate: async (variables, context) => {
599
+ await context.queryClient.cancelQueries({ queryKey });
600
+ const previousData = context.queryClient.getQueryData(queryKey);
601
+ context.queryClient.setQueryData(queryKey, (old) => updateFn(old, variables));
602
+ return { previousData };
603
+ },
604
+ onError: (_err, _variables, context) => {
605
+ if (rollbackOnError && context?.previousData !== void 0) context.queryClient.setQueryData(queryKey, context.previousData);
606
+ },
607
+ onSettled: (_data, _error, _variables, context) => {
608
+ if (invalidateOnSettled) context.queryClient.invalidateQueries({ queryKey });
609
+ }
610
+ };
611
+ }
612
+ /**
613
+ * Creates optimistic update callbacks that work with multiple query keys.
614
+ *
615
+ * Useful when a mutation affects multiple cached queries.
616
+ *
617
+ * @experimental This API is experimental and may change in future versions.
618
+ * Use with caution in production code.
619
+ *
620
+ * @param configs - Array of optimistic update configurations
621
+ * @returns Combined callbacks that handle all specified queries
622
+ *
623
+ * @example
624
+ * ```ts
625
+ * // Updating a user affects both user detail and user list queries
626
+ * const updateUser = client.mutation({
627
+ * method: 'PATCH',
628
+ * url: '/users/$userId',
629
+ * requestSchema: updateUserSchema,
630
+ * responseSchema: userSchema,
631
+ * processResponse: (data) => data,
632
+ * ...createMultiOptimisticUpdate([
633
+ * {
634
+ * queryKey: ['users', userId],
635
+ * updateFn: (oldData, variables) => ({ ...oldData, ...variables.data }),
636
+ * },
637
+ * {
638
+ * queryKey: ['users'],
639
+ * updateFn: (oldList, variables) =>
640
+ * (oldList ?? []).map((u) =>
641
+ * u.id === userId ? { ...u, ...variables.data } : u
642
+ * ),
643
+ * },
644
+ * ]),
645
+ * })
646
+ * ```
647
+ */
648
+ function createMultiOptimisticUpdate(configs) {
649
+ return {
650
+ onMutate: async (variables, context) => {
651
+ const previousData = /* @__PURE__ */ new Map();
652
+ for (const config of configs) {
653
+ await context.queryClient.cancelQueries({ queryKey: config.queryKey });
654
+ const key = JSON.stringify(config.queryKey);
655
+ previousData.set(key, context.queryClient.getQueryData(config.queryKey));
656
+ context.queryClient.setQueryData(config.queryKey, (old) => config.updateFn(old, variables));
657
+ }
658
+ return { previousData };
659
+ },
660
+ onError: (_err, _variables, context) => {
661
+ if (context?.previousData) {
662
+ for (const config of configs) if (config.rollbackOnError !== false) {
663
+ const key = JSON.stringify(config.queryKey);
664
+ const previous = context.previousData.get(key);
665
+ if (previous !== void 0) context.queryClient.setQueryData(config.queryKey, previous);
666
+ }
667
+ }
668
+ },
669
+ onSettled: (_data, _error, _variables, context) => {
670
+ for (const config of configs) if (config.invalidateOnSettled !== false) context.queryClient.invalidateQueries({ queryKey: config.queryKey });
671
+ }
672
+ };
673
+ }
674
+
266
675
  //#endregion
267
676
  //#region src/client/declare-client.mts
268
677
  /**
269
678
  * Creates a client instance for making type-safe queries and mutations.
270
679
  *
680
+ * @template UseDiscriminator - When `true`, errors are returned as union types.
681
+ * When `false` (default), errors are thrown and not included in TData.
682
+ *
271
683
  * @param options - Client configuration including the API builder and defaults
272
684
  * @returns A client instance with query, infiniteQuery, and mutation methods
273
685
  *
274
686
  * @example
275
687
  * ```typescript
276
- * const api = createBuilder({ baseUrl: '/api' });
688
+ * const api = builder({});
277
689
  * const client = declareClient({ api });
278
690
  *
279
691
  * const getUser = client.query({
@@ -293,7 +705,8 @@ function declareClient({ api, defaults = {} }) {
293
705
  url: config.url,
294
706
  querySchema: config.querySchema,
295
707
  requestSchema: config.requestSchema,
296
- responseSchema: config.responseSchema
708
+ responseSchema: config.responseSchema,
709
+ errorSchema: config.errorSchema
297
710
  });
298
711
  const queryOptions$1 = makeQueryOptions(endpoint, {
299
712
  ...defaults,
@@ -314,7 +727,8 @@ function declareClient({ api, defaults = {} }) {
314
727
  url: config.url,
315
728
  querySchema: config.querySchema,
316
729
  requestSchema: config.requestSchema,
317
- responseSchema: config.responseSchema
730
+ responseSchema: config.responseSchema,
731
+ errorSchema: config.errorSchema
318
732
  });
319
733
  const infiniteQueryOptions$1 = makeInfiniteQueryOptions(endpoint, {
320
734
  ...defaults,
@@ -341,13 +755,16 @@ function declareClient({ api, defaults = {} }) {
341
755
  url: config.url,
342
756
  querySchema: config.querySchema,
343
757
  requestSchema: config.requestSchema,
344
- responseSchema: config.responseSchema
758
+ responseSchema: config.responseSchema,
759
+ errorSchema: config.errorSchema
345
760
  });
346
761
  const useMutation$1 = makeMutation(endpoint, {
347
762
  processResponse: config.processResponse ?? ((data) => data),
348
763
  useContext: config.useContext,
764
+ onMutate: config.onMutate,
349
765
  onSuccess: config.onSuccess,
350
766
  onError: config.onError,
767
+ onSettled: config.onSettled,
351
768
  useKey: config.useKey,
352
769
  meta: config.meta,
353
770
  ...defaults
@@ -374,7 +791,8 @@ function declareClient({ api, defaults = {} }) {
374
791
  url: config.url,
375
792
  querySchema: config.querySchema,
376
793
  requestSchema: config.requestSchema,
377
- responseSchema: config.responseSchema
794
+ responseSchema: config.responseSchema,
795
+ errorSchema: config.errorSchema
378
796
  });
379
797
  const useMutation$1 = makeMutation(endpoint, {
380
798
  processResponse: config.processResponse ?? ((data) => data),
@@ -401,5 +819,5 @@ function declareClient({ api, defaults = {} }) {
401
819
  }
402
820
 
403
821
  //#endregion
404
- export { createMutationKey, createQueryKey, declareClient, makeInfiniteQueryOptions, makeMutation, makeQueryOptions, mutationKeyCreator, queryKeyCreator };
822
+ export { createMultiOptimisticUpdate, createMutationKey, createOptimisticUpdate, createPrefetchHelper, createPrefetchHelpers, createQueryKey, declareClient, makeInfiniteQueryOptions, makeMutation, makeQueryOptions, mutationKeyCreator, prefetchAll, queryKeyCreator };
405
823
  //# sourceMappingURL=index.mjs.map