@navios/react-query 0.7.0 → 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.
Files changed (124) hide show
  1. package/CHANGELOG.md +166 -0
  2. package/README.md +152 -4
  3. package/dist/src/__tests__/errorSchema.spec.d.mts +2 -0
  4. package/dist/src/__tests__/errorSchema.spec.d.mts.map +1 -0
  5. package/dist/src/client/__type-tests__/from-endpoint.spec-d.d.mts +2 -0
  6. package/dist/src/client/__type-tests__/from-endpoint.spec-d.d.mts.map +1 -0
  7. package/dist/src/client/__type-tests__/infinite-query.spec-d.d.mts +2 -0
  8. package/dist/src/client/__type-tests__/infinite-query.spec-d.d.mts.map +1 -0
  9. package/dist/src/client/__type-tests__/multipart-mutation.spec-d.d.mts +2 -0
  10. package/dist/src/client/__type-tests__/multipart-mutation.spec-d.d.mts.map +1 -0
  11. package/dist/src/client/__type-tests__/mutation.spec-d.d.mts +2 -0
  12. package/dist/src/client/__type-tests__/mutation.spec-d.d.mts.map +1 -0
  13. package/dist/src/client/__type-tests__/query.spec-d.d.mts +2 -0
  14. package/dist/src/client/__type-tests__/query.spec-d.d.mts.map +1 -0
  15. package/dist/src/client/declare-client.d.mts +15 -8
  16. package/dist/src/client/declare-client.d.mts.map +1 -1
  17. package/dist/src/client/types/from-endpoint.d.mts +130 -0
  18. package/dist/src/client/types/from-endpoint.d.mts.map +1 -0
  19. package/dist/src/client/types/helpers.d.mts +74 -0
  20. package/dist/src/client/types/helpers.d.mts.map +1 -0
  21. package/dist/src/client/types/index.d.mts +21 -0
  22. package/dist/src/client/types/index.d.mts.map +1 -0
  23. package/dist/src/client/types/infinite-query.d.mts +61 -0
  24. package/dist/src/client/types/infinite-query.d.mts.map +1 -0
  25. package/dist/src/client/types/multipart-mutation.d.mts +98 -0
  26. package/dist/src/client/types/multipart-mutation.d.mts.map +1 -0
  27. package/dist/src/client/types/mutation.d.mts +75 -0
  28. package/dist/src/client/types/mutation.d.mts.map +1 -0
  29. package/dist/src/client/types/query.d.mts +65 -0
  30. package/dist/src/client/types/query.d.mts.map +1 -0
  31. package/dist/src/client/types.d.mts +1 -608
  32. package/dist/src/client/types.d.mts.map +1 -1
  33. package/dist/src/common/types.d.mts +30 -3
  34. package/dist/src/common/types.d.mts.map +1 -1
  35. package/dist/src/mutation/index.d.mts +1 -0
  36. package/dist/src/mutation/index.d.mts.map +1 -1
  37. package/dist/src/mutation/make-hook.d.mts +42 -16
  38. package/dist/src/mutation/make-hook.d.mts.map +1 -1
  39. package/dist/src/mutation/optimistic.d.mts +166 -0
  40. package/dist/src/mutation/optimistic.d.mts.map +1 -0
  41. package/dist/src/mutation/types.d.mts +51 -19
  42. package/dist/src/mutation/types.d.mts.map +1 -1
  43. package/dist/src/query/index.d.mts +1 -0
  44. package/dist/src/query/index.d.mts.map +1 -1
  45. package/dist/src/query/key-creator.d.mts.map +1 -1
  46. package/dist/src/query/make-infinite-options.d.mts +3 -2
  47. package/dist/src/query/make-infinite-options.d.mts.map +1 -1
  48. package/dist/src/query/make-options.d.mts +42 -12
  49. package/dist/src/query/make-options.d.mts.map +1 -1
  50. package/dist/src/query/prefetch.d.mts +245 -0
  51. package/dist/src/query/prefetch.d.mts.map +1 -0
  52. package/dist/src/query/types.d.mts +35 -17
  53. package/dist/src/query/types.d.mts.map +1 -1
  54. package/dist/tsconfig.tsbuildinfo +1 -1
  55. package/lib/index.cjs +454 -37
  56. package/lib/index.cjs.map +1 -1
  57. package/lib/index.d.cts +1021 -598
  58. package/lib/index.d.cts.map +1 -1
  59. package/lib/index.d.mts +1019 -596
  60. package/lib/index.d.mts.map +1 -1
  61. package/lib/index.mjs +441 -29
  62. package/lib/index.mjs.map +1 -1
  63. package/package.json +8 -8
  64. package/src/__tests__/declare-client.spec.mts +1 -2
  65. package/src/__tests__/errorSchema.spec.mts +391 -0
  66. package/src/__tests__/make-mutation.spec.mts +6 -5
  67. package/src/__tests__/makeDataTag.spec.mts +2 -1
  68. package/src/__tests__/makeQueryOptions.spec.mts +2 -1
  69. package/src/client/__type-tests__/from-endpoint.spec-d.mts +550 -0
  70. package/src/client/__type-tests__/infinite-query.spec-d.mts +648 -0
  71. package/src/client/__type-tests__/multipart-mutation.spec-d.mts +725 -0
  72. package/src/client/__type-tests__/mutation.spec-d.mts +757 -0
  73. package/src/client/__type-tests__/query.spec-d.mts +701 -0
  74. package/src/client/declare-client.mts +59 -34
  75. package/src/client/types/from-endpoint.mts +345 -0
  76. package/src/client/types/helpers.mts +140 -0
  77. package/src/client/types/index.mts +26 -0
  78. package/src/client/types/infinite-query.mts +133 -0
  79. package/src/client/types/multipart-mutation.mts +264 -0
  80. package/src/client/types/mutation.mts +176 -0
  81. package/src/client/types/query.mts +132 -0
  82. package/src/client/types.mts +1 -1935
  83. package/src/common/types.mts +48 -3
  84. package/src/mutation/index.mts +1 -0
  85. package/src/mutation/make-hook.mts +171 -63
  86. package/src/mutation/optimistic.mts +294 -0
  87. package/src/mutation/types.mts +102 -29
  88. package/src/query/index.mts +1 -0
  89. package/src/query/key-creator.mts +24 -13
  90. package/src/query/make-infinite-options.mts +53 -10
  91. package/src/query/make-options.mts +184 -43
  92. package/src/query/prefetch.mts +326 -0
  93. package/src/query/types.mts +76 -16
  94. package/dist/src/declare-client.d.mts +0 -31
  95. package/dist/src/declare-client.d.mts.map +0 -1
  96. package/dist/src/make-infinite-query-options.d.mts +0 -13
  97. package/dist/src/make-infinite-query-options.d.mts.map +0 -1
  98. package/dist/src/make-mutation.d.mts +0 -15
  99. package/dist/src/make-mutation.d.mts.map +0 -1
  100. package/dist/src/make-query-options.d.mts +0 -15
  101. package/dist/src/make-query-options.d.mts.map +0 -1
  102. package/dist/src/types/client-endpoint-helper.d.mts +0 -8
  103. package/dist/src/types/client-endpoint-helper.d.mts.map +0 -1
  104. package/dist/src/types/client-instance.d.mts +0 -211
  105. package/dist/src/types/client-instance.d.mts.map +0 -1
  106. package/dist/src/types/index.d.mts +0 -8
  107. package/dist/src/types/index.d.mts.map +0 -1
  108. package/dist/src/types/mutation-args.d.mts +0 -10
  109. package/dist/src/types/mutation-args.d.mts.map +0 -1
  110. package/dist/src/types/mutation-helpers.d.mts +0 -10
  111. package/dist/src/types/mutation-helpers.d.mts.map +0 -1
  112. package/dist/src/types/query-args.d.mts +0 -8
  113. package/dist/src/types/query-args.d.mts.map +0 -1
  114. package/dist/src/types/query-helpers.d.mts +0 -14
  115. package/dist/src/types/query-helpers.d.mts.map +0 -1
  116. package/dist/src/types/query-url-params-args.d.mts +0 -5
  117. package/dist/src/types/query-url-params-args.d.mts.map +0 -1
  118. package/dist/src/types.d.mts +0 -49
  119. package/dist/src/types.d.mts.map +0 -1
  120. package/dist/src/utils/mutation-key.creator.d.mts +0 -39
  121. package/dist/src/utils/mutation-key.creator.d.mts.map +0 -1
  122. package/dist/src/utils/query-key-creator.d.mts +0 -24
  123. package/dist/src/utils/query-key-creator.d.mts.map +0 -1
  124. package/src/client/__type-tests__/client-instance.spec-d.mts +0 -852
package/lib/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
- let __navios_builder = require("@navios/builder");
2
- let __tanstack_react_query = require("@tanstack/react-query");
1
+ let _navios_builder = require("@navios/builder");
2
+ let _tanstack_react_query = require("@tanstack/react-query");
3
3
 
4
4
  //#region src/query/key-creator.mts
5
5
  /**
@@ -15,27 +15,28 @@ let __tanstack_react_query = require("@tanstack/react-query");
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 = (0, _navios_builder.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 = (0, _navios_builder.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 (0, __navios_builder.bindUrlParams)(url, params ?? {});
39
+ return (0, _navios_builder.bindUrlParams)(url, params && "urlParams" in params ? params : {}, config.urlParamsSchema);
39
40
  }
40
41
  };
41
42
  }
@@ -50,17 +51,34 @@ 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;
62
80
  const result = (params) => {
63
- return (0, __tanstack_react_query.queryOptions)({
81
+ return (0, _tanstack_react_query.queryOptions)({
64
82
  queryKey: queryKey.dataTag(params),
65
83
  queryFn: async ({ signal }) => {
66
84
  let result$1;
@@ -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
- return (0, __tanstack_react_query.useQuery)(result(params));
109
+ return (0, _tanstack_react_query.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
- return (0, __tanstack_react_query.useSuspenseQuery)(result(params));
120
+ return (0, _tanstack_react_query.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
  });
@@ -115,7 +177,7 @@ function makeInfiniteQueryOptions(endpoint, options, baseQuery = {}) {
115
177
  const queryKey = createQueryKey(config, options, true);
116
178
  const processResponse = options.processResponse;
117
179
  const res = (params) => {
118
- return (0, __tanstack_react_query.infiniteQueryOptions)({
180
+ return (0, _tanstack_react_query.infiniteQueryOptions)({
119
181
  queryKey: queryKey.dataTag(params),
120
182
  queryFn: async ({ signal, pageParam }) => {
121
183
  let result;
@@ -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
- return (0, __tanstack_react_query.useInfiniteQuery)(res(params));
215
+ return (0, _tanstack_react_query.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
- return (0, __tanstack_react_query.useSuspenseInfiniteQuery)(res(params));
226
+ return (0, _tanstack_react_query.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, {
@@ -218,7 +475,7 @@ function makeMutation(endpoint, options) {
218
475
  const result = (keyParams) => {
219
476
  const { useKey, useContext, onMutate, onError, onSuccess, onSettled, keyPrefix: _keyPrefix, keySuffix: _keySuffix, processResponse, ...rest } = options;
220
477
  const ownContext = useContext?.() ?? {};
221
- return (0, __tanstack_react_query.useMutation)({
478
+ return (0, _tanstack_react_query.useMutation)({
222
479
  ...rest,
223
480
  mutationKey: useKey ? mutationKey(keyParams) : void 0,
224
481
  scope: useKey ? { id: JSON.stringify(mutationKey(keyParams)) } : void 0,
@@ -257,23 +514,172 @@ 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 (0, __tanstack_react_query.useIsMutating)({ mutationKey: mutationKey({ urlParams: keyParams }) }) > 0;
517
+ return (0, _tanstack_react_query.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
+ * @param config - Configuration for the optimistic update
536
+ * @returns Object containing onMutate, onError, and onSettled callbacks
537
+ *
538
+ * @example
539
+ * ```ts
540
+ * // Create a mutation with optimistic updates
541
+ * const updateUser = client.mutation({
542
+ * method: 'PATCH',
543
+ * url: '/users/$userId',
544
+ * requestSchema: updateUserSchema,
545
+ * responseSchema: userSchema,
546
+ * processResponse: (data) => data,
547
+ * ...createOptimisticUpdate({
548
+ * queryKey: ['users', userId],
549
+ * updateFn: (oldData, variables) => ({
550
+ * ...oldData,
551
+ * ...variables.data,
552
+ * }),
553
+ * }),
554
+ * })
555
+ * ```
556
+ *
557
+ * @example
558
+ * ```ts
559
+ * // Optimistic update for adding an item to a list
560
+ * const addTodo = client.mutation({
561
+ * method: 'POST',
562
+ * url: '/todos',
563
+ * requestSchema: createTodoSchema,
564
+ * responseSchema: todoSchema,
565
+ * processResponse: (data) => data,
566
+ * ...createOptimisticUpdate({
567
+ * queryKey: ['todos'],
568
+ * updateFn: (oldData, variables) => [
569
+ * ...(oldData ?? []),
570
+ * { id: 'temp-id', ...variables.data, createdAt: new Date() },
571
+ * ],
572
+ * }),
573
+ * })
574
+ * ```
575
+ *
576
+ * @example
577
+ * ```ts
578
+ * // Optimistic delete
579
+ * const deleteTodo = client.mutation({
580
+ * method: 'DELETE',
581
+ * url: '/todos/$todoId',
582
+ * responseSchema: z.object({ success: z.boolean() }),
583
+ * processResponse: (data) => data,
584
+ * ...createOptimisticUpdate({
585
+ * queryKey: ['todos'],
586
+ * updateFn: (oldData, variables) =>
587
+ * (oldData ?? []).filter((t) => t.id !== variables.urlParams.todoId),
588
+ * }),
589
+ * })
590
+ * ```
591
+ */
592
+ function createOptimisticUpdate(config) {
593
+ const { queryKey, updateFn, rollbackOnError = true, invalidateOnSettled = true } = config;
594
+ return {
595
+ onMutate: async (variables, context) => {
596
+ await context.queryClient.cancelQueries({ queryKey });
597
+ const previousData = context.queryClient.getQueryData(queryKey);
598
+ context.queryClient.setQueryData(queryKey, (old) => updateFn(old, variables));
599
+ return { previousData };
600
+ },
601
+ onError: (_err, _variables, context) => {
602
+ if (rollbackOnError && context?.previousData !== void 0) context.queryClient.setQueryData(queryKey, context.previousData);
603
+ },
604
+ onSettled: (_data, _error, _variables, context) => {
605
+ if (invalidateOnSettled) context.queryClient.invalidateQueries({ queryKey });
606
+ }
607
+ };
608
+ }
609
+ /**
610
+ * Creates optimistic update callbacks that work with multiple query keys.
611
+ *
612
+ * Useful when a mutation affects multiple cached queries.
613
+ *
614
+ * @param configs - Array of optimistic update configurations
615
+ * @returns Combined callbacks that handle all specified queries
616
+ *
617
+ * @example
618
+ * ```ts
619
+ * // Updating a user affects both user detail and user list queries
620
+ * const updateUser = client.mutation({
621
+ * method: 'PATCH',
622
+ * url: '/users/$userId',
623
+ * requestSchema: updateUserSchema,
624
+ * responseSchema: userSchema,
625
+ * processResponse: (data) => data,
626
+ * ...createMultiOptimisticUpdate([
627
+ * {
628
+ * queryKey: ['users', userId],
629
+ * updateFn: (oldData, variables) => ({ ...oldData, ...variables.data }),
630
+ * },
631
+ * {
632
+ * queryKey: ['users'],
633
+ * updateFn: (oldList, variables) =>
634
+ * (oldList ?? []).map((u) =>
635
+ * u.id === userId ? { ...u, ...variables.data } : u
636
+ * ),
637
+ * },
638
+ * ]),
639
+ * })
640
+ * ```
641
+ */
642
+ function createMultiOptimisticUpdate(configs) {
643
+ return {
644
+ onMutate: async (variables, context) => {
645
+ const previousData = /* @__PURE__ */ new Map();
646
+ for (const config of configs) {
647
+ await context.queryClient.cancelQueries({ queryKey: config.queryKey });
648
+ const key = JSON.stringify(config.queryKey);
649
+ previousData.set(key, context.queryClient.getQueryData(config.queryKey));
650
+ context.queryClient.setQueryData(config.queryKey, (old) => config.updateFn(old, variables));
651
+ }
652
+ return { previousData };
653
+ },
654
+ onError: (_err, _variables, context) => {
655
+ if (context?.previousData) {
656
+ for (const config of configs) if (config.rollbackOnError !== false) {
657
+ const key = JSON.stringify(config.queryKey);
658
+ const previous = context.previousData.get(key);
659
+ if (previous !== void 0) context.queryClient.setQueryData(config.queryKey, previous);
660
+ }
661
+ }
662
+ },
663
+ onSettled: (_data, _error, _variables, context) => {
664
+ for (const config of configs) if (config.invalidateOnSettled !== false) context.queryClient.invalidateQueries({ queryKey: config.queryKey });
665
+ }
666
+ };
667
+ }
668
+
266
669
  //#endregion
267
670
  //#region src/client/declare-client.mts
268
671
  /**
269
672
  * Creates a client instance for making type-safe queries and mutations.
270
673
  *
674
+ * @template UseDiscriminator - When `true`, errors are returned as union types.
675
+ * When `false` (default), errors are thrown and not included in TData.
676
+ *
271
677
  * @param options - Client configuration including the API builder and defaults
272
678
  * @returns A client instance with query, infiniteQuery, and mutation methods
273
679
  *
274
680
  * @example
275
681
  * ```typescript
276
- * const api = createBuilder({ baseUrl: '/api' });
682
+ * const api = builder({});
277
683
  * const client = declareClient({ api });
278
684
  *
279
685
  * const getUser = client.query({
@@ -293,7 +699,8 @@ function declareClient({ api, defaults = {} }) {
293
699
  url: config.url,
294
700
  querySchema: config.querySchema,
295
701
  requestSchema: config.requestSchema,
296
- responseSchema: config.responseSchema
702
+ responseSchema: config.responseSchema,
703
+ errorSchema: config.errorSchema
297
704
  });
298
705
  const queryOptions$1 = makeQueryOptions(endpoint, {
299
706
  ...defaults,
@@ -314,7 +721,8 @@ function declareClient({ api, defaults = {} }) {
314
721
  url: config.url,
315
722
  querySchema: config.querySchema,
316
723
  requestSchema: config.requestSchema,
317
- responseSchema: config.responseSchema
724
+ responseSchema: config.responseSchema,
725
+ errorSchema: config.errorSchema
318
726
  });
319
727
  const infiniteQueryOptions$1 = makeInfiniteQueryOptions(endpoint, {
320
728
  ...defaults,
@@ -341,13 +749,16 @@ function declareClient({ api, defaults = {} }) {
341
749
  url: config.url,
342
750
  querySchema: config.querySchema,
343
751
  requestSchema: config.requestSchema,
344
- responseSchema: config.responseSchema
752
+ responseSchema: config.responseSchema,
753
+ errorSchema: config.errorSchema
345
754
  });
346
755
  const useMutation$1 = makeMutation(endpoint, {
347
756
  processResponse: config.processResponse ?? ((data) => data),
348
757
  useContext: config.useContext,
758
+ onMutate: config.onMutate,
349
759
  onSuccess: config.onSuccess,
350
760
  onError: config.onError,
761
+ onSettled: config.onSettled,
351
762
  useKey: config.useKey,
352
763
  meta: config.meta,
353
764
  ...defaults
@@ -374,7 +785,8 @@ function declareClient({ api, defaults = {} }) {
374
785
  url: config.url,
375
786
  querySchema: config.querySchema,
376
787
  requestSchema: config.requestSchema,
377
- responseSchema: config.responseSchema
788
+ responseSchema: config.responseSchema,
789
+ errorSchema: config.errorSchema
378
790
  });
379
791
  const useMutation$1 = makeMutation(endpoint, {
380
792
  processResponse: config.processResponse ?? ((data) => data),
@@ -401,12 +813,17 @@ function declareClient({ api, defaults = {} }) {
401
813
  }
402
814
 
403
815
  //#endregion
816
+ exports.createMultiOptimisticUpdate = createMultiOptimisticUpdate;
404
817
  exports.createMutationKey = createMutationKey;
818
+ exports.createOptimisticUpdate = createOptimisticUpdate;
819
+ exports.createPrefetchHelper = createPrefetchHelper;
820
+ exports.createPrefetchHelpers = createPrefetchHelpers;
405
821
  exports.createQueryKey = createQueryKey;
406
822
  exports.declareClient = declareClient;
407
823
  exports.makeInfiniteQueryOptions = makeInfiniteQueryOptions;
408
824
  exports.makeMutation = makeMutation;
409
825
  exports.makeQueryOptions = makeQueryOptions;
410
826
  exports.mutationKeyCreator = mutationKeyCreator;
827
+ exports.prefetchAll = prefetchAll;
411
828
  exports.queryKeyCreator = queryKeyCreator;
412
829
  //# sourceMappingURL=index.cjs.map