@wisemen/vue-core-api-utils 1.0.0-beta.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.
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { err, ok } from "neverthrow";
2
- import { useInfiniteQuery, useMutation as useMutation$1, useQuery as useQuery$1, useQueryClient } from "@tanstack/vue-query";
3
- import { computed, unref } from "vue";
2
+ import { QueryClient as QueryClient$1, VueQueryPlugin, useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
3
+ import { computed } from "vue";
4
4
 
5
5
  //#region src/async-result/asyncResult.ts
6
6
  /**
@@ -135,60 +135,8 @@ const AsyncResult = {
135
135
  return AsyncResultOk._create(value);
136
136
  }
137
137
  };
138
-
139
- //#endregion
140
- //#region src/composables/mutation/mutation.composable.ts
141
- function useMutation(options) {
142
- const isDebug = options.isDebug ?? false;
143
- const queryClient = useQueryClient();
144
- async function onSuccess(responseData, params) {
145
- await Promise.all(Object.entries(options.queryKeysToInvalidate).map(async ([queryKey, queryKeyParams]) => {
146
- const qkp = queryKeyParams;
147
- const paramsWithValues = Object.entries(qkp).reduce((acc, [key, value]) => {
148
- acc[key] = value(params, responseData);
149
- return acc;
150
- }, {});
151
- if (isDebug) console.log(`[MUTATION] Invalidating ${queryKey}`, paramsWithValues);
152
- await queryClient.invalidateQueries({
153
- exact: false,
154
- queryKey: [queryKey, paramsWithValues]
155
- });
156
- }));
157
- }
158
- const mutation = useMutation$1({
159
- mutationFn: options.queryFn,
160
- onSuccess: async (result$1, variables) => {
161
- if (!result$1.isOk()) return;
162
- const data = result$1.value;
163
- if (variables !== void 0 && "params" in variables) {
164
- await onSuccess(data, variables.params);
165
- return;
166
- }
167
- await onSuccess(data, {});
168
- }
169
- });
170
- async function execute(data) {
171
- return await mutation.mutateAsync(data);
172
- }
173
- const result = computed(() => {
174
- if (mutation.isPending.value) return AsyncResult.loading();
175
- if (mutation.isError.value && mutation.error.value) return AsyncResult.err(mutation.error.value);
176
- if (mutation.isSuccess.value && mutation.data.value !== void 0) {
177
- const apiResult = mutation.data.value;
178
- if (apiResult.isOk()) return AsyncResult.ok(apiResult.value);
179
- if (apiResult.isErr()) return AsyncResult.err(apiResult.error);
180
- }
181
- return AsyncResult.loading();
182
- });
183
- return {
184
- isLoading: computed(() => mutation.isPending.value),
185
- data: computed(() => {
186
- if (mutation.data.value?.isOk()) return mutation.data.value.value;
187
- return null;
188
- }),
189
- execute,
190
- result
191
- };
138
+ function isAsyncResult(value) {
139
+ return value instanceof AsyncResultErr || value instanceof AsyncResultLoading || value instanceof AsyncResultOk;
192
140
  }
193
141
 
194
142
  //#endregion
@@ -199,6 +147,34 @@ const QUERY_CONFIG = {
199
147
  prefetchStaleTime: DEFAULT_PREFETCH_STALE_TIME,
200
148
  limit: DEFAULT_LIMIT$3
201
149
  };
150
+ let globalQueryClient = null;
151
+ /**
152
+ * Initialize the API utilities with a QueryClient.
153
+ * Call this once during app setup (e.g. in a plugin or main.ts).
154
+ *
155
+ * After calling this, `createApiUtils()` can be called without options.
156
+ *
157
+ * @example
158
+ * ```typescript
159
+ * import { initializeApiUtils } from '@wisemen/vue-core-api-utils'
160
+ *
161
+ * const queryClient = new QueryClient()
162
+ * initializeApiUtils(queryClient)
163
+ *
164
+ * // Then in your api lib:
165
+ * export const { useQuery, useMutation, ... } = createApiUtils<MyQueryKeys>()
166
+ * ```
167
+ */
168
+ function initializeApiUtils(queryClient) {
169
+ globalQueryClient = queryClient;
170
+ }
171
+ /**
172
+ * @internal
173
+ */
174
+ function getQueryClient() {
175
+ if (globalQueryClient == null) throw new Error("[api-utils] QueryClient not available. Call initializeApiUtils(queryClient) before using createApiUtils().");
176
+ return globalQueryClient;
177
+ }
202
178
  function setQueryConfig(config) {
203
179
  if (config.limit != null && config.limit > 0) QUERY_CONFIG.limit = config.limit;
204
180
  if (config.prefetchStaleTime != null && config.prefetchStaleTime > 0) QUERY_CONFIG.prefetchStaleTime = config.prefetchStaleTime;
@@ -216,15 +192,17 @@ function useKeysetInfiniteQuery(options) {
216
192
  staleTime: options.staleTime,
217
193
  enabled: options.isEnabled,
218
194
  getNextPageParam: (lastPage) => {
219
- if (lastPage.isErr()) return null;
220
- return lastPage.value.meta.next ?? null;
195
+ if (lastPage.isErr() || lastPage.isLoading()) return null;
196
+ return lastPage.getValue().meta.next ?? null;
221
197
  },
222
198
  initialPageParam: void 0,
223
199
  placeholderData: (data) => data,
224
- queryFn: ({ pageParam }) => options.queryFn({
225
- key: pageParam,
226
- limit: options.limit ?? DEFAULT_LIMIT$2
227
- }),
200
+ queryFn: async ({ pageParam }) => {
201
+ return AsyncResult.fromResult(await options.queryFn({
202
+ key: pageParam,
203
+ limit: options.limit ?? DEFAULT_LIMIT$2
204
+ }));
205
+ },
228
206
  queryKey: getQueryKey()
229
207
  });
230
208
  const hasError = computed(() => {
@@ -233,10 +211,10 @@ function useKeysetInfiniteQuery(options) {
233
211
  const result = computed(() => {
234
212
  if (infiniteQuery.isLoading.value) return AsyncResult.loading();
235
213
  const firstError = infiniteQuery.data.value?.pages.find((page) => page.isErr());
236
- if (firstError) return AsyncResult.err(firstError.error);
237
- const data = infiniteQuery.data.value?.pages.filter((page) => page.isOk()).flatMap((page) => page.value.data) ?? [];
214
+ if (firstError) return AsyncResult.err(firstError.getError());
215
+ const data = infiniteQuery.data.value?.pages.filter((page) => page.isOk()).flatMap((page) => page.getValue().data) ?? [];
238
216
  const firstPage = infiniteQuery.data.value?.pages[0];
239
- const meta = firstPage?.isOk() ? firstPage.value.meta : { next: null };
217
+ const meta = firstPage?.isOk() ? firstPage.getValue().meta : { next: null };
240
218
  const response = {
241
219
  data,
242
220
  meta: { next: infiniteQuery.hasNextPage.value ? meta.next : null }
@@ -278,17 +256,17 @@ function useOffsetInfiniteQuery(options) {
278
256
  staleTime: options.staleTime,
279
257
  enabled: options.isEnabled,
280
258
  getNextPageParam: (lastPage) => {
281
- if (lastPage.isErr()) return null;
282
- const total = lastPage.value.meta.offset + lastPage.value.meta.limit;
283
- if (total >= lastPage.value.meta.total) return null;
259
+ if (lastPage.isErr() || lastPage.isLoading()) return null;
260
+ const total = lastPage.getValue().meta.offset + lastPage.getValue().meta.limit;
261
+ if (total >= lastPage.getValue().meta.total) return null;
284
262
  return total;
285
263
  },
286
264
  initialPageParam: 0,
287
265
  placeholderData: (data) => data,
288
- queryFn: ({ pageParam }) => options.queryFn({
266
+ queryFn: async ({ pageParam }) => AsyncResult.fromResult(await options.queryFn({
289
267
  limit: options.limit ?? DEFAULT_LIMIT$1,
290
268
  offset: pageParam ?? 0
291
- }),
269
+ })),
292
270
  queryKey: getQueryKey()
293
271
  });
294
272
  const hasError = computed(() => {
@@ -297,10 +275,10 @@ function useOffsetInfiniteQuery(options) {
297
275
  const result = computed(() => {
298
276
  if (infiniteQuery.isLoading.value) return AsyncResult.loading();
299
277
  const firstError = infiniteQuery.data.value?.pages.find((page) => page.isErr());
300
- if (firstError) return AsyncResult.err(firstError.error);
301
- const data = infiniteQuery.data.value?.pages.filter((page) => page.isOk()).flatMap((page) => page.value.data) ?? [];
278
+ if (firstError) return AsyncResult.err(firstError.getError());
279
+ const data = infiniteQuery.data.value?.pages.filter((page) => page.isOk()).flatMap((page) => page.getValue().data) ?? [];
302
280
  const firstPage = infiniteQuery.data.value?.pages[0];
303
- const meta = firstPage?.isOk() ? firstPage.value.meta : null;
281
+ const meta = firstPage?.isOk() ? firstPage.getValue().meta : null;
304
282
  const response = {
305
283
  data,
306
284
  meta: {
@@ -332,62 +310,6 @@ function useOffsetInfiniteQuery(options) {
332
310
  };
333
311
  }
334
312
 
335
- //#endregion
336
- //#region src/composables/query/prefetchQuery.composable.ts
337
- function usePrefetchQuery(query) {
338
- const queryClient = useQueryClient();
339
- function getQueryKey() {
340
- const [first] = Object.entries(query.queryKey);
341
- if (!first) return [];
342
- const [queryKey, params] = first;
343
- return [queryKey, params];
344
- }
345
- async function execute() {
346
- await queryClient.prefetchQuery({
347
- staleTime: query.staleTime ?? QUERY_CONFIG.prefetchStaleTime,
348
- queryFn: query.queryFn,
349
- queryKey: getQueryKey()
350
- });
351
- }
352
- return { execute };
353
- }
354
-
355
- //#endregion
356
- //#region src/composables/query/query.composable.ts
357
- function useQuery(options) {
358
- const isDebug = options.isDebug ?? false;
359
- const query = useQuery$1({
360
- staleTime: options.staleTime,
361
- enabled: options.isEnabled,
362
- placeholderData: (data) => data,
363
- queryFn: async () => {
364
- return AsyncResult.fromResult(await options.queryFn());
365
- },
366
- queryKey: getQueryKey()
367
- });
368
- function getQueryKey() {
369
- const [queryKey, params] = Object.entries(options.queryKey)[0];
370
- if (isDebug) console.debug(`Create query with key ${queryKey}`, params);
371
- return [queryKey, params];
372
- }
373
- async function refetch() {
374
- await query.refetch();
375
- }
376
- return {
377
- isError: computed(() => query.data.value?.isErr() ?? false),
378
- isFetching: computed(() => query.isFetching.value),
379
- isLoading: computed(() => query.isLoading.value),
380
- isSuccess: computed(() => query.data.value?.isOk() ?? false),
381
- refetch,
382
- result: computed(() => {
383
- if (query.isLoading.value) return AsyncResult.loading();
384
- if (query.data.value?.isOk()) return AsyncResult.ok(query.data.value.getValue());
385
- if (query.data.value?.isErr()) return AsyncResult.err(query.data.value.getError());
386
- return AsyncResult.loading();
387
- })
388
- };
389
- }
390
-
391
313
  //#endregion
392
314
  //#region src/factory/createApiInfiniteQueryUtils.ts
393
315
  function createApiInfiniteQueryUtils() {
@@ -420,11 +342,164 @@ function createApiInfiniteQueryUtils() {
420
342
  }
421
343
 
422
344
  //#endregion
423
- //#region src/utils/optimisticUpdates.ts
345
+ //#region src/composables/mutation/mutation.composable.ts
346
+ function useMutation$1(options) {
347
+ const isDebug = options.isDebug ?? false;
348
+ const queryClient = useQueryClient();
349
+ async function onSuccess(responseData, params) {
350
+ if (!options.queryKeysToInvalidate) return;
351
+ await Promise.all(Object.entries(options.queryKeysToInvalidate).map(async ([queryKey, queryKeyParams]) => {
352
+ if (!queryKeyParams) {
353
+ if (isDebug) console.log(`[MUTATION] Invalidating ${queryKey}`);
354
+ await queryClient.invalidateQueries({ queryKey: [queryKey] });
355
+ return;
356
+ }
357
+ const qkp = queryKeyParams;
358
+ const paramEntries = Object.entries(qkp);
359
+ if (paramEntries.length === 0) {
360
+ if (isDebug) console.log(`[MUTATION] Invalidating ${queryKey}`);
361
+ await queryClient.invalidateQueries({ queryKey: [queryKey] });
362
+ return;
363
+ }
364
+ const paramsWithValues = paramEntries.reduce((acc, [key, value]) => {
365
+ acc[key] = value(params, responseData);
366
+ return acc;
367
+ }, {});
368
+ if (isDebug) console.log(`[MUTATION] Invalidating ${queryKey}`, paramsWithValues);
369
+ await queryClient.invalidateQueries({
370
+ exact: false,
371
+ queryKey: [queryKey, paramsWithValues]
372
+ });
373
+ }));
374
+ }
375
+ const mutation = useMutation({
376
+ mutationFn: options.queryFn,
377
+ onSuccess: async (result$1, variables) => {
378
+ if (!result$1.isOk()) return;
379
+ const data = result$1.value;
380
+ if (variables !== void 0 && "params" in variables) {
381
+ await onSuccess(data, variables.params);
382
+ return;
383
+ }
384
+ await onSuccess(data, {});
385
+ }
386
+ });
387
+ async function execute(data) {
388
+ return await mutation.mutateAsync(data);
389
+ }
390
+ const result = computed(() => {
391
+ if (mutation.isPending.value) return AsyncResult.loading();
392
+ if (mutation.isError.value) return AsyncResult.err(mutation.error.value);
393
+ if (mutation.isSuccess.value && mutation.data.value !== void 0) {
394
+ const apiResult = mutation.data.value;
395
+ if (apiResult.isOk()) return AsyncResult.ok(apiResult.value);
396
+ if (apiResult.isErr()) return AsyncResult.err(apiResult.error);
397
+ }
398
+ return AsyncResult.loading();
399
+ });
400
+ return {
401
+ isLoading: computed(() => mutation.isPending.value),
402
+ data: computed(() => {
403
+ if (mutation.data.value?.isOk()) return mutation.data.value.value;
404
+ return null;
405
+ }),
406
+ execute,
407
+ result
408
+ };
409
+ }
410
+
411
+ //#endregion
412
+ //#region src/factory/createApiMutationUtils.ts
413
+ function createApiMutationUtils() {
414
+ function useMutation$2(options) {
415
+ return useMutation$1({
416
+ isDebug: options.isDebug,
417
+ queryFn: options.queryFn,
418
+ queryKeysToInvalidate: options.queryKeysToInvalidate ?? {}
419
+ });
420
+ }
421
+ return { useMutation: useMutation$2 };
422
+ }
423
+
424
+ //#endregion
425
+ //#region src/factory/createApiPrefetchInfiniteQueryUtils.ts
426
+ const DEFAULT_LIMIT = QUERY_CONFIG.limit;
427
+ function createApiPrefetchInfiniteQueryUtils() {
428
+ function usePrefetchOffsetInfiniteQuery(key, queryOptions) {
429
+ const queryKey = [key, queryOptions.params ?? {}];
430
+ const queryClient = getQueryClient();
431
+ async function execute() {
432
+ await queryClient.prefetchInfiniteQuery({
433
+ staleTime: queryOptions.staleTime ?? QUERY_CONFIG.prefetchStaleTime,
434
+ getNextPageParam: (lastPage) => {
435
+ if (lastPage.isErr()) return null;
436
+ const total = lastPage.value.meta.offset + lastPage.value.meta.limit;
437
+ if (total >= lastPage.value.meta.total) return null;
438
+ return total;
439
+ },
440
+ initialPageParam: 0,
441
+ queryFn: ({ pageParam }) => queryOptions.queryFn({
442
+ limit: queryOptions.limit ?? DEFAULT_LIMIT,
443
+ offset: pageParam ?? 0
444
+ }),
445
+ queryKey
446
+ });
447
+ }
448
+ return { execute };
449
+ }
450
+ function usePrefetchKeysetInfiniteQuery(key, queryOptions) {
451
+ const queryKey = [key, queryOptions.params ?? {}];
452
+ async function execute() {
453
+ await getQueryClient().prefetchInfiniteQuery({
454
+ staleTime: queryOptions.staleTime ?? QUERY_CONFIG.prefetchStaleTime,
455
+ getNextPageParam: (lastPage) => {
456
+ if (lastPage.isErr()) return null;
457
+ const next = lastPage.value.meta.next;
458
+ if (next === null || next === void 0) return null;
459
+ return next;
460
+ },
461
+ initialPageParam: void 0,
462
+ queryFn: ({ pageParam }) => queryOptions.queryFn({
463
+ key: pageParam,
464
+ limit: queryOptions.limit ?? DEFAULT_LIMIT
465
+ }),
466
+ queryKey
467
+ });
468
+ }
469
+ return { execute };
470
+ }
471
+ return {
472
+ usePrefetchKeysetInfiniteQuery,
473
+ usePrefetchOffsetInfiniteQuery
474
+ };
475
+ }
476
+
477
+ //#endregion
478
+ //#region src/factory/createApiPrefetchQueryUtils.ts
479
+ function createApiPrefetchQueryUtils() {
480
+ function usePrefetchQuery(key, queryOptions) {
481
+ const queryClient = getQueryClient();
482
+ const queryKey = [key, queryOptions.params ?? {}];
483
+ async function execute() {
484
+ await queryClient.prefetchQuery({
485
+ staleTime: queryOptions.staleTime ?? QUERY_CONFIG.prefetchStaleTime,
486
+ queryFn: async () => {
487
+ return AsyncResult.fromResult(await queryOptions.queryFn());
488
+ },
489
+ queryKey
490
+ });
491
+ }
492
+ return { execute };
493
+ }
494
+ return { usePrefetchQuery };
495
+ }
496
+
497
+ //#endregion
498
+ //#region src/utils/query-client/queryClient.ts
424
499
  /**
425
- * OptimisticUpdates utility class for type-safe optimistic updates
500
+ * QueryClient utility class for type-safe query operations
426
501
  */
427
- var OptimisticUpdates = class {
502
+ var QueryClient = class {
428
503
  constructor(queryClient) {
429
504
  this.queryClient = queryClient;
430
505
  }
@@ -432,13 +507,11 @@ var OptimisticUpdates = class {
432
507
  * Extract the raw entity from AsyncResult data
433
508
  */
434
509
  extractEntityFromAsyncResult(data) {
435
- if (data === void 0 || data === null) return null;
436
- if (typeof data === "object" && "isOk" in data) {
437
- const asyncResult = data;
438
- if (asyncResult.isOk()) return asyncResult.getValue();
439
- return null;
440
- }
441
- return data;
510
+ if (data.isOk()) return data.getValue();
511
+ return null;
512
+ }
513
+ hasDataArray(value) {
514
+ return Boolean(value && typeof value === "object" && Array.isArray(value.data));
442
515
  }
443
516
  isInfiniteDataLike(data) {
444
517
  return Boolean(data && typeof data === "object" && "pages" in data && Array.isArray(data.pages));
@@ -446,33 +519,26 @@ var OptimisticUpdates = class {
446
519
  /**
447
520
  * Determine if an item should be updated
448
521
  */
449
- shouldUpdateItem(by, item, value) {
450
- if (typeof by === "function") return by(item);
451
- if (by && typeof by === "object") return Object.entries(by).every(([key, matchValue]) => {
452
- const itemValue = item[key];
453
- return unref(itemValue) === unref(matchValue);
454
- });
455
- const idFromValue = value.id;
456
- const itemId = item["id"];
457
- if (idFromValue !== void 0 && itemId !== void 0) return unref(itemId) === unref(idFromValue);
458
- return false;
522
+ shouldUpdateItem(by, item) {
523
+ return by(item);
459
524
  }
460
525
  /**
461
526
  * Internal method to update entity based on the "by" option
462
527
  */
463
528
  updateEntity(by, currentData, value) {
464
529
  if (Array.isArray(currentData)) return currentData.map((item) => {
465
- return this.shouldUpdateItem(by, item, value) ? {
466
- ...item,
467
- ...value
468
- } : item;
530
+ return this.shouldUpdateItem(by, item) ? value(item) : item;
469
531
  });
470
- if (this.shouldUpdateItem(by, currentData, value)) return {
471
- ...currentData,
472
- ...value
473
- };
532
+ if (this.shouldUpdateItem(by, currentData)) return value(currentData);
474
533
  return currentData;
475
534
  }
535
+ updateInfinitePageValue(by, value, pageValue) {
536
+ if (!this.hasDataArray(pageValue)) return pageValue;
537
+ return {
538
+ ...pageValue,
539
+ data: this.updateEntity(by, pageValue.data, value)
540
+ };
541
+ }
476
542
  /**
477
543
  * Wrap a raw entity in an AsyncResult (preserving ok state)
478
544
  */
@@ -482,11 +548,13 @@ var OptimisticUpdates = class {
482
548
  get(queryKey, options) {
483
549
  if (Array.isArray(queryKey)) {
484
550
  const data = this.queryClient.getQueryData(queryKey);
551
+ if (data == null) return null;
485
552
  return this.extractEntityFromAsyncResult(data);
486
553
  }
487
554
  if (options?.isExact ?? false) {
488
555
  const normalizedKey = [queryKey];
489
556
  const data = this.queryClient.getQueryData(normalizedKey);
557
+ if (data == null) return null;
490
558
  return this.extractEntityFromAsyncResult(data);
491
559
  }
492
560
  const allQueries = this.queryClient.getQueryCache().findAll({ predicate: (query) => {
@@ -519,7 +587,7 @@ var OptimisticUpdates = class {
519
587
  this.queryClient.setQueryData(normalizedKey, wrappedData);
520
588
  }
521
589
  update(keyOrTuple, options) {
522
- const by = options.by ?? void 0;
590
+ const by = options.by;
523
591
  const value = options.value;
524
592
  const isSpecific = Array.isArray(keyOrTuple);
525
593
  const key = isSpecific ? keyOrTuple[0] : keyOrTuple;
@@ -538,19 +606,14 @@ var OptimisticUpdates = class {
538
606
  const updatedInfiniteData = {
539
607
  ...currentData,
540
608
  pages: currentData.pages.map((page) => {
541
- if (page && typeof page === "object" && "map" in page && typeof page.map === "function") return page.map((pageValue) => {
542
- if (pageValue && typeof pageValue === "object" && Array.isArray(pageValue.data)) return {
543
- ...pageValue,
544
- data: this.updateEntity(by, pageValue.data, value)
545
- };
546
- return pageValue;
547
- });
548
- return page;
609
+ if (!isAsyncResult(page)) return page;
610
+ return page.map((pageValue) => this.updateInfinitePageValue(by, value, pageValue));
549
611
  })
550
612
  };
551
613
  this.queryClient.setQueryData(query.queryKey, updatedInfiniteData);
552
614
  continue;
553
615
  }
616
+ if (!isAsyncResult(currentData)) continue;
554
617
  const rawEntity = this.extractEntityFromAsyncResult(currentData);
555
618
  if (rawEntity === null) continue;
556
619
  const updatedEntity = this.updateEntity(by, rawEntity, value);
@@ -559,100 +622,59 @@ var OptimisticUpdates = class {
559
622
  }
560
623
  }
561
624
  };
562
- /**
563
- * Create an OptimisticUpdates instance
564
- */
565
- function createOptimisticUpdates(queryClient) {
566
- return new OptimisticUpdates(queryClient);
567
- }
568
625
 
569
626
  //#endregion
570
- //#region src/factory/createApiOptimisticUpdatesUtils.ts
571
- function createApiOptimisticUpdatesUtils(options) {
572
- function useOptimisticUpdates() {
573
- return new OptimisticUpdates(options.queryClient);
627
+ //#region src/factory/createApiQueryClientUtils.ts
628
+ function createApiQueryClientUtils() {
629
+ function useQueryClient$1() {
630
+ return new QueryClient(getQueryClient());
574
631
  }
575
- return { useOptimisticUpdates };
632
+ return { useQueryClient: useQueryClient$1 };
576
633
  }
577
634
 
578
635
  //#endregion
579
- //#region src/factory/createApiPrefetchInfiniteQueryUtils.ts
580
- const DEFAULT_LIMIT = QUERY_CONFIG.limit;
581
- function createApiPrefetchInfiniteQueryUtils(options) {
582
- function usePrefetchOffsetInfiniteQuery(key, queryOptions) {
583
- const queryKey = [key, queryOptions.params ?? {}];
584
- async function execute() {
585
- await options.queryClient.prefetchInfiniteQuery({
586
- staleTime: queryOptions.staleTime,
587
- getNextPageParam: (lastPage) => {
588
- if (lastPage.isErr()) return null;
589
- const total = lastPage.value.meta.offset + lastPage.value.meta.limit;
590
- if (total >= lastPage.value.meta.total) return null;
591
- return total;
592
- },
593
- initialPageParam: 0,
594
- queryFn: ({ pageParam }) => queryOptions.queryFn({
595
- limit: queryOptions.limit ?? DEFAULT_LIMIT,
596
- offset: pageParam ?? 0
597
- }),
598
- queryKey
599
- });
600
- }
601
- return { execute };
636
+ //#region src/composables/query/query.composable.ts
637
+ function useQuery$1(options) {
638
+ const isDebug = options.isDebug ?? false;
639
+ const query = useQuery({
640
+ staleTime: options.staleTime,
641
+ enabled: options.isEnabled,
642
+ placeholderData: (data) => data,
643
+ queryFn: async () => {
644
+ return AsyncResult.fromResult(await options.queryFn());
645
+ },
646
+ queryKey: getQueryKey()
647
+ });
648
+ function getQueryKey() {
649
+ const [queryKey, params] = Object.entries(options.queryKey)[0];
650
+ if (isDebug) console.debug(`Create query with key ${queryKey}`, params);
651
+ return [queryKey, params];
602
652
  }
603
- function usePrefetchKeysetInfiniteQuery(key, queryOptions) {
604
- const queryKey = [key, queryOptions.params ?? {}];
605
- async function execute() {
606
- await options.queryClient.prefetchInfiniteQuery({
607
- staleTime: queryOptions.staleTime,
608
- getNextPageParam: (lastPage) => {
609
- if (lastPage.isErr()) return null;
610
- const next = lastPage.value.meta.next;
611
- if (next === null || next === void 0) return null;
612
- return next;
613
- },
614
- initialPageParam: void 0,
615
- queryFn: ({ pageParam }) => queryOptions.queryFn({
616
- key: pageParam,
617
- limit: queryOptions.limit ?? DEFAULT_LIMIT
618
- }),
619
- queryKey
620
- });
621
- }
622
- return { execute };
653
+ async function refetch() {
654
+ await query.refetch();
623
655
  }
624
656
  return {
625
- usePrefetchKeysetInfiniteQuery,
626
- usePrefetchOffsetInfiniteQuery
657
+ isError: computed(() => query.data.value?.isErr() ?? false),
658
+ isFetching: computed(() => query.isFetching.value),
659
+ isLoading: computed(() => query.isLoading.value),
660
+ isSuccess: computed(() => query.data.value?.isOk() ?? false),
661
+ refetch,
662
+ result: computed(() => {
663
+ if (query.isLoading.value) return AsyncResult.loading();
664
+ if (query.data.value?.isOk()) return AsyncResult.ok(query.data.value.getValue());
665
+ if (query.data.value?.isErr()) return AsyncResult.err(query.data.value.getError());
666
+ return AsyncResult.loading();
667
+ })
627
668
  };
628
669
  }
629
670
 
630
- //#endregion
631
- //#region src/factory/createApiPrefetchQueryUtils.ts
632
- function createApiPrefetchQueryUtils(options) {
633
- function usePrefetchQuery$1(key, queryOptions) {
634
- const queryKey = [key, queryOptions.params ?? {}];
635
- async function execute() {
636
- await options.queryClient.prefetchQuery({
637
- staleTime: queryOptions.staleTime ?? QUERY_CONFIG.prefetchStaleTime,
638
- queryFn: async () => {
639
- return AsyncResult.fromResult(await queryOptions.queryFn());
640
- },
641
- queryKey
642
- });
643
- }
644
- return { execute };
645
- }
646
- return { usePrefetchQuery: usePrefetchQuery$1 };
647
- }
648
-
649
671
  //#endregion
650
672
  //#region src/factory/createApiQueryUtils.ts
651
673
  function createApiQueryUtils() {
652
674
  function useQuery$2(key, queryOptions) {
653
675
  const params = queryOptions.params ?? {};
654
676
  const queryKey = { [key]: params };
655
- return useQuery({
677
+ return useQuery$1({
656
678
  staleTime: queryOptions.staleTime,
657
679
  isDebug: queryOptions.isDebug,
658
680
  isEnabled: queryOptions.isEnabled,
@@ -667,17 +689,76 @@ function createApiQueryUtils() {
667
689
  //#region src/factory/createApiUtils.ts
668
690
  /**
669
691
  * Factory that creates typed composables based on a user-provided query-keys config.
670
- * This is an alternative to module augmentation of `QueryKeys`.
692
+ *
693
+ * Requires `initializeApiUtils(queryClient)` to be called first.
694
+ *
695
+ * @example
696
+ * ```typescript
697
+ * // In app setup (plugin or main.ts):
698
+ * initializeApiUtils(queryClient)
699
+ *
700
+ * // In your api lib:
701
+ * export const { useQuery, useMutation, useQueryClient } = createApiUtils<MyQueryKeys>()
702
+ * ```
671
703
  */
672
- function createApiUtils(options) {
704
+ function createApiUtils() {
673
705
  return {
674
706
  ...createApiQueryUtils(),
675
- ...createApiPrefetchQueryUtils(options),
676
- ...createApiPrefetchInfiniteQueryUtils(options),
707
+ ...createApiPrefetchQueryUtils(),
708
+ ...createApiPrefetchInfiniteQueryUtils(),
677
709
  ...createApiInfiniteQueryUtils(),
678
- ...createApiOptimisticUpdatesUtils(options)
710
+ ...createApiMutationUtils(),
711
+ ...createApiQueryClientUtils()
679
712
  };
680
713
  }
681
714
 
682
715
  //#endregion
683
- export { AsyncResult, AsyncResultErr, AsyncResultLoading, AsyncResultOk, OptimisticUpdates, createApiInfiniteQueryUtils, createApiOptimisticUpdatesUtils, createApiPrefetchInfiniteQueryUtils, createApiPrefetchQueryUtils, createApiQueryUtils, createApiUtils, createOptimisticUpdates, setQueryConfig, useKeysetInfiniteQuery, useMutation, useOffsetInfiniteQuery, usePrefetchQuery, useQuery };
716
+ //#region src/plugin/apiUtilsPlugin.ts
717
+ /**
718
+ * Create a Vue plugin that sets up TanStack Query and initializes API utilities.
719
+ *
720
+ * This plugin handles:
721
+ * - Creating a QueryClient with the provided config
722
+ * - Installing VueQueryPlugin on the app
723
+ * - Initializing the global QueryClient for api-utils
724
+ *
725
+ * @example
726
+ * ```typescript
727
+ * import { apiUtilsPlugin } from '@wisemen/vue-core-api-utils'
728
+ * import { vueQueryClientConfig } from '@wisemen/vue-core-configs'
729
+ *
730
+ * app.use(apiUtilsPlugin(vueQueryClientConfig()))
731
+ * ```
732
+ *
733
+ * @param config - QueryClient configuration
734
+ * @returns A Vue plugin that can be used with app.use()
735
+ */
736
+ function apiUtilsPlugin(config) {
737
+ const queryClient = new QueryClient$1(config);
738
+ return { install: (app) => {
739
+ app.use(VueQueryPlugin, { queryClient });
740
+ initializeApiUtils(queryClient);
741
+ } };
742
+ }
743
+
744
+ //#endregion
745
+ //#region src/types/sort.type.ts
746
+ let SortDirection = /* @__PURE__ */ function(SortDirection$1) {
747
+ SortDirection$1["ASC"] = "asc";
748
+ SortDirection$1["DESC"] = "desc";
749
+ return SortDirection$1;
750
+ }({});
751
+
752
+ //#endregion
753
+ //#region src/utils/sort/sort.utils.ts
754
+ var SortUtil = class {
755
+ static toDto(sort, sortKeyMap) {
756
+ return sort.filter((s) => s.direction !== null).map((s) => ({
757
+ key: sortKeyMap[s.key],
758
+ order: s.direction === SortDirection.ASC ? SortDirection.ASC : SortDirection.DESC
759
+ }));
760
+ }
761
+ };
762
+
763
+ //#endregion
764
+ export { ApiError, ApiErrorObject, ApiExpectedError, ApiKnownErrorObject, ApiResult, ApiUnexpectedError, ApiUnknownErrorObject, AsyncApiResult, AsyncResult, AsyncResultErr, AsyncResultLoading, AsyncResultOk, KeysetPagination, KeysetPaginationParams, KeysetPaginationResponse, KeysetPaginationResult, OffsetPagination, OffsetPaginationParams, OffsetPaginationResponse, OffsetPaginationResult, PaginatedDataDto, QueryClient, QueryClientUpdateOptions, Sort, SortDirection, SortUtil, apiUtilsPlugin, createApiInfiniteQueryUtils, createApiMutationUtils, createApiPrefetchInfiniteQueryUtils, createApiPrefetchQueryUtils, createApiQueryClientUtils, createApiQueryUtils, createApiUtils, getQueryClient as getTanstackQueryClient, initializeApiUtils, setQueryConfig };