@wisemen/vue-core-api-utils 2.0.2 → 2.0.3

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 ADDED
@@ -0,0 +1,704 @@
1
+ import { ResultAsync, err, ok } from "neverthrow";
2
+ import { QueryClient as QueryClient$1, VueQueryPlugin, useInfiniteQuery, useMutation as useMutation$1, useQuery as useQuery$1, useQueryClient } from "@tanstack/vue-query";
3
+ import { computed } from "vue";
4
+ import z from "zod";
5
+
6
+ //#region src/async-result/asyncResult.ts
7
+ /**
8
+ * Base class for AsyncResult - internal use only.
9
+ * Use AsyncResult<T, E> as the public type.
10
+ */
11
+ var AsyncResultBase = class {
12
+ _error;
13
+ _status;
14
+ _value;
15
+ constructor(status, value, error) {
16
+ this._status = status;
17
+ this._value = value;
18
+ this._error = error;
19
+ }
20
+ /**
21
+ * Check if the result is an error (type predicate for narrowing)
22
+ */
23
+ isErr() {
24
+ return this._status === "err";
25
+ }
26
+ /**
27
+ * Check if the result is in loading state (type predicate for narrowing)
28
+ */
29
+ isLoading() {
30
+ return this._status === "loading";
31
+ }
32
+ /**
33
+ * Check if the result is a success (type predicate for narrowing)
34
+ */
35
+ isOk() {
36
+ return this._status === "ok";
37
+ }
38
+ /**
39
+ * Map the success value to a new value
40
+ */
41
+ map(fn) {
42
+ if (this._status === "loading") return AsyncResult.loading();
43
+ if (this._status === "ok") return AsyncResult.ok(fn(this._value));
44
+ return AsyncResult.err(this._error);
45
+ }
46
+ /**
47
+ * Map the error to a new error
48
+ */
49
+ mapErr(fn) {
50
+ if (this._status === "loading") return AsyncResult.loading();
51
+ if (this._status === "err") return AsyncResult.err(fn(this._error));
52
+ return AsyncResult.ok(this._value);
53
+ }
54
+ /**
55
+ * Pattern match on all three states
56
+ */
57
+ match(handlers) {
58
+ if (this._status === "loading") return handlers.loading();
59
+ if (this._status === "ok") return handlers.ok(this._value);
60
+ return handlers.err(this._error);
61
+ }
62
+ unwrapOr(defaultValue) {
63
+ if (this._status === "ok") return this._value;
64
+ return defaultValue;
65
+ }
66
+ };
67
+ /**
68
+ * AsyncResult representing an error state
69
+ */
70
+ var AsyncResultErr = class AsyncResultErr extends AsyncResultBase {
71
+ constructor(error) {
72
+ super("err", void 0, error);
73
+ }
74
+ /** @internal */
75
+ static _create(error) {
76
+ return new AsyncResultErr(error);
77
+ }
78
+ /** Get the error value - only available after isErr() check */
79
+ getError() {
80
+ return this._error;
81
+ }
82
+ getResult() {
83
+ return err(this._error);
84
+ }
85
+ };
86
+ /**
87
+ * AsyncResult representing a loading state
88
+ */
89
+ var AsyncResultLoading = class AsyncResultLoading extends AsyncResultBase {
90
+ constructor() {
91
+ super("loading");
92
+ }
93
+ /** @internal */
94
+ static _create() {
95
+ return new AsyncResultLoading();
96
+ }
97
+ getResult() {
98
+ return null;
99
+ }
100
+ };
101
+ /**
102
+ * AsyncResult representing a success state
103
+ */
104
+ var AsyncResultOk = class AsyncResultOk extends AsyncResultBase {
105
+ constructor(value) {
106
+ super("ok", value);
107
+ }
108
+ /** @internal */
109
+ static _create(value) {
110
+ return new AsyncResultOk(value);
111
+ }
112
+ getResult() {
113
+ return ok(this._value);
114
+ }
115
+ /** Get the success value - only available after isOk() check */
116
+ getValue() {
117
+ return this._value;
118
+ }
119
+ };
120
+ /**
121
+ * Static factory methods for creating AsyncResult instances.
122
+ * This pattern (same name for type and value) is intentional for ergonomic API.
123
+ */
124
+ const AsyncResult = {
125
+ err(error) {
126
+ return AsyncResultErr._create(error);
127
+ },
128
+ fromResult(result) {
129
+ if (result.isOk()) return AsyncResult.ok(result.value);
130
+ return AsyncResult.err(result.error);
131
+ },
132
+ loading() {
133
+ return AsyncResultLoading._create();
134
+ },
135
+ ok(value) {
136
+ return AsyncResultOk._create(value);
137
+ }
138
+ };
139
+ function isAsyncResult(value) {
140
+ return value instanceof AsyncResultErr || value instanceof AsyncResultLoading || value instanceof AsyncResultOk;
141
+ }
142
+
143
+ //#endregion
144
+ //#region src/composables/mutation/mutation.composable.ts
145
+ function useMutation(options) {
146
+ const isDebug = options.isDebug ?? false;
147
+ const queryClient = useQueryClient();
148
+ async function onSuccess(responseData, params) {
149
+ if (!options.queryKeysToInvalidate) return;
150
+ await Promise.all(Object.entries(options.queryKeysToInvalidate).map(async ([queryKey, queryKeyParams]) => {
151
+ if (!queryKeyParams) {
152
+ if (isDebug) console.log(`[MUTATION] Invalidating ${queryKey}`);
153
+ await queryClient.invalidateQueries({ queryKey: [queryKey] });
154
+ return;
155
+ }
156
+ const qkp = queryKeyParams;
157
+ const paramEntries = Object.entries(qkp);
158
+ if (paramEntries.length === 0) {
159
+ if (isDebug) console.log(`[MUTATION] Invalidating ${queryKey}`);
160
+ await queryClient.invalidateQueries({ queryKey: [queryKey] });
161
+ return;
162
+ }
163
+ const paramsWithValues = paramEntries.reduce((acc, [key, value]) => {
164
+ acc[key] = value(params, responseData);
165
+ return acc;
166
+ }, {});
167
+ if (isDebug) console.log(`[MUTATION] Invalidating ${queryKey}`, paramsWithValues);
168
+ await queryClient.invalidateQueries({
169
+ exact: false,
170
+ queryKey: [queryKey, paramsWithValues]
171
+ });
172
+ }));
173
+ }
174
+ const mutation = useMutation$1({
175
+ mutationFn: options.queryFn,
176
+ onSuccess: async (result$1, variables) => {
177
+ if (!result$1.isOk()) return;
178
+ const data = result$1.value;
179
+ if (variables !== void 0 && "params" in variables) {
180
+ await onSuccess(data, variables.params);
181
+ return;
182
+ }
183
+ await onSuccess(data, {});
184
+ }
185
+ });
186
+ async function execute(data) {
187
+ return await mutation.mutateAsync(data);
188
+ }
189
+ const result = computed(() => {
190
+ if (mutation.isPending.value) return AsyncResult.loading();
191
+ if (mutation.isError.value) return AsyncResult.err(mutation.error.value);
192
+ if (mutation.isSuccess.value && mutation.data.value !== void 0) {
193
+ const apiResult = mutation.data.value;
194
+ if (apiResult.isOk()) return AsyncResult.ok(apiResult.value);
195
+ if (apiResult.isErr()) return AsyncResult.err(apiResult.error);
196
+ }
197
+ return AsyncResult.loading();
198
+ });
199
+ return {
200
+ isLoading: computed(() => mutation.isPending.value),
201
+ data: computed(() => {
202
+ if (mutation.data.value?.isOk()) return mutation.data.value.value;
203
+ return null;
204
+ }),
205
+ execute,
206
+ result
207
+ };
208
+ }
209
+
210
+ //#endregion
211
+ //#region src/config/config.ts
212
+ const DEFAULT_LIMIT$2 = 20;
213
+ const DEFAULT_PREFETCH_STALE_TIME = 60;
214
+ const QUERY_CONFIG = {
215
+ prefetchStaleTime: DEFAULT_PREFETCH_STALE_TIME,
216
+ limit: DEFAULT_LIMIT$2
217
+ };
218
+ let globalQueryClient = null;
219
+ /**
220
+ * Initialize the API utilities with a QueryClient.
221
+ * Call this once during app setup (e.g. in a plugin or main.ts).
222
+ *
223
+ * After calling this, `createApiUtils()` can be called without options.
224
+ *
225
+ * @example
226
+ * ```typescript
227
+ * import { initializeApiUtils } from '@wisemen/vue-core-api-utils'
228
+ *
229
+ * const queryClient = new QueryClient()
230
+ * initializeApiUtils(queryClient)
231
+ *
232
+ * // Then in your api lib:
233
+ * export const { useQuery, useMutation, ... } = createApiUtils<MyQueryKeys>()
234
+ * ```
235
+ */
236
+ function initializeApiUtils(queryClient) {
237
+ globalQueryClient = queryClient;
238
+ }
239
+ /**
240
+ * @internal
241
+ */
242
+ function getQueryClient() {
243
+ if (globalQueryClient == null) throw new Error("[api-utils] QueryClient not available. Call initializeApiUtils(queryClient) before using createApiUtils().");
244
+ return globalQueryClient;
245
+ }
246
+ function setQueryConfig(config) {
247
+ if (config.limit != null && config.limit > 0) QUERY_CONFIG.limit = config.limit;
248
+ if (config.prefetchStaleTime != null && config.prefetchStaleTime > 0) QUERY_CONFIG.prefetchStaleTime = config.prefetchStaleTime;
249
+ }
250
+
251
+ //#endregion
252
+ //#region src/composables/query/keysetInfiniteQuery.composable.ts
253
+ const DEFAULT_LIMIT$1 = QUERY_CONFIG.limit;
254
+ function useKeysetInfiniteQuery(key, options) {
255
+ const queryKey = [key, options.params];
256
+ const infiniteQuery = useInfiniteQuery({
257
+ staleTime: options.staleTime,
258
+ enabled: options.isEnabled,
259
+ getNextPageParam: (lastPage) => {
260
+ if (lastPage.isErr() || lastPage.isLoading()) return null;
261
+ return lastPage.getValue().meta.next ?? null;
262
+ },
263
+ initialPageParam: void 0,
264
+ placeholderData: (data) => data,
265
+ queryFn: async ({ pageParam }) => {
266
+ return AsyncResult.fromResult(await options.queryFn({
267
+ key: pageParam,
268
+ limit: options.limit ?? DEFAULT_LIMIT$1
269
+ }));
270
+ },
271
+ queryKey
272
+ });
273
+ const hasError = computed(() => {
274
+ return Boolean(infiniteQuery.data.value?.pages.find((page) => page.isErr()));
275
+ });
276
+ const result = computed(() => {
277
+ if (infiniteQuery.isLoading.value) return AsyncResult.loading();
278
+ const firstError = infiniteQuery.data.value?.pages.find((page) => page.isErr());
279
+ if (firstError) return AsyncResult.err(firstError.getError());
280
+ const data = infiniteQuery.data.value?.pages.filter((page) => page.isOk()).flatMap((page) => page.getValue().data) ?? [];
281
+ const firstPage = infiniteQuery.data.value?.pages[0];
282
+ const meta = firstPage?.isOk() ? firstPage.getValue().meta : { next: null };
283
+ const response = {
284
+ data,
285
+ meta: { next: infiniteQuery.hasNextPage.value ? meta.next : null }
286
+ };
287
+ return AsyncResult.ok(response);
288
+ });
289
+ function fetchNextPage() {
290
+ if (!infiniteQuery.hasNextPage.value || infiniteQuery.isFetchingNextPage.value) return;
291
+ return infiniteQuery.fetchNextPage();
292
+ }
293
+ return {
294
+ hasNextPage: computed(() => infiniteQuery.hasNextPage.value),
295
+ isError: computed(() => hasError.value),
296
+ isFetching: computed(() => infiniteQuery.isFetching.value),
297
+ isFetchingNextPage: computed(() => infiniteQuery.isFetchingNextPage.value),
298
+ isLoading: computed(() => infiniteQuery.isLoading.value),
299
+ isSuccess: computed(() => !hasError.value),
300
+ fetchNextPage: async () => {
301
+ await fetchNextPage();
302
+ },
303
+ refetch: async () => {
304
+ await infiniteQuery.refetch();
305
+ },
306
+ result
307
+ };
308
+ }
309
+
310
+ //#endregion
311
+ //#region src/composables/query/offsetInfiniteQuery.composable.ts
312
+ const DEFAULT_LIMIT = QUERY_CONFIG.limit;
313
+ function useOffsetInfiniteQuery(key, options) {
314
+ const queryKey = [key, options.params];
315
+ const infiniteQuery = useInfiniteQuery({
316
+ staleTime: options.staleTime,
317
+ enabled: options.isEnabled,
318
+ getNextPageParam: (lastPage) => {
319
+ if (lastPage.isErr() || lastPage.isLoading()) return null;
320
+ const total = lastPage.getValue().meta.offset + lastPage.getValue().meta.limit;
321
+ if (total >= lastPage.getValue().meta.total) return null;
322
+ return total;
323
+ },
324
+ initialPageParam: 0,
325
+ placeholderData: (data) => data,
326
+ queryFn: async ({ pageParam }) => AsyncResult.fromResult(await options.queryFn({
327
+ limit: options.limit ?? DEFAULT_LIMIT,
328
+ offset: pageParam ?? 0
329
+ })),
330
+ queryKey
331
+ });
332
+ const hasError = computed(() => {
333
+ return Boolean(infiniteQuery.data.value?.pages.find((page) => page.isErr()));
334
+ });
335
+ const result = computed(() => {
336
+ if (infiniteQuery.isLoading.value) return AsyncResult.loading();
337
+ const firstError = infiniteQuery.data.value?.pages.find((page) => page.isErr());
338
+ if (firstError) return AsyncResult.err(firstError.getError());
339
+ const data = infiniteQuery.data.value?.pages.filter((page) => page.isOk()).flatMap((page) => page.getValue().data) ?? [];
340
+ const firstPage = infiniteQuery.data.value?.pages[0];
341
+ const meta = firstPage?.isOk() ? firstPage.getValue().meta : null;
342
+ const response = {
343
+ data,
344
+ meta: {
345
+ limit: meta?.limit ?? 0,
346
+ offset: meta?.offset ?? 0,
347
+ total: meta?.total ?? data.length
348
+ }
349
+ };
350
+ return AsyncResult.ok(response);
351
+ });
352
+ function fetchNextPage() {
353
+ if (!infiniteQuery.hasNextPage.value || infiniteQuery.isFetchingNextPage.value) return;
354
+ return infiniteQuery.fetchNextPage();
355
+ }
356
+ return {
357
+ hasNextPage: computed(() => infiniteQuery.hasNextPage.value),
358
+ isError: computed(() => hasError.value),
359
+ isFetching: computed(() => infiniteQuery.isFetching.value),
360
+ isFetchingNextPage: computed(() => infiniteQuery.isFetchingNextPage.value),
361
+ isLoading: computed(() => infiniteQuery.isLoading.value),
362
+ isSuccess: computed(() => !hasError.value),
363
+ fetchNextPage: async () => {
364
+ await fetchNextPage();
365
+ },
366
+ refetch: async () => {
367
+ await infiniteQuery.refetch();
368
+ },
369
+ result
370
+ };
371
+ }
372
+
373
+ //#endregion
374
+ //#region src/composables/query/prefetchKeysetInfiniteQuery.composable.ts
375
+ function usePrefetchKeysetInfiniteQuery(key, options) {
376
+ const queryClient = useQueryClient();
377
+ const params = options.params;
378
+ async function execute() {
379
+ await queryClient.prefetchInfiniteQuery({
380
+ staleTime: options.staleTime ?? QUERY_CONFIG.prefetchStaleTime,
381
+ getNextPageParam: (lastPage) => {
382
+ if (!lastPage.isOk()) return null;
383
+ const next = lastPage.getValue().meta.next;
384
+ return next === null || next === void 0 ? null : next;
385
+ },
386
+ initialPageParam: void 0,
387
+ queryFn: async ({ pageParam }) => AsyncResult.fromResult(await options.queryFn({
388
+ key: pageParam,
389
+ limit: options.limit ?? QUERY_CONFIG.limit
390
+ })),
391
+ queryKey: [key, params]
392
+ });
393
+ }
394
+ return { execute };
395
+ }
396
+
397
+ //#endregion
398
+ //#region src/composables/query/prefetchOffsetInfiniteQuery.composable.ts
399
+ function usePrefetchOffsetInfiniteQuery(key, options) {
400
+ const queryClient = useQueryClient();
401
+ const params = options.params;
402
+ async function execute() {
403
+ await queryClient.prefetchInfiniteQuery({
404
+ staleTime: options.staleTime ?? QUERY_CONFIG.prefetchStaleTime,
405
+ getNextPageParam: (lastPage) => {
406
+ if (!lastPage.isOk()) return null;
407
+ const total = lastPage.getValue().meta.offset + lastPage.getValue().meta.limit;
408
+ if (total >= lastPage.getValue().meta.total) return null;
409
+ return total;
410
+ },
411
+ initialPageParam: 0,
412
+ queryFn: async ({ pageParam }) => AsyncResult.fromResult(await options.queryFn({
413
+ limit: options.limit ?? QUERY_CONFIG.limit,
414
+ offset: pageParam ?? 0
415
+ })),
416
+ queryKey: [key, params]
417
+ });
418
+ }
419
+ return { execute };
420
+ }
421
+
422
+ //#endregion
423
+ //#region src/composables/query/prefetchQuery.composable.ts
424
+ function usePrefetchQuery(key, options) {
425
+ const queryClient = useQueryClient();
426
+ const params = options.params;
427
+ async function execute() {
428
+ await queryClient.prefetchQuery({
429
+ staleTime: options.staleTime ?? QUERY_CONFIG.prefetchStaleTime,
430
+ queryFn: async () => AsyncResult.fromResult(await options.queryFn()),
431
+ queryKey: [key, params]
432
+ });
433
+ }
434
+ return { execute };
435
+ }
436
+
437
+ //#endregion
438
+ //#region src/composables/query/query.composable.ts
439
+ function useQuery(key, options) {
440
+ const isDebug = options.isDebug ?? false;
441
+ const params = options.params;
442
+ const query = useQuery$1({
443
+ staleTime: options.staleTime,
444
+ enabled: options.isEnabled,
445
+ placeholderData: (data) => data,
446
+ queryFn: async () => {
447
+ return AsyncResult.fromResult(await options.queryFn());
448
+ },
449
+ queryKey: [key, params]
450
+ });
451
+ if (isDebug) console.debug(`Create query with key ${String(key)}`, params);
452
+ async function refetch() {
453
+ await query.refetch();
454
+ }
455
+ return {
456
+ isError: computed(() => query.data.value?.isErr() ?? false),
457
+ isFetching: computed(() => query.isFetching.value),
458
+ isLoading: computed(() => query.isLoading.value),
459
+ isSuccess: computed(() => query.data.value?.isOk() ?? false),
460
+ refetch,
461
+ result: computed(() => {
462
+ if (query.isLoading.value) return AsyncResult.loading();
463
+ if (query.data.value?.isOk()) return AsyncResult.ok(query.data.value.getValue());
464
+ if (query.data.value?.isErr()) return AsyncResult.err(query.data.value.getError());
465
+ return AsyncResult.loading();
466
+ })
467
+ };
468
+ }
469
+
470
+ //#endregion
471
+ //#region src/plugin/apiUtilsPlugin.ts
472
+ /**
473
+ * Create a Vue plugin that sets up TanStack Query and initializes API utilities.
474
+ *
475
+ * This plugin handles:
476
+ * - Creating a QueryClient with the provided config
477
+ * - Installing VueQueryPlugin on the app
478
+ * - Initializing the global QueryClient for api-utils
479
+ *
480
+ * @example
481
+ * ```typescript
482
+ * import { apiUtilsPlugin } from '@wisemen/vue-core-api-utils'
483
+ * import { vueQueryClientConfig } from '@wisemen/vue-core-configs'
484
+ *
485
+ * app.use(apiUtilsPlugin(vueQueryClientConfig()))
486
+ * ```
487
+ *
488
+ * @param config - QueryClient configuration
489
+ * @returns A Vue plugin that can be used with app.use()
490
+ */
491
+ function apiUtilsPlugin(config) {
492
+ const queryClient = new QueryClient$1(config);
493
+ return { install: (app) => {
494
+ app.use(VueQueryPlugin, { queryClient });
495
+ initializeApiUtils(queryClient);
496
+ } };
497
+ }
498
+
499
+ //#endregion
500
+ //#region src/utils/api-error/apiError.util.ts
501
+ var ApiErrorUtil = class ApiErrorUtil {
502
+ static getApiErrorCode(error) {
503
+ return error.errors?.[0]?.code ?? null;
504
+ }
505
+ static getApiErrorMessage(error) {
506
+ return error.errors?.[0]?.detail ?? null;
507
+ }
508
+ static getMessage(error) {
509
+ return error.errors?.[0]?.detail ?? null;
510
+ }
511
+ static handleApiError({ error, message }) {
512
+ if (ApiErrorUtil.isExpectedApiError(error)) return error;
513
+ console.error(`Unexpected API error: ${error}`);
514
+ if (error instanceof Error) return error;
515
+ return new Error(message ?? "An unknown error occurred");
516
+ }
517
+ static isExpectedApiError(error) {
518
+ return error?.errors !== void 0;
519
+ }
520
+ static isZodError(error) {
521
+ return error instanceof z.ZodError;
522
+ }
523
+ };
524
+
525
+ //#endregion
526
+ //#region src/utils/api/api.util.ts
527
+ var ApiUtil = class ApiUtil {
528
+ static async fromPromise(promise, message) {
529
+ return await ResultAsync.fromPromise(promise, (error) => {
530
+ return ApiErrorUtil.handleApiError({
531
+ error,
532
+ message
533
+ });
534
+ });
535
+ }
536
+ static getKeysetPaginationNextOffset(keysetPaginationMeta) {
537
+ return keysetPaginationMeta.next?.offset ?? null;
538
+ }
539
+ static getResultError(result) {
540
+ if (result === null) return null;
541
+ if (!result.isErr()) return null;
542
+ if (ApiUtil.isAsyncResult(result)) return result.getError();
543
+ return result.error;
544
+ }
545
+ static isAsyncResult(value) {
546
+ return value.getResult !== void 0;
547
+ }
548
+ static void(result) {
549
+ if (result.isErr()) return err(result.error);
550
+ return ok();
551
+ }
552
+ };
553
+
554
+ //#endregion
555
+ //#region src/utils/query-client/queryClient.ts
556
+ /**
557
+ * QueryClient utility class for type-safe query operations
558
+ */
559
+ var QueryClient = class {
560
+ constructor(queryClient) {
561
+ this.queryClient = queryClient;
562
+ }
563
+ /**
564
+ * Extract the raw entity from AsyncResult data
565
+ */
566
+ extractEntityFromAsyncResult(data) {
567
+ if (data.isOk()) return data.getValue();
568
+ return null;
569
+ }
570
+ hasDataArray(value) {
571
+ return Boolean(value && typeof value === "object" && Array.isArray(value.data));
572
+ }
573
+ isInfiniteDataLike(data) {
574
+ return Boolean(data && typeof data === "object" && "pages" in data && Array.isArray(data.pages));
575
+ }
576
+ /**
577
+ * Determine if an item should be updated
578
+ */
579
+ shouldUpdateItem(by, item) {
580
+ return by(item);
581
+ }
582
+ /**
583
+ * Internal method to update entity based on the "by" option
584
+ */
585
+ updateEntity(by, currentData, value) {
586
+ if (Array.isArray(currentData)) return currentData.map((item) => {
587
+ return this.shouldUpdateItem(by, item) ? value(item) : item;
588
+ });
589
+ if (this.shouldUpdateItem(by, currentData)) return value(currentData);
590
+ return currentData;
591
+ }
592
+ updateInfinitePageValue(by, value, pageValue) {
593
+ if (!this.hasDataArray(pageValue)) return pageValue;
594
+ return {
595
+ ...pageValue,
596
+ data: this.updateEntity(by, pageValue.data, value)
597
+ };
598
+ }
599
+ /**
600
+ * Wrap a raw entity in an AsyncResult (preserving ok state)
601
+ */
602
+ wrapEntityInAsyncResult(entity) {
603
+ return AsyncResult.ok(entity);
604
+ }
605
+ get(queryKey, options) {
606
+ if (Array.isArray(queryKey)) {
607
+ const data = this.queryClient.getQueryData(queryKey);
608
+ if (data == null) return null;
609
+ return this.extractEntityFromAsyncResult(data);
610
+ }
611
+ if (options?.isExact ?? false) {
612
+ const normalizedKey = [queryKey];
613
+ const data = this.queryClient.getQueryData(normalizedKey);
614
+ if (data == null) return null;
615
+ return this.extractEntityFromAsyncResult(data);
616
+ }
617
+ const allQueries = this.queryClient.getQueryCache().findAll({ predicate: (query) => {
618
+ return query.queryKey[0] === queryKey;
619
+ } });
620
+ const results = [];
621
+ for (const query of allQueries) {
622
+ const data = query.state.data;
623
+ const entity = this.extractEntityFromAsyncResult(data);
624
+ if (entity !== null) results.push(entity);
625
+ }
626
+ return results;
627
+ }
628
+ async invalidate(keyOrTuple) {
629
+ const isSpecific = Array.isArray(keyOrTuple);
630
+ const key = isSpecific ? keyOrTuple[0] : keyOrTuple;
631
+ const params = isSpecific ? keyOrTuple[1] : null;
632
+ await this.queryClient.invalidateQueries({ predicate: (query) => {
633
+ const queryKey = query.queryKey;
634
+ if (queryKey[0] !== key) return false;
635
+ if (isSpecific && params && queryKey[1]) return Object.entries(params).every(([paramKey, paramValue]) => {
636
+ return queryKey[1][paramKey] === paramValue;
637
+ });
638
+ return !isSpecific;
639
+ } });
640
+ }
641
+ set(queryKey, entity) {
642
+ const wrappedData = this.wrapEntityInAsyncResult(entity);
643
+ const normalizedKey = Array.isArray(queryKey) ? queryKey : [queryKey];
644
+ this.queryClient.setQueryData(normalizedKey, wrappedData);
645
+ }
646
+ update(keyOrTuple, options) {
647
+ const by = options.by;
648
+ const value = options.value;
649
+ const isSpecific = Array.isArray(keyOrTuple);
650
+ const key = isSpecific ? keyOrTuple[0] : keyOrTuple;
651
+ const params = isSpecific ? keyOrTuple[1] : null;
652
+ const queries = this.queryClient.getQueryCache().findAll({ predicate: (query) => {
653
+ const queryKey = query.queryKey;
654
+ if (queryKey[0] !== key) return false;
655
+ if (isSpecific && params && queryKey[1]) return Object.entries(params).every(([paramKey, paramValue]) => {
656
+ return queryKey[1][paramKey] === paramValue;
657
+ });
658
+ return !isSpecific;
659
+ } });
660
+ const snapshots = /* @__PURE__ */ new Map();
661
+ for (const query of queries) {
662
+ const currentData = query.state.data;
663
+ if (this.isInfiniteDataLike(currentData)) {
664
+ snapshots.set(query.queryKey, currentData);
665
+ const updatedInfiniteData = {
666
+ ...currentData,
667
+ pages: currentData.pages.map((page) => {
668
+ if (!isAsyncResult(page)) return page;
669
+ return page.map((pageValue) => this.updateInfinitePageValue(by, value, pageValue));
670
+ })
671
+ };
672
+ this.queryClient.setQueryData(query.queryKey, updatedInfiniteData);
673
+ continue;
674
+ }
675
+ if (!isAsyncResult(currentData)) continue;
676
+ const rawEntity = this.extractEntityFromAsyncResult(currentData);
677
+ if (rawEntity === null) continue;
678
+ snapshots.set(query.queryKey, currentData);
679
+ const updatedEntity = this.updateEntity(by, rawEntity, value);
680
+ const wrappedData = this.wrapEntityInAsyncResult(updatedEntity);
681
+ this.queryClient.setQueryData(query.queryKey, wrappedData);
682
+ }
683
+ let rolledBack = false;
684
+ return { rollback: () => {
685
+ if (rolledBack) return;
686
+ rolledBack = true;
687
+ for (const [queryKey, data] of snapshots) this.queryClient.setQueryData(queryKey, data);
688
+ } };
689
+ }
690
+ };
691
+
692
+ //#endregion
693
+ //#region src/utils/sort/sort.utils.ts
694
+ var SortUtil = class {
695
+ static toDto(sort, sortKeyMap) {
696
+ return sort.filter((s) => s.direction !== null).map((s) => ({
697
+ key: sortKeyMap[s.key],
698
+ order: s.direction === "asc" ? "asc" : "desc"
699
+ }));
700
+ }
701
+ };
702
+
703
+ //#endregion
704
+ export { ApiErrorUtil, ApiUtil, AsyncResult, AsyncResultErr, AsyncResultLoading, AsyncResultOk, QueryClient, SortUtil, apiUtilsPlugin, getQueryClient as getTanstackQueryClient, initializeApiUtils, setQueryConfig, useKeysetInfiniteQuery, useMutation, useOffsetInfiniteQuery, usePrefetchKeysetInfiniteQuery, usePrefetchOffsetInfiniteQuery, usePrefetchQuery, useQuery };
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public"
5
5
  },
6
6
  "type": "module",
7
- "version": "2.0.2",
7
+ "version": "2.0.3",
8
8
  "license": "SEE LICENSE IN LICENSE.md",
9
9
  "repository": {
10
10
  "type": "git",