@rpcbase/client 0.381.0 → 0.383.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.
@@ -1,14 +1,28 @@
1
- import { createContext, useContext, useId, useState, useRef, useEffect, useMemo } from "react";
1
+ import { createContext, useContext, useId, useMemo, useState, useRef, useEffect } from "react";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  const STATIC_RPCBASE_RTS_HYDRATION_DATA_KEY = "__staticRpcbaseRtsHydrationData";
4
4
  const RtsSsrRuntimeContext = createContext(null);
5
5
  const hydrationDataStore = /* @__PURE__ */ new Map();
6
+ const hydrationPageInfoStore = /* @__PURE__ */ new Map();
6
7
  const makeStoreKey = (modelName, queryKey) => `${modelName}.${queryKey}`;
7
8
  const normalizeStringOrNull = (value) => {
8
9
  if (typeof value !== "string") return null;
9
10
  const normalized = value.trim();
10
11
  return normalized ? normalized : null;
11
12
  };
13
+ const normalizePageInfo$2 = (value) => {
14
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
15
+ const raw = value;
16
+ if (typeof raw.hasNextPage !== "boolean" || typeof raw.hasPrevPage !== "boolean") return void 0;
17
+ const nextCursor = typeof raw.nextCursor === "string" && raw.nextCursor ? raw.nextCursor : void 0;
18
+ const prevCursor = typeof raw.prevCursor === "string" && raw.prevCursor ? raw.prevCursor : void 0;
19
+ return {
20
+ hasNextPage: raw.hasNextPage,
21
+ hasPrevPage: raw.hasPrevPage,
22
+ ...nextCursor ? { nextCursor } : {},
23
+ ...prevCursor ? { prevCursor } : {}
24
+ };
25
+ };
12
26
  const parseHydrationData = (value) => {
13
27
  if (!value || typeof value !== "object") return null;
14
28
  const raw = value;
@@ -22,10 +36,12 @@ const parseHydrationData = (value) => {
22
36
  const queryKey = normalizeStringOrNull(query.queryKey);
23
37
  if (!modelName || !queryKey) continue;
24
38
  if (!Array.isArray(query.data)) continue;
39
+ const pageInfo = normalizePageInfo$2(query.pageInfo);
25
40
  queries.push({
26
41
  modelName,
27
42
  queryKey,
28
- data: query.data
43
+ data: query.data,
44
+ ...pageInfo ? { pageInfo } : {}
29
45
  });
30
46
  }
31
47
  return {
@@ -35,58 +51,34 @@ const parseHydrationData = (value) => {
35
51
  queries
36
52
  };
37
53
  };
38
- const hydrateRtsFromWindow = (identity) => {
54
+ const hydrateRtsFromWindow = () => {
39
55
  if (typeof window === "undefined") return;
40
56
  const browserWindow = window;
41
57
  const raw = browserWindow[STATIC_RPCBASE_RTS_HYDRATION_DATA_KEY];
42
58
  delete browserWindow[STATIC_RPCBASE_RTS_HYDRATION_DATA_KEY];
43
59
  const parsed = parseHydrationData(raw);
44
60
  if (!parsed) return;
45
- if (parsed.tenantId !== identity.tenantId || parsed.uid !== identity.uid) {
46
- return;
47
- }
48
61
  hydrationDataStore.clear();
62
+ hydrationPageInfoStore.clear();
49
63
  for (const query of parsed.queries) {
50
64
  hydrationDataStore.set(makeStoreKey(query.modelName, query.queryKey), query.data);
65
+ hydrationPageInfoStore.set(makeStoreKey(query.modelName, query.queryKey), query.pageInfo);
51
66
  }
52
67
  };
53
68
  const peekHydratedRtsQueryData = (modelName, queryKey) => {
54
69
  return hydrationDataStore.get(makeStoreKey(modelName, queryKey));
55
70
  };
71
+ const peekHydratedRtsQueryPageInfo = (modelName, queryKey) => {
72
+ return hydrationPageInfoStore.get(makeStoreKey(modelName, queryKey));
73
+ };
74
+ const consumeHydratedRtsQueryData = (modelName, queryKey) => {
75
+ const key = makeStoreKey(modelName, queryKey);
76
+ hydrationDataStore.delete(key);
77
+ hydrationPageInfoStore.delete(key);
78
+ };
56
79
  const clearHydratedRtsQueryData = () => {
57
80
  hydrationDataStore.clear();
58
- };
59
- const readRootLikeLoaderData = () => {
60
- if (typeof window === "undefined") return null;
61
- const hydration = window.__staticRouterHydrationData;
62
- if (!hydration || typeof hydration !== "object") return null;
63
- const loaderData = hydration.loaderData;
64
- if (!loaderData || typeof loaderData !== "object") return null;
65
- const entries = Object.entries(loaderData);
66
- let fallback = null;
67
- for (const [routeId, data] of entries) {
68
- if (!data || typeof data !== "object" || Array.isArray(data)) continue;
69
- const obj = data;
70
- const hasTenantId = Object.prototype.hasOwnProperty.call(obj, "tenantId");
71
- const hasUser = Object.prototype.hasOwnProperty.call(obj, "user");
72
- if (!hasTenantId && !hasUser) continue;
73
- if (routeId === "root") return obj;
74
- if (!fallback) fallback = obj;
75
- }
76
- return fallback;
77
- };
78
- const resolveRtsHydrationIdentityFromStaticRouterData = () => {
79
- const rootData = readRootLikeLoaderData();
80
- if (!rootData) {
81
- return { tenantId: null, uid: null };
82
- }
83
- const tenantId = normalizeStringOrNull(rootData.tenantId);
84
- const user = rootData.user;
85
- const userId = user && typeof user === "object" ? normalizeStringOrNull(user.id) : null;
86
- return {
87
- tenantId,
88
- uid: userId
89
- };
81
+ hydrationPageInfoStore.clear();
90
82
  };
91
83
  const RtsSsrRuntimeProvider = ({
92
84
  value,
@@ -426,7 +418,8 @@ const computeRtsQueryKey = (query, options) => {
426
418
  const sort = options.sort ? JSON.stringify(options.sort) : "";
427
419
  const limit = typeof options.limit === "number" ? String(options.limit) : "";
428
420
  const populate = options.populate ? JSON.stringify(options.populate) : "";
429
- return `${key}${JSON.stringify(query)}${projection}${sort}${limit}${populate}`;
421
+ const pagination = options.pagination ? JSON.stringify(options.pagination) : "";
422
+ return `${key}${JSON.stringify(query)}${projection}${sort}${limit}${populate}${pagination}`;
430
423
  };
431
424
  const TENANT_ID_QUERY_PARAM = "rb-tenant-id";
432
425
  const RTS_CHANGES_ROUTE = "/api/rb/rts/changes";
@@ -520,15 +513,31 @@ const isDocWithId = (doc) => {
520
513
  if (!doc || typeof doc !== "object") return false;
521
514
  return typeof doc._id === "string";
522
515
  };
516
+ const normalizePageInfo$1 = (value) => {
517
+ if (!value || typeof value !== "object") return void 0;
518
+ if (Array.isArray(value)) return void 0;
519
+ const raw = value;
520
+ if (typeof raw.hasNextPage !== "boolean" || typeof raw.hasPrevPage !== "boolean") return void 0;
521
+ const nextCursor = typeof raw.nextCursor === "string" && raw.nextCursor ? raw.nextCursor : void 0;
522
+ const prevCursor = typeof raw.prevCursor === "string" && raw.prevCursor ? raw.prevCursor : void 0;
523
+ return {
524
+ hasNextPage: raw.hasNextPage,
525
+ hasPrevPage: raw.hasPrevPage,
526
+ ...nextCursor ? { nextCursor } : {},
527
+ ...prevCursor ? { prevCursor } : {}
528
+ };
529
+ };
523
530
  const handleQueryPayload = (payload) => {
524
531
  const { modelName, queryKey, data, error, txnId } = payload;
525
532
  const cbKey = `${modelName}.${queryKey}`;
526
533
  const callbacks = queryCallbacks.get(cbKey);
527
534
  if (!callbacks || !callbacks.size) return;
528
535
  const subscription = subscriptions.get(cbKey);
536
+ const pageInfo = normalizePageInfo$1(payload.pageInfo);
529
537
  const hasPopulate = Boolean(subscription?.options?.populate);
538
+ const hasPagination = Boolean(subscription?.options?.pagination || pageInfo);
530
539
  const isLocal = !!(txnId && localTxnBuf.includes(txnId));
531
- const context = { source: "network", isLocal, txnId };
540
+ const context = { source: "network", isLocal, txnId, ...pageInfo ? { pageInfo } : {} };
532
541
  if (error) {
533
542
  for (const cb of callbacks) cb(error, void 0, context);
534
543
  return;
@@ -538,6 +547,7 @@ const handleQueryPayload = (payload) => {
538
547
  const docs = Array.isArray(data) ? data.filter(isDocWithId) : [];
539
548
  if (!docs.length) return;
540
549
  if (hasPopulate) return;
550
+ if (hasPagination) return;
541
551
  void updateDocs(modelName, docs, currentUid).catch(() => {
542
552
  });
543
553
  };
@@ -780,11 +790,14 @@ const registerQuery = (modelName, query, optionsOrCallback, callbackMaybe, behav
780
790
  const queryKey = computeRtsQueryKey(query, options);
781
791
  const cbKey = `${modelName}.${queryKey}`;
782
792
  const runInitialNetworkQuery = behavior?.runInitialNetworkQuery !== false;
793
+ const runInitialLocalQuery = behavior?.runInitialLocalQuery !== false;
794
+ const hasPopulate = Boolean(options.populate);
795
+ const hasPagination = Boolean(options.pagination);
783
796
  const set = queryCallbacks.get(cbKey) ?? /* @__PURE__ */ new Set();
784
797
  set.add(callback);
785
798
  queryCallbacks.set(cbKey, set);
786
799
  subscriptions.set(cbKey, { modelName, query, options, queryKey, runInitialNetworkQuery });
787
- if (currentUid) {
800
+ if (currentUid && runInitialLocalQuery && !hasPopulate && !hasPagination) {
788
801
  void runQuery({
789
802
  modelName,
790
803
  query,
@@ -810,6 +823,57 @@ const registerQuery = (modelName, query, optionsOrCallback, callbackMaybe, behav
810
823
  }
811
824
  };
812
825
  };
826
+ const makeRunQueryKey = () => `run-query.${Date.now().toString(36)}.${Math.random().toString(36).slice(2, 10)}`;
827
+ const runNetworkQuery = async ({
828
+ modelName,
829
+ query,
830
+ options = {},
831
+ timeoutMs = 1e4
832
+ }) => {
833
+ if (typeof modelName !== "string" || modelName.trim().length === 0) {
834
+ throw new Error("runNetworkQuery: modelName must be a non-empty string");
835
+ }
836
+ if (!socket || socket.readyState !== WebSocket.OPEN) {
837
+ throw new Error("runNetworkQuery: RTS socket is not connected");
838
+ }
839
+ const resolvedOptions = options.key ? options : { ...options, key: makeRunQueryKey() };
840
+ const queryKey = computeRtsQueryKey(query, resolvedOptions);
841
+ const cbKey = `${modelName}.${queryKey}`;
842
+ return await new Promise((resolve, reject) => {
843
+ let settled = false;
844
+ let timeoutId = null;
845
+ const cleanup = () => {
846
+ const callbacks2 = queryCallbacks.get(cbKey);
847
+ callbacks2?.delete(callback);
848
+ if (callbacks2 && callbacks2.size === 0) queryCallbacks.delete(cbKey);
849
+ if (timeoutId !== null && typeof window !== "undefined") {
850
+ window.clearTimeout(timeoutId);
851
+ }
852
+ };
853
+ const settle = (next) => {
854
+ if (settled) return;
855
+ settled = true;
856
+ cleanup();
857
+ next();
858
+ };
859
+ const callback = (error, data, context) => {
860
+ if (error) {
861
+ settle(() => reject(error));
862
+ return;
863
+ }
864
+ settle(() => resolve({ data, context }));
865
+ };
866
+ const callbacks = queryCallbacks.get(cbKey) ?? /* @__PURE__ */ new Set();
867
+ callbacks.add(callback);
868
+ queryCallbacks.set(cbKey, callbacks);
869
+ if (typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0) {
870
+ timeoutId = window.setTimeout(() => {
871
+ settle(() => reject(new Error("runNetworkQuery: request timed out")));
872
+ }, timeoutMs);
873
+ }
874
+ sendToServer({ type: "run-query", modelName, queryKey, query, options: resolvedOptions });
875
+ });
876
+ };
813
877
  const sendMessage = (event, payload) => {
814
878
  sendToServer({ type: "event", event, payload });
815
879
  };
@@ -823,6 +887,43 @@ const onMessage = (event, callback) => {
823
887
  if (callbacks && callbacks.size === 0) messageCallbacks.delete(event);
824
888
  };
825
889
  };
890
+ const normalizePageInfo = (value) => {
891
+ if (!value || typeof value !== "object") return void 0;
892
+ if (Array.isArray(value)) return void 0;
893
+ const raw = value;
894
+ if (typeof raw.hasNextPage !== "boolean" || typeof raw.hasPrevPage !== "boolean") return void 0;
895
+ const nextCursor = typeof raw.nextCursor === "string" && raw.nextCursor ? raw.nextCursor : void 0;
896
+ const prevCursor = typeof raw.prevCursor === "string" && raw.prevCursor ? raw.prevCursor : void 0;
897
+ return {
898
+ hasNextPage: raw.hasNextPage,
899
+ hasPrevPage: raw.hasPrevPage,
900
+ ...nextCursor ? { nextCursor } : {},
901
+ ...prevCursor ? { prevCursor } : {}
902
+ };
903
+ };
904
+ const getDocId = (doc) => {
905
+ if (!doc || typeof doc !== "object") return "";
906
+ const id = doc._id;
907
+ return typeof id === "string" ? id.trim() : "";
908
+ };
909
+ const dedupeById = (docs) => {
910
+ const seen = /* @__PURE__ */ new Set();
911
+ const merged = [];
912
+ for (const doc of docs) {
913
+ const id = getDocId(doc);
914
+ if (id && seen.has(id)) continue;
915
+ if (id) seen.add(id);
916
+ merged.push(doc);
917
+ }
918
+ return merged;
919
+ };
920
+ const flattenLoadedPages = (previousPages, headPage, nextPages) => {
921
+ const merged = [];
922
+ for (const page of previousPages) merged.push(...page.nodes);
923
+ if (headPage) merged.push(...headPage.nodes);
924
+ for (const page of nextPages) merged.push(...page.nodes);
925
+ return dedupeById(merged);
926
+ };
826
927
  const useQuery = (modelName, query = {}, options = {}) => {
827
928
  if (typeof modelName !== "string" || modelName.trim().length === 0) {
828
929
  throw new Error("useQuery: modelName must be a non-empty string");
@@ -837,12 +938,16 @@ const useQuery = (modelName, query = {}, options = {}) => {
837
938
  const sortJson = options.sort ? JSON.stringify(options.sort) : "";
838
939
  const limitStr = typeof options.limit === "number" ? String(options.limit) : "";
839
940
  const populateJson = options.populate ? JSON.stringify(options.populate) : "";
941
+ const paginationJson = options.pagination ? JSON.stringify(options.pagination) : "";
942
+ const hasPopulate = Boolean(options.populate);
943
+ const isPaginated = Boolean(options.pagination);
840
944
  const queryKey = computeRtsQueryKey(query, {
841
945
  key,
842
946
  projection: options.projection,
843
947
  sort: options.sort,
844
948
  limit: options.limit,
845
- populate: options.populate
949
+ populate: options.populate,
950
+ pagination: options.pagination
846
951
  });
847
952
  const ssrRuntime = useRtsSsrRuntime();
848
953
  if (enabled && ssrEnabled && ssrRuntime) {
@@ -854,14 +959,23 @@ const useQuery = (modelName, query = {}, options = {}) => {
854
959
  projection: options.projection,
855
960
  sort: options.sort,
856
961
  limit: options.limit,
857
- populate: options.populate
962
+ populate: options.populate,
963
+ pagination: options.pagination
858
964
  },
859
965
  queryKey
860
966
  });
861
967
  }
862
- const seedDataRaw = enabled && ssrEnabled ? ssrRuntime ? ssrRuntime.getQueryData(modelName, queryKey) : peekHydratedRtsQueryData(modelName, queryKey) : void 0;
968
+ const seedDataRaw = useMemo(
969
+ () => enabled && ssrEnabled ? ssrRuntime ? ssrRuntime.getQueryData(modelName, queryKey) : peekHydratedRtsQueryData(modelName, queryKey) : void 0,
970
+ [enabled, ssrEnabled, ssrRuntime, modelName, queryKey]
971
+ );
972
+ const seedPageInfoRaw = useMemo(
973
+ () => enabled && ssrEnabled ? ssrRuntime ? ssrRuntime.getQueryPageInfo(modelName, queryKey) : peekHydratedRtsQueryPageInfo(modelName, queryKey) : void 0,
974
+ [enabled, ssrEnabled, ssrRuntime, modelName, queryKey]
975
+ );
863
976
  const hasSeedData = Array.isArray(seedDataRaw);
864
977
  const seedData = hasSeedData ? seedDataRaw : void 0;
978
+ const seedPageInfo = normalizePageInfo(seedPageInfoRaw);
865
979
  const seedJson = (() => {
866
980
  if (!hasSeedData) return "";
867
981
  try {
@@ -870,35 +984,74 @@ const useQuery = (modelName, query = {}, options = {}) => {
870
984
  return "";
871
985
  }
872
986
  })();
873
- const [data, setData] = useState(() => seedData);
987
+ const [data, setData] = useState(() => isPaginated ? void 0 : seedData);
988
+ const [headPage, setHeadPage] = useState(() => isPaginated && seedData ? { nodes: seedData, ...seedPageInfo ? { pageInfo: seedPageInfo } : {} } : null);
989
+ const [previousPages, setPreviousPages] = useState([]);
990
+ const [nextPages, setNextPages] = useState([]);
874
991
  const [source, setSource] = useState(() => hasSeedData ? "cache" : void 0);
875
992
  const [error, setError] = useState(void 0);
876
993
  const [loading, setLoading] = useState(enabled && !hasSeedData);
994
+ const [pagingDirection, setPagingDirection] = useState(null);
877
995
  const hasFirstReply = useRef(false);
878
996
  const hasNetworkReply = useRef(false);
879
997
  const lastDataJsonRef = useRef("");
998
+ const previousPagesRef = useRef([]);
999
+ const nextPagesRef = useRef([]);
1000
+ const headPageRef = useRef(null);
1001
+ const pagingDirectionRef = useRef(null);
1002
+ useEffect(() => {
1003
+ previousPagesRef.current = previousPages;
1004
+ }, [previousPages]);
1005
+ useEffect(() => {
1006
+ nextPagesRef.current = nextPages;
1007
+ }, [nextPages]);
880
1008
  useEffect(() => {
1009
+ headPageRef.current = headPage;
1010
+ }, [headPage]);
1011
+ useEffect(() => {
1012
+ pagingDirectionRef.current = pagingDirection;
1013
+ }, [pagingDirection]);
1014
+ useEffect(() => {
1015
+ if (!ssrRuntime && enabled && ssrEnabled && hasSeedData) {
1016
+ consumeHydratedRtsQueryData(modelName, queryKey);
1017
+ }
881
1018
  hasFirstReply.current = hasSeedData;
882
1019
  hasNetworkReply.current = false;
883
1020
  lastDataJsonRef.current = seedJson;
884
1021
  setError(void 0);
1022
+ setPreviousPages([]);
1023
+ setNextPages([]);
885
1024
  if (!enabled) {
886
1025
  setLoading(false);
887
1026
  setData(void 0);
1027
+ setHeadPage(null);
888
1028
  setSource(void 0);
889
1029
  return;
890
1030
  }
891
1031
  if (hasSeedData) {
1032
+ const nextSeedData = seedData;
892
1033
  setLoading(false);
893
- setData(seedData);
894
1034
  setSource("cache");
1035
+ if (isPaginated) {
1036
+ setHeadPage({
1037
+ nodes: nextSeedData,
1038
+ ...seedPageInfo ? { pageInfo: seedPageInfo } : {}
1039
+ });
1040
+ setData(void 0);
1041
+ } else {
1042
+ setData(nextSeedData);
1043
+ setHeadPage(null);
1044
+ }
895
1045
  return;
896
1046
  }
1047
+ setData(void 0);
1048
+ setHeadPage(null);
897
1049
  setLoading(true);
898
- }, [enabled, modelName, queryKey, hasSeedData, seedData, seedJson]);
1050
+ }, [enabled, ssrEnabled, ssrRuntime, modelName, queryKey, hasSeedData, seedData, seedJson, isPaginated, seedPageInfo]);
899
1051
  useEffect(() => {
900
1052
  if (!enabled) return;
901
1053
  const runInitialNetworkQuery = refreshOnMount || !hasSeedData;
1054
+ const runInitialLocalQuery = !hasSeedData && !hasPopulate && !isPaginated;
902
1055
  const unsubscribe = registerQuery(
903
1056
  modelName,
904
1057
  query,
@@ -907,7 +1060,8 @@ const useQuery = (modelName, query = {}, options = {}) => {
907
1060
  projection: options.projection,
908
1061
  sort: options.sort,
909
1062
  limit: options.limit,
910
- populate: options.populate
1063
+ populate: options.populate,
1064
+ pagination: options.pagination
911
1065
  },
912
1066
  (err, result, context) => {
913
1067
  if (context.source === "cache" && hasNetworkReply.current) return;
@@ -924,9 +1078,11 @@ const useQuery = (modelName, query = {}, options = {}) => {
924
1078
  return;
925
1079
  }
926
1080
  hasFirstReply.current = true;
1081
+ const networkPageInfo = context.source === "network" ? context.pageInfo : void 0;
1082
+ const payloadForHash = isPaginated ? { result, pageInfo: networkPageInfo } : result;
927
1083
  let nextJson = "";
928
1084
  try {
929
- nextJson = JSON.stringify(result);
1085
+ nextJson = JSON.stringify(payloadForHash);
930
1086
  } catch {
931
1087
  nextJson = "";
932
1088
  }
@@ -936,24 +1092,174 @@ const useQuery = (modelName, query = {}, options = {}) => {
936
1092
  }
937
1093
  lastDataJsonRef.current = nextJson;
938
1094
  setSource(context.source);
1095
+ setError(void 0);
1096
+ if (isPaginated) {
1097
+ setHeadPage({
1098
+ nodes: result,
1099
+ ...networkPageInfo ? { pageInfo: networkPageInfo } : {}
1100
+ });
1101
+ return;
1102
+ }
939
1103
  setData(result);
940
1104
  },
941
1105
  {
942
- runInitialNetworkQuery
1106
+ runInitialNetworkQuery,
1107
+ runInitialLocalQuery
943
1108
  }
944
1109
  );
945
1110
  return () => {
946
1111
  unsubscribe?.();
947
1112
  };
948
- }, [enabled, modelName, queryKey, queryJson, projectionJson, sortJson, limitStr, populateJson, hasSeedData, refreshOnMount]);
1113
+ }, [
1114
+ enabled,
1115
+ modelName,
1116
+ queryKey,
1117
+ queryJson,
1118
+ projectionJson,
1119
+ sortJson,
1120
+ limitStr,
1121
+ populateJson,
1122
+ paginationJson,
1123
+ hasSeedData,
1124
+ refreshOnMount,
1125
+ isPaginated
1126
+ ]);
1127
+ const effectivePageInfo = useMemo(() => {
1128
+ if (!isPaginated) return void 0;
1129
+ const firstPageInfo = previousPages.length > 0 ? previousPages[0]?.pageInfo : headPage?.pageInfo;
1130
+ const lastPageInfo = nextPages.length > 0 ? nextPages[nextPages.length - 1]?.pageInfo : headPage?.pageInfo;
1131
+ if (!firstPageInfo && !lastPageInfo) return void 0;
1132
+ const hasPrevPage = Boolean(firstPageInfo?.hasPrevPage);
1133
+ const hasNextPage = Boolean(lastPageInfo?.hasNextPage);
1134
+ const prevCursor = firstPageInfo?.prevCursor;
1135
+ const nextCursor = lastPageInfo?.nextCursor;
1136
+ return {
1137
+ hasPrevPage,
1138
+ hasNextPage,
1139
+ ...prevCursor ? { prevCursor } : {},
1140
+ ...nextCursor ? { nextCursor } : {}
1141
+ };
1142
+ }, [headPage, isPaginated, nextPages, previousPages]);
1143
+ const mergedPaginatedData = useMemo(() => {
1144
+ if (!isPaginated) return void 0;
1145
+ if (!headPage && previousPages.length === 0 && nextPages.length === 0) return void 0;
1146
+ return flattenLoadedPages(previousPages, headPage, nextPages);
1147
+ }, [headPage, isPaginated, nextPages, previousPages]);
1148
+ const fetchNext = async () => {
1149
+ if (!enabled || !isPaginated || !options.pagination) return false;
1150
+ if (pagingDirectionRef.current) return false;
1151
+ const currentHead = headPageRef.current;
1152
+ const currentNextPages = nextPagesRef.current;
1153
+ const cursor = currentNextPages.length > 0 ? currentNextPages[currentNextPages.length - 1]?.pageInfo?.nextCursor : currentHead?.pageInfo?.nextCursor;
1154
+ const hasNextPage = currentNextPages.length > 0 ? Boolean(currentNextPages[currentNextPages.length - 1]?.pageInfo?.hasNextPage) : Boolean(currentHead?.pageInfo?.hasNextPage);
1155
+ if (!cursor || !hasNextPage) return false;
1156
+ setPagingDirection("next");
1157
+ setLoading(true);
1158
+ setError(void 0);
1159
+ try {
1160
+ const response = await runNetworkQuery({
1161
+ modelName,
1162
+ query,
1163
+ options: {
1164
+ key,
1165
+ projection: options.projection,
1166
+ sort: options.sort,
1167
+ limit: options.limit,
1168
+ populate: options.populate,
1169
+ pagination: {
1170
+ ...options.pagination,
1171
+ direction: "next",
1172
+ cursor
1173
+ }
1174
+ }
1175
+ });
1176
+ if (!Array.isArray(response.data)) return false;
1177
+ const page = {
1178
+ nodes: response.data,
1179
+ ...response.context.source === "network" && response.context.pageInfo ? { pageInfo: response.context.pageInfo } : {}
1180
+ };
1181
+ setSource("network");
1182
+ setNextPages((current) => [...current, page]);
1183
+ return true;
1184
+ } catch (err) {
1185
+ setError(err);
1186
+ return false;
1187
+ } finally {
1188
+ setPagingDirection(null);
1189
+ setLoading(false);
1190
+ }
1191
+ };
1192
+ const fetchPrevious = async () => {
1193
+ if (!enabled || !isPaginated || !options.pagination) return false;
1194
+ if (pagingDirectionRef.current) return false;
1195
+ const currentHead = headPageRef.current;
1196
+ const currentPreviousPages = previousPagesRef.current;
1197
+ const cursor = currentPreviousPages.length > 0 ? currentPreviousPages[0]?.pageInfo?.prevCursor : currentHead?.pageInfo?.prevCursor;
1198
+ const hasPrevPage = currentPreviousPages.length > 0 ? Boolean(currentPreviousPages[0]?.pageInfo?.hasPrevPage) : Boolean(currentHead?.pageInfo?.hasPrevPage);
1199
+ if (!cursor || !hasPrevPage) return false;
1200
+ setPagingDirection("prev");
1201
+ setLoading(true);
1202
+ setError(void 0);
1203
+ try {
1204
+ const response = await runNetworkQuery({
1205
+ modelName,
1206
+ query,
1207
+ options: {
1208
+ key,
1209
+ projection: options.projection,
1210
+ sort: options.sort,
1211
+ limit: options.limit,
1212
+ populate: options.populate,
1213
+ pagination: {
1214
+ ...options.pagination,
1215
+ direction: "prev",
1216
+ cursor
1217
+ }
1218
+ }
1219
+ });
1220
+ if (!Array.isArray(response.data)) return false;
1221
+ const page = {
1222
+ nodes: response.data,
1223
+ ...response.context.source === "network" && response.context.pageInfo ? { pageInfo: response.context.pageInfo } : {}
1224
+ };
1225
+ setSource("network");
1226
+ setPreviousPages((current) => [page, ...current]);
1227
+ return true;
1228
+ } catch (err) {
1229
+ setError(err);
1230
+ return false;
1231
+ } finally {
1232
+ setPagingDirection(null);
1233
+ setLoading(false);
1234
+ }
1235
+ };
1236
+ const resetPagination = () => {
1237
+ if (!isPaginated) return;
1238
+ setPreviousPages([]);
1239
+ setNextPages([]);
1240
+ };
949
1241
  return useMemo(
950
1242
  () => ({
951
- data,
1243
+ data: isPaginated ? mergedPaginatedData : data,
1244
+ pageInfo: effectivePageInfo,
952
1245
  source,
953
1246
  error,
954
- loading
1247
+ loading,
1248
+ fetchNext,
1249
+ fetchPrevious,
1250
+ resetPagination
955
1251
  }),
956
- [data, source, error, loading]
1252
+ [
1253
+ data,
1254
+ effectivePageInfo,
1255
+ error,
1256
+ fetchNext,
1257
+ fetchPrevious,
1258
+ isPaginated,
1259
+ loading,
1260
+ mergedPaginatedData,
1261
+ source
1262
+ ]
957
1263
  );
958
1264
  };
959
1265
  export {
@@ -970,15 +1276,16 @@ export {
970
1276
  disconnect as i,
971
1277
  reconnect as j,
972
1278
  registerQuery as k,
973
- syncRtsChanges as l,
974
- hydrateRtsFromWindow as m,
975
- clearHydratedRtsQueryData as n,
1279
+ runNetworkQuery as l,
1280
+ syncRtsChanges as m,
1281
+ hydrateRtsFromWindow as n,
976
1282
  onMessage as o,
977
- peekHydratedRtsQueryData as p,
978
- resolveRtsHydrationIdentityFromStaticRouterData as q,
1283
+ clearHydratedRtsQueryData as p,
1284
+ peekHydratedRtsQueryData as q,
979
1285
  resetRtsPouchStore as r,
980
1286
  sendMessage as s,
981
- useQuery as t,
982
- updateDocs as u
1287
+ peekHydratedRtsQueryPageInfo as t,
1288
+ updateDocs as u,
1289
+ useQuery as v
983
1290
  };
984
- //# sourceMappingURL=useQuery-n0OatV-X.js.map
1291
+ //# sourceMappingURL=useQuery-Ce_EmI2F.js.map