@krymskyimaksym/react-api-client 2.0.0-beta.1 → 2.0.0-beta.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/README.md +64 -0
- package/dist/index.d.mts +82 -6
- package/dist/index.d.ts +82 -6
- package/dist/index.js +160 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +160 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext,
|
|
1
|
+
import { createContext, useCallback, useState, useEffect, useMemo, createElement, useContext, useRef } from 'react';
|
|
2
2
|
|
|
3
3
|
// src/config.ts
|
|
4
4
|
var globalConfig = null;
|
|
@@ -151,6 +151,21 @@ function handleResponse(result, onSuccess, onError) {
|
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
// src/logger.ts
|
|
155
|
+
function getLogger() {
|
|
156
|
+
if (!isConfigured()) return void 0;
|
|
157
|
+
return getConfig().logger;
|
|
158
|
+
}
|
|
159
|
+
function callLogger(method, ...args) {
|
|
160
|
+
const logger = getLogger();
|
|
161
|
+
const fn = logger?.[method];
|
|
162
|
+
if (!fn) return;
|
|
163
|
+
try {
|
|
164
|
+
fn(...args);
|
|
165
|
+
} catch {
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
154
169
|
// src/query/focus-manager.ts
|
|
155
170
|
var FocusManager = class {
|
|
156
171
|
constructor() {
|
|
@@ -322,6 +337,7 @@ var QueryCache = class {
|
|
|
322
337
|
inflight: null,
|
|
323
338
|
inflightController: null,
|
|
324
339
|
gcTimer: null,
|
|
340
|
+
lastQueryFn: null,
|
|
325
341
|
staleTime: staleTime ?? DEFAULT_STALE_TIME,
|
|
326
342
|
gcTime: gcTime ?? DEFAULT_GC_TIME
|
|
327
343
|
};
|
|
@@ -392,6 +408,7 @@ var QueryCache = class {
|
|
|
392
408
|
const gcTime = options.gcTime ?? DEFAULT_GC_TIME;
|
|
393
409
|
const entry = this.ensureEntry(key, staleTime, gcTime);
|
|
394
410
|
const isFresh = entry.state.status === "success" && !entry.state.isStale && Date.now() - entry.state.updatedAt < staleTime;
|
|
411
|
+
entry.lastQueryFn = queryFn;
|
|
395
412
|
if (!options.force && isFresh && entry.state.data !== void 0) {
|
|
396
413
|
return entry.state.data;
|
|
397
414
|
}
|
|
@@ -451,6 +468,26 @@ var QueryCache = class {
|
|
|
451
468
|
}
|
|
452
469
|
return invalidated;
|
|
453
470
|
}
|
|
471
|
+
/**
|
|
472
|
+
* Перезапускает все записи, матчинг predicate, у которых сохранён
|
|
473
|
+
* `lastQueryFn` (т.е. их хоть раз кто-то загрузил через `fetch`).
|
|
474
|
+
* Возвращает promise, который резолвится когда все запросы завершились.
|
|
475
|
+
* Ошибки отдельных запросов проглатываются — общий promise успешный.
|
|
476
|
+
*/
|
|
477
|
+
refetchQueries(predicate) {
|
|
478
|
+
const match = typeof predicate === "function" ? predicate : (k) => matchQueryKey(predicate, k);
|
|
479
|
+
const promises = [];
|
|
480
|
+
for (const entry of this.entries.values()) {
|
|
481
|
+
if (!match(entry.key)) continue;
|
|
482
|
+
if (!entry.lastQueryFn) continue;
|
|
483
|
+
promises.push(
|
|
484
|
+
this.fetch(entry.key, entry.lastQueryFn, { force: true }).catch(
|
|
485
|
+
() => void 0
|
|
486
|
+
)
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
return Promise.all(promises).then(() => void 0);
|
|
490
|
+
}
|
|
454
491
|
/**
|
|
455
492
|
* Отменяет «привязку» inflight-промиса к ключу. Сам HTTP-запрос
|
|
456
493
|
* продолжит исполняться (executeRequest не использует AbortSignal),
|
|
@@ -488,6 +525,19 @@ var QueryCache = class {
|
|
|
488
525
|
}
|
|
489
526
|
if (removed) this.notifyGlobal();
|
|
490
527
|
}
|
|
528
|
+
/**
|
|
529
|
+
* Количество inflight-запросов в кэше, опционально отфильтрованных
|
|
530
|
+
* по predicate. Используется `useIsFetching()` для глобального
|
|
531
|
+
* индикатора загрузки.
|
|
532
|
+
*/
|
|
533
|
+
countFetching(predicate) {
|
|
534
|
+
const match = !predicate ? () => true : typeof predicate === "function" ? predicate : (k) => matchQueryKey(predicate, k);
|
|
535
|
+
let n = 0;
|
|
536
|
+
for (const entry of this.entries.values()) {
|
|
537
|
+
if (entry.state.status === "loading" && match(entry.key)) n++;
|
|
538
|
+
}
|
|
539
|
+
return n;
|
|
540
|
+
}
|
|
491
541
|
/** Только для тестов / DevTools. */
|
|
492
542
|
_debugEntries() {
|
|
493
543
|
return this.entries;
|
|
@@ -545,7 +595,8 @@ var QueryClient = class {
|
|
|
545
595
|
return this.cache.fetch(key, queryFn, options);
|
|
546
596
|
}
|
|
547
597
|
invalidateQueries(predicate) {
|
|
548
|
-
this.cache.invalidate(predicate);
|
|
598
|
+
const hashes = this.cache.invalidate(predicate);
|
|
599
|
+
if (hashes.length > 0) callLogger("onInvalidate", hashes);
|
|
549
600
|
}
|
|
550
601
|
removeQueries(predicate) {
|
|
551
602
|
this.cache.remove(predicate);
|
|
@@ -553,6 +604,19 @@ var QueryClient = class {
|
|
|
553
604
|
cancelQueries(predicate) {
|
|
554
605
|
this.cache.cancelQueries(predicate);
|
|
555
606
|
}
|
|
607
|
+
refetchQueries(predicate) {
|
|
608
|
+
return this.cache.refetchQueries(predicate);
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Кладёт запрос в кэш, не пробрасывая ошибки. Подходит для оптимистичной
|
|
612
|
+
* подгрузки следующего экрана при наведении / долгом тапе.
|
|
613
|
+
*/
|
|
614
|
+
prefetchQuery(key, queryFn, options) {
|
|
615
|
+
return this.cache.fetch(key, queryFn, options).then(
|
|
616
|
+
() => void 0,
|
|
617
|
+
() => void 0
|
|
618
|
+
);
|
|
619
|
+
}
|
|
556
620
|
};
|
|
557
621
|
var globalClient = null;
|
|
558
622
|
function getQueryClient() {
|
|
@@ -614,12 +678,14 @@ function createUseFetch(endpoint, fetchConfig) {
|
|
|
614
678
|
const lastNotifiedRef = useRef({});
|
|
615
679
|
const runFetch = useCallback(
|
|
616
680
|
async (force) => {
|
|
681
|
+
callLogger("onFetchStart", queryKey);
|
|
617
682
|
try {
|
|
618
683
|
const data = await cache.fetch(queryKey, queryFn, {
|
|
619
684
|
staleTime,
|
|
620
685
|
gcTime,
|
|
621
686
|
force
|
|
622
687
|
});
|
|
688
|
+
callLogger("onFetchSuccess", queryKey, data);
|
|
623
689
|
if (lastNotifiedRef.current.success !== data) {
|
|
624
690
|
lastNotifiedRef.current.success = data;
|
|
625
691
|
handleResponse(
|
|
@@ -629,6 +695,7 @@ function createUseFetch(endpoint, fetchConfig) {
|
|
|
629
695
|
);
|
|
630
696
|
}
|
|
631
697
|
} catch (err) {
|
|
698
|
+
callLogger("onFetchError", queryKey, err);
|
|
632
699
|
const e = err;
|
|
633
700
|
const hash = `${e.name}:${e.message}`;
|
|
634
701
|
if (lastNotifiedRef.current.errorHash !== hash) {
|
|
@@ -712,6 +779,47 @@ function createUseFetch(endpoint, fetchConfig) {
|
|
|
712
779
|
};
|
|
713
780
|
};
|
|
714
781
|
}
|
|
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
|
|
715
823
|
function createUseMutation(endpoint, fetchConfig) {
|
|
716
824
|
return (options = {}) => {
|
|
717
825
|
const {
|
|
@@ -738,10 +846,21 @@ function createUseMutation(endpoint, fetchConfig) {
|
|
|
738
846
|
(vars, result) => {
|
|
739
847
|
if (!invalidateKeys) return;
|
|
740
848
|
const client = getQueryClient();
|
|
741
|
-
|
|
742
|
-
|
|
849
|
+
if (Array.isArray(invalidateKeys)) {
|
|
850
|
+
if (invalidateKeys.length === 0) return;
|
|
851
|
+
client.invalidateQueries(
|
|
852
|
+
(k) => invalidateKeys.some((prefix) => matchQueryKey(prefix, k))
|
|
853
|
+
);
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
const out = invalidateKeys(vars, result);
|
|
857
|
+
if (typeof out === "function") {
|
|
858
|
+
client.invalidateQueries(out);
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
if (out.length === 0) return;
|
|
743
862
|
client.invalidateQueries(
|
|
744
|
-
(k) =>
|
|
863
|
+
(k) => out.some((prefix) => matchQueryKey(prefix, k))
|
|
745
864
|
);
|
|
746
865
|
},
|
|
747
866
|
[invalidateKeys]
|
|
@@ -752,6 +871,10 @@ function createUseMutation(endpoint, fetchConfig) {
|
|
|
752
871
|
setIsSuccess(false);
|
|
753
872
|
setIsError(false);
|
|
754
873
|
setError(null);
|
|
874
|
+
const scope = Array.isArray(invalidateKeys) && invalidateKeys.length > 0 ? invalidateKeys[0] : void 0;
|
|
875
|
+
const mutationId = mutationCounter.start(scope);
|
|
876
|
+
const endpointId = buildEndpoint(endpoint, variables);
|
|
877
|
+
callLogger("onMutationStart", endpointId, variables);
|
|
755
878
|
let context;
|
|
756
879
|
try {
|
|
757
880
|
if (onMutate) {
|
|
@@ -762,6 +885,7 @@ function createUseMutation(endpoint, fetchConfig) {
|
|
|
762
885
|
setData(result);
|
|
763
886
|
if (result.status) {
|
|
764
887
|
setIsSuccess(true);
|
|
888
|
+
callLogger("onMutationSuccess", endpointId, variables, result);
|
|
765
889
|
if (setQueryData) {
|
|
766
890
|
setQueryData(getQueryClient(), variables, result);
|
|
767
891
|
}
|
|
@@ -773,6 +897,7 @@ function createUseMutation(endpoint, fetchConfig) {
|
|
|
773
897
|
const err = new Error(result.message ?? "Mutation failed");
|
|
774
898
|
setIsError(true);
|
|
775
899
|
setError(err);
|
|
900
|
+
callLogger("onMutationError", endpointId, variables, err);
|
|
776
901
|
if (onError) {
|
|
777
902
|
await onError(err, variables, context);
|
|
778
903
|
}
|
|
@@ -786,6 +911,7 @@ function createUseMutation(endpoint, fetchConfig) {
|
|
|
786
911
|
setError(error2);
|
|
787
912
|
setIsError(true);
|
|
788
913
|
setIsSuccess(false);
|
|
914
|
+
callLogger("onMutationError", endpointId, variables, error2);
|
|
789
915
|
if (onError) {
|
|
790
916
|
await onError(error2, variables, context);
|
|
791
917
|
}
|
|
@@ -794,6 +920,7 @@ function createUseMutation(endpoint, fetchConfig) {
|
|
|
794
920
|
}
|
|
795
921
|
throw error2;
|
|
796
922
|
} finally {
|
|
923
|
+
mutationCounter.stop(mutationId);
|
|
797
924
|
setIsLoading(false);
|
|
798
925
|
}
|
|
799
926
|
},
|
|
@@ -996,6 +1123,33 @@ function createUsePaginate(endpoint, fetchConfig, options) {
|
|
|
996
1123
|
};
|
|
997
1124
|
};
|
|
998
1125
|
}
|
|
1126
|
+
function useIsFetching(predicate) {
|
|
1127
|
+
const client = getQueryClient();
|
|
1128
|
+
const get = useCallback(
|
|
1129
|
+
() => client.cache.countFetching(predicate),
|
|
1130
|
+
[client.cache, predicate]
|
|
1131
|
+
);
|
|
1132
|
+
const [count, setCount] = useState(get);
|
|
1133
|
+
useEffect(() => {
|
|
1134
|
+
setCount(get());
|
|
1135
|
+
const unsub = client.cache.subscribeAll(() => setCount(get()));
|
|
1136
|
+
return unsub;
|
|
1137
|
+
}, [client.cache, get]);
|
|
1138
|
+
return count;
|
|
1139
|
+
}
|
|
1140
|
+
function useIsMutating(predicate) {
|
|
1141
|
+
const get = useCallback(
|
|
1142
|
+
() => mutationCounter.count(predicate),
|
|
1143
|
+
[predicate]
|
|
1144
|
+
);
|
|
1145
|
+
const [count, setCount] = useState(get);
|
|
1146
|
+
useEffect(() => {
|
|
1147
|
+
setCount(get());
|
|
1148
|
+
const unsub = mutationCounter.subscribe(() => setCount(get()));
|
|
1149
|
+
return unsub;
|
|
1150
|
+
}, [get]);
|
|
1151
|
+
return count;
|
|
1152
|
+
}
|
|
999
1153
|
|
|
1000
1154
|
// src/query/persist.ts
|
|
1001
1155
|
function persistQueryClient(options) {
|
|
@@ -1157,6 +1311,6 @@ function apiPaginate(endpoint, fetchConfig = {}, options) {
|
|
|
1157
1311
|
}
|
|
1158
1312
|
var index_default = apiClient;
|
|
1159
1313
|
|
|
1160
|
-
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, useQueryClient };
|
|
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 };
|
|
1161
1315
|
//# sourceMappingURL=index.mjs.map
|
|
1162
1316
|
//# sourceMappingURL=index.mjs.map
|