@rpcbase/client 0.415.0 → 0.417.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.
@@ -0,0 +1,2573 @@
1
+ import { createContext, useContext, useId, useMemo, useState, useRef, useEffect } from "react";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { c } from "react/compiler-runtime";
4
+ const STATIC_RPCBASE_RTS_HYDRATION_DATA_KEY = "__staticRpcbaseRtsHydrationData";
5
+ const RtsSsrRuntimeContext = createContext(null);
6
+ const hydrationDataStore = /* @__PURE__ */ new Map();
7
+ const hydrationPageInfoStore = /* @__PURE__ */ new Map();
8
+ const hydrationTotalCountStore = /* @__PURE__ */ new Map();
9
+ const hydrationCountStore = /* @__PURE__ */ new Map();
10
+ const makeStoreKey = (modelName, queryKey) => `${modelName}.${queryKey}`;
11
+ const normalizeStringOrNull = (value) => {
12
+ if (typeof value !== "string") return null;
13
+ const normalized = value.trim();
14
+ return normalized ? normalized : null;
15
+ };
16
+ const normalizePageInfo$2 = (value) => {
17
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
18
+ const raw = value;
19
+ if (typeof raw.hasNextPage !== "boolean" || typeof raw.hasPrevPage !== "boolean") return void 0;
20
+ const nextCursor = typeof raw.nextCursor === "string" && raw.nextCursor ? raw.nextCursor : void 0;
21
+ const prevCursor = typeof raw.prevCursor === "string" && raw.prevCursor ? raw.prevCursor : void 0;
22
+ return {
23
+ hasNextPage: raw.hasNextPage,
24
+ hasPrevPage: raw.hasPrevPage,
25
+ ...nextCursor ? {
26
+ nextCursor
27
+ } : {},
28
+ ...prevCursor ? {
29
+ prevCursor
30
+ } : {}
31
+ };
32
+ };
33
+ const normalizeTotalCount$2 = (value) => {
34
+ if (typeof value !== "number") return void 0;
35
+ if (!Number.isFinite(value) || value < 0) return void 0;
36
+ return Math.floor(value);
37
+ };
38
+ const parseHydrationData = (value) => {
39
+ if (!value || typeof value !== "object") return null;
40
+ const raw = value;
41
+ if (raw.v !== 1) return null;
42
+ if (!Array.isArray(raw.queries)) return null;
43
+ const rawCounts = Array.isArray(raw.counts) ? raw.counts : [];
44
+ const queries = [];
45
+ for (const entry of raw.queries) {
46
+ if (!entry || typeof entry !== "object") continue;
47
+ const query = entry;
48
+ const modelName = normalizeStringOrNull(query.modelName);
49
+ const queryKey = normalizeStringOrNull(query.queryKey);
50
+ if (!modelName || !queryKey) continue;
51
+ if (!Array.isArray(query.data)) continue;
52
+ const pageInfo = normalizePageInfo$2(query.pageInfo);
53
+ const totalCount = normalizeTotalCount$2(query.totalCount);
54
+ queries.push({
55
+ modelName,
56
+ queryKey,
57
+ data: query.data,
58
+ ...pageInfo ? {
59
+ pageInfo
60
+ } : {},
61
+ ...totalCount !== void 0 ? {
62
+ totalCount
63
+ } : {}
64
+ });
65
+ }
66
+ const counts = [];
67
+ for (const entry of rawCounts) {
68
+ if (!entry || typeof entry !== "object") continue;
69
+ const countEntry = entry;
70
+ const modelName = normalizeStringOrNull(countEntry.modelName);
71
+ const queryKey = normalizeStringOrNull(countEntry.queryKey);
72
+ const count = normalizeTotalCount$2(countEntry.count);
73
+ if (!modelName || !queryKey || count === void 0) continue;
74
+ counts.push({
75
+ modelName,
76
+ queryKey,
77
+ count
78
+ });
79
+ }
80
+ return {
81
+ v: 1,
82
+ tenantId: normalizeStringOrNull(raw.tenantId),
83
+ uid: normalizeStringOrNull(raw.uid),
84
+ queries,
85
+ counts
86
+ };
87
+ };
88
+ const hydrateRtsFromWindow = () => {
89
+ if (typeof window === "undefined") return;
90
+ const browserWindow = window;
91
+ const raw = browserWindow[STATIC_RPCBASE_RTS_HYDRATION_DATA_KEY];
92
+ delete browserWindow[STATIC_RPCBASE_RTS_HYDRATION_DATA_KEY];
93
+ const parsed = parseHydrationData(raw);
94
+ if (!parsed) return;
95
+ hydrationDataStore.clear();
96
+ hydrationPageInfoStore.clear();
97
+ hydrationTotalCountStore.clear();
98
+ hydrationCountStore.clear();
99
+ for (const query of parsed.queries) {
100
+ hydrationDataStore.set(makeStoreKey(query.modelName, query.queryKey), query.data);
101
+ hydrationPageInfoStore.set(makeStoreKey(query.modelName, query.queryKey), query.pageInfo);
102
+ hydrationTotalCountStore.set(makeStoreKey(query.modelName, query.queryKey), query.totalCount);
103
+ }
104
+ for (const count of parsed.counts) {
105
+ hydrationCountStore.set(makeStoreKey(count.modelName, count.queryKey), count.count);
106
+ }
107
+ };
108
+ const peekHydratedRtsQueryData = (modelName, queryKey) => {
109
+ return hydrationDataStore.get(makeStoreKey(modelName, queryKey));
110
+ };
111
+ const peekHydratedRtsQueryPageInfo = (modelName, queryKey) => {
112
+ return hydrationPageInfoStore.get(makeStoreKey(modelName, queryKey));
113
+ };
114
+ const peekHydratedRtsQueryTotalCount = (modelName, queryKey) => {
115
+ return hydrationTotalCountStore.get(makeStoreKey(modelName, queryKey));
116
+ };
117
+ const peekHydratedRtsCount = (modelName, queryKey) => {
118
+ return hydrationCountStore.get(makeStoreKey(modelName, queryKey));
119
+ };
120
+ const consumeHydratedRtsQueryData = (modelName, queryKey) => {
121
+ const key = makeStoreKey(modelName, queryKey);
122
+ hydrationDataStore.delete(key);
123
+ hydrationPageInfoStore.delete(key);
124
+ hydrationTotalCountStore.delete(key);
125
+ };
126
+ const consumeHydratedRtsCount = (modelName, queryKey) => {
127
+ hydrationCountStore.delete(makeStoreKey(modelName, queryKey));
128
+ };
129
+ const clearHydratedRtsQueryData = () => {
130
+ hydrationDataStore.clear();
131
+ hydrationPageInfoStore.clear();
132
+ hydrationTotalCountStore.clear();
133
+ hydrationCountStore.clear();
134
+ };
135
+ const RtsSsrRuntimeProvider = (t0) => {
136
+ const $ = c(3);
137
+ const {
138
+ value,
139
+ children
140
+ } = t0;
141
+ let t1;
142
+ if ($[0] !== children || $[1] !== value) {
143
+ t1 = /* @__PURE__ */ jsx(RtsSsrRuntimeContext.Provider, { value, children });
144
+ $[0] = children;
145
+ $[1] = value;
146
+ $[2] = t1;
147
+ } else {
148
+ t1 = $[2];
149
+ }
150
+ return t1;
151
+ };
152
+ const useRtsSsrRuntime = () => {
153
+ return useContext(RtsSsrRuntimeContext);
154
+ };
155
+ const memoryStore = /* @__PURE__ */ new Map();
156
+ let reactNativeStorage = null;
157
+ const MMKV_STORAGE_ID = "rpcbase-rts";
158
+ const memoryStorage = {
159
+ getItem: (key) => memoryStore.get(key) ?? null,
160
+ setItem: (key, value) => {
161
+ memoryStore.set(key, String(value));
162
+ },
163
+ removeItem: (key) => {
164
+ memoryStore.delete(key);
165
+ }
166
+ };
167
+ const asRuntimeStorage = (value) => {
168
+ if (!value || typeof value !== "object") return null;
169
+ const candidate = value;
170
+ if (typeof candidate.getItem !== "function" || typeof candidate.setItem !== "function" || typeof candidate.removeItem !== "function") {
171
+ return null;
172
+ }
173
+ return {
174
+ getItem: candidate.getItem.bind(value),
175
+ setItem: candidate.setItem.bind(value),
176
+ removeItem: candidate.removeItem.bind(value)
177
+ };
178
+ };
179
+ const isReactNativeRuntime = () => {
180
+ if (typeof navigator === "undefined") return false;
181
+ return navigator.product === "ReactNative";
182
+ };
183
+ const getRuntimeRequire = () => {
184
+ const globalRequire = globalThis.require;
185
+ if (typeof globalRequire === "function") {
186
+ return globalRequire;
187
+ }
188
+ try {
189
+ return (0, eval)("require");
190
+ } catch {
191
+ return null;
192
+ }
193
+ };
194
+ const getReactNativeStorage = () => {
195
+ if (reactNativeStorage) return reactNativeStorage;
196
+ const runtimeRequire = getRuntimeRequire();
197
+ if (!runtimeRequire) {
198
+ throw new Error("RTS storage: react-native-mmkv is required in React Native runtime");
199
+ }
200
+ let mmkvModule;
201
+ try {
202
+ mmkvModule = runtimeRequire("react-native-mmkv");
203
+ } catch (error) {
204
+ const runtimeError = error instanceof Error ? ` ${error.message}` : "";
205
+ throw new Error(`RTS storage: react-native-mmkv is required in React Native runtime.${runtimeError}`);
206
+ }
207
+ const MMKV = mmkvModule.MMKV;
208
+ if (typeof MMKV !== "function") {
209
+ throw new Error("RTS storage: invalid react-native-mmkv module shape");
210
+ }
211
+ const mmkv = new MMKV({
212
+ id: MMKV_STORAGE_ID
213
+ });
214
+ reactNativeStorage = {
215
+ getItem: (key) => {
216
+ const value = mmkv.getString(key);
217
+ return typeof value === "string" ? value : null;
218
+ },
219
+ setItem: (key, value) => {
220
+ mmkv.set(key, String(value));
221
+ },
222
+ removeItem: (key) => {
223
+ mmkv.delete(key);
224
+ }
225
+ };
226
+ return reactNativeStorage;
227
+ };
228
+ const getRuntimeStorage = () => {
229
+ if (isReactNativeRuntime()) {
230
+ return getReactNativeStorage();
231
+ }
232
+ const direct = asRuntimeStorage(globalThis.localStorage);
233
+ if (direct) return direct;
234
+ const windowStorage = asRuntimeStorage(globalThis.window?.localStorage);
235
+ if (windowStorage) return windowStorage;
236
+ return memoryStorage;
237
+ };
238
+ const UNDERSCORE_PREFIX = "$_";
239
+ const DEFAULT_FIND_LIMIT = 4096;
240
+ const INDEXED_DB_ADAPTER = "indexeddb";
241
+ const REACT_NATIVE_SQLITE_ADAPTER = "react-native-sqlite";
242
+ let storeConfig = null;
243
+ let pouchDbPromise = null;
244
+ let lastAppliedPrefix = null;
245
+ let activePouchAdapter = INDEXED_DB_ADAPTER;
246
+ const collections = /* @__PURE__ */ new Map();
247
+ const dbNamesByPrefix = /* @__PURE__ */ new Map();
248
+ const unwrapDefault = (mod) => {
249
+ if (!mod || typeof mod !== "object") return mod;
250
+ const maybe = mod;
251
+ return maybe.default ?? mod;
252
+ };
253
+ const computeBasePrefix = ({
254
+ tenantId,
255
+ appName
256
+ }) => {
257
+ let prefix = "rb/";
258
+ if (appName) prefix += `${appName}/`;
259
+ prefix += `${tenantId}/`;
260
+ return prefix;
261
+ };
262
+ const getDbNamesKey = (prefix) => `rb:rts:pouchDbs:${prefix}`;
263
+ const getPrefixOverrideKey = ({
264
+ tenantId,
265
+ appName
266
+ }) => `rb:rts:pouchPrefix:${appName ?? ""}:${tenantId}`;
267
+ const readPrefixOverride = ({
268
+ tenantId,
269
+ appName
270
+ }) => {
271
+ const storage = getRuntimeStorage();
272
+ try {
273
+ const value = storage.getItem(getPrefixOverrideKey({
274
+ tenantId,
275
+ appName
276
+ }));
277
+ if (!value) return null;
278
+ if (!value.endsWith("/")) return `${value}/`;
279
+ return value;
280
+ } catch {
281
+ return null;
282
+ }
283
+ };
284
+ const getPrefix = () => {
285
+ if (!storeConfig) {
286
+ throw new Error("RTS PouchDB store is not configured");
287
+ }
288
+ if (storeConfig.prefix) return storeConfig.prefix;
289
+ const basePrefix = computeBasePrefix({
290
+ tenantId: storeConfig.tenantId,
291
+ appName: storeConfig.appName
292
+ });
293
+ const override = readPrefixOverride({
294
+ tenantId: storeConfig.tenantId,
295
+ appName: storeConfig.appName
296
+ });
297
+ return override ?? basePrefix;
298
+ };
299
+ const loadDbNames = (prefix) => {
300
+ const existing = dbNamesByPrefix.get(prefix);
301
+ if (existing) return existing;
302
+ const names = /* @__PURE__ */ new Set();
303
+ const storage = getRuntimeStorage();
304
+ try {
305
+ const raw = storage.getItem(getDbNamesKey(prefix));
306
+ if (raw) {
307
+ const parsed = JSON.parse(raw);
308
+ if (Array.isArray(parsed)) {
309
+ for (const value of parsed) {
310
+ if (typeof value === "string" && value) names.add(value);
311
+ }
312
+ }
313
+ }
314
+ } catch {
315
+ return names;
316
+ }
317
+ dbNamesByPrefix.set(prefix, names);
318
+ return names;
319
+ };
320
+ const persistDbNames = (prefix, names) => {
321
+ const storage = getRuntimeStorage();
322
+ try {
323
+ if (!names.size) {
324
+ storage.removeItem(getDbNamesKey(prefix));
325
+ dbNamesByPrefix.delete(prefix);
326
+ return;
327
+ }
328
+ storage.setItem(getDbNamesKey(prefix), JSON.stringify(Array.from(names)));
329
+ dbNamesByPrefix.set(prefix, names);
330
+ } catch {
331
+ return;
332
+ }
333
+ };
334
+ const registerDbName = (prefix, dbName) => {
335
+ if (!prefix || !dbName) return;
336
+ const names = loadDbNames(prefix);
337
+ if (names.has(dbName)) return;
338
+ names.add(dbName);
339
+ persistDbNames(prefix, names);
340
+ };
341
+ const unregisterDbName = (prefix, dbName) => {
342
+ if (!prefix || !dbName) return;
343
+ const names = loadDbNames(prefix);
344
+ if (!names.delete(dbName)) return;
345
+ persistDbNames(prefix, names);
346
+ };
347
+ const getPouchDb = async () => {
348
+ if (!pouchDbPromise) {
349
+ pouchDbPromise = (async () => {
350
+ const [core, findPlugin] = await Promise.all([import("pouchdb-core"), import("pouchdb-find")]);
351
+ const PouchDB = unwrapDefault(core);
352
+ if (isReactNativeRuntime()) {
353
+ const moduleName = "pouchdb-adapter-react-native-sqlite";
354
+ let sqliteAdapterModule;
355
+ try {
356
+ sqliteAdapterModule = await import(
357
+ /* @vite-ignore */
358
+ moduleName
359
+ );
360
+ } catch (error) {
361
+ const runtimeError = error instanceof Error ? ` ${error.message}` : "";
362
+ throw new Error(`RTS PouchDB: missing react-native sqlite adapter. Install \`pouchdb-adapter-react-native-sqlite\` in the app.${runtimeError}`);
363
+ }
364
+ PouchDB.plugin(unwrapDefault(sqliteAdapterModule));
365
+ activePouchAdapter = REACT_NATIVE_SQLITE_ADAPTER;
366
+ } else {
367
+ const indexedDbAdapter = await import("pouchdb-adapter-indexeddb");
368
+ PouchDB.plugin(unwrapDefault(indexedDbAdapter));
369
+ activePouchAdapter = INDEXED_DB_ADAPTER;
370
+ }
371
+ PouchDB.plugin(unwrapDefault(findPlugin));
372
+ return PouchDB;
373
+ })();
374
+ }
375
+ return pouchDbPromise;
376
+ };
377
+ const applyPrefix = (PouchDB) => {
378
+ const prefix = getPrefix();
379
+ if (prefix === lastAppliedPrefix) return;
380
+ PouchDB.prefix = prefix;
381
+ lastAppliedPrefix = prefix;
382
+ };
383
+ const configureRtsPouchStore = (config) => {
384
+ storeConfig = config;
385
+ lastAppliedPrefix = null;
386
+ collections.clear();
387
+ };
388
+ const getCollection = async (modelName, options) => {
389
+ const PouchDB = await getPouchDb();
390
+ applyPrefix(PouchDB);
391
+ const prefix = getPrefix();
392
+ const dbName = `${options.uid}/${modelName}`;
393
+ const dbKey = `${prefix}${dbName}`;
394
+ const existing = collections.get(dbKey);
395
+ if (existing) return existing;
396
+ registerDbName(prefix, dbName);
397
+ const db = new PouchDB(dbName, {
398
+ adapter: activePouchAdapter,
399
+ revs_limit: 1
400
+ });
401
+ collections.set(dbKey, db);
402
+ return db;
403
+ };
404
+ const replaceQueryKeys = (value, replaceKey) => {
405
+ if (typeof value !== "object" || value === null) {
406
+ return value;
407
+ }
408
+ if (Array.isArray(value)) {
409
+ return value.map((item) => replaceQueryKeys(item, replaceKey));
410
+ }
411
+ const obj = value;
412
+ const next = Object.create(Object.getPrototypeOf(obj));
413
+ for (const key of Object.keys(obj)) {
414
+ if (/^\$/.test(key) || /\.\d+$/.test(key)) {
415
+ throw new Error(`replaceQueryKeys: Unexpected key format: ${key}`);
416
+ }
417
+ const newKey = replaceKey(key);
418
+ next[newKey] = replaceQueryKeys(obj[key], replaceKey);
419
+ }
420
+ return next;
421
+ };
422
+ const getKeys = (obj, parentKey = "") => {
423
+ const keys = [];
424
+ for (const key of Object.keys(obj)) {
425
+ const nextKey = parentKey ? `${parentKey}.${key}` : key;
426
+ const value = obj[key];
427
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
428
+ keys.push(...getKeys(value, nextKey));
429
+ } else {
430
+ keys.push(nextKey);
431
+ }
432
+ }
433
+ return keys;
434
+ };
435
+ const satisfiesProjection = (doc, projection) => {
436
+ const docKeys = new Set(getKeys(doc));
437
+ const projectionKeys = new Set(Object.keys(projection).filter((key) => projection[key] === 1));
438
+ if (!projectionKeys.has("_id")) {
439
+ docKeys.delete("_id");
440
+ }
441
+ if (projectionKeys.size > docKeys.size) return false;
442
+ for (const key of projectionKeys) {
443
+ if (!docKeys.has(key)) return false;
444
+ }
445
+ return true;
446
+ };
447
+ const getValueAtPath = (doc, path) => {
448
+ const parts = path.split(".").map((part) => part.trim()).filter(Boolean);
449
+ if (!parts.length) return void 0;
450
+ let current = doc;
451
+ for (const part of parts) {
452
+ if (!current || typeof current !== "object" || Array.isArray(current)) return void 0;
453
+ current = current[part];
454
+ }
455
+ return current;
456
+ };
457
+ const setValueAtPath = (doc, path, value) => {
458
+ const parts = path.split(".").map((part) => part.trim()).filter(Boolean);
459
+ if (!parts.length) return;
460
+ let current = doc;
461
+ for (let i = 0; i < parts.length - 1; i += 1) {
462
+ const key = parts[i];
463
+ const existing = current[key];
464
+ if (!existing || typeof existing !== "object" || Array.isArray(existing)) {
465
+ current[key] = {};
466
+ }
467
+ current = current[key];
468
+ }
469
+ current[parts[parts.length - 1]] = value;
470
+ };
471
+ const unsetValueAtPath = (doc, path) => {
472
+ const parts = path.split(".").map((part) => part.trim()).filter(Boolean);
473
+ if (!parts.length) return;
474
+ let current = doc;
475
+ for (let i = 0; i < parts.length - 1; i += 1) {
476
+ const key = parts[i];
477
+ const next = current[key];
478
+ if (!next || typeof next !== "object" || Array.isArray(next)) return;
479
+ current = next;
480
+ }
481
+ delete current[parts[parts.length - 1]];
482
+ };
483
+ const cloneDoc = (doc) => {
484
+ try {
485
+ return structuredClone(doc);
486
+ } catch {
487
+ return JSON.parse(JSON.stringify(doc));
488
+ }
489
+ };
490
+ const toProjectionSpec = (projection) => {
491
+ const spec = {};
492
+ for (const [key, value] of Object.entries(projection)) {
493
+ const path = key.trim();
494
+ if (!path) continue;
495
+ if (value === 1 || value === 0) {
496
+ spec[path] = value;
497
+ }
498
+ }
499
+ return spec;
500
+ };
501
+ const applyProjection = (doc, projection) => {
502
+ const spec = toProjectionSpec(projection);
503
+ const includeKeys = Object.keys(spec).filter((key) => spec[key] === 1);
504
+ const excludeKeys = Object.keys(spec).filter((key) => spec[key] === 0);
505
+ if (includeKeys.length > 0) {
506
+ const projected2 = {};
507
+ for (const key of includeKeys) {
508
+ const value = getValueAtPath(doc, key);
509
+ if (value !== void 0) setValueAtPath(projected2, key, value);
510
+ }
511
+ if (spec._id !== 0 && Object.hasOwn(doc, "_id")) {
512
+ projected2._id = doc._id;
513
+ }
514
+ return projected2;
515
+ }
516
+ const projected = cloneDoc(doc);
517
+ for (const key of excludeKeys) {
518
+ unsetValueAtPath(projected, key);
519
+ }
520
+ return projected;
521
+ };
522
+ const compareSort = (a, b, sort) => {
523
+ for (const key of Object.keys(sort)) {
524
+ const dir = sort[key];
525
+ const aVal = getValueAtPath(a, key);
526
+ const bVal = getValueAtPath(b, key);
527
+ if (typeof aVal === "number" && typeof bVal === "number") {
528
+ if (aVal < bVal) return -1 * dir;
529
+ if (aVal > bVal) return 1 * dir;
530
+ continue;
531
+ }
532
+ if (typeof aVal === "string" && typeof bVal === "string") {
533
+ if (aVal < bVal) return -1 * dir;
534
+ if (aVal > bVal) return 1 * dir;
535
+ continue;
536
+ }
537
+ }
538
+ return 0;
539
+ };
540
+ const extractDocId = (value) => {
541
+ if (typeof value === "string") return value;
542
+ if (!value || typeof value !== "object" || Array.isArray(value)) return "";
543
+ const id = value._id;
544
+ return typeof id === "string" ? id : "";
545
+ };
546
+ const matchValue = (docValue, matchValueRaw) => {
547
+ if (Array.isArray(matchValueRaw)) {
548
+ if (!Array.isArray(docValue)) return false;
549
+ if (docValue.length !== matchValueRaw.length) return false;
550
+ for (let i = 0; i < matchValueRaw.length; i += 1) {
551
+ const matched = matchValue(docValue[i], matchValueRaw[i]);
552
+ if (matched === null) return null;
553
+ if (!matched) return false;
554
+ }
555
+ return true;
556
+ }
557
+ if (matchValueRaw && typeof matchValueRaw === "object") {
558
+ const matchObj = matchValueRaw;
559
+ if (Object.keys(matchObj).some((key) => key.startsWith("$"))) return null;
560
+ if (!docValue || typeof docValue !== "object" || Array.isArray(docValue)) return false;
561
+ const docObj = docValue;
562
+ for (const key of Object.keys(matchObj)) {
563
+ const matched = matchValue(docObj[key], matchObj[key]);
564
+ if (matched === null) return null;
565
+ if (!matched) return false;
566
+ }
567
+ return true;
568
+ }
569
+ return Object.is(docValue, matchValueRaw);
570
+ };
571
+ const matchesSimpleQuery = (doc, query) => {
572
+ for (const [key, expected] of Object.entries(query)) {
573
+ if (key.startsWith("$")) return null;
574
+ const actual = getValueAtPath(doc, key);
575
+ const matched = matchValue(actual, expected);
576
+ if (matched === null) return null;
577
+ if (!matched) return false;
578
+ }
579
+ return true;
580
+ };
581
+ const remapDocFromStorage = (doc) => {
582
+ const next = {};
583
+ for (const [key, value] of Object.entries(doc)) {
584
+ const newKey = key.startsWith(UNDERSCORE_PREFIX) ? key.replace(/^\$_/, "") : key;
585
+ next[newKey] = value;
586
+ }
587
+ return next;
588
+ };
589
+ const runQueryInternal = async ({
590
+ modelName,
591
+ query = {},
592
+ options,
593
+ strictProjection = false
594
+ }) => {
595
+ const collection = await getCollection(modelName, {
596
+ uid: options.uid
597
+ });
598
+ const replacedQuery = replaceQueryKeys(query, (key) => key.startsWith("_") && key !== "_id" ? `${UNDERSCORE_PREFIX}${key}` : key);
599
+ const limit = typeof options.limit === "number" ? Math.abs(options.limit) : DEFAULT_FIND_LIMIT;
600
+ const {
601
+ docs
602
+ } = await collection.find({
603
+ selector: replacedQuery,
604
+ limit
605
+ });
606
+ const mappedDocs = docs.map(({
607
+ _rev: _revIgnored,
608
+ ...rest
609
+ }) => remapDocFromStorage(rest));
610
+ let filteredDocs = mappedDocs;
611
+ if (options.projection) {
612
+ if (strictProjection && mappedDocs.some((entry) => !satisfiesProjection(entry, options.projection))) {
613
+ return {
614
+ data: [],
615
+ context: {
616
+ source: "cache"
617
+ },
618
+ projectionMismatch: true
619
+ };
620
+ }
621
+ filteredDocs = filteredDocs.filter((entry) => satisfiesProjection(entry, options.projection));
622
+ }
623
+ let result = filteredDocs;
624
+ if (options.sort) {
625
+ result = result.sort((a, b) => compareSort(a, b, options.sort));
626
+ }
627
+ return {
628
+ data: result,
629
+ context: {
630
+ source: "cache"
631
+ },
632
+ projectionMismatch: false
633
+ };
634
+ };
635
+ const runQuery = async ({
636
+ modelName,
637
+ query = {},
638
+ options
639
+ }) => {
640
+ const result = await runQueryInternal({
641
+ modelName,
642
+ query,
643
+ options
644
+ });
645
+ return {
646
+ data: result.data,
647
+ context: result.context
648
+ };
649
+ };
650
+ const addWriteDoc = (writes, modelName, doc) => {
651
+ const docsById = writes.get(modelName) ?? /* @__PURE__ */ new Map();
652
+ docsById.set(doc._id, doc);
653
+ writes.set(modelName, docsById);
654
+ };
655
+ const sanitizePopulatedDoc = (doc, populate, writes) => {
656
+ const next = cloneDoc(doc);
657
+ for (const entry of populate) {
658
+ const value = getValueAtPath(next, entry.path);
659
+ if (value === void 0) continue;
660
+ if (Array.isArray(value)) {
661
+ const ids = [];
662
+ for (const candidate of value) {
663
+ const id2 = extractDocId(candidate);
664
+ if (!id2) continue;
665
+ ids.push(id2);
666
+ if (!entry.model) continue;
667
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) continue;
668
+ const candidateDoc = candidate;
669
+ const nested2 = entry.populate?.length ? sanitizePopulatedDoc(candidateDoc, entry.populate, writes) : cloneDoc(candidateDoc);
670
+ addWriteDoc(writes, entry.model, nested2);
671
+ }
672
+ setValueAtPath(next, entry.path, ids);
673
+ continue;
674
+ }
675
+ const id = extractDocId(value);
676
+ setValueAtPath(next, entry.path, id || null);
677
+ if (!id) continue;
678
+ if (!entry.model) continue;
679
+ if (!value || typeof value !== "object" || Array.isArray(value)) continue;
680
+ const valueDoc = value;
681
+ const nested = entry.populate?.length ? sanitizePopulatedDoc(valueDoc, entry.populate, writes) : cloneDoc(valueDoc);
682
+ addWriteDoc(writes, entry.model, nested);
683
+ }
684
+ return next;
685
+ };
686
+ const updatePopulatedDocs = async ({
687
+ modelName,
688
+ data,
689
+ uid,
690
+ populate
691
+ }) => {
692
+ if (!data.length) return;
693
+ const writes = /* @__PURE__ */ new Map();
694
+ const roots = data.map((doc) => sanitizePopulatedDoc(doc, populate, writes));
695
+ await updateDocs(modelName, roots, uid);
696
+ for (const [targetModelName, docsById] of writes.entries()) {
697
+ if (!docsById.size) continue;
698
+ await updateDocs(targetModelName, Array.from(docsById.values()), uid);
699
+ }
700
+ };
701
+ const loadProjectedDocsByIds = async ({
702
+ modelName,
703
+ ids,
704
+ uid,
705
+ projection
706
+ }) => {
707
+ const uniqueIds = Array.from(new Set(ids.filter(Boolean)));
708
+ if (!uniqueIds.length) {
709
+ return {
710
+ rawDocsById: /* @__PURE__ */ new Map(),
711
+ projectedDocsById: /* @__PURE__ */ new Map(),
712
+ projectionMismatch: false
713
+ };
714
+ }
715
+ const collection = await getCollection(modelName, {
716
+ uid
717
+ });
718
+ const {
719
+ docs
720
+ } = await collection.find({
721
+ selector: {
722
+ _id: {
723
+ $in: uniqueIds
724
+ }
725
+ },
726
+ limit: uniqueIds.length
727
+ });
728
+ const rawDocsById = /* @__PURE__ */ new Map();
729
+ const projectedDocsById = /* @__PURE__ */ new Map();
730
+ let projectionMismatch = false;
731
+ for (const rawDoc of docs) {
732
+ const id = typeof rawDoc._id === "string" ? rawDoc._id : "";
733
+ if (!id) continue;
734
+ const {
735
+ _rev: _revIgnored,
736
+ ...rest
737
+ } = rawDoc;
738
+ const remapped = remapDocFromStorage(rest);
739
+ if (!satisfiesProjection(remapped, projection)) {
740
+ projectionMismatch = true;
741
+ continue;
742
+ }
743
+ rawDocsById.set(id, remapped);
744
+ projectedDocsById.set(id, applyProjection(remapped, projection));
745
+ }
746
+ if (projectedDocsById.size < uniqueIds.length) {
747
+ projectionMismatch = true;
748
+ }
749
+ return {
750
+ rawDocsById,
751
+ projectedDocsById,
752
+ projectionMismatch
753
+ };
754
+ };
755
+ const hydratePopulateEntries = async ({
756
+ docs,
757
+ populate,
758
+ uid
759
+ }) => {
760
+ const currentDocs = docs;
761
+ for (const entry of populate) {
762
+ if (!entry.model) {
763
+ return {
764
+ hit: false,
765
+ data: []
766
+ };
767
+ }
768
+ const descriptors = currentDocs.map((doc) => {
769
+ const rawValue = getValueAtPath(doc, entry.path);
770
+ const isArray = Array.isArray(rawValue);
771
+ const ids = (isArray ? rawValue : [rawValue]).map((candidate) => extractDocId(candidate)).filter(Boolean);
772
+ return {
773
+ doc,
774
+ hasValue: rawValue !== void 0,
775
+ isArray,
776
+ ids
777
+ };
778
+ });
779
+ const allIds = descriptors.flatMap((descriptor) => descriptor.ids);
780
+ const loaded = await loadProjectedDocsByIds({
781
+ modelName: entry.model,
782
+ ids: allIds,
783
+ uid,
784
+ projection: entry.select
785
+ });
786
+ if (loaded.projectionMismatch) {
787
+ return {
788
+ hit: false,
789
+ data: []
790
+ };
791
+ }
792
+ let populatedDocsById = loaded.projectedDocsById;
793
+ if (entry.match) {
794
+ const filtered = /* @__PURE__ */ new Map();
795
+ for (const [id, candidate] of loaded.rawDocsById.entries()) {
796
+ const matched = matchesSimpleQuery(candidate, entry.match);
797
+ if (matched === null) return {
798
+ hit: false,
799
+ data: []
800
+ };
801
+ if (!matched) continue;
802
+ const projected = populatedDocsById.get(id);
803
+ if (projected) filtered.set(id, projected);
804
+ }
805
+ populatedDocsById = filtered;
806
+ }
807
+ if (entry.populate?.length) {
808
+ const nestedResult = await hydratePopulateEntries({
809
+ docs: Array.from(populatedDocsById.values()).map((candidate) => cloneDoc(candidate)),
810
+ populate: entry.populate,
811
+ uid
812
+ });
813
+ if (!nestedResult.hit) {
814
+ return {
815
+ hit: false,
816
+ data: []
817
+ };
818
+ }
819
+ populatedDocsById = nestedResult.data.reduce((acc, candidate) => {
820
+ const id = extractDocId(candidate);
821
+ if (id) acc.set(id, candidate);
822
+ return acc;
823
+ }, /* @__PURE__ */ new Map());
824
+ }
825
+ for (const descriptor of descriptors) {
826
+ if (!descriptor.hasValue) continue;
827
+ if (!descriptor.ids.length) {
828
+ setValueAtPath(descriptor.doc, entry.path, descriptor.isArray ? [] : null);
829
+ continue;
830
+ }
831
+ if (descriptor.isArray) {
832
+ let values = descriptor.ids.map((id) => populatedDocsById.get(id)).filter((candidate) => Boolean(candidate));
833
+ if (entry.options?.sort) {
834
+ values = values.sort((a, b) => compareSort(a, b, entry.options.sort));
835
+ }
836
+ if (typeof entry.options?.limit === "number" && Number.isFinite(entry.options.limit)) {
837
+ values = values.slice(0, Math.max(0, Math.floor(Math.abs(entry.options.limit))));
838
+ }
839
+ setValueAtPath(descriptor.doc, entry.path, values);
840
+ continue;
841
+ }
842
+ const value = populatedDocsById.get(descriptor.ids[0]);
843
+ setValueAtPath(descriptor.doc, entry.path, value ?? null);
844
+ }
845
+ }
846
+ return {
847
+ hit: true,
848
+ data: currentDocs
849
+ };
850
+ };
851
+ const runPopulatedQuery = async ({
852
+ modelName,
853
+ query = {},
854
+ options
855
+ }) => {
856
+ const rootResult = await runQueryInternal({
857
+ modelName,
858
+ query,
859
+ options: {
860
+ uid: options.uid,
861
+ projection: options.projection,
862
+ sort: options.sort,
863
+ limit: options.limit
864
+ },
865
+ strictProjection: true
866
+ });
867
+ if (rootResult.projectionMismatch) {
868
+ return {
869
+ hit: false,
870
+ data: [],
871
+ context: rootResult.context
872
+ };
873
+ }
874
+ const projectedRoots = rootResult.data.map((doc) => applyProjection(doc, options.projection));
875
+ const hydrated = await hydratePopulateEntries({
876
+ docs: projectedRoots.map((doc) => cloneDoc(doc)),
877
+ populate: options.populate,
878
+ uid: options.uid
879
+ });
880
+ if (!hydrated.hit) {
881
+ return {
882
+ hit: false,
883
+ data: [],
884
+ context: rootResult.context
885
+ };
886
+ }
887
+ return {
888
+ hit: true,
889
+ data: hydrated.data,
890
+ context: rootResult.context
891
+ };
892
+ };
893
+ const updateDocs = async (modelName, data, uid) => {
894
+ const collection = await getCollection(modelName, {
895
+ uid
896
+ });
897
+ const allIds = data.map((doc) => doc._id).filter(Boolean);
898
+ if (!allIds.length) return;
899
+ const {
900
+ docs: currentDocs
901
+ } = await collection.find({
902
+ selector: {
903
+ _id: {
904
+ $in: allIds
905
+ }
906
+ },
907
+ fields: ["_id", "_rev"],
908
+ limit: allIds.length
909
+ });
910
+ const currentDocsById = currentDocs.reduce((acc, doc) => {
911
+ const id = String(doc._id ?? "");
912
+ if (id) acc[id] = doc;
913
+ return acc;
914
+ }, {});
915
+ const newDocs = data.map((mongoDoc) => {
916
+ const currentDoc = currentDocsById[mongoDoc._id] ?? {
917
+ _id: mongoDoc._id
918
+ };
919
+ const nextDoc = Object.entries(mongoDoc).reduce((acc, [key, value]) => {
920
+ const newKey = key !== "_id" && key.startsWith("_") ? `${UNDERSCORE_PREFIX}${key}` : key;
921
+ acc[newKey] = value;
922
+ return acc;
923
+ }, {
924
+ ...currentDoc
925
+ });
926
+ const rev = currentDoc._rev;
927
+ if (typeof rev === "string" && rev) {
928
+ nextDoc._rev = rev;
929
+ } else {
930
+ delete nextDoc._rev;
931
+ }
932
+ return nextDoc;
933
+ });
934
+ await collection.bulkDocs(newDocs);
935
+ };
936
+ const deleteDocs = async (modelName, ids, uid) => {
937
+ const collection = await getCollection(modelName, {
938
+ uid
939
+ });
940
+ const allIds = ids.map((id) => String(id ?? "")).filter(Boolean);
941
+ if (!allIds.length) return;
942
+ const {
943
+ docs: currentDocs
944
+ } = await collection.find({
945
+ selector: {
946
+ _id: {
947
+ $in: allIds
948
+ }
949
+ },
950
+ fields: ["_id", "_rev"],
951
+ limit: allIds.length
952
+ });
953
+ const deletions = currentDocs.map((doc) => ({
954
+ _id: String(doc?._id ?? ""),
955
+ _rev: doc?._rev,
956
+ _deleted: true
957
+ })).filter((doc) => doc._id && typeof doc._rev === "string" && doc._rev);
958
+ if (!deletions.length) return;
959
+ await collection.bulkDocs(deletions);
960
+ };
961
+ const destroyCollection = async (modelName, uid) => {
962
+ const collection = await getCollection(modelName, {
963
+ uid
964
+ });
965
+ const prefix = getPrefix();
966
+ const dbName = `${uid}/${modelName}`;
967
+ collections.delete(`${prefix}${dbName}`);
968
+ unregisterDbName(prefix, dbName);
969
+ await collection.destroy();
970
+ };
971
+ const resetRtsPouchStore = ({
972
+ tenantId,
973
+ appName
974
+ }) => {
975
+ const basePrefix = computeBasePrefix({
976
+ tenantId,
977
+ appName
978
+ });
979
+ const oldPrefix = readPrefixOverride({
980
+ tenantId,
981
+ appName
982
+ }) ?? basePrefix;
983
+ const dbNames = Array.from(loadDbNames(oldPrefix));
984
+ const openDbs = Array.from(collections.entries()).filter(([key]) => key.startsWith(oldPrefix)).map(([, db]) => db);
985
+ void (async () => {
986
+ const remaining = new Set(dbNames);
987
+ await Promise.all(openDbs.map((db) => db.destroy().catch(() => {
988
+ })));
989
+ if (remaining.size) {
990
+ const PouchDB = await getPouchDb();
991
+ const PouchDBForPrefix = PouchDB.defaults?.({}) ?? PouchDB;
992
+ PouchDBForPrefix.prefix = oldPrefix;
993
+ await Promise.all(Array.from(remaining).map(async (name) => {
994
+ const db = new PouchDBForPrefix(name, {
995
+ adapter: activePouchAdapter,
996
+ revs_limit: 1
997
+ });
998
+ await db.destroy().then(() => {
999
+ remaining.delete(name);
1000
+ }).catch(() => {
1001
+ });
1002
+ }));
1003
+ }
1004
+ if (remaining.size) {
1005
+ persistDbNames(oldPrefix, remaining);
1006
+ } else {
1007
+ persistDbNames(oldPrefix, /* @__PURE__ */ new Set());
1008
+ }
1009
+ })();
1010
+ const newPrefix = `${basePrefix}reset-${Date.now().toString(16)}/`;
1011
+ const storage = getRuntimeStorage();
1012
+ try {
1013
+ storage.setItem(getPrefixOverrideKey({
1014
+ tenantId,
1015
+ appName
1016
+ }), newPrefix);
1017
+ } catch {
1018
+ return newPrefix;
1019
+ }
1020
+ lastAppliedPrefix = null;
1021
+ collections.clear();
1022
+ return newPrefix;
1023
+ };
1024
+ const destroyAllCollections = async () => {
1025
+ const dbs = Array.from(collections.values());
1026
+ await Promise.all(dbs.map((db) => db.destroy()));
1027
+ collections.clear();
1028
+ };
1029
+ const EXCLUDE_PROJECTION_ERROR = "must be include-only (value 1); exclusion projection is not supported";
1030
+ const sortProjectionSpec = (projection) => {
1031
+ const sorted = {};
1032
+ for (const key of Object.keys(projection).sort()) {
1033
+ sorted[key] = projection[key];
1034
+ }
1035
+ return sorted;
1036
+ };
1037
+ const normalizeProjectionSpec = (value, source, label) => {
1038
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1039
+ const raw = value;
1040
+ const normalized = {};
1041
+ let hasExclude = false;
1042
+ for (const [key, rawValue] of Object.entries(raw)) {
1043
+ const path = key.trim();
1044
+ if (!path) continue;
1045
+ if (rawValue === 1 || rawValue === true) {
1046
+ normalized[path] = 1;
1047
+ continue;
1048
+ }
1049
+ if (rawValue === 0 || rawValue === false) {
1050
+ hasExclude = true;
1051
+ continue;
1052
+ }
1053
+ if (typeof rawValue === "number" && Number.isFinite(rawValue)) {
1054
+ if (rawValue === 1) normalized[path] = 1;
1055
+ if (rawValue === 0) hasExclude = true;
1056
+ }
1057
+ }
1058
+ if (hasExclude) {
1059
+ throw new Error(`${source}: ${label} ${EXCLUDE_PROJECTION_ERROR}`);
1060
+ }
1061
+ return Object.keys(normalized).length > 0 ? sortProjectionSpec(normalized) : void 0;
1062
+ };
1063
+ const normalizeSelectString = (value, source) => {
1064
+ const tokens = value.split(/\s+/).map((token) => token.trim()).filter(Boolean);
1065
+ if (!tokens.length) return void 0;
1066
+ const normalized = {};
1067
+ for (const token of tokens) {
1068
+ let path = token;
1069
+ if (token.startsWith("-")) {
1070
+ throw new Error(`${source}: populate select ${EXCLUDE_PROJECTION_ERROR}`);
1071
+ } else if (token.startsWith("+")) {
1072
+ path = token.slice(1);
1073
+ }
1074
+ path = path.trim();
1075
+ if (!path) continue;
1076
+ normalized[path] = 1;
1077
+ }
1078
+ return Object.keys(normalized).length > 0 ? sortProjectionSpec(normalized) : void 0;
1079
+ };
1080
+ const normalizePopulateSelect = (value, source) => {
1081
+ if (typeof value === "string") {
1082
+ return normalizeSelectString(value, source);
1083
+ }
1084
+ return normalizeProjectionSpec(value, source, "populate select");
1085
+ };
1086
+ const normalizePopulateOptions = (value) => {
1087
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1088
+ const raw = value;
1089
+ const normalized = {};
1090
+ if (raw.sort && typeof raw.sort === "object" && !Array.isArray(raw.sort)) {
1091
+ const sortRaw = raw.sort;
1092
+ const sort = {};
1093
+ for (const [key, rawDirection] of Object.entries(sortRaw)) {
1094
+ const path = key.trim();
1095
+ if (!path) continue;
1096
+ if (rawDirection === 1 || rawDirection === "asc") {
1097
+ sort[path] = 1;
1098
+ continue;
1099
+ }
1100
+ if (rawDirection === -1 || rawDirection === "desc") {
1101
+ sort[path] = -1;
1102
+ }
1103
+ }
1104
+ if (Object.keys(sort).length > 0) {
1105
+ normalized.sort = sort;
1106
+ }
1107
+ }
1108
+ if (typeof raw.limit === "number" && Number.isFinite(raw.limit)) {
1109
+ normalized.limit = Math.max(0, Math.floor(Math.abs(raw.limit)));
1110
+ }
1111
+ return Object.keys(normalized).length > 0 ? normalized : void 0;
1112
+ };
1113
+ const normalizeString = (value) => {
1114
+ if (typeof value !== "string") return void 0;
1115
+ const normalized = value.trim();
1116
+ return normalized || void 0;
1117
+ };
1118
+ const normalizeObject = (value) => {
1119
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1120
+ return value;
1121
+ };
1122
+ const normalizePopulateObject = (value, source) => {
1123
+ const path = normalizeString(value.path);
1124
+ if (!path) {
1125
+ throw new Error(`${source}: populate entries must define a non-empty path`);
1126
+ }
1127
+ const select = normalizePopulateSelect(value.select, source);
1128
+ if (!select) {
1129
+ throw new Error(`${source}: populate entries must define a select projection`);
1130
+ }
1131
+ const nested = value.populate !== void 0 ? normalizePopulateOption(value.populate, source) : void 0;
1132
+ return {
1133
+ path,
1134
+ select,
1135
+ ...normalizeString(value.model) ? {
1136
+ model: normalizeString(value.model)
1137
+ } : {},
1138
+ ...normalizeObject(value.match) ? {
1139
+ match: normalizeObject(value.match)
1140
+ } : {},
1141
+ ...normalizePopulateOptions(value.options) ? {
1142
+ options: normalizePopulateOptions(value.options)
1143
+ } : {},
1144
+ ...nested && nested.length > 0 ? {
1145
+ populate: nested
1146
+ } : {}
1147
+ };
1148
+ };
1149
+ const normalizePopulateOption = (value, source) => {
1150
+ if (typeof value === "string") {
1151
+ throw new Error(`${source}: populate string syntax is not supported; use object entries with select`);
1152
+ }
1153
+ if (Array.isArray(value)) {
1154
+ if (value.length === 0) {
1155
+ throw new Error(`${source}: populate must contain at least one entry`);
1156
+ }
1157
+ return value.map((entry) => {
1158
+ if (typeof entry === "string") {
1159
+ throw new Error(`${source}: populate string syntax is not supported; use object entries with select`);
1160
+ }
1161
+ return normalizePopulateObject(entry, source);
1162
+ });
1163
+ }
1164
+ return [normalizePopulateObject(value, source)];
1165
+ };
1166
+ const preparePopulateCacheOptions = (options, source) => {
1167
+ if (!options.populate) return void 0;
1168
+ const rootProjection = normalizeProjectionSpec(options.projection, source, "projection");
1169
+ if (!rootProjection) {
1170
+ throw new Error(`${source}: projection is required when populate is used`);
1171
+ }
1172
+ const populate = normalizePopulateOption(options.populate, source);
1173
+ if (!populate.length) {
1174
+ throw new Error(`${source}: populate must contain at least one entry`);
1175
+ }
1176
+ return {
1177
+ rootProjection,
1178
+ populate
1179
+ };
1180
+ };
1181
+ const computeRtsQueryKey = (query, options) => {
1182
+ const key = options.key ?? "";
1183
+ const projection = options.projection ? JSON.stringify(options.projection) : "";
1184
+ const sort = options.sort ? JSON.stringify(options.sort) : "";
1185
+ const limit = typeof options.limit === "number" ? String(options.limit) : "";
1186
+ const populate = options.populate ? JSON.stringify(options.populate) : "";
1187
+ const pagination = options.pagination ? JSON.stringify(options.pagination) : "";
1188
+ return `${key}${JSON.stringify(query)}${projection}${sort}${limit}${populate}${pagination}`;
1189
+ };
1190
+ const TENANT_ID_QUERY_PARAM = "rb-tenant-id";
1191
+ const RTS_CHANGES_PATH = "/api/rb/rts/changes";
1192
+ const MAX_TXN_BUF = 2048;
1193
+ const SERVER_RECONNECT_DELAY_MIN_MS = 1e4;
1194
+ const SERVER_RECONNECT_DELAY_MAX_MS = 15e3;
1195
+ const RUN_NETWORK_QUERY_TIMEOUT_ERROR = "runNetworkQuery: request timed out";
1196
+ const RUN_NETWORK_COUNT_TIMEOUT_ERROR = "runNetworkCount: request timed out";
1197
+ let socket = null;
1198
+ let connectPromise = null;
1199
+ let explicitDisconnect = false;
1200
+ let currentTenantId = null;
1201
+ let currentUid = null;
1202
+ let connectOptions = {};
1203
+ const localTxnBuf = [];
1204
+ const queryCallbacks = /* @__PURE__ */ new Map();
1205
+ const countCallbacks = /* @__PURE__ */ new Map();
1206
+ const subscriptions = /* @__PURE__ */ new Map();
1207
+ const countSubscriptions = /* @__PURE__ */ new Map();
1208
+ const messageCallbacks = /* @__PURE__ */ new Map();
1209
+ let reconnectTimer = null;
1210
+ let reconnectAttempts = 0;
1211
+ let hasEstablishedConnection = false;
1212
+ let pendingServerReconnectJitter = false;
1213
+ let syncPromise = null;
1214
+ let syncKey = null;
1215
+ let connectionStatus = "idle";
1216
+ let connectionError = null;
1217
+ const connectionStatusCallbacks = /* @__PURE__ */ new Set();
1218
+ const ensureRealtimeRuntime = () => {
1219
+ if (typeof WebSocket !== "function") {
1220
+ throw new Error("RTS websocket client requires WebSocket support");
1221
+ }
1222
+ if (typeof globalThis.setTimeout !== "function" || typeof globalThis.clearTimeout !== "function") {
1223
+ throw new Error("RTS websocket client requires timer support");
1224
+ }
1225
+ };
1226
+ const ensureSyncRuntime = () => {
1227
+ if (typeof fetch !== "function") {
1228
+ throw new Error("syncRtsChanges requires fetch support");
1229
+ }
1230
+ };
1231
+ const getRuntimeLocationHref = () => {
1232
+ if (typeof window !== "undefined" && typeof window.location?.href === "string" && window.location.href) {
1233
+ return window.location.href;
1234
+ }
1235
+ const location = globalThis.location;
1236
+ if (typeof location?.href === "string" && location.href) {
1237
+ return location.href;
1238
+ }
1239
+ return null;
1240
+ };
1241
+ const setRuntimeTimeout = (handler, delayMs) => {
1242
+ return globalThis.setTimeout(handler, delayMs);
1243
+ };
1244
+ const clearRuntimeTimeout = (timer) => {
1245
+ globalThis.clearTimeout(timer);
1246
+ };
1247
+ const setConnectionStatus = (status, error = null) => {
1248
+ connectionStatus = status;
1249
+ connectionError = error;
1250
+ for (const callback of connectionStatusCallbacks) {
1251
+ callback(status, error);
1252
+ }
1253
+ };
1254
+ const resolveWebSocketUrlFromCandidate = (candidateUrl, options) => {
1255
+ const url = new URL(candidateUrl);
1256
+ if (url.protocol === "http:") {
1257
+ url.protocol = "ws:";
1258
+ } else if (url.protocol === "https:") {
1259
+ url.protocol = "wss:";
1260
+ }
1261
+ if (!url.pathname || url.pathname === "/") {
1262
+ url.pathname = options.path ?? "/rts";
1263
+ }
1264
+ return url;
1265
+ };
1266
+ const resolveApiOriginUrl = ({
1267
+ url
1268
+ }) => {
1269
+ if (url) {
1270
+ const base2 = new URL(url);
1271
+ if (base2.protocol === "ws:") {
1272
+ base2.protocol = "http:";
1273
+ } else if (base2.protocol === "wss:") {
1274
+ base2.protocol = "https:";
1275
+ }
1276
+ base2.pathname = "/";
1277
+ base2.search = "";
1278
+ base2.hash = "";
1279
+ return base2;
1280
+ }
1281
+ const locationHref = getRuntimeLocationHref();
1282
+ if (!locationHref) {
1283
+ throw new Error("syncRtsChanges: options.url is required when location.href is unavailable");
1284
+ }
1285
+ const base = new URL(locationHref);
1286
+ base.pathname = "/";
1287
+ base.search = "";
1288
+ base.hash = "";
1289
+ return base;
1290
+ };
1291
+ const buildSyncChangesUrl = (tenantId, options) => {
1292
+ const base = resolveApiOriginUrl(options);
1293
+ const url = new URL(RTS_CHANGES_PATH, base);
1294
+ url.searchParams.set(TENANT_ID_QUERY_PARAM, tenantId);
1295
+ return url.toString();
1296
+ };
1297
+ const buildSocketUrl = (tenantId, _uid, options) => {
1298
+ if (options.url) {
1299
+ const url = resolveWebSocketUrlFromCandidate(options.url, options);
1300
+ url.searchParams.set(TENANT_ID_QUERY_PARAM, tenantId);
1301
+ return url.toString();
1302
+ }
1303
+ const locationHref = getRuntimeLocationHref();
1304
+ if (!locationHref) {
1305
+ throw new Error("connect: options.url is required when location.href is unavailable");
1306
+ }
1307
+ const base = new URL(locationHref);
1308
+ base.protocol = base.protocol === "https:" ? "wss:" : "ws:";
1309
+ base.pathname = options.path ?? "/rts";
1310
+ base.search = "";
1311
+ base.hash = "";
1312
+ base.searchParams.set(TENANT_ID_QUERY_PARAM, tenantId);
1313
+ return base.toString();
1314
+ };
1315
+ const sendToServer = (message) => {
1316
+ if (!socket) return;
1317
+ if (socket.readyState !== WebSocket.OPEN) return;
1318
+ socket.send(JSON.stringify(message));
1319
+ };
1320
+ const resubscribeAll = ({
1321
+ forceInitialQuery
1322
+ }) => {
1323
+ for (const sub of subscriptions.values()) {
1324
+ const runInitialQuery = forceInitialQuery || sub.runInitialNetworkQuery;
1325
+ sendToServer({
1326
+ type: "register-query",
1327
+ modelName: sub.modelName,
1328
+ queryKey: sub.queryKey,
1329
+ query: sub.query,
1330
+ options: sub.options,
1331
+ runInitialQuery
1332
+ });
1333
+ }
1334
+ for (const sub of countSubscriptions.values()) {
1335
+ const runInitialQuery = forceInitialQuery || sub.runInitialNetworkQuery;
1336
+ sendToServer({
1337
+ type: "register-count",
1338
+ modelName: sub.modelName,
1339
+ queryKey: sub.queryKey,
1340
+ query: sub.query,
1341
+ options: sub.options,
1342
+ runInitialQuery
1343
+ });
1344
+ }
1345
+ };
1346
+ const clearReconnectTimer = () => {
1347
+ if (reconnectTimer === null) return;
1348
+ clearRuntimeTimeout(reconnectTimer);
1349
+ reconnectTimer = null;
1350
+ };
1351
+ const scheduleReconnect = () => {
1352
+ clearReconnectTimer();
1353
+ if (explicitDisconnect) {
1354
+ pendingServerReconnectJitter = false;
1355
+ return;
1356
+ }
1357
+ if (!currentTenantId || !currentUid) return;
1358
+ const cfg = connectOptions.reconnect ?? {};
1359
+ const maxAttempts = cfg.attempts ?? 128;
1360
+ const delayMs = cfg.delayMs ?? 400;
1361
+ const delayMaxMs = cfg.delayMaxMs ?? 1e4;
1362
+ if (reconnectAttempts >= maxAttempts) return;
1363
+ let delay = Math.min(delayMaxMs, delayMs * Math.pow(2, reconnectAttempts));
1364
+ if (pendingServerReconnectJitter) {
1365
+ const span = SERVER_RECONNECT_DELAY_MAX_MS - SERVER_RECONNECT_DELAY_MIN_MS;
1366
+ delay = SERVER_RECONNECT_DELAY_MIN_MS + Math.floor(Math.random() * (span + 1));
1367
+ pendingServerReconnectJitter = false;
1368
+ }
1369
+ reconnectAttempts += 1;
1370
+ reconnectTimer = setRuntimeTimeout(() => {
1371
+ void connectInternal(currentTenantId, currentUid, connectOptions, {
1372
+ resetReconnectAttempts: false
1373
+ });
1374
+ }, delay);
1375
+ };
1376
+ const isDocWithId = (doc) => {
1377
+ if (!doc || typeof doc !== "object") return false;
1378
+ return typeof doc._id === "string";
1379
+ };
1380
+ const normalizePageInfo$1 = (value) => {
1381
+ if (!value || typeof value !== "object") return void 0;
1382
+ if (Array.isArray(value)) return void 0;
1383
+ const raw = value;
1384
+ if (typeof raw.hasNextPage !== "boolean" || typeof raw.hasPrevPage !== "boolean") return void 0;
1385
+ const nextCursor = typeof raw.nextCursor === "string" && raw.nextCursor ? raw.nextCursor : void 0;
1386
+ const prevCursor = typeof raw.prevCursor === "string" && raw.prevCursor ? raw.prevCursor : void 0;
1387
+ return {
1388
+ hasNextPage: raw.hasNextPage,
1389
+ hasPrevPage: raw.hasPrevPage,
1390
+ ...nextCursor ? {
1391
+ nextCursor
1392
+ } : {},
1393
+ ...prevCursor ? {
1394
+ prevCursor
1395
+ } : {}
1396
+ };
1397
+ };
1398
+ const normalizeTotalCount$1 = (value) => {
1399
+ if (typeof value !== "number") return void 0;
1400
+ if (!Number.isFinite(value) || value < 0) return void 0;
1401
+ return Math.floor(value);
1402
+ };
1403
+ const handleQueryPayload = (payload) => {
1404
+ const {
1405
+ modelName,
1406
+ queryKey,
1407
+ data,
1408
+ error,
1409
+ txnId
1410
+ } = payload;
1411
+ const cbKey = `${modelName}.${queryKey}`;
1412
+ const callbacks = queryCallbacks.get(cbKey);
1413
+ if (!callbacks || !callbacks.size) return;
1414
+ const subscription = subscriptions.get(cbKey);
1415
+ const pageInfo = normalizePageInfo$1(payload.pageInfo);
1416
+ const totalCount = normalizeTotalCount$1(payload.totalCount);
1417
+ const populateCache = subscription?.populateCache;
1418
+ const hasPopulate = Boolean(populateCache);
1419
+ const hasPagination = Boolean(subscription?.options?.pagination || pageInfo || totalCount !== void 0);
1420
+ const isLocal = !!(txnId && localTxnBuf.includes(txnId));
1421
+ const context = {
1422
+ source: "network",
1423
+ isLocal,
1424
+ txnId,
1425
+ ...pageInfo ? {
1426
+ pageInfo
1427
+ } : {},
1428
+ ...totalCount !== void 0 ? {
1429
+ totalCount
1430
+ } : {}
1431
+ };
1432
+ if (error) {
1433
+ for (const cb of callbacks) cb(error, void 0, context);
1434
+ return;
1435
+ }
1436
+ for (const cb of callbacks) cb(null, data, context);
1437
+ if (!currentUid) return;
1438
+ const docs = Array.isArray(data) ? data.filter(isDocWithId) : [];
1439
+ if (hasPagination) return;
1440
+ if (!docs.length) return;
1441
+ if (hasPopulate && populateCache) {
1442
+ void updatePopulatedDocs({
1443
+ modelName,
1444
+ data: docs,
1445
+ uid: currentUid,
1446
+ populate: populateCache.populate
1447
+ }).catch(() => {
1448
+ });
1449
+ return;
1450
+ }
1451
+ void updateDocs(modelName, docs, currentUid).catch(() => {
1452
+ });
1453
+ };
1454
+ const handleCountPayload = (payload) => {
1455
+ const {
1456
+ modelName,
1457
+ queryKey,
1458
+ count,
1459
+ error,
1460
+ txnId
1461
+ } = payload;
1462
+ const cbKey = `${modelName}.${queryKey}`;
1463
+ const callbacks = countCallbacks.get(cbKey);
1464
+ if (!callbacks || !callbacks.size) return;
1465
+ const isLocal = !!(txnId && localTxnBuf.includes(txnId));
1466
+ const context = {
1467
+ source: "network",
1468
+ isLocal,
1469
+ txnId
1470
+ };
1471
+ if (error) {
1472
+ for (const cb of callbacks) cb(error, void 0, context);
1473
+ return;
1474
+ }
1475
+ const normalizedCount = normalizeTotalCount$1(count);
1476
+ for (const cb of callbacks) cb(null, normalizedCount, context);
1477
+ };
1478
+ const handleEvent = (payload) => {
1479
+ const callbacks = messageCallbacks.get(payload.event);
1480
+ if (!callbacks || !callbacks.size) return;
1481
+ for (const cb of callbacks) cb(payload.payload);
1482
+ };
1483
+ const handleMessage = (event) => {
1484
+ let parsed;
1485
+ try {
1486
+ parsed = JSON.parse(typeof event.data === "string" ? event.data : String(event.data));
1487
+ } catch {
1488
+ return;
1489
+ }
1490
+ if (!parsed || typeof parsed !== "object") return;
1491
+ const message = parsed;
1492
+ if (message.type === "query-payload") {
1493
+ handleQueryPayload(message);
1494
+ return;
1495
+ }
1496
+ if (message.type === "count-payload") {
1497
+ handleCountPayload(message);
1498
+ return;
1499
+ }
1500
+ if (message.type === "event") {
1501
+ handleEvent(message);
1502
+ }
1503
+ };
1504
+ const addLocalTxn = (txnId) => {
1505
+ if (!txnId) return;
1506
+ localTxnBuf.push(txnId);
1507
+ if (localTxnBuf.length > MAX_TXN_BUF) {
1508
+ localTxnBuf.shift();
1509
+ }
1510
+ };
1511
+ const getSyncStorageKey = ({
1512
+ tenantId,
1513
+ uid,
1514
+ appName
1515
+ }) => `rb:rts:changesSeq:${appName ?? ""}:${tenantId}:${uid}`;
1516
+ const readStoredSeq = (key) => {
1517
+ const storage = getRuntimeStorage();
1518
+ try {
1519
+ const raw = storage.getItem(key);
1520
+ const num = raw ? Number(raw) : 0;
1521
+ return Number.isFinite(num) && num >= 0 ? Math.floor(num) : 0;
1522
+ } catch {
1523
+ return 0;
1524
+ }
1525
+ };
1526
+ const writeStoredSeq = (key, value) => {
1527
+ const storage = getRuntimeStorage();
1528
+ try {
1529
+ storage.setItem(key, String(Math.max(0, Math.floor(value))));
1530
+ } catch {
1531
+ return;
1532
+ }
1533
+ };
1534
+ const applyChangeBatch = async (changes, uid) => {
1535
+ const resetModels = /* @__PURE__ */ new Set();
1536
+ const deletesByModel = /* @__PURE__ */ new Map();
1537
+ for (const change of changes) {
1538
+ const modelName = typeof change.modelName === "string" ? change.modelName : "";
1539
+ if (!modelName) continue;
1540
+ if (change.op === "reset_model") {
1541
+ resetModels.add(modelName);
1542
+ continue;
1543
+ }
1544
+ if (change.op === "delete") {
1545
+ const docId = typeof change.docId === "string" ? change.docId : "";
1546
+ if (!docId) continue;
1547
+ const existing = deletesByModel.get(modelName) ?? [];
1548
+ existing.push(docId);
1549
+ deletesByModel.set(modelName, existing);
1550
+ }
1551
+ }
1552
+ for (const modelName of resetModels) {
1553
+ await destroyCollection(modelName, uid).catch(() => {
1554
+ });
1555
+ }
1556
+ for (const [modelName, ids] of deletesByModel.entries()) {
1557
+ if (resetModels.has(modelName)) continue;
1558
+ await deleteDocs(modelName, ids, uid).catch(() => {
1559
+ });
1560
+ }
1561
+ };
1562
+ const syncRtsChanges = async (tenantId, uid, options = {}) => {
1563
+ ensureSyncRuntime();
1564
+ if (!tenantId || !uid) return;
1565
+ const storageKey = getSyncStorageKey({
1566
+ tenantId,
1567
+ uid,
1568
+ appName: options.appName
1569
+ });
1570
+ let sinceSeq = readStoredSeq(storageKey);
1571
+ const syncUrl = buildSyncChangesUrl(tenantId, {
1572
+ url: options.url
1573
+ });
1574
+ for (let i = 0; i < 32; i += 1) {
1575
+ const response = await fetch(syncUrl, {
1576
+ method: "POST",
1577
+ credentials: "include",
1578
+ headers: {
1579
+ "Content-Type": "application/json"
1580
+ },
1581
+ body: JSON.stringify({
1582
+ sinceSeq,
1583
+ limit: 2e3
1584
+ })
1585
+ });
1586
+ if (!response.ok) return;
1587
+ const payload = await response.json().catch(() => null);
1588
+ if (!payload || typeof payload !== "object") return;
1589
+ const payloadObj = payload;
1590
+ const ok = payloadObj.ok;
1591
+ if (ok !== true) return;
1592
+ const latestSeq = Number(payloadObj.latestSeq ?? 0);
1593
+ const needsFullResync = Boolean(payloadObj.needsFullResync);
1594
+ if (needsFullResync) {
1595
+ resetRtsPouchStore({
1596
+ tenantId,
1597
+ appName: options.appName
1598
+ });
1599
+ writeStoredSeq(storageKey, latestSeq);
1600
+ return;
1601
+ }
1602
+ const changesRaw = payloadObj.changes;
1603
+ const changes = Array.isArray(changesRaw) ? changesRaw : [];
1604
+ const normalized = changes.map((c2) => {
1605
+ if (!c2 || typeof c2 !== "object") return null;
1606
+ const obj = c2;
1607
+ const seq = Number(obj.seq ?? 0);
1608
+ const modelName = typeof obj.modelName === "string" ? obj.modelName : String(obj.modelName ?? "");
1609
+ const op = obj.op === "reset_model" ? "reset_model" : "delete";
1610
+ const docId = typeof obj.docId === "string" && obj.docId ? obj.docId : obj.docId ? String(obj.docId) : void 0;
1611
+ return {
1612
+ seq,
1613
+ modelName,
1614
+ op,
1615
+ ...docId ? {
1616
+ docId
1617
+ } : {}
1618
+ };
1619
+ }).filter((c2) => c2 !== null).filter((c2) => Number.isFinite(c2.seq) && c2.seq > 0 && c2.modelName && (c2.op === "reset_model" || !!c2.docId));
1620
+ if (!normalized.length) {
1621
+ writeStoredSeq(storageKey, latestSeq);
1622
+ return;
1623
+ }
1624
+ await applyChangeBatch(normalized, uid);
1625
+ const lastSeq = normalized.reduce((max, c2) => c2.seq > max ? c2.seq : max, sinceSeq);
1626
+ sinceSeq = lastSeq;
1627
+ writeStoredSeq(storageKey, sinceSeq);
1628
+ if (latestSeq > 0 && sinceSeq >= latestSeq) {
1629
+ return;
1630
+ }
1631
+ }
1632
+ };
1633
+ const ensureSynced = (tenantId, uid, options) => {
1634
+ if (options.syncChanges === false) return;
1635
+ const key = `${options.appName ?? ""}:${tenantId}:${uid}`;
1636
+ if (syncPromise && syncKey === key) return;
1637
+ syncKey = key;
1638
+ syncPromise = syncRtsChanges(tenantId, uid, {
1639
+ appName: options.appName,
1640
+ url: options.url
1641
+ }).catch(() => {
1642
+ }).finally(() => {
1643
+ if (syncKey === key) {
1644
+ syncPromise = null;
1645
+ }
1646
+ });
1647
+ };
1648
+ const connectInternal = (tenantId, uid, options, {
1649
+ resetReconnectAttempts
1650
+ }) => {
1651
+ ensureRealtimeRuntime();
1652
+ if (!tenantId) return Promise.resolve();
1653
+ if (!uid) throw new Error("Missing uid");
1654
+ currentTenantId = tenantId;
1655
+ currentUid = uid;
1656
+ connectOptions = options;
1657
+ if (options.configureStore !== false) {
1658
+ configureRtsPouchStore({
1659
+ tenantId,
1660
+ appName: options.appName
1661
+ });
1662
+ }
1663
+ ensureSynced(tenantId, uid, options);
1664
+ if (socket && (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING)) {
1665
+ return connectPromise ?? Promise.resolve();
1666
+ }
1667
+ explicitDisconnect = false;
1668
+ clearReconnectTimer();
1669
+ const url = buildSocketUrl(tenantId, uid, options);
1670
+ connectPromise = new Promise((resolve, reject) => {
1671
+ if (resetReconnectAttempts) reconnectAttempts = 0;
1672
+ let opened = false;
1673
+ let settled = false;
1674
+ setConnectionStatus("connecting");
1675
+ socket = new WebSocket(url);
1676
+ socket.addEventListener("open", () => {
1677
+ opened = true;
1678
+ settled = true;
1679
+ reconnectAttempts = 0;
1680
+ pendingServerReconnectJitter = false;
1681
+ setConnectionStatus("connected");
1682
+ const forceInitialQuery = hasEstablishedConnection;
1683
+ resubscribeAll({
1684
+ forceInitialQuery
1685
+ });
1686
+ hasEstablishedConnection = true;
1687
+ resolve();
1688
+ });
1689
+ socket.addEventListener("message", handleMessage);
1690
+ socket.addEventListener("close", (event) => {
1691
+ if (!opened && !settled) {
1692
+ settled = true;
1693
+ const error = new Error(`RTS WebSocket closed before opening (code=${event.code})`);
1694
+ setConnectionStatus("error", error);
1695
+ reject(error);
1696
+ }
1697
+ if (!explicitDisconnect) {
1698
+ pendingServerReconnectJitter = opened;
1699
+ setConnectionStatus("connecting", connectionError);
1700
+ } else {
1701
+ setConnectionStatus("idle");
1702
+ }
1703
+ socket = null;
1704
+ connectPromise = null;
1705
+ scheduleReconnect();
1706
+ });
1707
+ socket.addEventListener("error", (err) => {
1708
+ if (settled) return;
1709
+ settled = true;
1710
+ const error = err instanceof Error ? err : new Error("RTS WebSocket error");
1711
+ setConnectionStatus("error", error);
1712
+ reject(error);
1713
+ });
1714
+ });
1715
+ return connectPromise;
1716
+ };
1717
+ const connect = (tenantId, uid, options = {}) => {
1718
+ return connectInternal(tenantId, uid, options, {
1719
+ resetReconnectAttempts: true
1720
+ });
1721
+ };
1722
+ const disconnect = () => {
1723
+ explicitDisconnect = true;
1724
+ clearReconnectTimer();
1725
+ hasEstablishedConnection = false;
1726
+ pendingServerReconnectJitter = false;
1727
+ if (socket) {
1728
+ try {
1729
+ socket.close();
1730
+ } catch {
1731
+ }
1732
+ }
1733
+ socket = null;
1734
+ connectPromise = null;
1735
+ setConnectionStatus("idle");
1736
+ };
1737
+ const reconnect = (tenantId, uid, options = {}) => {
1738
+ explicitDisconnect = true;
1739
+ clearReconnectTimer();
1740
+ pendingServerReconnectJitter = false;
1741
+ if (socket) {
1742
+ try {
1743
+ socket.close();
1744
+ } catch {
1745
+ }
1746
+ }
1747
+ socket = null;
1748
+ connectPromise = null;
1749
+ return connect(tenantId, uid, options);
1750
+ };
1751
+ const getConnectionStatus = () => {
1752
+ return connectionStatus;
1753
+ };
1754
+ const getConnectionError = () => {
1755
+ return connectionError;
1756
+ };
1757
+ const onConnectionStatusChange = (callback) => {
1758
+ connectionStatusCallbacks.add(callback);
1759
+ return () => {
1760
+ connectionStatusCallbacks.delete(callback);
1761
+ };
1762
+ };
1763
+ const registerQuery = (modelName, query, optionsOrCallback, callbackMaybe, behavior) => {
1764
+ let options;
1765
+ let callback;
1766
+ if (typeof optionsOrCallback === "function") {
1767
+ options = {};
1768
+ callback = optionsOrCallback;
1769
+ } else {
1770
+ options = optionsOrCallback ?? {};
1771
+ callback = callbackMaybe;
1772
+ }
1773
+ if (!callback) return void 0;
1774
+ if (typeof modelName !== "string" || modelName.trim().length === 0) {
1775
+ throw new Error("registerQuery: modelName must be a non-empty string");
1776
+ }
1777
+ const queryKey = computeRtsQueryKey(query, options);
1778
+ const cbKey = `${modelName}.${queryKey}`;
1779
+ const runInitialNetworkQuery = behavior?.runInitialNetworkQuery !== false;
1780
+ const runInitialLocalQuery = behavior?.runInitialLocalQuery !== false;
1781
+ const populateCache = preparePopulateCacheOptions({
1782
+ projection: options.projection,
1783
+ populate: options.populate
1784
+ }, "registerQuery");
1785
+ const hasPopulate = Boolean(populateCache);
1786
+ const hasPagination = Boolean(options.pagination);
1787
+ const set = queryCallbacks.get(cbKey) ?? /* @__PURE__ */ new Set();
1788
+ set.add(callback);
1789
+ queryCallbacks.set(cbKey, set);
1790
+ subscriptions.set(cbKey, {
1791
+ modelName,
1792
+ query,
1793
+ options,
1794
+ queryKey,
1795
+ runInitialNetworkQuery,
1796
+ ...populateCache ? {
1797
+ populateCache
1798
+ } : {}
1799
+ });
1800
+ if (currentUid && runInitialLocalQuery && !hasPagination) {
1801
+ if (hasPopulate && populateCache) {
1802
+ void runPopulatedQuery({
1803
+ modelName,
1804
+ query,
1805
+ options: {
1806
+ uid: currentUid,
1807
+ projection: populateCache.rootProjection,
1808
+ sort: options.sort,
1809
+ limit: options.limit,
1810
+ populate: populateCache.populate
1811
+ }
1812
+ }).then(({
1813
+ hit,
1814
+ data,
1815
+ context
1816
+ }) => {
1817
+ if (!hit) return;
1818
+ callback?.(null, data, context);
1819
+ }).catch(() => {
1820
+ });
1821
+ } else {
1822
+ void runQuery({
1823
+ modelName,
1824
+ query,
1825
+ options: {
1826
+ uid: currentUid,
1827
+ projection: options.projection,
1828
+ sort: options.sort,
1829
+ limit: options.limit
1830
+ }
1831
+ }).then(({
1832
+ data,
1833
+ context
1834
+ }) => {
1835
+ callback?.(null, data, context);
1836
+ }).catch(() => {
1837
+ });
1838
+ }
1839
+ }
1840
+ sendToServer({
1841
+ type: "register-query",
1842
+ modelName,
1843
+ queryKey,
1844
+ query,
1845
+ options,
1846
+ runInitialQuery: runInitialNetworkQuery
1847
+ });
1848
+ return () => {
1849
+ sendToServer({
1850
+ type: "remove-query",
1851
+ modelName,
1852
+ queryKey
1853
+ });
1854
+ const callbacks = queryCallbacks.get(cbKey);
1855
+ callbacks?.delete(callback);
1856
+ if (callbacks && callbacks.size === 0) {
1857
+ queryCallbacks.delete(cbKey);
1858
+ subscriptions.delete(cbKey);
1859
+ }
1860
+ };
1861
+ };
1862
+ const registerCount = (modelName, query, optionsOrCallback, callbackMaybe, behavior) => {
1863
+ let options;
1864
+ let callback;
1865
+ if (typeof optionsOrCallback === "function") {
1866
+ options = {};
1867
+ callback = optionsOrCallback;
1868
+ } else {
1869
+ options = optionsOrCallback ?? {};
1870
+ callback = callbackMaybe;
1871
+ }
1872
+ if (!callback) return void 0;
1873
+ if (typeof modelName !== "string" || modelName.trim().length === 0) {
1874
+ throw new Error("registerCount: modelName must be a non-empty string");
1875
+ }
1876
+ const queryKey = computeRtsQueryKey(query, options);
1877
+ const cbKey = `${modelName}.${queryKey}`;
1878
+ const runInitialNetworkQuery = behavior?.runInitialNetworkQuery !== false;
1879
+ const set = countCallbacks.get(cbKey) ?? /* @__PURE__ */ new Set();
1880
+ set.add(callback);
1881
+ countCallbacks.set(cbKey, set);
1882
+ countSubscriptions.set(cbKey, {
1883
+ modelName,
1884
+ query,
1885
+ options,
1886
+ queryKey,
1887
+ runInitialNetworkQuery
1888
+ });
1889
+ sendToServer({
1890
+ type: "register-count",
1891
+ modelName,
1892
+ queryKey,
1893
+ query,
1894
+ options,
1895
+ runInitialQuery: runInitialNetworkQuery
1896
+ });
1897
+ return () => {
1898
+ const callbacks = countCallbacks.get(cbKey);
1899
+ callbacks?.delete(callback);
1900
+ if (callbacks && callbacks.size === 0) {
1901
+ countCallbacks.delete(cbKey);
1902
+ countSubscriptions.delete(cbKey);
1903
+ sendToServer({
1904
+ type: "remove-count",
1905
+ modelName,
1906
+ queryKey
1907
+ });
1908
+ }
1909
+ };
1910
+ };
1911
+ const makeRunQueryKey = () => `run-query.${Date.now().toString(36)}.${Math.random().toString(36).slice(2, 10)}`;
1912
+ const runNetworkQuery = async ({
1913
+ modelName,
1914
+ query,
1915
+ options = {},
1916
+ timeoutMs = 1e4
1917
+ }) => {
1918
+ if (typeof modelName !== "string" || modelName.trim().length === 0) {
1919
+ throw new Error("runNetworkQuery: modelName must be a non-empty string");
1920
+ }
1921
+ preparePopulateCacheOptions({
1922
+ projection: options.projection,
1923
+ populate: options.populate
1924
+ }, "runNetworkQuery");
1925
+ const hasTimeout = typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0;
1926
+ const timeoutStartedAt = hasTimeout ? Date.now() : 0;
1927
+ if (!socket || socket.readyState !== WebSocket.OPEN) {
1928
+ if (currentTenantId && currentUid) {
1929
+ try {
1930
+ const connectAttempt = connectInternal(currentTenantId, currentUid, connectOptions, {
1931
+ resetReconnectAttempts: false
1932
+ });
1933
+ if (hasTimeout) {
1934
+ await new Promise((resolve, reject) => {
1935
+ const timeoutId = setRuntimeTimeout(() => {
1936
+ reject(new Error(RUN_NETWORK_COUNT_TIMEOUT_ERROR));
1937
+ }, timeoutMs);
1938
+ connectAttempt.then(() => {
1939
+ clearRuntimeTimeout(timeoutId);
1940
+ resolve();
1941
+ }, (error) => {
1942
+ clearRuntimeTimeout(timeoutId);
1943
+ reject(error);
1944
+ });
1945
+ });
1946
+ } else {
1947
+ await connectAttempt;
1948
+ }
1949
+ } catch {
1950
+ }
1951
+ }
1952
+ }
1953
+ if (!socket || socket.readyState !== WebSocket.OPEN) {
1954
+ if (hasTimeout && Date.now() - timeoutStartedAt >= timeoutMs) {
1955
+ throw new Error(RUN_NETWORK_QUERY_TIMEOUT_ERROR);
1956
+ }
1957
+ throw new Error("runNetworkQuery: RTS socket is not connected");
1958
+ }
1959
+ const remainingTimeoutMs = hasTimeout ? Math.max(0, timeoutMs - (Date.now() - timeoutStartedAt)) : null;
1960
+ if (remainingTimeoutMs !== null && remainingTimeoutMs <= 0) {
1961
+ throw new Error(RUN_NETWORK_QUERY_TIMEOUT_ERROR);
1962
+ }
1963
+ const resolvedOptions = options.key ? options : {
1964
+ ...options,
1965
+ key: makeRunQueryKey()
1966
+ };
1967
+ const queryKey = computeRtsQueryKey(query, resolvedOptions);
1968
+ const cbKey = `${modelName}.${queryKey}`;
1969
+ return await new Promise((resolve, reject) => {
1970
+ let settled = false;
1971
+ let timeoutId = null;
1972
+ const cleanup = () => {
1973
+ const callbacks2 = queryCallbacks.get(cbKey);
1974
+ callbacks2?.delete(callback);
1975
+ if (callbacks2 && callbacks2.size === 0) queryCallbacks.delete(cbKey);
1976
+ if (timeoutId !== null) {
1977
+ clearRuntimeTimeout(timeoutId);
1978
+ }
1979
+ };
1980
+ const settle = (next) => {
1981
+ if (settled) return;
1982
+ settled = true;
1983
+ cleanup();
1984
+ next();
1985
+ };
1986
+ const callback = (error, data, context) => {
1987
+ if (error) {
1988
+ settle(() => reject(error));
1989
+ return;
1990
+ }
1991
+ settle(() => resolve({
1992
+ data,
1993
+ context
1994
+ }));
1995
+ };
1996
+ const callbacks = queryCallbacks.get(cbKey) ?? /* @__PURE__ */ new Set();
1997
+ callbacks.add(callback);
1998
+ queryCallbacks.set(cbKey, callbacks);
1999
+ if (remainingTimeoutMs !== null) {
2000
+ timeoutId = setRuntimeTimeout(() => {
2001
+ settle(() => reject(new Error(RUN_NETWORK_QUERY_TIMEOUT_ERROR)));
2002
+ }, remainingTimeoutMs);
2003
+ }
2004
+ sendToServer({
2005
+ type: "run-query",
2006
+ modelName,
2007
+ queryKey,
2008
+ query,
2009
+ options: resolvedOptions
2010
+ });
2011
+ });
2012
+ };
2013
+ const runNetworkCount = async ({
2014
+ modelName,
2015
+ query,
2016
+ options = {},
2017
+ timeoutMs = 1e4
2018
+ }) => {
2019
+ if (typeof modelName !== "string" || modelName.trim().length === 0) {
2020
+ throw new Error("runNetworkCount: modelName must be a non-empty string");
2021
+ }
2022
+ const hasTimeout = typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0;
2023
+ const timeoutStartedAt = hasTimeout ? Date.now() : 0;
2024
+ if (!socket || socket.readyState !== WebSocket.OPEN) {
2025
+ if (currentTenantId && currentUid) {
2026
+ try {
2027
+ const connectAttempt = connectInternal(currentTenantId, currentUid, connectOptions, {
2028
+ resetReconnectAttempts: false
2029
+ });
2030
+ if (hasTimeout) {
2031
+ await new Promise((resolve, reject) => {
2032
+ const timeoutId = setRuntimeTimeout(() => {
2033
+ reject(new Error(RUN_NETWORK_QUERY_TIMEOUT_ERROR));
2034
+ }, timeoutMs);
2035
+ connectAttempt.then(() => {
2036
+ clearRuntimeTimeout(timeoutId);
2037
+ resolve();
2038
+ }, (error) => {
2039
+ clearRuntimeTimeout(timeoutId);
2040
+ reject(error);
2041
+ });
2042
+ });
2043
+ } else {
2044
+ await connectAttempt;
2045
+ }
2046
+ } catch {
2047
+ }
2048
+ }
2049
+ }
2050
+ if (!socket || socket.readyState !== WebSocket.OPEN) {
2051
+ if (hasTimeout && Date.now() - timeoutStartedAt >= timeoutMs) {
2052
+ throw new Error(RUN_NETWORK_COUNT_TIMEOUT_ERROR);
2053
+ }
2054
+ throw new Error("runNetworkCount: RTS socket is not connected");
2055
+ }
2056
+ const remainingTimeoutMs = hasTimeout ? Math.max(0, timeoutMs - (Date.now() - timeoutStartedAt)) : null;
2057
+ if (remainingTimeoutMs !== null && remainingTimeoutMs <= 0) {
2058
+ throw new Error(RUN_NETWORK_COUNT_TIMEOUT_ERROR);
2059
+ }
2060
+ const resolvedOptions = options.key ? options : {
2061
+ ...options,
2062
+ key: makeRunQueryKey()
2063
+ };
2064
+ const queryKey = computeRtsQueryKey(query, resolvedOptions);
2065
+ const cbKey = `${modelName}.${queryKey}`;
2066
+ return await new Promise((resolve, reject) => {
2067
+ let settled = false;
2068
+ let timeoutId = null;
2069
+ const cleanup = () => {
2070
+ const callbacks = countCallbacks.get(cbKey);
2071
+ callbacks?.delete(callback);
2072
+ if (callbacks && callbacks.size === 0) countCallbacks.delete(cbKey);
2073
+ if (timeoutId !== null) {
2074
+ clearRuntimeTimeout(timeoutId);
2075
+ }
2076
+ };
2077
+ const settle = (next) => {
2078
+ if (settled) return;
2079
+ settled = true;
2080
+ cleanup();
2081
+ next();
2082
+ };
2083
+ const callback = (error, count, context) => {
2084
+ if (error) {
2085
+ settle(() => reject(typeof error === "string" ? new Error(error) : error));
2086
+ return;
2087
+ }
2088
+ if (count === void 0) {
2089
+ settle(() => reject(new Error("runNetworkCount: invalid count payload")));
2090
+ return;
2091
+ }
2092
+ settle(() => resolve({
2093
+ count,
2094
+ context
2095
+ }));
2096
+ };
2097
+ const set = countCallbacks.get(cbKey) ?? /* @__PURE__ */ new Set();
2098
+ set.add(callback);
2099
+ countCallbacks.set(cbKey, set);
2100
+ if (remainingTimeoutMs !== null) {
2101
+ timeoutId = setRuntimeTimeout(() => {
2102
+ settle(() => reject(new Error(RUN_NETWORK_COUNT_TIMEOUT_ERROR)));
2103
+ }, remainingTimeoutMs);
2104
+ }
2105
+ sendToServer({
2106
+ type: "run-count",
2107
+ modelName,
2108
+ queryKey,
2109
+ query,
2110
+ options: resolvedOptions
2111
+ });
2112
+ });
2113
+ };
2114
+ const sendMessage = (event, payload) => {
2115
+ sendToServer({
2116
+ type: "event",
2117
+ event,
2118
+ payload
2119
+ });
2120
+ };
2121
+ const onMessage = (event, callback) => {
2122
+ const set = messageCallbacks.get(event) ?? /* @__PURE__ */ new Set();
2123
+ set.add(callback);
2124
+ messageCallbacks.set(event, set);
2125
+ return () => {
2126
+ const callbacks = messageCallbacks.get(event);
2127
+ callbacks?.delete(callback);
2128
+ if (callbacks && callbacks.size === 0) messageCallbacks.delete(event);
2129
+ };
2130
+ };
2131
+ const normalizePageInfo = (value) => {
2132
+ if (!value || typeof value !== "object") return void 0;
2133
+ if (Array.isArray(value)) return void 0;
2134
+ const raw = value;
2135
+ if (typeof raw.hasNextPage !== "boolean" || typeof raw.hasPrevPage !== "boolean") return void 0;
2136
+ const nextCursor = typeof raw.nextCursor === "string" && raw.nextCursor ? raw.nextCursor : void 0;
2137
+ const prevCursor = typeof raw.prevCursor === "string" && raw.prevCursor ? raw.prevCursor : void 0;
2138
+ return {
2139
+ hasNextPage: raw.hasNextPage,
2140
+ hasPrevPage: raw.hasPrevPage,
2141
+ ...nextCursor ? {
2142
+ nextCursor
2143
+ } : {},
2144
+ ...prevCursor ? {
2145
+ prevCursor
2146
+ } : {}
2147
+ };
2148
+ };
2149
+ const normalizeTotalCount = (value) => {
2150
+ if (typeof value !== "number") return void 0;
2151
+ if (!Number.isFinite(value) || value < 0) return void 0;
2152
+ return Math.floor(value);
2153
+ };
2154
+ const getDocId = (doc) => {
2155
+ if (!doc || typeof doc !== "object") return "";
2156
+ const id = doc._id;
2157
+ return typeof id === "string" ? id.trim() : "";
2158
+ };
2159
+ const dedupeById = (docs) => {
2160
+ const seen = /* @__PURE__ */ new Set();
2161
+ const merged = [];
2162
+ for (const doc of docs) {
2163
+ const id = getDocId(doc);
2164
+ if (id && seen.has(id)) continue;
2165
+ if (id) seen.add(id);
2166
+ merged.push(doc);
2167
+ }
2168
+ return merged;
2169
+ };
2170
+ const flattenLoadedPages = (previousPages, headPage, nextPages) => {
2171
+ const merged = [];
2172
+ for (const page of previousPages) merged.push(...page.nodes);
2173
+ if (headPage) merged.push(...headPage.nodes);
2174
+ for (const page of nextPages) merged.push(...page.nodes);
2175
+ return dedupeById(merged);
2176
+ };
2177
+ const assertIncludeOnlyProjection = (projection) => {
2178
+ if (!projection) return;
2179
+ for (const [path, value] of Object.entries(projection)) {
2180
+ if (!path.trim()) continue;
2181
+ if (value !== 1) {
2182
+ throw new Error("useQuery: projection must be include-only (value 1); exclusion projection is not supported");
2183
+ }
2184
+ }
2185
+ };
2186
+ const useQuery = (modelName, query = {}, options = {}) => {
2187
+ if (typeof modelName !== "string" || modelName.trim().length === 0) {
2188
+ throw new Error("useQuery: modelName must be a non-empty string");
2189
+ }
2190
+ assertIncludeOnlyProjection(options.projection);
2191
+ const id = useId();
2192
+ const enabled = options.enabled ?? true;
2193
+ const ssrEnabled = options.ssr !== false;
2194
+ const refreshOnMount = options.refreshOnMount === true;
2195
+ const key = options.key ?? id;
2196
+ const queryJson = JSON.stringify(query);
2197
+ const projectionJson = options.projection ? JSON.stringify(options.projection) : "";
2198
+ const sortJson = options.sort ? JSON.stringify(options.sort) : "";
2199
+ const limitStr = typeof options.limit === "number" ? String(options.limit) : "";
2200
+ const populateJson = options.populate ? JSON.stringify(options.populate) : "";
2201
+ const paginationJson = options.pagination ? JSON.stringify(options.pagination) : "";
2202
+ preparePopulateCacheOptions({
2203
+ projection: options.projection,
2204
+ populate: options.populate
2205
+ }, "useQuery");
2206
+ const isPaginated = Boolean(options.pagination);
2207
+ const queryKey = computeRtsQueryKey(query, {
2208
+ key,
2209
+ projection: options.projection,
2210
+ sort: options.sort,
2211
+ limit: options.limit,
2212
+ populate: options.populate,
2213
+ pagination: options.pagination
2214
+ });
2215
+ const ssrRuntime = useRtsSsrRuntime();
2216
+ if (enabled && ssrEnabled && ssrRuntime) {
2217
+ ssrRuntime.registerQuery({
2218
+ modelName,
2219
+ query,
2220
+ options: {
2221
+ key,
2222
+ projection: options.projection,
2223
+ sort: options.sort,
2224
+ limit: options.limit,
2225
+ populate: options.populate,
2226
+ pagination: options.pagination
2227
+ },
2228
+ queryKey
2229
+ });
2230
+ }
2231
+ const seedDataRaw = useMemo(() => enabled && ssrEnabled ? ssrRuntime ? ssrRuntime.getQueryData(modelName, queryKey) : peekHydratedRtsQueryData(modelName, queryKey) : void 0, [enabled, ssrEnabled, ssrRuntime, modelName, queryKey]);
2232
+ const seedPageInfoRaw = useMemo(() => enabled && ssrEnabled ? ssrRuntime ? ssrRuntime.getQueryPageInfo(modelName, queryKey) : peekHydratedRtsQueryPageInfo(modelName, queryKey) : void 0, [enabled, ssrEnabled, ssrRuntime, modelName, queryKey]);
2233
+ const seedTotalCountRaw = useMemo(() => enabled && ssrEnabled ? ssrRuntime ? ssrRuntime.getQueryTotalCount(modelName, queryKey) : peekHydratedRtsQueryTotalCount(modelName, queryKey) : void 0, [enabled, ssrEnabled, ssrRuntime, modelName, queryKey]);
2234
+ const hasSeedData = Array.isArray(seedDataRaw);
2235
+ const seedData = hasSeedData ? seedDataRaw : void 0;
2236
+ const seedPageInfo = normalizePageInfo(seedPageInfoRaw);
2237
+ const seedTotalCount = normalizeTotalCount(seedTotalCountRaw);
2238
+ const seedJson = (() => {
2239
+ if (!hasSeedData) return "";
2240
+ try {
2241
+ return JSON.stringify(seedDataRaw);
2242
+ } catch {
2243
+ return "";
2244
+ }
2245
+ })();
2246
+ const seedPageInfoJson = (() => {
2247
+ if (!seedPageInfo) return "";
2248
+ try {
2249
+ return JSON.stringify(seedPageInfo);
2250
+ } catch {
2251
+ return "";
2252
+ }
2253
+ })();
2254
+ const seedTotalCountStr = seedTotalCount !== void 0 ? String(seedTotalCount) : "";
2255
+ const [data, setData] = useState(() => isPaginated ? void 0 : seedData);
2256
+ const [headPage, setHeadPage] = useState(() => isPaginated && seedData ? {
2257
+ nodes: seedData,
2258
+ ...seedPageInfo ? {
2259
+ pageInfo: seedPageInfo
2260
+ } : {}
2261
+ } : null);
2262
+ const [totalCount, setTotalCount] = useState(() => isPaginated ? seedTotalCount : void 0);
2263
+ const [previousPages, setPreviousPages] = useState([]);
2264
+ const [nextPages, setNextPages] = useState([]);
2265
+ const [source, setSource] = useState(() => hasSeedData ? "cache" : void 0);
2266
+ const [error, setError] = useState(void 0);
2267
+ const [loading, setLoading] = useState(enabled && !hasSeedData);
2268
+ const [pagingDirection, setPagingDirection] = useState(null);
2269
+ const hasFirstReply = useRef(false);
2270
+ const hasNetworkReply = useRef(false);
2271
+ const lastDataJsonRef = useRef("");
2272
+ const previousPagesRef = useRef([]);
2273
+ const nextPagesRef = useRef([]);
2274
+ const headPageRef = useRef(null);
2275
+ const pagingDirectionRef = useRef(null);
2276
+ useEffect(() => {
2277
+ previousPagesRef.current = previousPages;
2278
+ }, [previousPages]);
2279
+ useEffect(() => {
2280
+ nextPagesRef.current = nextPages;
2281
+ }, [nextPages]);
2282
+ useEffect(() => {
2283
+ headPageRef.current = headPage;
2284
+ }, [headPage]);
2285
+ useEffect(() => {
2286
+ pagingDirectionRef.current = pagingDirection;
2287
+ }, [pagingDirection]);
2288
+ useEffect(() => {
2289
+ if (!ssrRuntime && enabled && ssrEnabled && hasSeedData) {
2290
+ consumeHydratedRtsQueryData(modelName, queryKey);
2291
+ }
2292
+ hasFirstReply.current = hasSeedData;
2293
+ hasNetworkReply.current = false;
2294
+ lastDataJsonRef.current = seedJson;
2295
+ setError(void 0);
2296
+ setPreviousPages([]);
2297
+ setNextPages([]);
2298
+ if (!enabled) {
2299
+ setLoading(false);
2300
+ setData(void 0);
2301
+ setHeadPage(null);
2302
+ setTotalCount(void 0);
2303
+ setSource(void 0);
2304
+ return;
2305
+ }
2306
+ if (hasSeedData) {
2307
+ const nextSeedData = seedData;
2308
+ setLoading(false);
2309
+ setSource("cache");
2310
+ if (isPaginated) {
2311
+ setHeadPage({
2312
+ nodes: nextSeedData,
2313
+ ...seedPageInfo ? {
2314
+ pageInfo: seedPageInfo
2315
+ } : {}
2316
+ });
2317
+ setTotalCount(seedTotalCount);
2318
+ setData(void 0);
2319
+ } else {
2320
+ setData(nextSeedData);
2321
+ setTotalCount(void 0);
2322
+ setHeadPage(null);
2323
+ }
2324
+ return;
2325
+ }
2326
+ setData(void 0);
2327
+ setHeadPage(null);
2328
+ setTotalCount(void 0);
2329
+ setLoading(true);
2330
+ }, [enabled, ssrEnabled, ssrRuntime, modelName, queryKey, hasSeedData, seedJson, seedPageInfoJson, seedTotalCountStr, isPaginated]);
2331
+ useEffect(() => {
2332
+ if (!enabled) return;
2333
+ const runInitialNetworkQuery = refreshOnMount || !hasSeedData;
2334
+ const runInitialLocalQuery = !hasSeedData && !isPaginated;
2335
+ const unsubscribe = registerQuery(modelName, query, {
2336
+ key,
2337
+ projection: options.projection,
2338
+ sort: options.sort,
2339
+ limit: options.limit,
2340
+ populate: options.populate,
2341
+ pagination: options.pagination
2342
+ }, (err, result, context) => {
2343
+ if (context.source === "cache" && hasNetworkReply.current) return;
2344
+ if (context.source === "network") {
2345
+ hasNetworkReply.current = true;
2346
+ }
2347
+ setLoading(false);
2348
+ if (err) {
2349
+ setError(err);
2350
+ return;
2351
+ }
2352
+ if (!Array.isArray(result)) return;
2353
+ if (context.source === "network" && context.isLocal && options.skipLocal && hasFirstReply.current) {
2354
+ return;
2355
+ }
2356
+ hasFirstReply.current = true;
2357
+ const networkPageInfo = context.source === "network" ? context.pageInfo : void 0;
2358
+ const networkTotalCount = context.source === "network" ? context.totalCount : void 0;
2359
+ const payloadForHash = isPaginated ? {
2360
+ result,
2361
+ pageInfo: networkPageInfo,
2362
+ totalCount: networkTotalCount
2363
+ } : result;
2364
+ let nextJson = "";
2365
+ try {
2366
+ nextJson = JSON.stringify(payloadForHash);
2367
+ } catch {
2368
+ nextJson = "";
2369
+ }
2370
+ if (nextJson && nextJson === lastDataJsonRef.current) {
2371
+ setSource(context.source);
2372
+ return;
2373
+ }
2374
+ lastDataJsonRef.current = nextJson;
2375
+ setSource(context.source);
2376
+ setError(void 0);
2377
+ if (isPaginated) {
2378
+ setHeadPage({
2379
+ nodes: result,
2380
+ ...networkPageInfo ? {
2381
+ pageInfo: networkPageInfo
2382
+ } : {}
2383
+ });
2384
+ setTotalCount((current) => networkTotalCount === void 0 ? current : networkTotalCount);
2385
+ return;
2386
+ }
2387
+ setData(result);
2388
+ setTotalCount(void 0);
2389
+ }, {
2390
+ runInitialNetworkQuery,
2391
+ runInitialLocalQuery
2392
+ });
2393
+ return () => {
2394
+ unsubscribe?.();
2395
+ };
2396
+ }, [enabled, modelName, queryKey, queryJson, projectionJson, sortJson, limitStr, populateJson, paginationJson, hasSeedData, refreshOnMount, isPaginated]);
2397
+ const effectivePageInfo = useMemo(() => {
2398
+ if (!isPaginated) return void 0;
2399
+ const firstPageInfo = previousPages.length > 0 ? previousPages[0]?.pageInfo : headPage?.pageInfo;
2400
+ const lastPageInfo = nextPages.length > 0 ? nextPages[nextPages.length - 1]?.pageInfo : headPage?.pageInfo;
2401
+ if (!firstPageInfo && !lastPageInfo) return void 0;
2402
+ const hasPrevPage = Boolean(firstPageInfo?.hasPrevPage);
2403
+ const hasNextPage = Boolean(lastPageInfo?.hasNextPage);
2404
+ const prevCursor = firstPageInfo?.prevCursor;
2405
+ const nextCursor = lastPageInfo?.nextCursor;
2406
+ return {
2407
+ hasPrevPage,
2408
+ hasNextPage,
2409
+ ...prevCursor ? {
2410
+ prevCursor
2411
+ } : {},
2412
+ ...nextCursor ? {
2413
+ nextCursor
2414
+ } : {}
2415
+ };
2416
+ }, [headPage, isPaginated, nextPages, previousPages]);
2417
+ const mergedPaginatedData = useMemo(() => {
2418
+ if (!isPaginated) return void 0;
2419
+ if (!headPage && previousPages.length === 0 && nextPages.length === 0) return void 0;
2420
+ return flattenLoadedPages(previousPages, headPage, nextPages);
2421
+ }, [headPage, isPaginated, nextPages, previousPages]);
2422
+ const fetchNext = async () => {
2423
+ if (!enabled || !isPaginated || !options.pagination) return false;
2424
+ if (pagingDirectionRef.current) return false;
2425
+ const currentHead = headPageRef.current;
2426
+ const currentNextPages = nextPagesRef.current;
2427
+ const cursor = currentNextPages.length > 0 ? currentNextPages[currentNextPages.length - 1]?.pageInfo?.nextCursor : currentHead?.pageInfo?.nextCursor;
2428
+ const hasNextPage_0 = currentNextPages.length > 0 ? Boolean(currentNextPages[currentNextPages.length - 1]?.pageInfo?.hasNextPage) : Boolean(currentHead?.pageInfo?.hasNextPage);
2429
+ if (!cursor || !hasNextPage_0) return false;
2430
+ setPagingDirection("next");
2431
+ setLoading(true);
2432
+ setError(void 0);
2433
+ try {
2434
+ const response = await runNetworkQuery({
2435
+ modelName,
2436
+ query,
2437
+ options: {
2438
+ key,
2439
+ projection: options.projection,
2440
+ sort: options.sort,
2441
+ limit: options.limit,
2442
+ populate: options.populate,
2443
+ pagination: {
2444
+ ...options.pagination,
2445
+ direction: "next",
2446
+ cursor
2447
+ }
2448
+ }
2449
+ });
2450
+ if (!Array.isArray(response.data)) return false;
2451
+ const page = {
2452
+ nodes: response.data,
2453
+ ...response.context.source === "network" && response.context.pageInfo ? {
2454
+ pageInfo: response.context.pageInfo
2455
+ } : {}
2456
+ };
2457
+ setSource("network");
2458
+ if (response.context.source === "network" && response.context.totalCount !== void 0) {
2459
+ setTotalCount(response.context.totalCount);
2460
+ }
2461
+ setNextPages((current_0) => [...current_0, page]);
2462
+ return true;
2463
+ } catch (err_0) {
2464
+ setError(err_0);
2465
+ return false;
2466
+ } finally {
2467
+ setPagingDirection(null);
2468
+ setLoading(false);
2469
+ }
2470
+ };
2471
+ const fetchPrevious = async () => {
2472
+ if (!enabled || !isPaginated || !options.pagination) return false;
2473
+ if (pagingDirectionRef.current) return false;
2474
+ const currentHead_0 = headPageRef.current;
2475
+ const currentPreviousPages = previousPagesRef.current;
2476
+ const cursor_0 = currentPreviousPages.length > 0 ? currentPreviousPages[0]?.pageInfo?.prevCursor : currentHead_0?.pageInfo?.prevCursor;
2477
+ const hasPrevPage_0 = currentPreviousPages.length > 0 ? Boolean(currentPreviousPages[0]?.pageInfo?.hasPrevPage) : Boolean(currentHead_0?.pageInfo?.hasPrevPage);
2478
+ if (!cursor_0 || !hasPrevPage_0) return false;
2479
+ setPagingDirection("prev");
2480
+ setLoading(true);
2481
+ setError(void 0);
2482
+ try {
2483
+ const response_0 = await runNetworkQuery({
2484
+ modelName,
2485
+ query,
2486
+ options: {
2487
+ key,
2488
+ projection: options.projection,
2489
+ sort: options.sort,
2490
+ limit: options.limit,
2491
+ populate: options.populate,
2492
+ pagination: {
2493
+ ...options.pagination,
2494
+ direction: "prev",
2495
+ cursor: cursor_0
2496
+ }
2497
+ }
2498
+ });
2499
+ if (!Array.isArray(response_0.data)) return false;
2500
+ const page_0 = {
2501
+ nodes: response_0.data,
2502
+ ...response_0.context.source === "network" && response_0.context.pageInfo ? {
2503
+ pageInfo: response_0.context.pageInfo
2504
+ } : {}
2505
+ };
2506
+ setSource("network");
2507
+ if (response_0.context.source === "network" && response_0.context.totalCount !== void 0) {
2508
+ setTotalCount(response_0.context.totalCount);
2509
+ }
2510
+ setPreviousPages((current_1) => [page_0, ...current_1]);
2511
+ return true;
2512
+ } catch (err_1) {
2513
+ setError(err_1);
2514
+ return false;
2515
+ } finally {
2516
+ setPagingDirection(null);
2517
+ setLoading(false);
2518
+ }
2519
+ };
2520
+ const resetPagination = () => {
2521
+ if (!isPaginated) return;
2522
+ setPreviousPages([]);
2523
+ setNextPages([]);
2524
+ };
2525
+ return useMemo(() => ({
2526
+ data: isPaginated ? mergedPaginatedData : data,
2527
+ pageInfo: effectivePageInfo,
2528
+ totalCount: isPaginated ? totalCount : void 0,
2529
+ source,
2530
+ error,
2531
+ loading,
2532
+ fetchNext,
2533
+ fetchPrevious,
2534
+ resetPagination
2535
+ }), [data, effectivePageInfo, error, fetchNext, fetchPrevious, isPaginated, loading, mergedPaginatedData, source, totalCount]);
2536
+ };
2537
+ export {
2538
+ runNetworkCount as A,
2539
+ runNetworkQuery as B,
2540
+ runQuery as C,
2541
+ sendMessage as D,
2542
+ syncRtsChanges as E,
2543
+ updateDocs as F,
2544
+ RtsSsrRuntimeProvider as R,
2545
+ STATIC_RPCBASE_RTS_HYDRATION_DATA_KEY as S,
2546
+ consumeHydratedRtsCount as a,
2547
+ peekHydratedRtsQueryData as b,
2548
+ clearHydratedRtsQueryData as c,
2549
+ peekHydratedRtsQueryPageInfo as d,
2550
+ peekHydratedRtsQueryTotalCount as e,
2551
+ computeRtsQueryKey as f,
2552
+ useRtsSsrRuntime as g,
2553
+ hydrateRtsFromWindow as h,
2554
+ addLocalTxn as i,
2555
+ configureRtsPouchStore as j,
2556
+ connect as k,
2557
+ deleteDocs as l,
2558
+ destroyAllCollections as m,
2559
+ destroyCollection as n,
2560
+ disconnect as o,
2561
+ peekHydratedRtsCount as p,
2562
+ getCollection as q,
2563
+ registerCount as r,
2564
+ getConnectionError as s,
2565
+ getConnectionStatus as t,
2566
+ useQuery as u,
2567
+ onConnectionStatusChange as v,
2568
+ onMessage as w,
2569
+ reconnect as x,
2570
+ registerQuery as y,
2571
+ resetRtsPouchStore as z
2572
+ };
2573
+ //# sourceMappingURL=useQuery-NQNva6kg.js.map