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