@krymskyimaksym/react-api-client 2.0.0-beta.2 → 2.1.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/README.md +143 -8
- package/dist/index.d.mts +252 -59
- package/dist/index.d.ts +252 -59
- package/dist/index.js +377 -57
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +372 -59
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext, useCallback, useState, useEffect, useMemo, createElement, useContext
|
|
1
|
+
import { createContext, useCallback, useState, useEffect, useRef, useMemo, createElement, useContext } from 'react';
|
|
2
2
|
|
|
3
3
|
// src/config.ts
|
|
4
4
|
var globalConfig = null;
|
|
@@ -110,6 +110,19 @@ async function executeRequest(endpoint, fetchConfig, params, signal) {
|
|
|
110
110
|
signal
|
|
111
111
|
});
|
|
112
112
|
}
|
|
113
|
+
if (config.responseAdapter) {
|
|
114
|
+
const adapter = config.responseAdapter;
|
|
115
|
+
if (adapter.isBusinessError?.(response)) {
|
|
116
|
+
const err = adapter.toError?.(response, 200) ?? new ApiError({
|
|
117
|
+
message: "Business error",
|
|
118
|
+
status: 200,
|
|
119
|
+
raw: response
|
|
120
|
+
});
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
const unwrapped = adapter.unwrap ? adapter.unwrap(response) : response;
|
|
124
|
+
return unwrapped;
|
|
125
|
+
}
|
|
113
126
|
const hasExplicitStatus = response && typeof response === "object" && "status" in response && typeof response.status === "boolean";
|
|
114
127
|
if (config.throwOnError && hasExplicitStatus && response.status === false) {
|
|
115
128
|
throw businessErrorToApiError(response);
|
|
@@ -127,6 +140,13 @@ async function executeRequest(endpoint, fetchConfig, params, signal) {
|
|
|
127
140
|
if (httpStatus === 401 && config.onUnauthorized) {
|
|
128
141
|
await config.onUnauthorized();
|
|
129
142
|
}
|
|
143
|
+
if (config.responseAdapter) {
|
|
144
|
+
const adapter = config.responseAdapter;
|
|
145
|
+
const body = error.response?.data ?? void 0;
|
|
146
|
+
const status = httpStatus ?? 0;
|
|
147
|
+
const err = adapter.toError?.(body, status) ?? toApiError(e);
|
|
148
|
+
throw err;
|
|
149
|
+
}
|
|
130
150
|
if (config.throwOnError) {
|
|
131
151
|
throw toApiError(e);
|
|
132
152
|
}
|
|
@@ -366,6 +386,7 @@ var QueryCache = class {
|
|
|
366
386
|
isStale: false
|
|
367
387
|
};
|
|
368
388
|
this.notify(entry);
|
|
389
|
+
return next;
|
|
369
390
|
}
|
|
370
391
|
/**
|
|
371
392
|
* Подписка на изменения ключа. Возвращает unsubscribe.
|
|
@@ -489,10 +510,16 @@ var QueryCache = class {
|
|
|
489
510
|
return Promise.all(promises).then(() => void 0);
|
|
490
511
|
}
|
|
491
512
|
/**
|
|
492
|
-
* Отменяет
|
|
493
|
-
*
|
|
494
|
-
*
|
|
495
|
-
*
|
|
513
|
+
* Отменяет inflight-запрос: пробрасывает abort через `AbortSignal`
|
|
514
|
+
* в `queryFn` (т.е. в `executeRequest` → `httpClient`) и отвязывает
|
|
515
|
+
* результат от ключа.
|
|
516
|
+
*
|
|
517
|
+
* Если `httpClient` уважает `signal` (`fetch` нативный, axios v1+
|
|
518
|
+
* с `signal`, и т.п.) — HTTP-запрос реально прерывается. Если
|
|
519
|
+
* игнорирует — поведение деградирует: запрос продолжит исполняться,
|
|
520
|
+
* но его ответ уже не попадёт в кэш и подписчиков не уведомит.
|
|
521
|
+
*
|
|
522
|
+
* Полезно при размонтировании / переключении страниц / logout'е.
|
|
496
523
|
*/
|
|
497
524
|
cancelQueries(predicate) {
|
|
498
525
|
const match = typeof predicate === "function" ? predicate : (k) => matchQueryKey(predicate, k);
|
|
@@ -580,6 +607,63 @@ var QueryCache = class {
|
|
|
580
607
|
}
|
|
581
608
|
};
|
|
582
609
|
|
|
610
|
+
// src/query/mutation-counter.ts
|
|
611
|
+
var MutationCounter = class {
|
|
612
|
+
constructor() {
|
|
613
|
+
this.active = /* @__PURE__ */ new Map();
|
|
614
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
615
|
+
}
|
|
616
|
+
start(scope, controller) {
|
|
617
|
+
const id = Symbol("mutation");
|
|
618
|
+
this.active.set(id, { scope, controller: controller ?? new AbortController() });
|
|
619
|
+
this.notify();
|
|
620
|
+
return id;
|
|
621
|
+
}
|
|
622
|
+
stop(id) {
|
|
623
|
+
if (this.active.delete(id)) this.notify();
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Отменяет inflight-мутации. Без predicate — все.
|
|
627
|
+
* С predicate (префикс QueryKey или функция-предикат scope'а) —
|
|
628
|
+
* только матчинг.
|
|
629
|
+
*/
|
|
630
|
+
cancel(predicate) {
|
|
631
|
+
for (const rec of this.active.values()) {
|
|
632
|
+
if (!predicate) {
|
|
633
|
+
rec.controller.abort();
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
if (typeof predicate === "function") {
|
|
637
|
+
if (predicate(rec.scope)) rec.controller.abort();
|
|
638
|
+
} else if (rec.scope && matchQueryKey(predicate, rec.scope)) {
|
|
639
|
+
rec.controller.abort();
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
count(predicate) {
|
|
644
|
+
if (!predicate) return this.active.size;
|
|
645
|
+
let n = 0;
|
|
646
|
+
for (const rec of this.active.values()) {
|
|
647
|
+
if (typeof predicate === "function") {
|
|
648
|
+
if (predicate(rec.scope)) n++;
|
|
649
|
+
} else {
|
|
650
|
+
if (rec.scope && matchQueryKey(predicate, rec.scope)) n++;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
return n;
|
|
654
|
+
}
|
|
655
|
+
subscribe(listener) {
|
|
656
|
+
this.listeners.add(listener);
|
|
657
|
+
return () => {
|
|
658
|
+
this.listeners.delete(listener);
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
notify() {
|
|
662
|
+
for (const l of this.listeners) l();
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
var mutationCounter = new MutationCounter();
|
|
666
|
+
|
|
583
667
|
// src/query/client.ts
|
|
584
668
|
var QueryClient = class {
|
|
585
669
|
constructor(cache) {
|
|
@@ -588,8 +672,12 @@ var QueryClient = class {
|
|
|
588
672
|
getQueryData(key) {
|
|
589
673
|
return this.cache.getData(key);
|
|
590
674
|
}
|
|
675
|
+
/**
|
|
676
|
+
* Точечно патчит данные под ключом. Возвращает новое значение —
|
|
677
|
+
* удобно для «пропатчил → передал дальше».
|
|
678
|
+
*/
|
|
591
679
|
setQueryData(key, updater) {
|
|
592
|
-
this.cache.setData(key, updater);
|
|
680
|
+
return this.cache.setData(key, updater);
|
|
593
681
|
}
|
|
594
682
|
fetchQuery(key, queryFn, options) {
|
|
595
683
|
return this.cache.fetch(key, queryFn, options);
|
|
@@ -604,12 +692,37 @@ var QueryClient = class {
|
|
|
604
692
|
cancelQueries(predicate) {
|
|
605
693
|
this.cache.cancelQueries(predicate);
|
|
606
694
|
}
|
|
695
|
+
/**
|
|
696
|
+
* Отменяет inflight-мутации через `AbortSignal`. Без аргумента — все.
|
|
697
|
+
* С префиксом QueryKey или функцией-предикатом — только матчинг
|
|
698
|
+
* (scope мутации = первый ключ из её `invalidateKeys`, если задан массив).
|
|
699
|
+
*
|
|
700
|
+
* Если `httpClient` уважает `signal` — HTTP-запрос реально прерывается;
|
|
701
|
+
* иначе результат отменённой мутации просто не повлияет на UI
|
|
702
|
+
* (`useMutation` останется в текущем состоянии).
|
|
703
|
+
*
|
|
704
|
+
* Типичный кейс — logout: `client.cancelMutations()` перед очисткой
|
|
705
|
+
* сессии, чтобы поздние ответы не сработали.
|
|
706
|
+
*/
|
|
707
|
+
cancelMutations(predicate) {
|
|
708
|
+
mutationCounter.cancel(predicate);
|
|
709
|
+
}
|
|
607
710
|
refetchQueries(predicate) {
|
|
608
711
|
return this.cache.refetchQueries(predicate);
|
|
609
712
|
}
|
|
610
713
|
/**
|
|
611
|
-
* Кладёт запрос в кэш, не пробрасывая ошибки. Подходит для
|
|
612
|
-
* подгрузки следующего экрана при наведении / долгом тапе.
|
|
714
|
+
* Кладёт запрос в кэш, не пробрасывая ошибки. Подходит для
|
|
715
|
+
* оптимистичной подгрузки следующего экрана при наведении / долгом тапе.
|
|
716
|
+
*
|
|
717
|
+
* Поведение:
|
|
718
|
+
* - **`staleTime`**: учитывается. Если данные ещё свежие — запрос не
|
|
719
|
+
* отправляется, promise резолвится сразу.
|
|
720
|
+
* - **`inflight`**: если по ключу уже идёт запрос, prefetch присоединяется
|
|
721
|
+
* к нему (dedupe). Не создаёт второй HTTP-вызов.
|
|
722
|
+
* - **Подписчики**: если на ключ подписан `useFetch`, успешный prefetch
|
|
723
|
+
* обновит его `data` (через notify подписчиков). При ошибке — статус
|
|
724
|
+
* подписчика тоже обновится (`error`).
|
|
725
|
+
* - **Возвращаемый promise**: всегда успешный — ошибки не пробрасываются.
|
|
613
726
|
*/
|
|
614
727
|
prefetchQuery(key, queryFn, options) {
|
|
615
728
|
return this.cache.fetch(key, queryFn, options).then(
|
|
@@ -636,11 +749,12 @@ function createUseFetch(endpoint, fetchConfig) {
|
|
|
636
749
|
refetchOnFocus = false,
|
|
637
750
|
refetchOnAppActive = false,
|
|
638
751
|
refetchOnReconnect = false,
|
|
639
|
-
staleTime = 0,
|
|
752
|
+
staleTime = isConfigured() ? getConfig().defaultStaleTime ?? 0 : 0,
|
|
640
753
|
gcTime,
|
|
641
754
|
pollingInterval,
|
|
642
755
|
queryKey: customKey,
|
|
643
756
|
select,
|
|
757
|
+
selectIsEqual,
|
|
644
758
|
onSuccess,
|
|
645
759
|
onError
|
|
646
760
|
} = options;
|
|
@@ -757,11 +871,19 @@ function createUseFetch(endpoint, fetchConfig) {
|
|
|
757
871
|
}, [enabled, pollingInterval, runFetch]);
|
|
758
872
|
const state = cache.getState(queryKey) ?? initialState;
|
|
759
873
|
const rawData = state?.data ?? null;
|
|
874
|
+
const lastSelectedRef = useRef(null);
|
|
760
875
|
const selectedData = useMemo(() => {
|
|
761
|
-
if (rawData === null)
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
876
|
+
if (rawData === null) {
|
|
877
|
+
lastSelectedRef.current = null;
|
|
878
|
+
return null;
|
|
879
|
+
}
|
|
880
|
+
const next = select ? select(rawData) : rawData;
|
|
881
|
+
const prev = lastSelectedRef.current;
|
|
882
|
+
const isEqual = selectIsEqual ?? Object.is;
|
|
883
|
+
if (prev !== null && isEqual(prev, next)) return prev;
|
|
884
|
+
lastSelectedRef.current = next;
|
|
885
|
+
return next;
|
|
886
|
+
}, [rawData, select, selectIsEqual]);
|
|
765
887
|
const status = state?.status ?? "idle";
|
|
766
888
|
const hasData = rawData !== null;
|
|
767
889
|
const isLoading = status === "loading" && !hasData;
|
|
@@ -779,47 +901,6 @@ function createUseFetch(endpoint, fetchConfig) {
|
|
|
779
901
|
};
|
|
780
902
|
};
|
|
781
903
|
}
|
|
782
|
-
|
|
783
|
-
// src/query/mutation-counter.ts
|
|
784
|
-
var MutationCounter = class {
|
|
785
|
-
constructor() {
|
|
786
|
-
this.active = /* @__PURE__ */ new Map();
|
|
787
|
-
this.listeners = /* @__PURE__ */ new Set();
|
|
788
|
-
}
|
|
789
|
-
start(scope) {
|
|
790
|
-
const id = Symbol("mutation");
|
|
791
|
-
this.active.set(id, scope);
|
|
792
|
-
this.notify();
|
|
793
|
-
return id;
|
|
794
|
-
}
|
|
795
|
-
stop(id) {
|
|
796
|
-
if (this.active.delete(id)) this.notify();
|
|
797
|
-
}
|
|
798
|
-
count(predicate) {
|
|
799
|
-
if (!predicate) return this.active.size;
|
|
800
|
-
let n = 0;
|
|
801
|
-
for (const scope of this.active.values()) {
|
|
802
|
-
if (typeof predicate === "function") {
|
|
803
|
-
if (predicate(scope)) n++;
|
|
804
|
-
} else {
|
|
805
|
-
if (scope && matchQueryKey(predicate, scope)) n++;
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
return n;
|
|
809
|
-
}
|
|
810
|
-
subscribe(listener) {
|
|
811
|
-
this.listeners.add(listener);
|
|
812
|
-
return () => {
|
|
813
|
-
this.listeners.delete(listener);
|
|
814
|
-
};
|
|
815
|
-
}
|
|
816
|
-
notify() {
|
|
817
|
-
for (const l of this.listeners) l();
|
|
818
|
-
}
|
|
819
|
-
};
|
|
820
|
-
var mutationCounter = new MutationCounter();
|
|
821
|
-
|
|
822
|
-
// src/hooks/use-mutation.ts
|
|
823
904
|
function createUseMutation(endpoint, fetchConfig) {
|
|
824
905
|
return (options = {}) => {
|
|
825
906
|
const {
|
|
@@ -872,7 +953,8 @@ function createUseMutation(endpoint, fetchConfig) {
|
|
|
872
953
|
setIsError(false);
|
|
873
954
|
setError(null);
|
|
874
955
|
const scope = Array.isArray(invalidateKeys) && invalidateKeys.length > 0 ? invalidateKeys[0] : void 0;
|
|
875
|
-
const
|
|
956
|
+
const controller = new AbortController();
|
|
957
|
+
const mutationId = mutationCounter.start(scope, controller);
|
|
876
958
|
const endpointId = buildEndpoint(endpoint, variables);
|
|
877
959
|
callLogger("onMutationStart", endpointId, variables);
|
|
878
960
|
let context;
|
|
@@ -881,7 +963,7 @@ function createUseMutation(endpoint, fetchConfig) {
|
|
|
881
963
|
const ctx = await onMutate(variables);
|
|
882
964
|
context = ctx;
|
|
883
965
|
}
|
|
884
|
-
const result = await executeRequest(endpoint, fetchConfig, variables);
|
|
966
|
+
const result = await executeRequest(endpoint, fetchConfig, variables, controller.signal);
|
|
885
967
|
setData(result);
|
|
886
968
|
if (result.status) {
|
|
887
969
|
setIsSuccess(true);
|
|
@@ -957,10 +1039,12 @@ function createUsePaginate(endpoint, fetchConfig, options) {
|
|
|
957
1039
|
enabled = true,
|
|
958
1040
|
initialPage = 1,
|
|
959
1041
|
initialLimit = 20,
|
|
960
|
-
staleTime = 0,
|
|
1042
|
+
staleTime = isConfigured() ? getConfig().defaultStaleTime ?? 0 : 0,
|
|
961
1043
|
gcTime,
|
|
962
1044
|
keepPreviousData = false,
|
|
963
1045
|
queryKey: customKey,
|
|
1046
|
+
select,
|
|
1047
|
+
selectIsEqual,
|
|
964
1048
|
onSuccess,
|
|
965
1049
|
onError
|
|
966
1050
|
} = hookOptions;
|
|
@@ -1061,7 +1145,16 @@ function createUsePaginate(endpoint, fetchConfig, options) {
|
|
|
1061
1145
|
const currentResult = currentState?.data;
|
|
1062
1146
|
const usingPlaceholder = keepPreviousData && !currentResult && previousPageKeyRef.current !== null;
|
|
1063
1147
|
const effectiveResult = usingPlaceholder ? cache.getData(previousPageKeyRef.current) : currentResult;
|
|
1064
|
-
const
|
|
1148
|
+
const rawData = effectiveResult && effectiveResult.status ? dataExtractor(effectiveResult) : [];
|
|
1149
|
+
const lastSelectedRef = useRef(null);
|
|
1150
|
+
const data = useMemo(() => {
|
|
1151
|
+
const next = select ? select(rawData) : rawData;
|
|
1152
|
+
const prev = lastSelectedRef.current;
|
|
1153
|
+
const isEqual = selectIsEqual ?? Object.is;
|
|
1154
|
+
if (prev !== null && isEqual(prev, next)) return prev;
|
|
1155
|
+
lastSelectedRef.current = next;
|
|
1156
|
+
return next;
|
|
1157
|
+
}, [rawData, select, selectIsEqual]);
|
|
1065
1158
|
const totalCount = effectiveResult && effectiveResult.status ? totalExtractor(effectiveResult) : null;
|
|
1066
1159
|
const total = totalCount;
|
|
1067
1160
|
const totalPages = totalCount !== null ? Math.ceil(totalCount / limit) : null;
|
|
@@ -1150,6 +1243,226 @@ function useIsMutating(predicate) {
|
|
|
1150
1243
|
}, [get]);
|
|
1151
1244
|
return count;
|
|
1152
1245
|
}
|
|
1246
|
+
function useQuery(queryKey, queryFn, options = {}) {
|
|
1247
|
+
const {
|
|
1248
|
+
enabled = true,
|
|
1249
|
+
refetchOnMount = true,
|
|
1250
|
+
refetchOnFocus = false,
|
|
1251
|
+
refetchOnAppActive = false,
|
|
1252
|
+
refetchOnReconnect = false,
|
|
1253
|
+
staleTime = 0,
|
|
1254
|
+
gcTime,
|
|
1255
|
+
pollingInterval,
|
|
1256
|
+
select,
|
|
1257
|
+
selectIsEqual
|
|
1258
|
+
} = options;
|
|
1259
|
+
const client = getQueryClient();
|
|
1260
|
+
const cache = client.cache;
|
|
1261
|
+
const [, forceRender] = useState(0);
|
|
1262
|
+
const rerender = useCallback(() => forceRender((v) => v + 1), []);
|
|
1263
|
+
useEffect(() => {
|
|
1264
|
+
const unsub = cache.subscribe(queryKey, rerender);
|
|
1265
|
+
return () => {
|
|
1266
|
+
unsub();
|
|
1267
|
+
const state2 = cache._debugEntries().get(hashQueryKey(queryKey));
|
|
1268
|
+
if (state2 && state2.subscribers.size === 0 && state2.inflight) {
|
|
1269
|
+
cache.cancelQueries(queryKey);
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
}, [cache, hashQueryKey(queryKey), rerender]);
|
|
1273
|
+
const runFetch = useCallback(
|
|
1274
|
+
async (force) => {
|
|
1275
|
+
callLogger("onFetchStart", queryKey);
|
|
1276
|
+
try {
|
|
1277
|
+
const data = await cache.fetch(queryKey, queryFn, {
|
|
1278
|
+
staleTime,
|
|
1279
|
+
gcTime,
|
|
1280
|
+
force
|
|
1281
|
+
});
|
|
1282
|
+
callLogger("onFetchSuccess", queryKey, data);
|
|
1283
|
+
} catch (err) {
|
|
1284
|
+
callLogger("onFetchError", queryKey, err);
|
|
1285
|
+
}
|
|
1286
|
+
},
|
|
1287
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1288
|
+
[cache, hashQueryKey(queryKey), queryFn, staleTime, gcTime]
|
|
1289
|
+
);
|
|
1290
|
+
useEffect(() => {
|
|
1291
|
+
if (!enabled || !refetchOnMount) return;
|
|
1292
|
+
void runFetch(false);
|
|
1293
|
+
}, [enabled, refetchOnMount, runFetch]);
|
|
1294
|
+
const stateForEffect = cache.getState(queryKey);
|
|
1295
|
+
const isStale = stateForEffect?.isStale ?? false;
|
|
1296
|
+
const hasFetchedData = stateForEffect?.data !== void 0;
|
|
1297
|
+
useEffect(() => {
|
|
1298
|
+
if (!enabled) return;
|
|
1299
|
+
if (isStale && hasFetchedData) void runFetch(true);
|
|
1300
|
+
}, [enabled, isStale, hasFetchedData, runFetch]);
|
|
1301
|
+
useEffect(() => {
|
|
1302
|
+
if (!enabled) return;
|
|
1303
|
+
if (!refetchOnFocus && !refetchOnAppActive) return;
|
|
1304
|
+
const unsub = focusManager.subscribe((focused) => {
|
|
1305
|
+
if (focused) void runFetch(false);
|
|
1306
|
+
});
|
|
1307
|
+
return unsub;
|
|
1308
|
+
}, [enabled, refetchOnFocus, refetchOnAppActive, runFetch]);
|
|
1309
|
+
useEffect(() => {
|
|
1310
|
+
if (!enabled || !refetchOnReconnect) return;
|
|
1311
|
+
const unsub = onlineManager.subscribe((online) => {
|
|
1312
|
+
if (online) void runFetch(false);
|
|
1313
|
+
});
|
|
1314
|
+
return unsub;
|
|
1315
|
+
}, [enabled, refetchOnReconnect, runFetch]);
|
|
1316
|
+
useEffect(() => {
|
|
1317
|
+
if (!enabled || !pollingInterval || pollingInterval <= 0) return;
|
|
1318
|
+
let timer = null;
|
|
1319
|
+
const start = () => {
|
|
1320
|
+
if (timer) return;
|
|
1321
|
+
timer = setInterval(() => {
|
|
1322
|
+
if (focusManager.isFocused()) void runFetch(true);
|
|
1323
|
+
}, pollingInterval);
|
|
1324
|
+
};
|
|
1325
|
+
const stop = () => {
|
|
1326
|
+
if (timer) clearInterval(timer);
|
|
1327
|
+
timer = null;
|
|
1328
|
+
};
|
|
1329
|
+
start();
|
|
1330
|
+
const unsub = focusManager.subscribe((focused) => {
|
|
1331
|
+
if (focused) start();
|
|
1332
|
+
else stop();
|
|
1333
|
+
});
|
|
1334
|
+
return () => {
|
|
1335
|
+
stop();
|
|
1336
|
+
unsub();
|
|
1337
|
+
};
|
|
1338
|
+
}, [enabled, pollingInterval, runFetch]);
|
|
1339
|
+
const state = cache.getState(queryKey);
|
|
1340
|
+
const rawData = state?.data ?? null;
|
|
1341
|
+
const lastSelectedRef = useRef(null);
|
|
1342
|
+
const selectedData = useMemo(() => {
|
|
1343
|
+
if (rawData === null) {
|
|
1344
|
+
lastSelectedRef.current = null;
|
|
1345
|
+
return null;
|
|
1346
|
+
}
|
|
1347
|
+
const next = select ? select(rawData) : rawData;
|
|
1348
|
+
const prev = lastSelectedRef.current;
|
|
1349
|
+
const isEqual = selectIsEqual ?? Object.is;
|
|
1350
|
+
if (prev !== null && isEqual(prev, next)) return prev;
|
|
1351
|
+
lastSelectedRef.current = next;
|
|
1352
|
+
return next;
|
|
1353
|
+
}, [rawData, select, selectIsEqual]);
|
|
1354
|
+
const status = state?.status ?? "idle";
|
|
1355
|
+
const hasData = rawData !== null;
|
|
1356
|
+
const isLoading = status === "loading" && !hasData;
|
|
1357
|
+
const isRefetching = status === "loading" && hasData;
|
|
1358
|
+
const error = state?.error ?? null;
|
|
1359
|
+
const refetch = useCallback(async () => {
|
|
1360
|
+
await runFetch(true);
|
|
1361
|
+
}, [runFetch]);
|
|
1362
|
+
return {
|
|
1363
|
+
data: selectedData,
|
|
1364
|
+
isLoading: enabled ? isLoading : false,
|
|
1365
|
+
isRefetching,
|
|
1366
|
+
error,
|
|
1367
|
+
refetch
|
|
1368
|
+
};
|
|
1369
|
+
}
|
|
1370
|
+
function useQueriesData(keys) {
|
|
1371
|
+
const client = getQueryClient();
|
|
1372
|
+
const get = useCallback(
|
|
1373
|
+
() => keys.map((k) => client.getQueryData(k)),
|
|
1374
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1375
|
+
[client, keys.map((k) => JSON.stringify(k)).join("|")]
|
|
1376
|
+
);
|
|
1377
|
+
const [snapshot, setSnapshot] = useState(get);
|
|
1378
|
+
useEffect(() => {
|
|
1379
|
+
setSnapshot(get());
|
|
1380
|
+
const unsubs = keys.map(
|
|
1381
|
+
(k) => client.cache.subscribe(k, () => setSnapshot(get()))
|
|
1382
|
+
);
|
|
1383
|
+
return () => {
|
|
1384
|
+
for (const u of unsubs) u();
|
|
1385
|
+
};
|
|
1386
|
+
}, [client, get]);
|
|
1387
|
+
return snapshot;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
// src/adapters.ts
|
|
1391
|
+
var isObject2 = (v) => typeof v === "object" && v !== null;
|
|
1392
|
+
var laravelAdapter = {
|
|
1393
|
+
isBusinessError: (r) => isObject2(r) && r.status === false,
|
|
1394
|
+
toError: (r, http) => {
|
|
1395
|
+
const body = isObject2(r) ? r : void 0;
|
|
1396
|
+
return new ApiError({
|
|
1397
|
+
message: (body && typeof body.message === "string" ? body.message : void 0) ?? `HTTP ${http}`,
|
|
1398
|
+
status: http,
|
|
1399
|
+
code: body && typeof body.code === "string" ? body.code : void 0,
|
|
1400
|
+
errors: body?.errors,
|
|
1401
|
+
isUnauthorized: http === 401,
|
|
1402
|
+
isValidationError: http === 422 || body?.errors !== void 0,
|
|
1403
|
+
raw: r
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
};
|
|
1407
|
+
var jsonApiAdapter = {
|
|
1408
|
+
unwrap: (r) => isObject2(r) ? r.data : r,
|
|
1409
|
+
isBusinessError: (r) => isObject2(r) && Array.isArray(r.errors) && r.errors.length > 0,
|
|
1410
|
+
toError: (r, http) => {
|
|
1411
|
+
const errors = isObject2(r) && Array.isArray(r.errors) ? r.errors : [];
|
|
1412
|
+
const first = isObject2(errors[0]) ? errors[0] : void 0;
|
|
1413
|
+
return new ApiError({
|
|
1414
|
+
message: (first && typeof first.detail === "string" ? first.detail : void 0) ?? (first && typeof first.title === "string" ? first.title : void 0) ?? `HTTP ${http}`,
|
|
1415
|
+
status: http,
|
|
1416
|
+
code: first && typeof first.code === "string" ? first.code : void 0,
|
|
1417
|
+
errors,
|
|
1418
|
+
isUnauthorized: http === 401,
|
|
1419
|
+
isValidationError: http === 422,
|
|
1420
|
+
raw: r
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
};
|
|
1424
|
+
var graphqlAdapter = {
|
|
1425
|
+
unwrap: (r) => isObject2(r) ? r.data : r,
|
|
1426
|
+
isBusinessError: (r) => isObject2(r) && Array.isArray(r.errors) && r.errors.length > 0,
|
|
1427
|
+
toError: (r, http) => {
|
|
1428
|
+
const errors = isObject2(r) && Array.isArray(r.errors) ? r.errors : [];
|
|
1429
|
+
const first = isObject2(errors[0]) ? errors[0] : void 0;
|
|
1430
|
+
return new ApiError({
|
|
1431
|
+
message: (first && typeof first.message === "string" ? first.message : void 0) ?? `HTTP ${http}`,
|
|
1432
|
+
status: http,
|
|
1433
|
+
errors,
|
|
1434
|
+
isUnauthorized: http === 401,
|
|
1435
|
+
raw: r
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
};
|
|
1439
|
+
var problemJsonAdapter = {
|
|
1440
|
+
isBusinessError: () => false,
|
|
1441
|
+
toError: (r, http) => {
|
|
1442
|
+
const body = isObject2(r) ? r : void 0;
|
|
1443
|
+
return new ApiError({
|
|
1444
|
+
message: (body && typeof body.title === "string" ? body.title : void 0) ?? (body && typeof body.detail === "string" ? body.detail : void 0) ?? `HTTP ${http}`,
|
|
1445
|
+
status: http,
|
|
1446
|
+
code: body && typeof body.type === "string" ? body.type : void 0,
|
|
1447
|
+
isUnauthorized: http === 401,
|
|
1448
|
+
isValidationError: http === 422,
|
|
1449
|
+
raw: r
|
|
1450
|
+
});
|
|
1451
|
+
}
|
|
1452
|
+
};
|
|
1453
|
+
var plainAdapter = {
|
|
1454
|
+
isBusinessError: () => false,
|
|
1455
|
+
toError: (r, http) => {
|
|
1456
|
+
const body = isObject2(r) ? r : void 0;
|
|
1457
|
+
return new ApiError({
|
|
1458
|
+
message: (body && typeof body.message === "string" ? body.message : void 0) ?? `HTTP ${http}`,
|
|
1459
|
+
status: http,
|
|
1460
|
+
isUnauthorized: http === 401,
|
|
1461
|
+
isNetworkError: http === 0,
|
|
1462
|
+
raw: r
|
|
1463
|
+
});
|
|
1464
|
+
}
|
|
1465
|
+
};
|
|
1153
1466
|
|
|
1154
1467
|
// src/query/persist.ts
|
|
1155
1468
|
function persistQueryClient(options) {
|
|
@@ -1311,6 +1624,6 @@ function apiPaginate(endpoint, fetchConfig = {}, options) {
|
|
|
1311
1624
|
}
|
|
1312
1625
|
var index_default = apiClient;
|
|
1313
1626
|
|
|
1314
|
-
export { ApiClientProvider, ApiError, QueryCache, QueryClient, apiMutation, apiPaginate, businessErrorToApiError, configureApiClient, index_default as default, focusManager, getConfig, getQueryClient, hashQueryKey, inspectCache, invalidateAll, isConfigured, matchQueryKey, onlineManager, persistQueryClient, setQueryClient, summarizeCache, toApiError, useIsFetching, useIsMutating, useQueryClient };
|
|
1627
|
+
export { ApiClientProvider, ApiError, QueryCache, QueryClient, apiMutation, apiPaginate, businessErrorToApiError, configureApiClient, index_default as default, focusManager, getConfig, getQueryClient, graphqlAdapter, hashQueryKey, inspectCache, invalidateAll, isConfigured, jsonApiAdapter, laravelAdapter, matchQueryKey, onlineManager, persistQueryClient, plainAdapter, problemJsonAdapter, setQueryClient, summarizeCache, toApiError, useIsFetching, useIsMutating, useQueriesData, useQuery, useQueryClient };
|
|
1315
1628
|
//# sourceMappingURL=index.mjs.map
|
|
1316
1629
|
//# sourceMappingURL=index.mjs.map
|