@rpcbase/client 0.465.0 → 0.466.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/errorReporting-BaPe0tII.js +18 -0
- package/dist/errorReporting-BaPe0tII.js.map +1 -0
- package/dist/getServerApiClient-Dtfqua8g.js +165 -0
- package/dist/getServerApiClient-Dtfqua8g.js.map +1 -0
- package/dist/index.js +2999 -3258
- package/dist/index.js.map +1 -1
- package/dist/instrument.js +32 -40
- package/dist/instrument.js.map +1 -1
- package/dist/rts/index.js +2 -317
- package/dist/rts-C9lvbdZX.js +3161 -0
- package/dist/rts-C9lvbdZX.js.map +1 -0
- package/dist/spatial-nav/index.js +4988 -4327
- package/dist/spatial-nav/index.js.map +1 -1
- package/dist/throttle-BFr_Yc5g.js +620 -0
- package/dist/throttle-BFr_Yc5g.js.map +1 -0
- package/package.json +1 -1
- package/dist/errorReporting-CVoUUKxW.js +0 -18
- package/dist/errorReporting-CVoUUKxW.js.map +0 -1
- package/dist/getServerApiClient-DnG8591C.js +0 -192
- package/dist/getServerApiClient-DnG8591C.js.map +0 -1
- package/dist/rts/index.js.map +0 -1
- package/dist/throttle-CXOc9Dto.js +0 -396
- package/dist/throttle-CXOc9Dto.js.map +0 -1
- package/dist/useQuery-DZqYIJog.js +0 -3474
- package/dist/useQuery-DZqYIJog.js.map +0 -1
|
@@ -0,0 +1,3161 @@
|
|
|
1
|
+
import { c } from "react/compiler-runtime";
|
|
2
|
+
import { createContext, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/rts/ssrHydration.tsx
|
|
5
|
+
var STATIC_RPCBASE_RTS_HYDRATION_DATA_KEY = "__staticRpcbaseRtsHydrationData";
|
|
6
|
+
var RtsSsrRuntimeContext = createContext(null);
|
|
7
|
+
var hydrationDataStore = /* @__PURE__ */ new Map();
|
|
8
|
+
var hydrationPageInfoStore = /* @__PURE__ */ new Map();
|
|
9
|
+
var hydrationTotalCountStore = /* @__PURE__ */ new Map();
|
|
10
|
+
var hydrationCountStore = /* @__PURE__ */ new Map();
|
|
11
|
+
var makeStoreKey = (modelName, queryKey) => `${modelName}.${queryKey}`;
|
|
12
|
+
var normalizeStringOrNull = (value) => {
|
|
13
|
+
if (typeof value !== "string") return null;
|
|
14
|
+
const normalized = value.trim();
|
|
15
|
+
return normalized ? normalized : null;
|
|
16
|
+
};
|
|
17
|
+
var normalizePageInfo$2 = (value) => {
|
|
18
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
19
|
+
const raw = value;
|
|
20
|
+
if (typeof raw.hasNextPage !== "boolean" || typeof raw.hasPrevPage !== "boolean") return void 0;
|
|
21
|
+
const nextCursor = typeof raw.nextCursor === "string" && raw.nextCursor ? raw.nextCursor : void 0;
|
|
22
|
+
const prevCursor = typeof raw.prevCursor === "string" && raw.prevCursor ? raw.prevCursor : void 0;
|
|
23
|
+
return {
|
|
24
|
+
hasNextPage: raw.hasNextPage,
|
|
25
|
+
hasPrevPage: raw.hasPrevPage,
|
|
26
|
+
...nextCursor ? { nextCursor } : {},
|
|
27
|
+
...prevCursor ? { prevCursor } : {}
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
var normalizeTotalCount$2 = (value) => {
|
|
31
|
+
if (typeof value !== "number") return void 0;
|
|
32
|
+
if (!Number.isFinite(value) || value < 0) return void 0;
|
|
33
|
+
return Math.floor(value);
|
|
34
|
+
};
|
|
35
|
+
var parseHydrationData = (value) => {
|
|
36
|
+
if (!value || typeof value !== "object") return null;
|
|
37
|
+
const raw = value;
|
|
38
|
+
if (raw.v !== 1) return null;
|
|
39
|
+
if (!Array.isArray(raw.queries)) return null;
|
|
40
|
+
const rawCounts = Array.isArray(raw.counts) ? raw.counts : [];
|
|
41
|
+
const queries = [];
|
|
42
|
+
for (const entry of raw.queries) {
|
|
43
|
+
if (!entry || typeof entry !== "object") continue;
|
|
44
|
+
const query = entry;
|
|
45
|
+
const modelName = normalizeStringOrNull(query.modelName);
|
|
46
|
+
const queryKey = normalizeStringOrNull(query.queryKey);
|
|
47
|
+
if (!modelName || !queryKey) continue;
|
|
48
|
+
if (!Array.isArray(query.data)) continue;
|
|
49
|
+
const pageInfo = normalizePageInfo$2(query.pageInfo);
|
|
50
|
+
const totalCount = normalizeTotalCount$2(query.totalCount);
|
|
51
|
+
queries.push({
|
|
52
|
+
modelName,
|
|
53
|
+
queryKey,
|
|
54
|
+
data: query.data,
|
|
55
|
+
...pageInfo ? { pageInfo } : {},
|
|
56
|
+
...totalCount !== void 0 ? { totalCount } : {}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const counts = [];
|
|
60
|
+
for (const entry of rawCounts) {
|
|
61
|
+
if (!entry || typeof entry !== "object") continue;
|
|
62
|
+
const countEntry = entry;
|
|
63
|
+
const modelName = normalizeStringOrNull(countEntry.modelName);
|
|
64
|
+
const queryKey = normalizeStringOrNull(countEntry.queryKey);
|
|
65
|
+
const count = normalizeTotalCount$2(countEntry.count);
|
|
66
|
+
if (!modelName || !queryKey || count === void 0) continue;
|
|
67
|
+
counts.push({
|
|
68
|
+
modelName,
|
|
69
|
+
queryKey,
|
|
70
|
+
count
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
v: 1,
|
|
75
|
+
tenantId: normalizeStringOrNull(raw.tenantId),
|
|
76
|
+
uid: normalizeStringOrNull(raw.uid),
|
|
77
|
+
queries,
|
|
78
|
+
counts
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
var hydrateRtsFromWindow = () => {
|
|
82
|
+
if (typeof window === "undefined") return;
|
|
83
|
+
const browserWindow = window;
|
|
84
|
+
const raw = browserWindow[STATIC_RPCBASE_RTS_HYDRATION_DATA_KEY];
|
|
85
|
+
delete browserWindow[STATIC_RPCBASE_RTS_HYDRATION_DATA_KEY];
|
|
86
|
+
const parsed = parseHydrationData(raw);
|
|
87
|
+
if (!parsed) return;
|
|
88
|
+
hydrationDataStore.clear();
|
|
89
|
+
hydrationPageInfoStore.clear();
|
|
90
|
+
hydrationTotalCountStore.clear();
|
|
91
|
+
hydrationCountStore.clear();
|
|
92
|
+
for (const query of parsed.queries) {
|
|
93
|
+
hydrationDataStore.set(makeStoreKey(query.modelName, query.queryKey), query.data);
|
|
94
|
+
hydrationPageInfoStore.set(makeStoreKey(query.modelName, query.queryKey), query.pageInfo);
|
|
95
|
+
hydrationTotalCountStore.set(makeStoreKey(query.modelName, query.queryKey), query.totalCount);
|
|
96
|
+
}
|
|
97
|
+
for (const count of parsed.counts) hydrationCountStore.set(makeStoreKey(count.modelName, count.queryKey), count.count);
|
|
98
|
+
};
|
|
99
|
+
var peekHydratedRtsQueryData = (modelName, queryKey) => {
|
|
100
|
+
return hydrationDataStore.get(makeStoreKey(modelName, queryKey));
|
|
101
|
+
};
|
|
102
|
+
var peekHydratedRtsQueryPageInfo = (modelName, queryKey) => {
|
|
103
|
+
return hydrationPageInfoStore.get(makeStoreKey(modelName, queryKey));
|
|
104
|
+
};
|
|
105
|
+
var peekHydratedRtsQueryTotalCount = (modelName, queryKey) => {
|
|
106
|
+
return hydrationTotalCountStore.get(makeStoreKey(modelName, queryKey));
|
|
107
|
+
};
|
|
108
|
+
var peekHydratedRtsCount = (modelName, queryKey) => {
|
|
109
|
+
return hydrationCountStore.get(makeStoreKey(modelName, queryKey));
|
|
110
|
+
};
|
|
111
|
+
var consumeHydratedRtsQueryData = (modelName, queryKey) => {
|
|
112
|
+
const key = makeStoreKey(modelName, queryKey);
|
|
113
|
+
hydrationDataStore.delete(key);
|
|
114
|
+
hydrationPageInfoStore.delete(key);
|
|
115
|
+
hydrationTotalCountStore.delete(key);
|
|
116
|
+
};
|
|
117
|
+
var consumeHydratedRtsCount = (modelName, queryKey) => {
|
|
118
|
+
hydrationCountStore.delete(makeStoreKey(modelName, queryKey));
|
|
119
|
+
};
|
|
120
|
+
var clearHydratedRtsQueryData = () => {
|
|
121
|
+
hydrationDataStore.clear();
|
|
122
|
+
hydrationPageInfoStore.clear();
|
|
123
|
+
hydrationTotalCountStore.clear();
|
|
124
|
+
hydrationCountStore.clear();
|
|
125
|
+
};
|
|
126
|
+
var RtsSsrRuntimeProvider = (t0) => {
|
|
127
|
+
const $ = c(3);
|
|
128
|
+
const { value, children } = t0;
|
|
129
|
+
let t1;
|
|
130
|
+
if ($[0] !== children || $[1] !== value) {
|
|
131
|
+
t1 = /* @__PURE__ */ jsx(RtsSsrRuntimeContext.Provider, {
|
|
132
|
+
value,
|
|
133
|
+
children
|
|
134
|
+
});
|
|
135
|
+
$[0] = children;
|
|
136
|
+
$[1] = value;
|
|
137
|
+
$[2] = t1;
|
|
138
|
+
} else t1 = $[2];
|
|
139
|
+
return t1;
|
|
140
|
+
};
|
|
141
|
+
var useRtsSsrRuntime = () => {
|
|
142
|
+
return useContext(RtsSsrRuntimeContext);
|
|
143
|
+
};
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region src/rts/queryWindow.ts
|
|
146
|
+
var RTS_QUERY_WINDOW_MAX_COUNT = 4096;
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region src/rts/runtimeStorage.ts
|
|
149
|
+
var memoryStore = /* @__PURE__ */ new Map();
|
|
150
|
+
var reactNativeStorage = null;
|
|
151
|
+
var MMKV_STORAGE_ID = "rpcbase-rts";
|
|
152
|
+
var memoryStorage = {
|
|
153
|
+
getItem: (key) => memoryStore.get(key) ?? null,
|
|
154
|
+
setItem: (key, value) => {
|
|
155
|
+
memoryStore.set(key, String(value));
|
|
156
|
+
},
|
|
157
|
+
removeItem: (key) => {
|
|
158
|
+
memoryStore.delete(key);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
var asRuntimeStorage = (value) => {
|
|
162
|
+
if (!value || typeof value !== "object") return null;
|
|
163
|
+
const candidate = value;
|
|
164
|
+
if (typeof candidate.getItem !== "function" || typeof candidate.setItem !== "function" || typeof candidate.removeItem !== "function") return null;
|
|
165
|
+
return {
|
|
166
|
+
getItem: candidate.getItem.bind(value),
|
|
167
|
+
setItem: candidate.setItem.bind(value),
|
|
168
|
+
removeItem: candidate.removeItem.bind(value)
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
var isReactNativeRuntime = () => {
|
|
172
|
+
if (typeof navigator === "undefined") return false;
|
|
173
|
+
return navigator.product === "ReactNative";
|
|
174
|
+
};
|
|
175
|
+
var getRuntimeRequire = () => {
|
|
176
|
+
const globalRequire = globalThis.require;
|
|
177
|
+
if (typeof globalRequire === "function") return globalRequire;
|
|
178
|
+
try {
|
|
179
|
+
return (0, eval)("require");
|
|
180
|
+
} catch {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
var getReactNativeStorage = () => {
|
|
185
|
+
if (reactNativeStorage) return reactNativeStorage;
|
|
186
|
+
const runtimeRequire = getRuntimeRequire();
|
|
187
|
+
if (!runtimeRequire) throw new Error("RTS storage: react-native-mmkv is required in React Native runtime");
|
|
188
|
+
let mmkvModule;
|
|
189
|
+
try {
|
|
190
|
+
mmkvModule = runtimeRequire("react-native-mmkv");
|
|
191
|
+
} catch (error) {
|
|
192
|
+
const runtimeError = error instanceof Error ? ` ${error.message}` : "";
|
|
193
|
+
throw new Error(`RTS storage: react-native-mmkv is required in React Native runtime.${runtimeError}`);
|
|
194
|
+
}
|
|
195
|
+
const MMKV = mmkvModule.MMKV;
|
|
196
|
+
if (typeof MMKV !== "function") throw new Error("RTS storage: invalid react-native-mmkv module shape");
|
|
197
|
+
const mmkv = new MMKV({ id: MMKV_STORAGE_ID });
|
|
198
|
+
reactNativeStorage = {
|
|
199
|
+
getItem: (key) => {
|
|
200
|
+
const value = mmkv.getString(key);
|
|
201
|
+
return typeof value === "string" ? value : null;
|
|
202
|
+
},
|
|
203
|
+
setItem: (key, value) => {
|
|
204
|
+
mmkv.set(key, String(value));
|
|
205
|
+
},
|
|
206
|
+
removeItem: (key) => {
|
|
207
|
+
mmkv.delete(key);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
return reactNativeStorage;
|
|
211
|
+
};
|
|
212
|
+
var getRuntimeStorage = () => {
|
|
213
|
+
if (isReactNativeRuntime()) return getReactNativeStorage();
|
|
214
|
+
const direct = asRuntimeStorage(globalThis.localStorage);
|
|
215
|
+
if (direct) return direct;
|
|
216
|
+
const windowStorage = asRuntimeStorage(globalThis.window?.localStorage);
|
|
217
|
+
if (windowStorage) return windowStorage;
|
|
218
|
+
return memoryStorage;
|
|
219
|
+
};
|
|
220
|
+
//#endregion
|
|
221
|
+
//#region src/rts/pouchStore.ts
|
|
222
|
+
var UNDERSCORE_PREFIX = "$_";
|
|
223
|
+
var DEFAULT_FIND_LIMIT = 4096;
|
|
224
|
+
var INDEXED_DB_ADAPTER = "indexeddb";
|
|
225
|
+
var REACT_NATIVE_SQLITE_ADAPTER = "react-native-sqlite";
|
|
226
|
+
var QUERY_WINDOW_COLLECTION = "$query-windows-v1";
|
|
227
|
+
var QUERY_WINDOW_DOC_TYPE = "rts-query-window";
|
|
228
|
+
var QUERY_WINDOW_SCHEMA_VERSION = 1;
|
|
229
|
+
var QUERY_WINDOW_TTL_MS = 10080 * 60 * 1e3;
|
|
230
|
+
var QUERY_WINDOW_WRITE_ATTEMPTS = 4;
|
|
231
|
+
var storeConfig = null;
|
|
232
|
+
var pouchDbPromise = null;
|
|
233
|
+
var lastAppliedPrefix = null;
|
|
234
|
+
var activePouchAdapter = INDEXED_DB_ADAPTER;
|
|
235
|
+
var collections = /* @__PURE__ */ new Map();
|
|
236
|
+
var dbNamesByPrefix = /* @__PURE__ */ new Map();
|
|
237
|
+
var unwrapDefault = (mod) => {
|
|
238
|
+
if (!mod || typeof mod !== "object") return mod;
|
|
239
|
+
return mod.default ?? mod;
|
|
240
|
+
};
|
|
241
|
+
var computeBasePrefix = ({ tenantId, appName }) => {
|
|
242
|
+
let prefix = "rb/";
|
|
243
|
+
if (appName) prefix += `${appName}/`;
|
|
244
|
+
prefix += `${tenantId}/`;
|
|
245
|
+
return prefix;
|
|
246
|
+
};
|
|
247
|
+
var getDbNamesKey = (prefix) => `rb:rts:pouchDbs:${prefix}`;
|
|
248
|
+
var getPrefixOverrideKey = ({ tenantId, appName }) => `rb:rts:pouchPrefix:${appName ?? ""}:${tenantId}`;
|
|
249
|
+
var readPrefixOverride = ({ tenantId, appName }) => {
|
|
250
|
+
const storage = getRuntimeStorage();
|
|
251
|
+
try {
|
|
252
|
+
const value = storage.getItem(getPrefixOverrideKey({
|
|
253
|
+
tenantId,
|
|
254
|
+
appName
|
|
255
|
+
}));
|
|
256
|
+
if (!value) return null;
|
|
257
|
+
if (!value.endsWith("/")) return `${value}/`;
|
|
258
|
+
return value;
|
|
259
|
+
} catch {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
var getPrefix = () => {
|
|
264
|
+
if (!storeConfig) throw new Error("RTS PouchDB store is not configured");
|
|
265
|
+
if (storeConfig.prefix) return storeConfig.prefix;
|
|
266
|
+
const basePrefix = computeBasePrefix({
|
|
267
|
+
tenantId: storeConfig.tenantId,
|
|
268
|
+
appName: storeConfig.appName
|
|
269
|
+
});
|
|
270
|
+
return readPrefixOverride({
|
|
271
|
+
tenantId: storeConfig.tenantId,
|
|
272
|
+
appName: storeConfig.appName
|
|
273
|
+
}) ?? basePrefix;
|
|
274
|
+
};
|
|
275
|
+
var loadDbNames = (prefix) => {
|
|
276
|
+
const existing = dbNamesByPrefix.get(prefix);
|
|
277
|
+
if (existing) return existing;
|
|
278
|
+
const names = /* @__PURE__ */ new Set();
|
|
279
|
+
const storage = getRuntimeStorage();
|
|
280
|
+
try {
|
|
281
|
+
const raw = storage.getItem(getDbNamesKey(prefix));
|
|
282
|
+
if (raw) {
|
|
283
|
+
const parsed = JSON.parse(raw);
|
|
284
|
+
if (Array.isArray(parsed)) {
|
|
285
|
+
for (const value of parsed) if (typeof value === "string" && value) names.add(value);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
} catch {
|
|
289
|
+
return names;
|
|
290
|
+
}
|
|
291
|
+
dbNamesByPrefix.set(prefix, names);
|
|
292
|
+
return names;
|
|
293
|
+
};
|
|
294
|
+
var persistDbNames = (prefix, names) => {
|
|
295
|
+
const storage = getRuntimeStorage();
|
|
296
|
+
try {
|
|
297
|
+
if (!names.size) {
|
|
298
|
+
storage.removeItem(getDbNamesKey(prefix));
|
|
299
|
+
dbNamesByPrefix.delete(prefix);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
storage.setItem(getDbNamesKey(prefix), JSON.stringify(Array.from(names)));
|
|
303
|
+
dbNamesByPrefix.set(prefix, names);
|
|
304
|
+
} catch {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
var registerDbName = (prefix, dbName) => {
|
|
309
|
+
if (!prefix || !dbName) return;
|
|
310
|
+
const names = loadDbNames(prefix);
|
|
311
|
+
if (names.has(dbName)) return;
|
|
312
|
+
names.add(dbName);
|
|
313
|
+
persistDbNames(prefix, names);
|
|
314
|
+
};
|
|
315
|
+
var unregisterDbName = (prefix, dbName) => {
|
|
316
|
+
if (!prefix || !dbName) return;
|
|
317
|
+
const names = loadDbNames(prefix);
|
|
318
|
+
if (!names.delete(dbName)) return;
|
|
319
|
+
persistDbNames(prefix, names);
|
|
320
|
+
};
|
|
321
|
+
var getPouchDb = async () => {
|
|
322
|
+
if (!pouchDbPromise) pouchDbPromise = (async () => {
|
|
323
|
+
const [core, findPlugin] = await Promise.all([import("pouchdb-core"), import("pouchdb-find")]);
|
|
324
|
+
const PouchDB = unwrapDefault(core);
|
|
325
|
+
if (isReactNativeRuntime()) {
|
|
326
|
+
const moduleName = "pouchdb-adapter-react-native-sqlite";
|
|
327
|
+
let sqliteAdapterModule;
|
|
328
|
+
try {
|
|
329
|
+
sqliteAdapterModule = await import(
|
|
330
|
+
/* @vite-ignore */
|
|
331
|
+
moduleName
|
|
332
|
+
);
|
|
333
|
+
} catch (error) {
|
|
334
|
+
const runtimeError = error instanceof Error ? ` ${error.message}` : "";
|
|
335
|
+
throw new Error(`RTS PouchDB: missing react-native sqlite adapter. Install \`pouchdb-adapter-react-native-sqlite\` in the app.${runtimeError}`);
|
|
336
|
+
}
|
|
337
|
+
PouchDB.plugin(unwrapDefault(sqliteAdapterModule));
|
|
338
|
+
activePouchAdapter = REACT_NATIVE_SQLITE_ADAPTER;
|
|
339
|
+
} else {
|
|
340
|
+
const indexedDbAdapter = await import("pouchdb-adapter-indexeddb");
|
|
341
|
+
PouchDB.plugin(unwrapDefault(indexedDbAdapter));
|
|
342
|
+
activePouchAdapter = INDEXED_DB_ADAPTER;
|
|
343
|
+
}
|
|
344
|
+
PouchDB.plugin(unwrapDefault(findPlugin));
|
|
345
|
+
return PouchDB;
|
|
346
|
+
})();
|
|
347
|
+
return pouchDbPromise;
|
|
348
|
+
};
|
|
349
|
+
var applyPrefix = (PouchDB, prefix = getPrefix()) => {
|
|
350
|
+
if (prefix === lastAppliedPrefix) return;
|
|
351
|
+
PouchDB.prefix = prefix;
|
|
352
|
+
lastAppliedPrefix = prefix;
|
|
353
|
+
};
|
|
354
|
+
var configureRtsPouchStore = (config) => {
|
|
355
|
+
storeConfig = config;
|
|
356
|
+
lastAppliedPrefix = null;
|
|
357
|
+
collections.clear();
|
|
358
|
+
};
|
|
359
|
+
var getCollection = async (modelName, options) => {
|
|
360
|
+
const prefix = getPrefix();
|
|
361
|
+
const PouchDB = await getPouchDb();
|
|
362
|
+
applyPrefix(PouchDB, prefix);
|
|
363
|
+
const dbName = `${options.uid}/${modelName}`;
|
|
364
|
+
const dbKey = `${prefix}${dbName}`;
|
|
365
|
+
const existing = collections.get(dbKey);
|
|
366
|
+
if (existing) return existing;
|
|
367
|
+
registerDbName(prefix, dbName);
|
|
368
|
+
const db = new PouchDB(dbName, {
|
|
369
|
+
adapter: activePouchAdapter,
|
|
370
|
+
revs_limit: 1
|
|
371
|
+
});
|
|
372
|
+
collections.set(dbKey, db);
|
|
373
|
+
return db;
|
|
374
|
+
};
|
|
375
|
+
var getQueryWindowScope = (uid) => {
|
|
376
|
+
if (!storeConfig) throw new Error("RTS PouchDB store is not configured");
|
|
377
|
+
return {
|
|
378
|
+
appName: storeConfig.appName ?? "",
|
|
379
|
+
tenantId: storeConfig.tenantId,
|
|
380
|
+
uid
|
|
381
|
+
};
|
|
382
|
+
};
|
|
383
|
+
var getQueryWindowFingerprint = (scope, modelName, queryKey) => JSON.stringify([
|
|
384
|
+
QUERY_WINDOW_SCHEMA_VERSION,
|
|
385
|
+
scope.appName,
|
|
386
|
+
scope.tenantId,
|
|
387
|
+
scope.uid,
|
|
388
|
+
modelName,
|
|
389
|
+
queryKey
|
|
390
|
+
]);
|
|
391
|
+
var getQueryWindowDocumentId = (fingerprint) => `${QUERY_WINDOW_DOC_TYPE}:${encodeURIComponent(fingerprint)}`;
|
|
392
|
+
var getPouchErrorStatus = (error) => {
|
|
393
|
+
if (!error || typeof error !== "object") return void 0;
|
|
394
|
+
const status = error.status;
|
|
395
|
+
return typeof status === "number" && Number.isFinite(status) ? status : void 0;
|
|
396
|
+
};
|
|
397
|
+
var getPouchDocument = async (collection, id) => {
|
|
398
|
+
try {
|
|
399
|
+
return await collection.get(id);
|
|
400
|
+
} catch (error) {
|
|
401
|
+
if (getPouchErrorStatus(error) === 404) return null;
|
|
402
|
+
throw error;
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
var normalizeQueryWindowPageInfo = (value) => {
|
|
406
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
407
|
+
const raw = value;
|
|
408
|
+
if (typeof raw.hasNextPage !== "boolean" || typeof raw.hasPrevPage !== "boolean") return null;
|
|
409
|
+
if (raw.nextCursor !== void 0 && typeof raw.nextCursor !== "string") return null;
|
|
410
|
+
if (raw.prevCursor !== void 0 && typeof raw.prevCursor !== "string") return null;
|
|
411
|
+
const nextCursor = typeof raw.nextCursor === "string" && raw.nextCursor ? raw.nextCursor : void 0;
|
|
412
|
+
const prevCursor = typeof raw.prevCursor === "string" && raw.prevCursor ? raw.prevCursor : void 0;
|
|
413
|
+
return {
|
|
414
|
+
hasNextPage: raw.hasNextPage,
|
|
415
|
+
hasPrevPage: raw.hasPrevPage,
|
|
416
|
+
...nextCursor ? { nextCursor } : {},
|
|
417
|
+
...prevCursor ? { prevCursor } : {}
|
|
418
|
+
};
|
|
419
|
+
};
|
|
420
|
+
var isValidQueryWindowServerVersion = (value) => {
|
|
421
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
422
|
+
};
|
|
423
|
+
var isQueryWindowData = (value) => Array.isArray(value) && value.every((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)));
|
|
424
|
+
var parseQueryWindowSnapshot = (doc, expected) => {
|
|
425
|
+
if (doc.type !== QUERY_WINDOW_DOC_TYPE || doc.schemaVersion !== QUERY_WINDOW_SCHEMA_VERSION) return null;
|
|
426
|
+
if (doc.fingerprint !== expected.fingerprint) return null;
|
|
427
|
+
if (doc.modelName !== expected.modelName || doc.queryKey !== expected.queryKey) return null;
|
|
428
|
+
const scope = doc.scope;
|
|
429
|
+
if (!scope || typeof scope !== "object" || Array.isArray(scope)) return null;
|
|
430
|
+
const rawScope = scope;
|
|
431
|
+
if (rawScope.appName !== expected.scope.appName || rawScope.tenantId !== expected.scope.tenantId || rawScope.uid !== expected.scope.uid) return null;
|
|
432
|
+
if (!isQueryWindowData(doc.data)) return null;
|
|
433
|
+
const pageInfo = normalizeQueryWindowPageInfo(doc.pageInfo);
|
|
434
|
+
if (!pageInfo) return null;
|
|
435
|
+
const requestedCount = doc.requestedCount;
|
|
436
|
+
if (!Number.isSafeInteger(requestedCount) || requestedCount <= 0 || requestedCount > 4096 || doc.data.length > requestedCount) return null;
|
|
437
|
+
const storedAt = doc.storedAt;
|
|
438
|
+
if (typeof storedAt !== "number" || !Number.isFinite(storedAt) || storedAt < 0) return null;
|
|
439
|
+
const totalCount = doc.totalCount;
|
|
440
|
+
if (totalCount !== void 0 && (!Number.isSafeInteger(totalCount) || totalCount < 0)) return null;
|
|
441
|
+
const serverVersion = doc.serverVersion;
|
|
442
|
+
if (serverVersion !== void 0 && !isValidQueryWindowServerVersion(serverVersion)) return null;
|
|
443
|
+
const serverEpoch = doc.serverEpoch;
|
|
444
|
+
if (serverEpoch !== void 0 && (typeof serverEpoch !== "string" || !serverEpoch)) return null;
|
|
445
|
+
if (serverEpoch === void 0 !== (serverVersion === void 0)) return null;
|
|
446
|
+
return {
|
|
447
|
+
data: doc.data,
|
|
448
|
+
pageInfo,
|
|
449
|
+
...typeof totalCount === "number" ? { totalCount } : {},
|
|
450
|
+
requestedCount,
|
|
451
|
+
...serverEpoch !== void 0 ? { serverEpoch } : {},
|
|
452
|
+
...serverVersion !== void 0 ? { serverVersion } : {},
|
|
453
|
+
storedAt
|
|
454
|
+
};
|
|
455
|
+
};
|
|
456
|
+
var deleteQueryWindowDocument = async (collection, doc) => {
|
|
457
|
+
const id = typeof doc._id === "string" ? doc._id : "";
|
|
458
|
+
const rev = typeof doc._rev === "string" ? doc._rev : "";
|
|
459
|
+
if (!id || !rev) return;
|
|
460
|
+
await collection.put({
|
|
461
|
+
_id: id,
|
|
462
|
+
_rev: rev,
|
|
463
|
+
_deleted: true
|
|
464
|
+
}).catch(() => void 0);
|
|
465
|
+
};
|
|
466
|
+
var readQueryWindowSnapshot = async ({ modelName, queryKey, uid, now = Date.now() }) => {
|
|
467
|
+
const scope = getQueryWindowScope(uid);
|
|
468
|
+
const fingerprint = getQueryWindowFingerprint(scope, modelName, queryKey);
|
|
469
|
+
const id = getQueryWindowDocumentId(fingerprint);
|
|
470
|
+
const collection = await getCollection(QUERY_WINDOW_COLLECTION, { uid });
|
|
471
|
+
const doc = await getPouchDocument(collection, id);
|
|
472
|
+
if (!doc) return { hit: false };
|
|
473
|
+
const snapshot = parseQueryWindowSnapshot(doc, {
|
|
474
|
+
fingerprint,
|
|
475
|
+
scope,
|
|
476
|
+
modelName,
|
|
477
|
+
queryKey
|
|
478
|
+
});
|
|
479
|
+
if (!snapshot || now - snapshot.storedAt >= QUERY_WINDOW_TTL_MS) {
|
|
480
|
+
await deleteQueryWindowDocument(collection, doc);
|
|
481
|
+
return { hit: false };
|
|
482
|
+
}
|
|
483
|
+
return {
|
|
484
|
+
hit: true,
|
|
485
|
+
snapshot
|
|
486
|
+
};
|
|
487
|
+
};
|
|
488
|
+
var writeQueryWindowSnapshot = async ({ modelName, queryKey, uid, data, pageInfo: rawPageInfo, totalCount, requestedCount, serverEpoch, serverVersion, storedAt: rawStoredAt }) => {
|
|
489
|
+
if (!modelName.trim()) throw new Error("writeQueryWindowSnapshot: modelName must be a non-empty string");
|
|
490
|
+
if (!queryKey) throw new Error("writeQueryWindowSnapshot: queryKey must be a non-empty string");
|
|
491
|
+
if (!uid.trim()) throw new Error("writeQueryWindowSnapshot: uid must be a non-empty string");
|
|
492
|
+
if (!isQueryWindowData(data)) throw new Error("writeQueryWindowSnapshot: data must contain objects");
|
|
493
|
+
const pageInfo = normalizeQueryWindowPageInfo(rawPageInfo);
|
|
494
|
+
if (!pageInfo) throw new Error("writeQueryWindowSnapshot: invalid pageInfo");
|
|
495
|
+
if (!Number.isSafeInteger(requestedCount) || requestedCount <= 0 || requestedCount > 4096 || data.length > requestedCount) throw new Error("writeQueryWindowSnapshot: requestedCount must cover data and be between 1 and 4096");
|
|
496
|
+
if (totalCount !== void 0 && (!Number.isSafeInteger(totalCount) || totalCount < 0)) throw new Error("writeQueryWindowSnapshot: totalCount must be a non-negative integer");
|
|
497
|
+
if (serverVersion !== void 0 && !isValidQueryWindowServerVersion(serverVersion)) throw new Error("writeQueryWindowSnapshot: invalid serverVersion");
|
|
498
|
+
if (serverEpoch !== void 0 && (typeof serverEpoch !== "string" || !serverEpoch)) throw new Error("writeQueryWindowSnapshot: invalid serverEpoch");
|
|
499
|
+
if (serverEpoch === void 0 !== (serverVersion === void 0)) throw new Error("writeQueryWindowSnapshot: serverEpoch and serverVersion must be provided together");
|
|
500
|
+
const storedAt = rawStoredAt ?? Date.now();
|
|
501
|
+
if (!Number.isFinite(storedAt) || storedAt < 0) throw new Error("writeQueryWindowSnapshot: storedAt must be a non-negative finite number");
|
|
502
|
+
const scope = getQueryWindowScope(uid);
|
|
503
|
+
const fingerprint = getQueryWindowFingerprint(scope, modelName, queryKey);
|
|
504
|
+
const id = getQueryWindowDocumentId(fingerprint);
|
|
505
|
+
const collection = await getCollection(QUERY_WINDOW_COLLECTION, { uid });
|
|
506
|
+
const snapshot = {
|
|
507
|
+
data,
|
|
508
|
+
pageInfo,
|
|
509
|
+
...totalCount !== void 0 ? { totalCount } : {},
|
|
510
|
+
requestedCount,
|
|
511
|
+
...serverEpoch !== void 0 ? { serverEpoch } : {},
|
|
512
|
+
...serverVersion !== void 0 ? { serverVersion } : {},
|
|
513
|
+
storedAt
|
|
514
|
+
};
|
|
515
|
+
let lastConflict;
|
|
516
|
+
for (let attempt = 0; attempt < QUERY_WINDOW_WRITE_ATTEMPTS; attempt += 1) {
|
|
517
|
+
const current = await getPouchDocument(collection, id);
|
|
518
|
+
const currentSnapshot = current ? parseQueryWindowSnapshot(current, {
|
|
519
|
+
fingerprint,
|
|
520
|
+
scope,
|
|
521
|
+
modelName,
|
|
522
|
+
queryKey
|
|
523
|
+
}) : null;
|
|
524
|
+
if (currentSnapshot && (currentSnapshot.serverEpoch === serverEpoch && currentSnapshot.serverVersion !== void 0 && serverVersion !== void 0 && currentSnapshot.serverVersion >= serverVersion || (!serverEpoch || currentSnapshot.serverEpoch !== serverEpoch) && currentSnapshot.storedAt > storedAt)) return currentSnapshot;
|
|
525
|
+
const rev = typeof current?._rev === "string" ? current._rev : void 0;
|
|
526
|
+
const doc = {
|
|
527
|
+
_id: id,
|
|
528
|
+
...rev ? { _rev: rev } : {},
|
|
529
|
+
type: QUERY_WINDOW_DOC_TYPE,
|
|
530
|
+
schemaVersion: QUERY_WINDOW_SCHEMA_VERSION,
|
|
531
|
+
fingerprint,
|
|
532
|
+
scope,
|
|
533
|
+
modelName,
|
|
534
|
+
queryKey,
|
|
535
|
+
data,
|
|
536
|
+
pageInfo,
|
|
537
|
+
...totalCount !== void 0 ? { totalCount } : {},
|
|
538
|
+
requestedCount,
|
|
539
|
+
...serverEpoch !== void 0 ? { serverEpoch } : {},
|
|
540
|
+
...serverVersion !== void 0 ? { serverVersion } : {},
|
|
541
|
+
storedAt
|
|
542
|
+
};
|
|
543
|
+
try {
|
|
544
|
+
await collection.put(doc);
|
|
545
|
+
return snapshot;
|
|
546
|
+
} catch (error) {
|
|
547
|
+
if (getPouchErrorStatus(error) !== 409) throw error;
|
|
548
|
+
lastConflict = error;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
throw lastConflict ?? /* @__PURE__ */ new Error("writeQueryWindowSnapshot: failed to persist snapshot");
|
|
552
|
+
};
|
|
553
|
+
var invalidateQueryWindowSnapshots = async ({ uid, modelName }) => {
|
|
554
|
+
const collection = await getCollection(QUERY_WINDOW_COLLECTION, { uid });
|
|
555
|
+
const selector = {
|
|
556
|
+
type: QUERY_WINDOW_DOC_TYPE,
|
|
557
|
+
...modelName ? { modelName } : {}
|
|
558
|
+
};
|
|
559
|
+
for (let batch = 0; batch < 32; batch += 1) {
|
|
560
|
+
const { docs } = await collection.find({
|
|
561
|
+
selector,
|
|
562
|
+
fields: ["_id", "_rev"],
|
|
563
|
+
limit: DEFAULT_FIND_LIMIT
|
|
564
|
+
});
|
|
565
|
+
const deletions = docs.map((doc) => ({
|
|
566
|
+
_id: typeof doc._id === "string" ? doc._id : "",
|
|
567
|
+
_rev: typeof doc._rev === "string" ? doc._rev : "",
|
|
568
|
+
_deleted: true
|
|
569
|
+
})).filter((doc) => doc._id && doc._rev);
|
|
570
|
+
if (!deletions.length) return;
|
|
571
|
+
const results = await collection.bulkDocs(deletions);
|
|
572
|
+
if (Array.isArray(results) && results.some((result) => {
|
|
573
|
+
if (!result || typeof result !== "object") return false;
|
|
574
|
+
const record = result;
|
|
575
|
+
return record.error === true || record.status === 409;
|
|
576
|
+
})) continue;
|
|
577
|
+
if (docs.length < DEFAULT_FIND_LIMIT) return;
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
var replaceQueryKeys = (value, replaceKey) => {
|
|
581
|
+
if (typeof value !== "object" || value === null) return value;
|
|
582
|
+
if (Array.isArray(value)) return value.map((item) => replaceQueryKeys(item, replaceKey));
|
|
583
|
+
const obj = value;
|
|
584
|
+
const next = Object.create(Object.getPrototypeOf(obj));
|
|
585
|
+
for (const key of Object.keys(obj)) {
|
|
586
|
+
if (/^\$/.test(key) || /\.\d+$/.test(key)) throw new Error(`replaceQueryKeys: Unexpected key format: ${key}`);
|
|
587
|
+
const newKey = replaceKey(key);
|
|
588
|
+
next[newKey] = replaceQueryKeys(obj[key], replaceKey);
|
|
589
|
+
}
|
|
590
|
+
return next;
|
|
591
|
+
};
|
|
592
|
+
var isRecord = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
593
|
+
var splitPath = (path) => path.split(".").map((part) => part.trim()).filter(Boolean);
|
|
594
|
+
var getKeys = (obj, parentKey = "") => {
|
|
595
|
+
const keys = [];
|
|
596
|
+
for (const key of Object.keys(obj)) {
|
|
597
|
+
const nextKey = parentKey ? `${parentKey}.${key}` : key;
|
|
598
|
+
const value = obj[key];
|
|
599
|
+
if (Array.isArray(value)) {
|
|
600
|
+
const arrayKeys = /* @__PURE__ */ new Set();
|
|
601
|
+
for (const item of value) if (isRecord(item)) for (const nestedKey of getKeys(item, nextKey)) arrayKeys.add(nestedKey);
|
|
602
|
+
if (arrayKeys.size > 0) keys.push(...arrayKeys);
|
|
603
|
+
else keys.push(nextKey);
|
|
604
|
+
} else if (isRecord(value)) keys.push(...getKeys(value, nextKey));
|
|
605
|
+
else keys.push(nextKey);
|
|
606
|
+
}
|
|
607
|
+
return keys;
|
|
608
|
+
};
|
|
609
|
+
var satisfiesProjection = (doc, projection) => {
|
|
610
|
+
const docKeys = new Set(getKeys(doc));
|
|
611
|
+
const projectionKeys = new Set(Object.keys(projection).filter((key) => projection[key] === 1));
|
|
612
|
+
if (!projectionKeys.has("_id")) docKeys.delete("_id");
|
|
613
|
+
if (projectionKeys.size > docKeys.size) return false;
|
|
614
|
+
for (const key of projectionKeys) if (!docKeys.has(key)) return false;
|
|
615
|
+
return true;
|
|
616
|
+
};
|
|
617
|
+
var collectValuesAtPath = (value, parts, index) => {
|
|
618
|
+
if (index >= parts.length) return [value];
|
|
619
|
+
if (Array.isArray(value)) return value.flatMap((item) => collectValuesAtPath(item, parts, index));
|
|
620
|
+
if (!isRecord(value)) return [void 0];
|
|
621
|
+
return collectValuesAtPath(value[parts[index]], parts, index + 1);
|
|
622
|
+
};
|
|
623
|
+
var getValueAtPath = (doc, path) => {
|
|
624
|
+
const parts = splitPath(path);
|
|
625
|
+
if (!parts.length) return void 0;
|
|
626
|
+
const values = collectValuesAtPath(doc, parts, 0);
|
|
627
|
+
if (values.length === 0) return void 0;
|
|
628
|
+
if (values.length === 1) return values[0];
|
|
629
|
+
return values;
|
|
630
|
+
};
|
|
631
|
+
var pathCrossesArrayBeforeLeaf = (value, parts, index = 0) => {
|
|
632
|
+
if (index >= parts.length - 1) return false;
|
|
633
|
+
if (!isRecord(value)) return false;
|
|
634
|
+
const next = value[parts[index]];
|
|
635
|
+
if (Array.isArray(next)) return true;
|
|
636
|
+
return pathCrossesArrayBeforeLeaf(next, parts, index + 1);
|
|
637
|
+
};
|
|
638
|
+
var assignValueAtPath = (current, parts, index, value, valuesQueue) => {
|
|
639
|
+
const key = parts[index];
|
|
640
|
+
if (index === parts.length - 1) {
|
|
641
|
+
current[key] = valuesQueue ? valuesQueue.shift() : value;
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
const next = current[key];
|
|
645
|
+
if (Array.isArray(next)) {
|
|
646
|
+
for (const item of next) if (isRecord(item)) assignValueAtPath(item, parts, index + 1, value, valuesQueue);
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
if (!isRecord(next)) current[key] = {};
|
|
650
|
+
assignValueAtPath(current[key], parts, index + 1, value, valuesQueue);
|
|
651
|
+
};
|
|
652
|
+
var setValueAtPath = (doc, path, value) => {
|
|
653
|
+
const parts = splitPath(path);
|
|
654
|
+
if (!parts.length) return;
|
|
655
|
+
assignValueAtPath(doc, parts, 0, value, pathCrossesArrayBeforeLeaf(doc, parts) && Array.isArray(value) ? [...value] : void 0);
|
|
656
|
+
};
|
|
657
|
+
var unsetValueAtPathParts = (current, parts, index) => {
|
|
658
|
+
if (Array.isArray(current)) {
|
|
659
|
+
for (const item of current) unsetValueAtPathParts(item, parts, index);
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
if (!isRecord(current)) return;
|
|
663
|
+
const key = parts[index];
|
|
664
|
+
if (index === parts.length - 1) {
|
|
665
|
+
delete current[key];
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
unsetValueAtPathParts(current[key], parts, index + 1);
|
|
669
|
+
};
|
|
670
|
+
var unsetValueAtPath = (doc, path) => {
|
|
671
|
+
const parts = splitPath(path);
|
|
672
|
+
if (!parts.length) return;
|
|
673
|
+
unsetValueAtPathParts(doc, parts, 0);
|
|
674
|
+
};
|
|
675
|
+
var cloneDoc = (doc) => {
|
|
676
|
+
try {
|
|
677
|
+
return structuredClone(doc);
|
|
678
|
+
} catch {
|
|
679
|
+
return JSON.parse(JSON.stringify(doc));
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
var toProjectionSpec = (projection) => {
|
|
683
|
+
const spec = {};
|
|
684
|
+
for (const [key, value] of Object.entries(projection)) {
|
|
685
|
+
const path = key.trim();
|
|
686
|
+
if (!path) continue;
|
|
687
|
+
if (value === 1 || value === 0) spec[path] = value;
|
|
688
|
+
}
|
|
689
|
+
return spec;
|
|
690
|
+
};
|
|
691
|
+
var copyValueAtPath = (source, target, parts, index = 0) => {
|
|
692
|
+
if (!isRecord(source)) return false;
|
|
693
|
+
const key = parts[index];
|
|
694
|
+
const sourceValue = source[key];
|
|
695
|
+
if (sourceValue === void 0) return false;
|
|
696
|
+
if (index === parts.length - 1) {
|
|
697
|
+
target[key] = cloneDoc(sourceValue);
|
|
698
|
+
return true;
|
|
699
|
+
}
|
|
700
|
+
if (Array.isArray(sourceValue)) {
|
|
701
|
+
const existing = Array.isArray(target[key]) ? target[key] : [];
|
|
702
|
+
let copiedAny = false;
|
|
703
|
+
const nextArray = sourceValue.map((item, itemIndex) => {
|
|
704
|
+
const currentTarget = isRecord(existing[itemIndex]) ? existing[itemIndex] : {};
|
|
705
|
+
if (copyValueAtPath(item, currentTarget, parts, index + 1)) copiedAny = true;
|
|
706
|
+
return currentTarget;
|
|
707
|
+
});
|
|
708
|
+
if (copiedAny || sourceValue.length === 0) {
|
|
709
|
+
target[key] = nextArray;
|
|
710
|
+
return true;
|
|
711
|
+
}
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
const nextTarget = isRecord(target[key]) ? target[key] : {};
|
|
715
|
+
if (!copyValueAtPath(sourceValue, nextTarget, parts, index + 1)) return false;
|
|
716
|
+
target[key] = nextTarget;
|
|
717
|
+
return true;
|
|
718
|
+
};
|
|
719
|
+
var applyProjection = (doc, projection) => {
|
|
720
|
+
const spec = toProjectionSpec(projection);
|
|
721
|
+
const includeKeys = Object.keys(spec).filter((key) => spec[key] === 1);
|
|
722
|
+
const excludeKeys = Object.keys(spec).filter((key) => spec[key] === 0);
|
|
723
|
+
if (includeKeys.length > 0) {
|
|
724
|
+
const projected = {};
|
|
725
|
+
for (const key of includeKeys) {
|
|
726
|
+
const parts = splitPath(key);
|
|
727
|
+
if (parts.length > 0) copyValueAtPath(doc, projected, parts);
|
|
728
|
+
}
|
|
729
|
+
if (spec._id !== 0 && Object.hasOwn(doc, "_id")) projected._id = doc._id;
|
|
730
|
+
return projected;
|
|
731
|
+
}
|
|
732
|
+
const projected = cloneDoc(doc);
|
|
733
|
+
for (const key of excludeKeys) unsetValueAtPath(projected, key);
|
|
734
|
+
return projected;
|
|
735
|
+
};
|
|
736
|
+
var compareSort = (a, b, sort) => {
|
|
737
|
+
for (const key of Object.keys(sort)) {
|
|
738
|
+
const dir = sort[key];
|
|
739
|
+
const aVal = getValueAtPath(a, key);
|
|
740
|
+
const bVal = getValueAtPath(b, key);
|
|
741
|
+
if (typeof aVal === "number" && typeof bVal === "number") {
|
|
742
|
+
if (aVal < bVal) return -1 * dir;
|
|
743
|
+
if (aVal > bVal) return 1 * dir;
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
if (typeof aVal === "string" && typeof bVal === "string") {
|
|
747
|
+
if (aVal < bVal) return -1 * dir;
|
|
748
|
+
if (aVal > bVal) return 1 * dir;
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
return 0;
|
|
753
|
+
};
|
|
754
|
+
var extractDocId = (value) => {
|
|
755
|
+
if (typeof value === "string") return value;
|
|
756
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return "";
|
|
757
|
+
const id = value._id;
|
|
758
|
+
return typeof id === "string" ? id : "";
|
|
759
|
+
};
|
|
760
|
+
var matchValue = (docValue, matchValueRaw) => {
|
|
761
|
+
if (Array.isArray(matchValueRaw)) {
|
|
762
|
+
if (!Array.isArray(docValue)) return false;
|
|
763
|
+
if (docValue.length !== matchValueRaw.length) return false;
|
|
764
|
+
for (let i = 0; i < matchValueRaw.length; i += 1) {
|
|
765
|
+
const matched = matchValue(docValue[i], matchValueRaw[i]);
|
|
766
|
+
if (matched === null) return null;
|
|
767
|
+
if (!matched) return false;
|
|
768
|
+
}
|
|
769
|
+
return true;
|
|
770
|
+
}
|
|
771
|
+
if (matchValueRaw && typeof matchValueRaw === "object") {
|
|
772
|
+
const matchObj = matchValueRaw;
|
|
773
|
+
if (Object.keys(matchObj).some((key) => key.startsWith("$"))) return null;
|
|
774
|
+
if (!docValue || typeof docValue !== "object" || Array.isArray(docValue)) return false;
|
|
775
|
+
const docObj = docValue;
|
|
776
|
+
for (const key of Object.keys(matchObj)) {
|
|
777
|
+
const matched = matchValue(docObj[key], matchObj[key]);
|
|
778
|
+
if (matched === null) return null;
|
|
779
|
+
if (!matched) return false;
|
|
780
|
+
}
|
|
781
|
+
return true;
|
|
782
|
+
}
|
|
783
|
+
return Object.is(docValue, matchValueRaw);
|
|
784
|
+
};
|
|
785
|
+
var matchesSimpleQuery = (doc, query) => {
|
|
786
|
+
for (const [key, expected] of Object.entries(query)) {
|
|
787
|
+
if (key.startsWith("$")) return null;
|
|
788
|
+
const matched = matchValue(getValueAtPath(doc, key), expected);
|
|
789
|
+
if (matched === null) return null;
|
|
790
|
+
if (!matched) return false;
|
|
791
|
+
}
|
|
792
|
+
return true;
|
|
793
|
+
};
|
|
794
|
+
var remapDocFromStorage = (doc) => {
|
|
795
|
+
const next = {};
|
|
796
|
+
for (const [key, value] of Object.entries(doc)) {
|
|
797
|
+
const newKey = key.startsWith(UNDERSCORE_PREFIX) ? key.replace(/^\$_/, "") : key;
|
|
798
|
+
next[newKey] = value;
|
|
799
|
+
}
|
|
800
|
+
return next;
|
|
801
|
+
};
|
|
802
|
+
var runQueryInternal = async ({ modelName, query = {}, options, strictProjection = false }) => {
|
|
803
|
+
const collection = await getCollection(modelName, { uid: options.uid });
|
|
804
|
+
const replacedQuery = replaceQueryKeys(query, (key) => key.startsWith("_") && key !== "_id" ? `${UNDERSCORE_PREFIX}${key}` : key);
|
|
805
|
+
const limit = typeof options.limit === "number" ? Math.abs(options.limit) : DEFAULT_FIND_LIMIT;
|
|
806
|
+
const { docs } = await collection.find({
|
|
807
|
+
selector: replacedQuery,
|
|
808
|
+
limit
|
|
809
|
+
});
|
|
810
|
+
const mappedDocs = docs.map(({ _rev: _revIgnored, ...rest }) => remapDocFromStorage(rest));
|
|
811
|
+
let filteredDocs = mappedDocs;
|
|
812
|
+
if (options.projection) {
|
|
813
|
+
if (strictProjection && mappedDocs.some((entry) => !satisfiesProjection(entry, options.projection))) return {
|
|
814
|
+
data: [],
|
|
815
|
+
context: { source: "cache" },
|
|
816
|
+
projectionMismatch: true
|
|
817
|
+
};
|
|
818
|
+
filteredDocs = filteredDocs.filter((entry) => satisfiesProjection(entry, options.projection));
|
|
819
|
+
}
|
|
820
|
+
let result = filteredDocs;
|
|
821
|
+
if (options.sort) result = result.sort((a, b) => compareSort(a, b, options.sort));
|
|
822
|
+
return {
|
|
823
|
+
data: result,
|
|
824
|
+
context: { source: "cache" },
|
|
825
|
+
projectionMismatch: false
|
|
826
|
+
};
|
|
827
|
+
};
|
|
828
|
+
var runQuery = async ({ modelName, query = {}, options }) => {
|
|
829
|
+
const result = await runQueryInternal({
|
|
830
|
+
modelName,
|
|
831
|
+
query,
|
|
832
|
+
options
|
|
833
|
+
});
|
|
834
|
+
return {
|
|
835
|
+
data: result.data,
|
|
836
|
+
context: result.context
|
|
837
|
+
};
|
|
838
|
+
};
|
|
839
|
+
var addWriteDoc = (writes, modelName, doc) => {
|
|
840
|
+
const docsById = writes.get(modelName) ?? /* @__PURE__ */ new Map();
|
|
841
|
+
docsById.set(doc._id, doc);
|
|
842
|
+
writes.set(modelName, docsById);
|
|
843
|
+
};
|
|
844
|
+
var sanitizePopulatedDoc = (doc, populate, writes) => {
|
|
845
|
+
const next = cloneDoc(doc);
|
|
846
|
+
for (const entry of populate) {
|
|
847
|
+
const crossesArray = pathCrossesArrayBeforeLeaf(next, splitPath(entry.path));
|
|
848
|
+
const value = getValueAtPath(next, entry.path);
|
|
849
|
+
if (value === void 0) continue;
|
|
850
|
+
if (Array.isArray(value)) {
|
|
851
|
+
const ids = [];
|
|
852
|
+
const nextValues = [];
|
|
853
|
+
for (const candidate of value) {
|
|
854
|
+
const id = extractDocId(candidate);
|
|
855
|
+
if (id) ids.push(id);
|
|
856
|
+
if (crossesArray) nextValues.push(id || null);
|
|
857
|
+
if (!entry.model) continue;
|
|
858
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) continue;
|
|
859
|
+
const candidateDoc = candidate;
|
|
860
|
+
const nested = entry.populate?.length ? sanitizePopulatedDoc(candidateDoc, entry.populate, writes) : cloneDoc(candidateDoc);
|
|
861
|
+
addWriteDoc(writes, entry.model, nested);
|
|
862
|
+
}
|
|
863
|
+
setValueAtPath(next, entry.path, crossesArray ? nextValues : ids);
|
|
864
|
+
continue;
|
|
865
|
+
}
|
|
866
|
+
const id = extractDocId(value);
|
|
867
|
+
setValueAtPath(next, entry.path, id || null);
|
|
868
|
+
if (!id) continue;
|
|
869
|
+
if (!entry.model) continue;
|
|
870
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
871
|
+
const valueDoc = value;
|
|
872
|
+
const nested = entry.populate?.length ? sanitizePopulatedDoc(valueDoc, entry.populate, writes) : cloneDoc(valueDoc);
|
|
873
|
+
addWriteDoc(writes, entry.model, nested);
|
|
874
|
+
}
|
|
875
|
+
return next;
|
|
876
|
+
};
|
|
877
|
+
var updatePopulatedDocs = async ({ modelName, data, uid, populate }) => {
|
|
878
|
+
if (!data.length) return;
|
|
879
|
+
const writes = /* @__PURE__ */ new Map();
|
|
880
|
+
await updateDocs(modelName, data.map((doc) => sanitizePopulatedDoc(doc, populate, writes)), uid);
|
|
881
|
+
for (const [targetModelName, docsById] of writes.entries()) {
|
|
882
|
+
if (!docsById.size) continue;
|
|
883
|
+
await updateDocs(targetModelName, Array.from(docsById.values()), uid);
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
var loadProjectedDocsByIds = async ({ modelName, ids, uid, projection }) => {
|
|
887
|
+
const uniqueIds = Array.from(new Set(ids.filter(Boolean)));
|
|
888
|
+
if (!uniqueIds.length) return {
|
|
889
|
+
rawDocsById: /* @__PURE__ */ new Map(),
|
|
890
|
+
projectedDocsById: /* @__PURE__ */ new Map(),
|
|
891
|
+
projectionMismatch: false
|
|
892
|
+
};
|
|
893
|
+
const { docs } = await (await getCollection(modelName, { uid })).find({
|
|
894
|
+
selector: { _id: { $in: uniqueIds } },
|
|
895
|
+
limit: uniqueIds.length
|
|
896
|
+
});
|
|
897
|
+
const rawDocsById = /* @__PURE__ */ new Map();
|
|
898
|
+
const projectedDocsById = /* @__PURE__ */ new Map();
|
|
899
|
+
let projectionMismatch = false;
|
|
900
|
+
for (const rawDoc of docs) {
|
|
901
|
+
const id = typeof rawDoc._id === "string" ? rawDoc._id : "";
|
|
902
|
+
if (!id) continue;
|
|
903
|
+
const { _rev: _revIgnored, ...rest } = rawDoc;
|
|
904
|
+
const remapped = remapDocFromStorage(rest);
|
|
905
|
+
if (!satisfiesProjection(remapped, projection)) {
|
|
906
|
+
projectionMismatch = true;
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
909
|
+
rawDocsById.set(id, remapped);
|
|
910
|
+
projectedDocsById.set(id, applyProjection(remapped, projection));
|
|
911
|
+
}
|
|
912
|
+
if (projectedDocsById.size < uniqueIds.length) projectionMismatch = true;
|
|
913
|
+
return {
|
|
914
|
+
rawDocsById,
|
|
915
|
+
projectedDocsById,
|
|
916
|
+
projectionMismatch
|
|
917
|
+
};
|
|
918
|
+
};
|
|
919
|
+
var hydratePopulateEntries = async ({ docs, populate, uid }) => {
|
|
920
|
+
const currentDocs = docs;
|
|
921
|
+
for (const entry of populate) {
|
|
922
|
+
if (!entry.model) return {
|
|
923
|
+
hit: false,
|
|
924
|
+
data: []
|
|
925
|
+
};
|
|
926
|
+
const descriptors = currentDocs.map((doc) => {
|
|
927
|
+
const crossesArray = pathCrossesArrayBeforeLeaf(doc, splitPath(entry.path));
|
|
928
|
+
const rawValue = getValueAtPath(doc, entry.path);
|
|
929
|
+
const isArray = Array.isArray(rawValue);
|
|
930
|
+
const values = isArray ? rawValue : [rawValue];
|
|
931
|
+
const ids = values.map((candidate) => extractDocId(candidate)).filter(Boolean);
|
|
932
|
+
return {
|
|
933
|
+
doc,
|
|
934
|
+
hasValue: rawValue !== void 0,
|
|
935
|
+
isArray,
|
|
936
|
+
crossesArray,
|
|
937
|
+
values,
|
|
938
|
+
ids
|
|
939
|
+
};
|
|
940
|
+
});
|
|
941
|
+
const allIds = descriptors.flatMap((descriptor) => descriptor.ids);
|
|
942
|
+
const loaded = await loadProjectedDocsByIds({
|
|
943
|
+
modelName: entry.model,
|
|
944
|
+
ids: allIds,
|
|
945
|
+
uid,
|
|
946
|
+
projection: entry.select
|
|
947
|
+
});
|
|
948
|
+
if (loaded.projectionMismatch) return {
|
|
949
|
+
hit: false,
|
|
950
|
+
data: []
|
|
951
|
+
};
|
|
952
|
+
let populatedDocsById = loaded.projectedDocsById;
|
|
953
|
+
if (entry.match) {
|
|
954
|
+
const filtered = /* @__PURE__ */ new Map();
|
|
955
|
+
for (const [id, candidate] of loaded.rawDocsById.entries()) {
|
|
956
|
+
const matched = matchesSimpleQuery(candidate, entry.match);
|
|
957
|
+
if (matched === null) return {
|
|
958
|
+
hit: false,
|
|
959
|
+
data: []
|
|
960
|
+
};
|
|
961
|
+
if (!matched) continue;
|
|
962
|
+
const projected = populatedDocsById.get(id);
|
|
963
|
+
if (projected) filtered.set(id, projected);
|
|
964
|
+
}
|
|
965
|
+
populatedDocsById = filtered;
|
|
966
|
+
}
|
|
967
|
+
if (entry.populate?.length) {
|
|
968
|
+
const nestedResult = await hydratePopulateEntries({
|
|
969
|
+
docs: Array.from(populatedDocsById.values()).map((candidate) => cloneDoc(candidate)),
|
|
970
|
+
populate: entry.populate,
|
|
971
|
+
uid
|
|
972
|
+
});
|
|
973
|
+
if (!nestedResult.hit) return {
|
|
974
|
+
hit: false,
|
|
975
|
+
data: []
|
|
976
|
+
};
|
|
977
|
+
populatedDocsById = nestedResult.data.reduce((acc, candidate) => {
|
|
978
|
+
const id = extractDocId(candidate);
|
|
979
|
+
if (id) acc.set(id, candidate);
|
|
980
|
+
return acc;
|
|
981
|
+
}, /* @__PURE__ */ new Map());
|
|
982
|
+
}
|
|
983
|
+
for (const descriptor of descriptors) {
|
|
984
|
+
if (!descriptor.hasValue) continue;
|
|
985
|
+
if (descriptor.isArray && descriptor.crossesArray) {
|
|
986
|
+
const values = descriptor.values.map((candidate) => {
|
|
987
|
+
const id = extractDocId(candidate);
|
|
988
|
+
return id ? populatedDocsById.get(id) ?? null : null;
|
|
989
|
+
});
|
|
990
|
+
setValueAtPath(descriptor.doc, entry.path, values);
|
|
991
|
+
continue;
|
|
992
|
+
}
|
|
993
|
+
if (!descriptor.ids.length) {
|
|
994
|
+
setValueAtPath(descriptor.doc, entry.path, descriptor.isArray ? [] : null);
|
|
995
|
+
continue;
|
|
996
|
+
}
|
|
997
|
+
if (descriptor.isArray) {
|
|
998
|
+
let values = descriptor.ids.map((id) => populatedDocsById.get(id)).filter((candidate) => Boolean(candidate));
|
|
999
|
+
if (entry.options?.sort) values = values.sort((a, b) => compareSort(a, b, entry.options.sort));
|
|
1000
|
+
if (typeof entry.options?.limit === "number" && Number.isFinite(entry.options.limit)) values = values.slice(0, Math.max(0, Math.floor(Math.abs(entry.options.limit))));
|
|
1001
|
+
setValueAtPath(descriptor.doc, entry.path, values);
|
|
1002
|
+
continue;
|
|
1003
|
+
}
|
|
1004
|
+
const value = populatedDocsById.get(descriptor.ids[0]);
|
|
1005
|
+
setValueAtPath(descriptor.doc, entry.path, value ?? null);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
return {
|
|
1009
|
+
hit: true,
|
|
1010
|
+
data: currentDocs
|
|
1011
|
+
};
|
|
1012
|
+
};
|
|
1013
|
+
var runPopulatedQuery = async ({ modelName, query = {}, options }) => {
|
|
1014
|
+
const rootResult = await runQueryInternal({
|
|
1015
|
+
modelName,
|
|
1016
|
+
query,
|
|
1017
|
+
options: {
|
|
1018
|
+
uid: options.uid,
|
|
1019
|
+
projection: options.projection,
|
|
1020
|
+
sort: options.sort,
|
|
1021
|
+
limit: options.limit
|
|
1022
|
+
},
|
|
1023
|
+
strictProjection: true
|
|
1024
|
+
});
|
|
1025
|
+
if (rootResult.projectionMismatch) return {
|
|
1026
|
+
hit: false,
|
|
1027
|
+
data: [],
|
|
1028
|
+
context: rootResult.context
|
|
1029
|
+
};
|
|
1030
|
+
const hydrated = await hydratePopulateEntries({
|
|
1031
|
+
docs: rootResult.data.map((doc) => applyProjection(doc, options.projection)).map((doc) => cloneDoc(doc)),
|
|
1032
|
+
populate: options.populate,
|
|
1033
|
+
uid: options.uid
|
|
1034
|
+
});
|
|
1035
|
+
if (!hydrated.hit) return {
|
|
1036
|
+
hit: false,
|
|
1037
|
+
data: [],
|
|
1038
|
+
context: rootResult.context
|
|
1039
|
+
};
|
|
1040
|
+
return {
|
|
1041
|
+
hit: true,
|
|
1042
|
+
data: hydrated.data,
|
|
1043
|
+
context: rootResult.context
|
|
1044
|
+
};
|
|
1045
|
+
};
|
|
1046
|
+
var updateDocs = async (modelName, data, uid) => {
|
|
1047
|
+
const collection = await getCollection(modelName, { uid });
|
|
1048
|
+
const allIds = data.map((doc) => doc._id).filter(Boolean);
|
|
1049
|
+
if (!allIds.length) return;
|
|
1050
|
+
const { docs: currentDocs } = await collection.find({
|
|
1051
|
+
selector: { _id: { $in: allIds } },
|
|
1052
|
+
fields: ["_id", "_rev"],
|
|
1053
|
+
limit: allIds.length
|
|
1054
|
+
});
|
|
1055
|
+
const currentDocsById = currentDocs.reduce((acc, doc) => {
|
|
1056
|
+
const id = String(doc._id ?? "");
|
|
1057
|
+
if (id) acc[id] = doc;
|
|
1058
|
+
return acc;
|
|
1059
|
+
}, {});
|
|
1060
|
+
const newDocs = data.map((mongoDoc) => {
|
|
1061
|
+
const currentDoc = currentDocsById[mongoDoc._id] ?? { _id: mongoDoc._id };
|
|
1062
|
+
const nextDoc = Object.entries(mongoDoc).reduce((acc, [key, value]) => {
|
|
1063
|
+
const newKey = key !== "_id" && key.startsWith("_") ? `${UNDERSCORE_PREFIX}${key}` : key;
|
|
1064
|
+
acc[newKey] = value;
|
|
1065
|
+
return acc;
|
|
1066
|
+
}, { ...currentDoc });
|
|
1067
|
+
const rev = currentDoc._rev;
|
|
1068
|
+
if (typeof rev === "string" && rev) nextDoc._rev = rev;
|
|
1069
|
+
else delete nextDoc._rev;
|
|
1070
|
+
return nextDoc;
|
|
1071
|
+
});
|
|
1072
|
+
await collection.bulkDocs(newDocs);
|
|
1073
|
+
};
|
|
1074
|
+
var deleteDocs = async (modelName, ids, uid) => {
|
|
1075
|
+
const collection = await getCollection(modelName, { uid });
|
|
1076
|
+
const allIds = ids.map((id) => String(id ?? "")).filter(Boolean);
|
|
1077
|
+
if (!allIds.length) return;
|
|
1078
|
+
const { docs: currentDocs } = await collection.find({
|
|
1079
|
+
selector: { _id: { $in: allIds } },
|
|
1080
|
+
fields: ["_id", "_rev"],
|
|
1081
|
+
limit: allIds.length
|
|
1082
|
+
});
|
|
1083
|
+
const deletions = currentDocs.map((doc) => ({
|
|
1084
|
+
_id: String(doc?._id ?? ""),
|
|
1085
|
+
_rev: doc?._rev,
|
|
1086
|
+
_deleted: true
|
|
1087
|
+
})).filter((doc) => doc._id && typeof doc._rev === "string" && doc._rev);
|
|
1088
|
+
if (!deletions.length) return;
|
|
1089
|
+
await collection.bulkDocs(deletions);
|
|
1090
|
+
};
|
|
1091
|
+
var destroyCollection = async (modelName, uid) => {
|
|
1092
|
+
const collection = await getCollection(modelName, { uid });
|
|
1093
|
+
const prefix = getPrefix();
|
|
1094
|
+
const dbName = `${uid}/${modelName}`;
|
|
1095
|
+
collections.delete(`${prefix}${dbName}`);
|
|
1096
|
+
unregisterDbName(prefix, dbName);
|
|
1097
|
+
await collection.destroy();
|
|
1098
|
+
};
|
|
1099
|
+
var resetRtsPouchStore = ({ tenantId, appName }) => {
|
|
1100
|
+
const basePrefix = computeBasePrefix({
|
|
1101
|
+
tenantId,
|
|
1102
|
+
appName
|
|
1103
|
+
});
|
|
1104
|
+
const oldPrefix = readPrefixOverride({
|
|
1105
|
+
tenantId,
|
|
1106
|
+
appName
|
|
1107
|
+
}) ?? basePrefix;
|
|
1108
|
+
const dbNames = Array.from(loadDbNames(oldPrefix));
|
|
1109
|
+
const openDbs = Array.from(collections.entries()).filter(([key]) => key.startsWith(oldPrefix)).map(([, db]) => db);
|
|
1110
|
+
(async () => {
|
|
1111
|
+
const remaining = new Set(dbNames);
|
|
1112
|
+
await Promise.all(openDbs.map((db) => db.destroy().catch(() => {})));
|
|
1113
|
+
if (remaining.size) {
|
|
1114
|
+
const PouchDB = await getPouchDb();
|
|
1115
|
+
const PouchDBForPrefix = PouchDB.defaults?.({}) ?? PouchDB;
|
|
1116
|
+
PouchDBForPrefix.prefix = oldPrefix;
|
|
1117
|
+
await Promise.all(Array.from(remaining).map(async (name) => {
|
|
1118
|
+
await new PouchDBForPrefix(name, {
|
|
1119
|
+
adapter: activePouchAdapter,
|
|
1120
|
+
revs_limit: 1
|
|
1121
|
+
}).destroy().then(() => {
|
|
1122
|
+
remaining.delete(name);
|
|
1123
|
+
}).catch(() => {});
|
|
1124
|
+
}));
|
|
1125
|
+
}
|
|
1126
|
+
if (remaining.size) persistDbNames(oldPrefix, remaining);
|
|
1127
|
+
else persistDbNames(oldPrefix, /* @__PURE__ */ new Set());
|
|
1128
|
+
})();
|
|
1129
|
+
const newPrefix = `${basePrefix}reset-${Date.now().toString(16)}/`;
|
|
1130
|
+
const storage = getRuntimeStorage();
|
|
1131
|
+
try {
|
|
1132
|
+
storage.setItem(getPrefixOverrideKey({
|
|
1133
|
+
tenantId,
|
|
1134
|
+
appName
|
|
1135
|
+
}), newPrefix);
|
|
1136
|
+
} catch {
|
|
1137
|
+
return newPrefix;
|
|
1138
|
+
}
|
|
1139
|
+
lastAppliedPrefix = null;
|
|
1140
|
+
collections.clear();
|
|
1141
|
+
return newPrefix;
|
|
1142
|
+
};
|
|
1143
|
+
var destroyAllCollections = async () => {
|
|
1144
|
+
const dbs = Array.from(collections.values());
|
|
1145
|
+
await Promise.all(dbs.map((db) => db.destroy()));
|
|
1146
|
+
collections.clear();
|
|
1147
|
+
};
|
|
1148
|
+
//#endregion
|
|
1149
|
+
//#region src/rts/populateCache.ts
|
|
1150
|
+
var EXCLUDE_PROJECTION_ERROR = "must be include-only (value 1); exclusion projection is not supported";
|
|
1151
|
+
var sortProjectionSpec = (projection) => {
|
|
1152
|
+
const sorted = {};
|
|
1153
|
+
for (const key of Object.keys(projection).sort()) sorted[key] = projection[key];
|
|
1154
|
+
return sorted;
|
|
1155
|
+
};
|
|
1156
|
+
var normalizeProjectionSpec = (value, source, label) => {
|
|
1157
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1158
|
+
const raw = value;
|
|
1159
|
+
const normalized = {};
|
|
1160
|
+
let hasExclude = false;
|
|
1161
|
+
for (const [key, rawValue] of Object.entries(raw)) {
|
|
1162
|
+
const path = key.trim();
|
|
1163
|
+
if (!path) continue;
|
|
1164
|
+
if (rawValue === 1 || rawValue === true) {
|
|
1165
|
+
normalized[path] = 1;
|
|
1166
|
+
continue;
|
|
1167
|
+
}
|
|
1168
|
+
if (rawValue === 0 || rawValue === false) {
|
|
1169
|
+
hasExclude = true;
|
|
1170
|
+
continue;
|
|
1171
|
+
}
|
|
1172
|
+
if (typeof rawValue === "number" && Number.isFinite(rawValue)) {
|
|
1173
|
+
if (rawValue === 1) normalized[path] = 1;
|
|
1174
|
+
if (rawValue === 0) hasExclude = true;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
if (hasExclude) throw new Error(`${source}: ${label} ${EXCLUDE_PROJECTION_ERROR}`);
|
|
1178
|
+
return Object.keys(normalized).length > 0 ? sortProjectionSpec(normalized) : void 0;
|
|
1179
|
+
};
|
|
1180
|
+
var normalizeSelectString = (value, source) => {
|
|
1181
|
+
const tokens = value.split(/\s+/).map((token) => token.trim()).filter(Boolean);
|
|
1182
|
+
if (!tokens.length) return void 0;
|
|
1183
|
+
const normalized = {};
|
|
1184
|
+
for (const token of tokens) {
|
|
1185
|
+
let path = token;
|
|
1186
|
+
if (token.startsWith("-")) throw new Error(`${source}: populate select ${EXCLUDE_PROJECTION_ERROR}`);
|
|
1187
|
+
else if (token.startsWith("+")) path = token.slice(1);
|
|
1188
|
+
path = path.trim();
|
|
1189
|
+
if (!path) continue;
|
|
1190
|
+
normalized[path] = 1;
|
|
1191
|
+
}
|
|
1192
|
+
return Object.keys(normalized).length > 0 ? sortProjectionSpec(normalized) : void 0;
|
|
1193
|
+
};
|
|
1194
|
+
var normalizePopulateSelect = (value, source) => {
|
|
1195
|
+
if (typeof value === "string") return normalizeSelectString(value, source);
|
|
1196
|
+
return normalizeProjectionSpec(value, source, "populate select");
|
|
1197
|
+
};
|
|
1198
|
+
var normalizePopulateOptions = (value) => {
|
|
1199
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1200
|
+
const raw = value;
|
|
1201
|
+
const normalized = {};
|
|
1202
|
+
if (raw.sort && typeof raw.sort === "object" && !Array.isArray(raw.sort)) {
|
|
1203
|
+
const sortRaw = raw.sort;
|
|
1204
|
+
const sort = {};
|
|
1205
|
+
for (const [key, rawDirection] of Object.entries(sortRaw)) {
|
|
1206
|
+
const path = key.trim();
|
|
1207
|
+
if (!path) continue;
|
|
1208
|
+
if (rawDirection === 1 || rawDirection === "asc") {
|
|
1209
|
+
sort[path] = 1;
|
|
1210
|
+
continue;
|
|
1211
|
+
}
|
|
1212
|
+
if (rawDirection === -1 || rawDirection === "desc") sort[path] = -1;
|
|
1213
|
+
}
|
|
1214
|
+
if (Object.keys(sort).length > 0) normalized.sort = sort;
|
|
1215
|
+
}
|
|
1216
|
+
if (typeof raw.limit === "number" && Number.isFinite(raw.limit)) normalized.limit = Math.max(0, Math.floor(Math.abs(raw.limit)));
|
|
1217
|
+
return Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
1218
|
+
};
|
|
1219
|
+
var normalizeString = (value) => {
|
|
1220
|
+
if (typeof value !== "string") return void 0;
|
|
1221
|
+
return value.trim() || void 0;
|
|
1222
|
+
};
|
|
1223
|
+
var normalizeObject = (value) => {
|
|
1224
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1225
|
+
return value;
|
|
1226
|
+
};
|
|
1227
|
+
var normalizePopulateObject = (value, source) => {
|
|
1228
|
+
const path = normalizeString(value.path);
|
|
1229
|
+
if (!path) throw new Error(`${source}: populate entries must define a non-empty path`);
|
|
1230
|
+
const select = normalizePopulateSelect(value.select, source);
|
|
1231
|
+
if (!select) throw new Error(`${source}: populate entries must define a select projection`);
|
|
1232
|
+
const nested = value.populate !== void 0 ? normalizePopulateOption(value.populate, source) : void 0;
|
|
1233
|
+
return {
|
|
1234
|
+
path,
|
|
1235
|
+
select,
|
|
1236
|
+
...normalizeString(value.model) ? { model: normalizeString(value.model) } : {},
|
|
1237
|
+
...normalizeObject(value.match) ? { match: normalizeObject(value.match) } : {},
|
|
1238
|
+
...normalizePopulateOptions(value.options) ? { options: normalizePopulateOptions(value.options) } : {},
|
|
1239
|
+
...nested && nested.length > 0 ? { populate: nested } : {}
|
|
1240
|
+
};
|
|
1241
|
+
};
|
|
1242
|
+
var normalizePopulateOption = (value, source) => {
|
|
1243
|
+
if (typeof value === "string") throw new Error(`${source}: populate string syntax is not supported; use object entries with select`);
|
|
1244
|
+
if (Array.isArray(value)) {
|
|
1245
|
+
if (value.length === 0) throw new Error(`${source}: populate must contain at least one entry`);
|
|
1246
|
+
return value.map((entry) => {
|
|
1247
|
+
if (typeof entry === "string") throw new Error(`${source}: populate string syntax is not supported; use object entries with select`);
|
|
1248
|
+
return normalizePopulateObject(entry, source);
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
return [normalizePopulateObject(value, source)];
|
|
1252
|
+
};
|
|
1253
|
+
var preparePopulateCacheOptions = (options, source) => {
|
|
1254
|
+
if (!options.populate) return void 0;
|
|
1255
|
+
const rootProjection = normalizeProjectionSpec(options.projection, source, "projection");
|
|
1256
|
+
if (!rootProjection) throw new Error(`${source}: projection is required when populate is used`);
|
|
1257
|
+
const populate = normalizePopulateOption(options.populate, source);
|
|
1258
|
+
if (!populate.length) throw new Error(`${source}: populate must contain at least one entry`);
|
|
1259
|
+
return {
|
|
1260
|
+
rootProjection,
|
|
1261
|
+
populate
|
|
1262
|
+
};
|
|
1263
|
+
};
|
|
1264
|
+
//#endregion
|
|
1265
|
+
//#region src/rts/queryKey.ts
|
|
1266
|
+
var isPlainObject = (value) => {
|
|
1267
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1268
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1269
|
+
return prototype === Object.prototype || prototype === null;
|
|
1270
|
+
};
|
|
1271
|
+
var serializeRtsQueryValue = (value) => {
|
|
1272
|
+
return JSON.stringify(value, (_key, currentValue) => {
|
|
1273
|
+
if (!isPlainObject(currentValue)) return currentValue;
|
|
1274
|
+
return Object.keys(currentValue).sort().reduce((acc, key) => {
|
|
1275
|
+
acc[key] = currentValue[key];
|
|
1276
|
+
return acc;
|
|
1277
|
+
}, {});
|
|
1278
|
+
}) ?? "";
|
|
1279
|
+
};
|
|
1280
|
+
var computeRtsQueryKey = (query, options) => {
|
|
1281
|
+
return serializeRtsQueryValue({
|
|
1282
|
+
version: 2,
|
|
1283
|
+
key: options.key ?? null,
|
|
1284
|
+
query,
|
|
1285
|
+
projection: options.projection ?? null,
|
|
1286
|
+
sort: options.sort ?? null,
|
|
1287
|
+
limit: typeof options.limit === "number" ? options.limit : null,
|
|
1288
|
+
populate: options.populate ?? null,
|
|
1289
|
+
pagination: options.pagination ?? null
|
|
1290
|
+
});
|
|
1291
|
+
};
|
|
1292
|
+
//#endregion
|
|
1293
|
+
//#region src/rts/wsClient.ts
|
|
1294
|
+
var hasSnapshotError = (snapshot) => {
|
|
1295
|
+
return snapshot?.error !== null && snapshot?.error !== void 0;
|
|
1296
|
+
};
|
|
1297
|
+
var RTS_CHANGES_PATH = "/api/rb/rts/changes";
|
|
1298
|
+
var MAX_TXN_BUF = 2048;
|
|
1299
|
+
var SERVER_RECONNECT_DELAY_MIN_MS = 1e4;
|
|
1300
|
+
var RUN_NETWORK_QUERY_TIMEOUT_ERROR = "runNetworkQuery: request timed out";
|
|
1301
|
+
var RUN_NETWORK_COUNT_TIMEOUT_ERROR = "runNetworkCount: request timed out";
|
|
1302
|
+
var socket = null;
|
|
1303
|
+
var socketReadyForMessages = false;
|
|
1304
|
+
var connectPromise = null;
|
|
1305
|
+
var pendingConnectionAttempt = null;
|
|
1306
|
+
var explicitDisconnect = false;
|
|
1307
|
+
var currentTenantId = null;
|
|
1308
|
+
var currentUid = null;
|
|
1309
|
+
var connectOptions = {};
|
|
1310
|
+
var connectionGeneration = 0;
|
|
1311
|
+
var connectionEpoch = null;
|
|
1312
|
+
var localTxnBuf = [];
|
|
1313
|
+
var queryCallbacks = /* @__PURE__ */ new Map();
|
|
1314
|
+
var countCallbacks = /* @__PURE__ */ new Map();
|
|
1315
|
+
var subscriptions = /* @__PURE__ */ new Map();
|
|
1316
|
+
var countSubscriptions = /* @__PURE__ */ new Map();
|
|
1317
|
+
var messageCallbacks = /* @__PURE__ */ new Map();
|
|
1318
|
+
var rtsMessageCallbacks = /* @__PURE__ */ new Map();
|
|
1319
|
+
var pendingWindowRequests = /* @__PURE__ */ new Map();
|
|
1320
|
+
var reconnectTimer = null;
|
|
1321
|
+
var reconnectAttempts = 0;
|
|
1322
|
+
var hasEstablishedConnection = false;
|
|
1323
|
+
var forceInitialQueryOnNextConnection = false;
|
|
1324
|
+
var pendingServerReconnectJitter = false;
|
|
1325
|
+
var syncPromise = null;
|
|
1326
|
+
var syncKey = null;
|
|
1327
|
+
var connectionStatus = "idle";
|
|
1328
|
+
var connectionError = null;
|
|
1329
|
+
var connectionStatusCallbacks = /* @__PURE__ */ new Set();
|
|
1330
|
+
var ensureRealtimeRuntime = () => {
|
|
1331
|
+
if (typeof WebSocket !== "function") throw new Error("RTS websocket client requires WebSocket support");
|
|
1332
|
+
if (typeof globalThis.setTimeout !== "function" || typeof globalThis.clearTimeout !== "function") throw new Error("RTS websocket client requires timer support");
|
|
1333
|
+
};
|
|
1334
|
+
var ensureSyncRuntime = () => {
|
|
1335
|
+
if (typeof fetch !== "function") throw new Error("syncRtsChanges requires fetch support");
|
|
1336
|
+
};
|
|
1337
|
+
var getRuntimeLocationHref = () => {
|
|
1338
|
+
if (typeof window !== "undefined" && typeof window.location?.href === "string" && window.location.href) return window.location.href;
|
|
1339
|
+
const location = globalThis.location;
|
|
1340
|
+
if (typeof location?.href === "string" && location.href) return location.href;
|
|
1341
|
+
return null;
|
|
1342
|
+
};
|
|
1343
|
+
var setRuntimeTimeout = (handler, delayMs) => {
|
|
1344
|
+
return globalThis.setTimeout(handler, delayMs);
|
|
1345
|
+
};
|
|
1346
|
+
var clearRuntimeTimeout = (timer) => {
|
|
1347
|
+
globalThis.clearTimeout(timer);
|
|
1348
|
+
};
|
|
1349
|
+
var setConnectionStatus = (status, error = null) => {
|
|
1350
|
+
connectionStatus = status;
|
|
1351
|
+
connectionError = error;
|
|
1352
|
+
for (const callback of connectionStatusCallbacks) callback(status, error);
|
|
1353
|
+
};
|
|
1354
|
+
var resolveWebSocketUrlFromCandidate = (candidateUrl, options) => {
|
|
1355
|
+
const url = new URL(candidateUrl);
|
|
1356
|
+
if (url.protocol === "http:") url.protocol = "ws:";
|
|
1357
|
+
else if (url.protocol === "https:") url.protocol = "wss:";
|
|
1358
|
+
if (!url.pathname || url.pathname === "/") url.pathname = options.path ?? "/rts";
|
|
1359
|
+
return url;
|
|
1360
|
+
};
|
|
1361
|
+
var resolveApiOriginUrl = ({ url }) => {
|
|
1362
|
+
if (url) {
|
|
1363
|
+
const base = new URL(url);
|
|
1364
|
+
if (base.protocol === "ws:") base.protocol = "http:";
|
|
1365
|
+
else if (base.protocol === "wss:") base.protocol = "https:";
|
|
1366
|
+
base.pathname = "/";
|
|
1367
|
+
base.search = "";
|
|
1368
|
+
base.hash = "";
|
|
1369
|
+
return base;
|
|
1370
|
+
}
|
|
1371
|
+
const locationHref = getRuntimeLocationHref();
|
|
1372
|
+
if (!locationHref) throw new Error("syncRtsChanges: options.url is required when location.href is unavailable");
|
|
1373
|
+
const base = new URL(locationHref);
|
|
1374
|
+
base.pathname = "/";
|
|
1375
|
+
base.search = "";
|
|
1376
|
+
base.hash = "";
|
|
1377
|
+
return base;
|
|
1378
|
+
};
|
|
1379
|
+
var buildSyncChangesUrl = (_tenantId, options) => {
|
|
1380
|
+
const base = resolveApiOriginUrl(options);
|
|
1381
|
+
return new URL(RTS_CHANGES_PATH, base).toString();
|
|
1382
|
+
};
|
|
1383
|
+
var buildSocketUrl = (_tenantId, _uid, options) => {
|
|
1384
|
+
if (options.url) return resolveWebSocketUrlFromCandidate(options.url, options).toString();
|
|
1385
|
+
const locationHref = getRuntimeLocationHref();
|
|
1386
|
+
if (!locationHref) throw new Error("connect: options.url is required when location.href is unavailable");
|
|
1387
|
+
const base = new URL(locationHref);
|
|
1388
|
+
base.protocol = base.protocol === "https:" ? "wss:" : "ws:";
|
|
1389
|
+
base.pathname = options.path ?? "/rts";
|
|
1390
|
+
base.search = "";
|
|
1391
|
+
base.hash = "";
|
|
1392
|
+
return base.toString();
|
|
1393
|
+
};
|
|
1394
|
+
var sendToServer = (message) => {
|
|
1395
|
+
if (!socket || !socketReadyForMessages) return;
|
|
1396
|
+
if (socket.readyState !== WebSocket.OPEN) return;
|
|
1397
|
+
socket.send(JSON.stringify(message));
|
|
1398
|
+
};
|
|
1399
|
+
var isSocketReady = () => Boolean(socket && socketReadyForMessages && socket.readyState === WebSocket.OPEN);
|
|
1400
|
+
var normalizeRequestedCount = (value) => {
|
|
1401
|
+
if (!Number.isSafeInteger(value)) return void 0;
|
|
1402
|
+
if (value < 1 || value > 4096) return void 0;
|
|
1403
|
+
return value;
|
|
1404
|
+
};
|
|
1405
|
+
var getSubscriptionServerOptions = (subscription) => {
|
|
1406
|
+
if (!subscription.options.pagination || !subscription.requestedCount) return subscription.options;
|
|
1407
|
+
const { cursor: _cursor, direction: _direction, ...pagination } = subscription.options.pagination;
|
|
1408
|
+
return {
|
|
1409
|
+
...subscription.options,
|
|
1410
|
+
pagination: {
|
|
1411
|
+
...pagination,
|
|
1412
|
+
limit: subscription.requestedCount
|
|
1413
|
+
}
|
|
1414
|
+
};
|
|
1415
|
+
};
|
|
1416
|
+
var resubscribeAll = ({ forceInitialQuery }) => {
|
|
1417
|
+
for (const sub of subscriptions.values()) {
|
|
1418
|
+
const runInitialQuery = forceInitialQuery || sub.runInitialNetworkQuery;
|
|
1419
|
+
sendToServer({
|
|
1420
|
+
type: "register-query",
|
|
1421
|
+
modelName: sub.modelName,
|
|
1422
|
+
queryKey: sub.queryKey,
|
|
1423
|
+
query: sub.query,
|
|
1424
|
+
options: getSubscriptionServerOptions(sub),
|
|
1425
|
+
runInitialQuery
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
for (const sub of countSubscriptions.values()) {
|
|
1429
|
+
const runInitialQuery = forceInitialQuery || sub.runInitialNetworkQuery;
|
|
1430
|
+
sendToServer({
|
|
1431
|
+
type: "register-count",
|
|
1432
|
+
modelName: sub.modelName,
|
|
1433
|
+
queryKey: sub.queryKey,
|
|
1434
|
+
query: sub.query,
|
|
1435
|
+
options: sub.options,
|
|
1436
|
+
runInitialQuery
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1440
|
+
var clearReconnectTimer = () => {
|
|
1441
|
+
if (reconnectTimer === null) return;
|
|
1442
|
+
clearRuntimeTimeout(reconnectTimer);
|
|
1443
|
+
reconnectTimer = null;
|
|
1444
|
+
};
|
|
1445
|
+
var rejectPendingConnectionAttempt = () => {
|
|
1446
|
+
const attempt = pendingConnectionAttempt;
|
|
1447
|
+
if (!attempt) return;
|
|
1448
|
+
pendingConnectionAttempt = null;
|
|
1449
|
+
attempt.reject(/* @__PURE__ */ new Error("RTS WebSocket connection attempt superseded"));
|
|
1450
|
+
};
|
|
1451
|
+
var scheduleReconnect = () => {
|
|
1452
|
+
clearReconnectTimer();
|
|
1453
|
+
if (explicitDisconnect) {
|
|
1454
|
+
pendingServerReconnectJitter = false;
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
if (!currentTenantId || !currentUid) return;
|
|
1458
|
+
const cfg = connectOptions.reconnect ?? {};
|
|
1459
|
+
const maxAttempts = cfg.attempts ?? 128;
|
|
1460
|
+
const delayMs = cfg.delayMs ?? 400;
|
|
1461
|
+
const delayMaxMs = cfg.delayMaxMs ?? 1e4;
|
|
1462
|
+
if (reconnectAttempts >= maxAttempts) return;
|
|
1463
|
+
let delay = Math.min(delayMaxMs, delayMs * Math.pow(2, reconnectAttempts));
|
|
1464
|
+
if (pendingServerReconnectJitter) {
|
|
1465
|
+
delay = SERVER_RECONNECT_DELAY_MIN_MS + Math.floor(Math.random() * 5001);
|
|
1466
|
+
pendingServerReconnectJitter = false;
|
|
1467
|
+
}
|
|
1468
|
+
reconnectAttempts += 1;
|
|
1469
|
+
reconnectTimer = setRuntimeTimeout(() => {
|
|
1470
|
+
connectInternal(currentTenantId, currentUid, connectOptions, { resetReconnectAttempts: false });
|
|
1471
|
+
}, delay);
|
|
1472
|
+
};
|
|
1473
|
+
var isDocWithId = (doc) => {
|
|
1474
|
+
if (!doc || typeof doc !== "object") return false;
|
|
1475
|
+
return typeof doc._id === "string";
|
|
1476
|
+
};
|
|
1477
|
+
var normalizePageInfo$1 = (value) => {
|
|
1478
|
+
if (!value || typeof value !== "object") return void 0;
|
|
1479
|
+
if (Array.isArray(value)) return void 0;
|
|
1480
|
+
const raw = value;
|
|
1481
|
+
if (typeof raw.hasNextPage !== "boolean" || typeof raw.hasPrevPage !== "boolean") return void 0;
|
|
1482
|
+
const nextCursor = typeof raw.nextCursor === "string" && raw.nextCursor ? raw.nextCursor : void 0;
|
|
1483
|
+
const prevCursor = typeof raw.prevCursor === "string" && raw.prevCursor ? raw.prevCursor : void 0;
|
|
1484
|
+
return {
|
|
1485
|
+
hasNextPage: raw.hasNextPage,
|
|
1486
|
+
hasPrevPage: raw.hasPrevPage,
|
|
1487
|
+
...nextCursor ? { nextCursor } : {},
|
|
1488
|
+
...prevCursor ? { prevCursor } : {}
|
|
1489
|
+
};
|
|
1490
|
+
};
|
|
1491
|
+
var normalizeTotalCount$1 = (value) => {
|
|
1492
|
+
if (typeof value !== "number") return void 0;
|
|
1493
|
+
if (!Number.isFinite(value) || value < 0) return void 0;
|
|
1494
|
+
return Math.floor(value);
|
|
1495
|
+
};
|
|
1496
|
+
var normalizeQueryWindow = (value) => {
|
|
1497
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1498
|
+
const raw = value;
|
|
1499
|
+
const requestedCount = normalizeRequestedCount(raw.requestedCount);
|
|
1500
|
+
if (!requestedCount) return void 0;
|
|
1501
|
+
const version = Number.isSafeInteger(raw.version) && raw.version >= 0 ? raw.version : void 0;
|
|
1502
|
+
return {
|
|
1503
|
+
requestedCount,
|
|
1504
|
+
...version !== void 0 ? { version } : {}
|
|
1505
|
+
};
|
|
1506
|
+
};
|
|
1507
|
+
var settlePendingWindowRequests = (cbKey, requestedCount, success) => {
|
|
1508
|
+
const pending = pendingWindowRequests.get(cbKey);
|
|
1509
|
+
if (!pending?.size) return;
|
|
1510
|
+
for (const request of Array.from(pending)) {
|
|
1511
|
+
if (request.requestedCount !== requestedCount) continue;
|
|
1512
|
+
clearRuntimeTimeout(request.timeoutId);
|
|
1513
|
+
pending.delete(request);
|
|
1514
|
+
if (!success) {
|
|
1515
|
+
const subscription = subscriptions.get(cbKey);
|
|
1516
|
+
if (subscription?.requestedCount === request.requestedCount) {
|
|
1517
|
+
const rollbackRequestedCount = (subscription.lastSnapshot && !hasSnapshotError(subscription.lastSnapshot) ? subscription.lastSnapshot.context.window?.requestedCount : void 0) ?? request.previousRequestedCount;
|
|
1518
|
+
subscription.windowIntentVersion += 1;
|
|
1519
|
+
subscription.requestedCount = rollbackRequestedCount;
|
|
1520
|
+
sendToServer({
|
|
1521
|
+
type: "set-query-window",
|
|
1522
|
+
modelName: subscription.modelName,
|
|
1523
|
+
queryKey: subscription.queryKey,
|
|
1524
|
+
requestedCount: rollbackRequestedCount
|
|
1525
|
+
});
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
request.resolve(success);
|
|
1529
|
+
}
|
|
1530
|
+
if (!pending.size) pendingWindowRequests.delete(cbKey);
|
|
1531
|
+
};
|
|
1532
|
+
var failPendingWindowRequests = (cbKey) => {
|
|
1533
|
+
const pending = pendingWindowRequests.get(cbKey);
|
|
1534
|
+
if (!pending?.size) return;
|
|
1535
|
+
for (const request of Array.from(pending)) settlePendingWindowRequests(cbKey, request.requestedCount, false);
|
|
1536
|
+
};
|
|
1537
|
+
var cancelPendingWindowRequests = (cbKey) => {
|
|
1538
|
+
const entries = cbKey ? [[cbKey, pendingWindowRequests.get(cbKey)]] : Array.from(pendingWindowRequests.entries());
|
|
1539
|
+
for (const [key, pending] of entries) {
|
|
1540
|
+
if (!pending) continue;
|
|
1541
|
+
for (const request of pending) {
|
|
1542
|
+
clearRuntimeTimeout(request.timeoutId);
|
|
1543
|
+
request.resolve(false);
|
|
1544
|
+
}
|
|
1545
|
+
pendingWindowRequests.delete(key);
|
|
1546
|
+
}
|
|
1547
|
+
};
|
|
1548
|
+
var updateQuerySubscriptionSnapshot = (subscription, snapshot) => {
|
|
1549
|
+
if (snapshot.context.source === "cache" && subscription.lastSnapshot?.context.source === "network") {
|
|
1550
|
+
const cachedCount = snapshot.context.window?.requestedCount;
|
|
1551
|
+
const networkCount = subscription.lastSnapshot.context.window?.requestedCount;
|
|
1552
|
+
if (!cachedCount || !networkCount || cachedCount <= networkCount) return false;
|
|
1553
|
+
}
|
|
1554
|
+
subscription.lastSnapshot = snapshot;
|
|
1555
|
+
return true;
|
|
1556
|
+
};
|
|
1557
|
+
var updateCountSubscriptionSnapshot = (subscription, snapshot) => {
|
|
1558
|
+
if (snapshot.context.source === "cache" && subscription.lastSnapshot?.context.source === "network") return false;
|
|
1559
|
+
subscription.lastSnapshot = snapshot;
|
|
1560
|
+
return true;
|
|
1561
|
+
};
|
|
1562
|
+
var dispatchQuerySnapshotToSubscription = (subscription, snapshot) => {
|
|
1563
|
+
for (const callback of subscription.callbacks) callback(snapshot.error, snapshot.data, snapshot.context);
|
|
1564
|
+
};
|
|
1565
|
+
var dispatchQuerySnapshotToOneshotCallbacks = (cbKey, snapshot) => {
|
|
1566
|
+
const callbacks = queryCallbacks.get(cbKey);
|
|
1567
|
+
if (!callbacks || !callbacks.size) return;
|
|
1568
|
+
for (const callback of callbacks) callback(snapshot.error, snapshot.data, snapshot.context);
|
|
1569
|
+
};
|
|
1570
|
+
var dispatchCountSnapshotToSubscription = (subscription, snapshot) => {
|
|
1571
|
+
for (const callback of subscription.callbacks) callback(snapshot.error, snapshot.count, snapshot.context);
|
|
1572
|
+
};
|
|
1573
|
+
var dispatchCountSnapshotToOneshotCallbacks = (cbKey, snapshot) => {
|
|
1574
|
+
const callbacks = countCallbacks.get(cbKey);
|
|
1575
|
+
if (!callbacks || !callbacks.size) return;
|
|
1576
|
+
for (const callback of callbacks) callback(snapshot.error, snapshot.count, snapshot.context);
|
|
1577
|
+
};
|
|
1578
|
+
var requestRegisteredQueryRefresh = (subscription) => {
|
|
1579
|
+
if (subscription.options.pagination && subscription.requestedCount) {
|
|
1580
|
+
sendToServer({
|
|
1581
|
+
type: "set-query-window",
|
|
1582
|
+
modelName: subscription.modelName,
|
|
1583
|
+
queryKey: subscription.queryKey,
|
|
1584
|
+
requestedCount: subscription.requestedCount
|
|
1585
|
+
});
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
sendToServer({
|
|
1589
|
+
type: "run-query",
|
|
1590
|
+
modelName: subscription.modelName,
|
|
1591
|
+
queryKey: subscription.queryKey,
|
|
1592
|
+
query: subscription.query,
|
|
1593
|
+
options: getSubscriptionServerOptions(subscription)
|
|
1594
|
+
});
|
|
1595
|
+
};
|
|
1596
|
+
var requestRegisteredCountRefresh = (subscription) => {
|
|
1597
|
+
sendToServer({
|
|
1598
|
+
type: "run-count",
|
|
1599
|
+
modelName: subscription.modelName,
|
|
1600
|
+
queryKey: subscription.queryKey,
|
|
1601
|
+
query: subscription.query,
|
|
1602
|
+
options: subscription.options
|
|
1603
|
+
});
|
|
1604
|
+
};
|
|
1605
|
+
var requestSubscriptionLocalQuery = (cbKey, subscription) => {
|
|
1606
|
+
if (!currentUid) return false;
|
|
1607
|
+
const uid = currentUid;
|
|
1608
|
+
const generation = connectionGeneration;
|
|
1609
|
+
if (subscription.options.pagination) {
|
|
1610
|
+
const requestedCountAtStart = subscription.requestedCount;
|
|
1611
|
+
const windowIntentVersionAtStart = subscription.windowIntentVersion;
|
|
1612
|
+
const localQueryPromise = readQueryWindowSnapshot({
|
|
1613
|
+
uid,
|
|
1614
|
+
modelName: subscription.modelName,
|
|
1615
|
+
queryKey: subscription.queryKey
|
|
1616
|
+
}).then((result) => {
|
|
1617
|
+
if (!result.hit || generation !== connectionGeneration) return;
|
|
1618
|
+
const currentSubscription = subscriptions.get(cbKey);
|
|
1619
|
+
if (!currentSubscription) return;
|
|
1620
|
+
const requestedCount = normalizeRequestedCount(result.snapshot.requestedCount);
|
|
1621
|
+
if (!requestedCount) return;
|
|
1622
|
+
if (currentSubscription.windowIntentVersion !== windowIntentVersionAtStart) return;
|
|
1623
|
+
if (currentSubscription.requestedCount !== requestedCountAtStart) return;
|
|
1624
|
+
const snapshot = {
|
|
1625
|
+
error: null,
|
|
1626
|
+
data: result.snapshot.data,
|
|
1627
|
+
context: {
|
|
1628
|
+
source: "cache",
|
|
1629
|
+
pageInfo: result.snapshot.pageInfo,
|
|
1630
|
+
...result.snapshot.totalCount !== void 0 ? { totalCount: result.snapshot.totalCount } : {},
|
|
1631
|
+
window: { requestedCount }
|
|
1632
|
+
}
|
|
1633
|
+
};
|
|
1634
|
+
if (!updateQuerySubscriptionSnapshot(currentSubscription, snapshot)) return;
|
|
1635
|
+
currentSubscription.requestedCount = requestedCount;
|
|
1636
|
+
currentSubscription.windowIntentVersion += 1;
|
|
1637
|
+
if (requestedCount !== requestedCountAtStart) sendToServer({
|
|
1638
|
+
type: "set-query-window",
|
|
1639
|
+
modelName: currentSubscription.modelName,
|
|
1640
|
+
queryKey: currentSubscription.queryKey,
|
|
1641
|
+
requestedCount
|
|
1642
|
+
});
|
|
1643
|
+
dispatchQuerySnapshotToSubscription(currentSubscription, snapshot);
|
|
1644
|
+
}).catch(() => {});
|
|
1645
|
+
subscription.localQueryPromise = localQueryPromise;
|
|
1646
|
+
localQueryPromise.finally(() => {
|
|
1647
|
+
const currentSubscription = subscriptions.get(cbKey);
|
|
1648
|
+
if (currentSubscription?.localQueryPromise === localQueryPromise) delete currentSubscription.localQueryPromise;
|
|
1649
|
+
});
|
|
1650
|
+
return true;
|
|
1651
|
+
}
|
|
1652
|
+
const populateCache = subscription.populateCache;
|
|
1653
|
+
if (Boolean(populateCache) && populateCache) {
|
|
1654
|
+
runPopulatedQuery({
|
|
1655
|
+
modelName: subscription.modelName,
|
|
1656
|
+
query: subscription.query,
|
|
1657
|
+
options: {
|
|
1658
|
+
uid,
|
|
1659
|
+
projection: populateCache.rootProjection,
|
|
1660
|
+
sort: subscription.options.sort,
|
|
1661
|
+
limit: subscription.options.limit,
|
|
1662
|
+
populate: populateCache.populate
|
|
1663
|
+
}
|
|
1664
|
+
}).then(({ hit, data, context }) => {
|
|
1665
|
+
if (!hit) return;
|
|
1666
|
+
if (generation !== connectionGeneration) return;
|
|
1667
|
+
const currentSubscription = subscriptions.get(cbKey);
|
|
1668
|
+
if (!currentSubscription) return;
|
|
1669
|
+
const snapshot = {
|
|
1670
|
+
error: null,
|
|
1671
|
+
data,
|
|
1672
|
+
context
|
|
1673
|
+
};
|
|
1674
|
+
if (!updateQuerySubscriptionSnapshot(currentSubscription, snapshot)) return;
|
|
1675
|
+
dispatchQuerySnapshotToSubscription(currentSubscription, snapshot);
|
|
1676
|
+
}).catch(() => {});
|
|
1677
|
+
return true;
|
|
1678
|
+
}
|
|
1679
|
+
runQuery({
|
|
1680
|
+
modelName: subscription.modelName,
|
|
1681
|
+
query: subscription.query,
|
|
1682
|
+
options: {
|
|
1683
|
+
uid,
|
|
1684
|
+
projection: subscription.options.projection,
|
|
1685
|
+
sort: subscription.options.sort,
|
|
1686
|
+
limit: subscription.options.limit
|
|
1687
|
+
}
|
|
1688
|
+
}).then(({ data, context }) => {
|
|
1689
|
+
if (generation !== connectionGeneration) return;
|
|
1690
|
+
const currentSubscription = subscriptions.get(cbKey);
|
|
1691
|
+
if (!currentSubscription) return;
|
|
1692
|
+
const snapshot = {
|
|
1693
|
+
error: null,
|
|
1694
|
+
data,
|
|
1695
|
+
context
|
|
1696
|
+
};
|
|
1697
|
+
if (!updateQuerySubscriptionSnapshot(currentSubscription, snapshot)) return;
|
|
1698
|
+
dispatchQuerySnapshotToSubscription(currentSubscription, snapshot);
|
|
1699
|
+
}).catch(() => {});
|
|
1700
|
+
return true;
|
|
1701
|
+
};
|
|
1702
|
+
var handleQueryPayload = (payload) => {
|
|
1703
|
+
const { modelName, queryKey, data, error, txnId } = payload;
|
|
1704
|
+
const cbKey = `${modelName}.${queryKey}`;
|
|
1705
|
+
const subscription = subscriptions.get(cbKey);
|
|
1706
|
+
const hasSubscriptionCallbacks = Boolean(subscription?.callbacks.size);
|
|
1707
|
+
const hasOneshotCallbacks = Boolean(queryCallbacks.get(cbKey)?.size);
|
|
1708
|
+
if (!hasSubscriptionCallbacks && !hasOneshotCallbacks) return;
|
|
1709
|
+
const pageInfo = normalizePageInfo$1(payload.pageInfo);
|
|
1710
|
+
const totalCount = normalizeTotalCount$1(payload.totalCount);
|
|
1711
|
+
const queryWindow = normalizeQueryWindow(payload.window);
|
|
1712
|
+
const populateCache = subscription?.populateCache;
|
|
1713
|
+
const hasPopulate = Boolean(populateCache);
|
|
1714
|
+
const hasPagination = Boolean(subscription?.options?.pagination || pageInfo || totalCount !== void 0);
|
|
1715
|
+
if (subscription?.options.pagination && queryWindow) {
|
|
1716
|
+
if (queryWindow.requestedCount !== subscription.requestedCount) return;
|
|
1717
|
+
if (queryWindow.version !== void 0 && queryWindow.version <= subscription.lastNetworkVersion) return;
|
|
1718
|
+
if (queryWindow.version !== void 0) subscription.lastNetworkVersion = queryWindow.version;
|
|
1719
|
+
}
|
|
1720
|
+
const context = {
|
|
1721
|
+
source: "network",
|
|
1722
|
+
isLocal: !!(txnId && localTxnBuf.includes(txnId)),
|
|
1723
|
+
txnId,
|
|
1724
|
+
...pageInfo ? { pageInfo } : {},
|
|
1725
|
+
...totalCount !== void 0 ? { totalCount } : {},
|
|
1726
|
+
...queryWindow ? { window: queryWindow } : {}
|
|
1727
|
+
};
|
|
1728
|
+
const snapshot = {
|
|
1729
|
+
error: error ?? null,
|
|
1730
|
+
data,
|
|
1731
|
+
context
|
|
1732
|
+
};
|
|
1733
|
+
if (error) {
|
|
1734
|
+
if (subscription && updateQuerySubscriptionSnapshot(subscription, snapshot)) dispatchQuerySnapshotToSubscription(subscription, snapshot);
|
|
1735
|
+
dispatchQuerySnapshotToOneshotCallbacks(cbKey, snapshot);
|
|
1736
|
+
if (queryWindow) settlePendingWindowRequests(cbKey, queryWindow.requestedCount, false);
|
|
1737
|
+
else if (subscription?.options.pagination) failPendingWindowRequests(cbKey);
|
|
1738
|
+
return;
|
|
1739
|
+
}
|
|
1740
|
+
if (subscription && updateQuerySubscriptionSnapshot(subscription, snapshot)) dispatchQuerySnapshotToSubscription(subscription, snapshot);
|
|
1741
|
+
dispatchQuerySnapshotToOneshotCallbacks(cbKey, snapshot);
|
|
1742
|
+
if (queryWindow) settlePendingWindowRequests(cbKey, queryWindow.requestedCount, true);
|
|
1743
|
+
if (!currentUid) return;
|
|
1744
|
+
if (subscription?.options.pagination && queryWindow && pageInfo && Array.isArray(data) && connectionEpoch) {
|
|
1745
|
+
const uid = currentUid;
|
|
1746
|
+
const generation = connectionGeneration;
|
|
1747
|
+
const serverEpoch = connectionEpoch;
|
|
1748
|
+
const localQueryPromise = subscription.localQueryPromise;
|
|
1749
|
+
Promise.resolve().then(async () => {
|
|
1750
|
+
await localQueryPromise;
|
|
1751
|
+
if (generation !== connectionGeneration || serverEpoch !== connectionEpoch) return;
|
|
1752
|
+
const currentSubscription = subscriptions.get(cbKey);
|
|
1753
|
+
if (!currentSubscription || currentSubscription.requestedCount !== queryWindow.requestedCount) return;
|
|
1754
|
+
await writeQueryWindowSnapshot({
|
|
1755
|
+
uid,
|
|
1756
|
+
modelName,
|
|
1757
|
+
queryKey,
|
|
1758
|
+
data,
|
|
1759
|
+
pageInfo,
|
|
1760
|
+
...totalCount !== void 0 ? { totalCount } : {},
|
|
1761
|
+
requestedCount: queryWindow.requestedCount,
|
|
1762
|
+
...queryWindow.version !== void 0 ? {
|
|
1763
|
+
serverEpoch,
|
|
1764
|
+
serverVersion: queryWindow.version
|
|
1765
|
+
} : {}
|
|
1766
|
+
});
|
|
1767
|
+
}).catch(() => {});
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
const docs = Array.isArray(data) ? data.filter(isDocWithId) : [];
|
|
1771
|
+
if (hasPagination) return;
|
|
1772
|
+
if (!docs.length) return;
|
|
1773
|
+
if (hasPopulate && populateCache) {
|
|
1774
|
+
updatePopulatedDocs({
|
|
1775
|
+
modelName,
|
|
1776
|
+
data: docs,
|
|
1777
|
+
uid: currentUid,
|
|
1778
|
+
populate: populateCache.populate
|
|
1779
|
+
}).catch(() => {});
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1782
|
+
updateDocs(modelName, docs, currentUid).catch(() => {});
|
|
1783
|
+
};
|
|
1784
|
+
var handleCountPayload = (payload) => {
|
|
1785
|
+
const { modelName, queryKey, count, error, txnId } = payload;
|
|
1786
|
+
const cbKey = `${modelName}.${queryKey}`;
|
|
1787
|
+
const subscription = countSubscriptions.get(cbKey);
|
|
1788
|
+
const hasSubscriptionCallbacks = Boolean(subscription?.callbacks.size);
|
|
1789
|
+
const hasOneshotCallbacks = Boolean(countCallbacks.get(cbKey)?.size);
|
|
1790
|
+
if (!hasSubscriptionCallbacks && !hasOneshotCallbacks) return;
|
|
1791
|
+
const context = {
|
|
1792
|
+
source: "network",
|
|
1793
|
+
isLocal: !!(txnId && localTxnBuf.includes(txnId)),
|
|
1794
|
+
txnId
|
|
1795
|
+
};
|
|
1796
|
+
const normalizedCount = normalizeTotalCount$1(count);
|
|
1797
|
+
const snapshot = {
|
|
1798
|
+
error: error ?? null,
|
|
1799
|
+
count: normalizedCount,
|
|
1800
|
+
context
|
|
1801
|
+
};
|
|
1802
|
+
if (error) {
|
|
1803
|
+
if (subscription && updateCountSubscriptionSnapshot(subscription, snapshot)) dispatchCountSnapshotToSubscription(subscription, snapshot);
|
|
1804
|
+
dispatchCountSnapshotToOneshotCallbacks(cbKey, snapshot);
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
if (subscription && updateCountSubscriptionSnapshot(subscription, snapshot)) dispatchCountSnapshotToSubscription(subscription, snapshot);
|
|
1808
|
+
dispatchCountSnapshotToOneshotCallbacks(cbKey, snapshot);
|
|
1809
|
+
};
|
|
1810
|
+
var handleEvent = (payload) => {
|
|
1811
|
+
const callbacks = messageCallbacks.get(payload.event);
|
|
1812
|
+
if (!callbacks || !callbacks.size) return;
|
|
1813
|
+
for (const cb of callbacks) cb(payload.payload);
|
|
1814
|
+
};
|
|
1815
|
+
var handleRtsMessage = (payload) => {
|
|
1816
|
+
const callbacks = rtsMessageCallbacks.get(payload.channel);
|
|
1817
|
+
if (!callbacks || !callbacks.size) return;
|
|
1818
|
+
for (const cb of callbacks) cb(payload.payload);
|
|
1819
|
+
};
|
|
1820
|
+
var handleMessage = (event) => {
|
|
1821
|
+
let parsed;
|
|
1822
|
+
try {
|
|
1823
|
+
parsed = JSON.parse(typeof event.data === "string" ? event.data : String(event.data));
|
|
1824
|
+
} catch {
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
if (!parsed || typeof parsed !== "object") return;
|
|
1828
|
+
const message = parsed;
|
|
1829
|
+
if (message.type === "query-payload") {
|
|
1830
|
+
handleQueryPayload(message);
|
|
1831
|
+
return;
|
|
1832
|
+
}
|
|
1833
|
+
if (message.type === "count-payload") {
|
|
1834
|
+
handleCountPayload(message);
|
|
1835
|
+
return;
|
|
1836
|
+
}
|
|
1837
|
+
if (message.type === "event") {
|
|
1838
|
+
handleEvent(message);
|
|
1839
|
+
return;
|
|
1840
|
+
}
|
|
1841
|
+
if (message.type === "rts-message") handleRtsMessage(message);
|
|
1842
|
+
};
|
|
1843
|
+
var addLocalTxn = (txnId) => {
|
|
1844
|
+
if (!txnId) return;
|
|
1845
|
+
localTxnBuf.push(txnId);
|
|
1846
|
+
if (localTxnBuf.length > MAX_TXN_BUF) localTxnBuf.shift();
|
|
1847
|
+
};
|
|
1848
|
+
var getSyncStorageKey = ({ tenantId, uid, appName }) => `rb:rts:changesSeq:${appName ?? ""}:${tenantId}:${uid}`;
|
|
1849
|
+
var readStoredSeq = (key) => {
|
|
1850
|
+
const storage = getRuntimeStorage();
|
|
1851
|
+
try {
|
|
1852
|
+
const raw = storage.getItem(key);
|
|
1853
|
+
const num = raw ? Number(raw) : 0;
|
|
1854
|
+
return Number.isFinite(num) && num >= 0 ? Math.floor(num) : 0;
|
|
1855
|
+
} catch {
|
|
1856
|
+
return 0;
|
|
1857
|
+
}
|
|
1858
|
+
};
|
|
1859
|
+
var writeStoredSeq = (key, value) => {
|
|
1860
|
+
const storage = getRuntimeStorage();
|
|
1861
|
+
try {
|
|
1862
|
+
storage.setItem(key, String(Math.max(0, Math.floor(value))));
|
|
1863
|
+
} catch {
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
};
|
|
1867
|
+
var applyChangeBatch = async (changes, uid, isCurrent) => {
|
|
1868
|
+
const resetModels = /* @__PURE__ */ new Set();
|
|
1869
|
+
const deletesByModel = /* @__PURE__ */ new Map();
|
|
1870
|
+
for (const change of changes) {
|
|
1871
|
+
const modelName = typeof change.modelName === "string" ? change.modelName : "";
|
|
1872
|
+
if (!modelName) continue;
|
|
1873
|
+
if (change.op === "reset_model") {
|
|
1874
|
+
resetModels.add(modelName);
|
|
1875
|
+
continue;
|
|
1876
|
+
}
|
|
1877
|
+
if (change.op === "delete") {
|
|
1878
|
+
const docId = typeof change.docId === "string" ? change.docId : "";
|
|
1879
|
+
if (!docId) continue;
|
|
1880
|
+
const existing = deletesByModel.get(modelName) ?? [];
|
|
1881
|
+
existing.push(docId);
|
|
1882
|
+
deletesByModel.set(modelName, existing);
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
for (const modelName of resetModels) {
|
|
1886
|
+
if (!isCurrent()) return false;
|
|
1887
|
+
await destroyCollection(modelName, uid).catch(() => {});
|
|
1888
|
+
}
|
|
1889
|
+
for (const [modelName, ids] of deletesByModel.entries()) {
|
|
1890
|
+
if (resetModels.has(modelName)) continue;
|
|
1891
|
+
if (!isCurrent()) return false;
|
|
1892
|
+
await deleteDocs(modelName, ids, uid).catch(() => {});
|
|
1893
|
+
}
|
|
1894
|
+
if (resetModels.size || deletesByModel.size) {
|
|
1895
|
+
if (!isCurrent()) return false;
|
|
1896
|
+
await invalidateQueryWindowSnapshots({ uid }).catch(() => {});
|
|
1897
|
+
}
|
|
1898
|
+
return true;
|
|
1899
|
+
};
|
|
1900
|
+
var syncRtsChangesWithResult = async (tenantId, uid, options = {}, isCurrent = () => true) => {
|
|
1901
|
+
ensureSyncRuntime();
|
|
1902
|
+
if (!tenantId || !uid) return false;
|
|
1903
|
+
const storageKey = getSyncStorageKey({
|
|
1904
|
+
tenantId,
|
|
1905
|
+
uid,
|
|
1906
|
+
appName: options.appName
|
|
1907
|
+
});
|
|
1908
|
+
let sinceSeq = readStoredSeq(storageKey);
|
|
1909
|
+
let cacheInvalidated = false;
|
|
1910
|
+
const syncUrl = buildSyncChangesUrl(tenantId, { url: options.url });
|
|
1911
|
+
for (let i = 0; i < 32; i += 1) {
|
|
1912
|
+
const response = await fetch(syncUrl, {
|
|
1913
|
+
method: "POST",
|
|
1914
|
+
credentials: "include",
|
|
1915
|
+
headers: { "Content-Type": "application/json" },
|
|
1916
|
+
body: JSON.stringify({
|
|
1917
|
+
sinceSeq,
|
|
1918
|
+
limit: 2e3
|
|
1919
|
+
})
|
|
1920
|
+
});
|
|
1921
|
+
if (!isCurrent()) return false;
|
|
1922
|
+
if (!response.ok) return cacheInvalidated;
|
|
1923
|
+
const payload = await response.json().catch(() => null);
|
|
1924
|
+
if (!payload || typeof payload !== "object") return cacheInvalidated;
|
|
1925
|
+
const payloadObj = payload;
|
|
1926
|
+
if (payloadObj.ok !== true) return cacheInvalidated;
|
|
1927
|
+
const latestSeq = Number(payloadObj.latestSeq ?? 0);
|
|
1928
|
+
if (Boolean(payloadObj.needsFullResync)) {
|
|
1929
|
+
if (!isCurrent()) return false;
|
|
1930
|
+
resetRtsPouchStore({
|
|
1931
|
+
tenantId,
|
|
1932
|
+
appName: options.appName
|
|
1933
|
+
});
|
|
1934
|
+
writeStoredSeq(storageKey, latestSeq);
|
|
1935
|
+
return true;
|
|
1936
|
+
}
|
|
1937
|
+
const changesRaw = payloadObj.changes;
|
|
1938
|
+
const normalized = (Array.isArray(changesRaw) ? changesRaw : []).map((c) => {
|
|
1939
|
+
if (!c || typeof c !== "object") return null;
|
|
1940
|
+
const obj = c;
|
|
1941
|
+
const seq = Number(obj.seq ?? 0);
|
|
1942
|
+
const modelName = typeof obj.modelName === "string" ? obj.modelName : String(obj.modelName ?? "");
|
|
1943
|
+
const op = obj.op === "reset_model" ? "reset_model" : "delete";
|
|
1944
|
+
const docId = typeof obj.docId === "string" && obj.docId ? obj.docId : obj.docId ? String(obj.docId) : void 0;
|
|
1945
|
+
return {
|
|
1946
|
+
seq,
|
|
1947
|
+
modelName,
|
|
1948
|
+
op,
|
|
1949
|
+
...docId ? { docId } : {}
|
|
1950
|
+
};
|
|
1951
|
+
}).filter((c) => c !== null).filter((c) => Number.isFinite(c.seq) && c.seq > 0 && c.modelName && (c.op === "reset_model" || !!c.docId));
|
|
1952
|
+
if (!normalized.length) {
|
|
1953
|
+
writeStoredSeq(storageKey, latestSeq);
|
|
1954
|
+
return cacheInvalidated;
|
|
1955
|
+
}
|
|
1956
|
+
if (!await applyChangeBatch(normalized, uid, isCurrent)) return false;
|
|
1957
|
+
cacheInvalidated = true;
|
|
1958
|
+
sinceSeq = normalized.reduce((max, c) => c.seq > max ? c.seq : max, sinceSeq);
|
|
1959
|
+
writeStoredSeq(storageKey, sinceSeq);
|
|
1960
|
+
if (latestSeq > 0 && sinceSeq >= latestSeq) return cacheInvalidated;
|
|
1961
|
+
}
|
|
1962
|
+
return cacheInvalidated;
|
|
1963
|
+
};
|
|
1964
|
+
var syncRtsChanges = async (tenantId, uid, options = {}) => {
|
|
1965
|
+
await syncRtsChangesWithResult(tenantId, uid, options);
|
|
1966
|
+
};
|
|
1967
|
+
var ensureSynced = (tenantId, uid, options) => {
|
|
1968
|
+
if (options.syncChanges === false) return Promise.resolve(false);
|
|
1969
|
+
const key = `${options.appName ?? ""}:${tenantId}:${uid}`;
|
|
1970
|
+
if (syncPromise && syncKey === key) return syncPromise;
|
|
1971
|
+
syncKey = key;
|
|
1972
|
+
syncPromise = syncRtsChangesWithResult(tenantId, uid, {
|
|
1973
|
+
appName: options.appName,
|
|
1974
|
+
url: options.url
|
|
1975
|
+
}, () => currentTenantId === tenantId && currentUid === uid && (connectOptions.appName ?? "") === (options.appName ?? "")).catch(() => false).finally(() => {
|
|
1976
|
+
if (syncKey === key) syncPromise = null;
|
|
1977
|
+
});
|
|
1978
|
+
return syncPromise;
|
|
1979
|
+
};
|
|
1980
|
+
var connectInternal = (tenantId, uid, options, { resetReconnectAttempts }) => {
|
|
1981
|
+
ensureRealtimeRuntime();
|
|
1982
|
+
if (!tenantId) return Promise.resolve();
|
|
1983
|
+
if (!uid) throw new Error("Missing uid");
|
|
1984
|
+
currentTenantId = tenantId;
|
|
1985
|
+
currentUid = uid;
|
|
1986
|
+
connectOptions = options;
|
|
1987
|
+
if (options.configureStore !== false) configureRtsPouchStore({
|
|
1988
|
+
tenantId,
|
|
1989
|
+
appName: options.appName
|
|
1990
|
+
});
|
|
1991
|
+
if (socket && (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING)) return connectPromise ?? Promise.resolve();
|
|
1992
|
+
const synchronization = ensureSynced(tenantId, uid, options);
|
|
1993
|
+
explicitDisconnect = false;
|
|
1994
|
+
clearReconnectTimer();
|
|
1995
|
+
socketReadyForMessages = false;
|
|
1996
|
+
const url = buildSocketUrl(tenantId, uid, options);
|
|
1997
|
+
connectPromise = new Promise((resolve, reject) => {
|
|
1998
|
+
if (resetReconnectAttempts) reconnectAttempts = 0;
|
|
1999
|
+
let opened = false;
|
|
2000
|
+
let settled = false;
|
|
2001
|
+
let subscriptionsStarted = false;
|
|
2002
|
+
let cacheInvalidatedBeforeSubscriptions = false;
|
|
2003
|
+
const generation = ++connectionGeneration;
|
|
2004
|
+
connectionEpoch = `${Date.now().toString(36)}.${generation.toString(36)}.${Math.random().toString(36).slice(2, 10)}`;
|
|
2005
|
+
pendingConnectionAttempt = {
|
|
2006
|
+
generation,
|
|
2007
|
+
reject: (error) => {
|
|
2008
|
+
if (settled) return;
|
|
2009
|
+
settled = true;
|
|
2010
|
+
reject(error);
|
|
2011
|
+
}
|
|
2012
|
+
};
|
|
2013
|
+
setConnectionStatus("connecting");
|
|
2014
|
+
const nextSocket = new WebSocket(url);
|
|
2015
|
+
socket = nextSocket;
|
|
2016
|
+
if (options.syncChanges !== false) synchronization.then((cacheInvalidated) => {
|
|
2017
|
+
if (!cacheInvalidated || generation !== connectionGeneration) return;
|
|
2018
|
+
if (!subscriptionsStarted) {
|
|
2019
|
+
cacheInvalidatedBeforeSubscriptions = true;
|
|
2020
|
+
return;
|
|
2021
|
+
}
|
|
2022
|
+
if (nextSocket.readyState !== WebSocket.OPEN) return;
|
|
2023
|
+
for (const subscription of subscriptions.values()) {
|
|
2024
|
+
if (!subscription.options.pagination) continue;
|
|
2025
|
+
requestRegisteredQueryRefresh(subscription);
|
|
2026
|
+
}
|
|
2027
|
+
});
|
|
2028
|
+
nextSocket.addEventListener("open", () => {
|
|
2029
|
+
if (generation !== connectionGeneration) return;
|
|
2030
|
+
opened = true;
|
|
2031
|
+
settled = true;
|
|
2032
|
+
if (pendingConnectionAttempt?.generation === generation) pendingConnectionAttempt = null;
|
|
2033
|
+
reconnectAttempts = 0;
|
|
2034
|
+
pendingServerReconnectJitter = false;
|
|
2035
|
+
socketReadyForMessages = true;
|
|
2036
|
+
setConnectionStatus("connected");
|
|
2037
|
+
for (const subscription of subscriptions.values()) {
|
|
2038
|
+
subscription.lastNetworkVersion = 0;
|
|
2039
|
+
if (subscription.runInitialLocalQuery && !subscription.hasRequestedInitialLocalQuery) subscription.hasRequestedInitialLocalQuery = requestSubscriptionLocalQuery(`${subscription.modelName}.${subscription.queryKey}`, subscription);
|
|
2040
|
+
}
|
|
2041
|
+
resubscribeAll({ forceInitialQuery: hasEstablishedConnection || forceInitialQueryOnNextConnection || cacheInvalidatedBeforeSubscriptions });
|
|
2042
|
+
forceInitialQueryOnNextConnection = false;
|
|
2043
|
+
subscriptionsStarted = true;
|
|
2044
|
+
hasEstablishedConnection = true;
|
|
2045
|
+
resolve();
|
|
2046
|
+
});
|
|
2047
|
+
nextSocket.addEventListener("message", (event) => {
|
|
2048
|
+
if (generation !== connectionGeneration) return;
|
|
2049
|
+
handleMessage(event);
|
|
2050
|
+
});
|
|
2051
|
+
nextSocket.addEventListener("close", (event) => {
|
|
2052
|
+
if (generation !== connectionGeneration) return;
|
|
2053
|
+
socketReadyForMessages = false;
|
|
2054
|
+
if (!settled) {
|
|
2055
|
+
settled = true;
|
|
2056
|
+
if (pendingConnectionAttempt?.generation === generation) pendingConnectionAttempt = null;
|
|
2057
|
+
const error = /* @__PURE__ */ new Error(`RTS WebSocket closed before becoming ready (code=${event.code})`);
|
|
2058
|
+
setConnectionStatus("error", error);
|
|
2059
|
+
reject(error);
|
|
2060
|
+
}
|
|
2061
|
+
if (!explicitDisconnect) {
|
|
2062
|
+
pendingServerReconnectJitter = opened;
|
|
2063
|
+
setConnectionStatus("connecting", connectionError);
|
|
2064
|
+
} else setConnectionStatus("idle");
|
|
2065
|
+
socket = null;
|
|
2066
|
+
connectPromise = null;
|
|
2067
|
+
scheduleReconnect();
|
|
2068
|
+
});
|
|
2069
|
+
nextSocket.addEventListener("error", (err) => {
|
|
2070
|
+
if (generation !== connectionGeneration) return;
|
|
2071
|
+
if (settled) return;
|
|
2072
|
+
settled = true;
|
|
2073
|
+
if (pendingConnectionAttempt?.generation === generation) pendingConnectionAttempt = null;
|
|
2074
|
+
socketReadyForMessages = false;
|
|
2075
|
+
const error = err instanceof Error ? err : /* @__PURE__ */ new Error("RTS WebSocket error");
|
|
2076
|
+
setConnectionStatus("error", error);
|
|
2077
|
+
reject(error);
|
|
2078
|
+
});
|
|
2079
|
+
});
|
|
2080
|
+
return connectPromise;
|
|
2081
|
+
};
|
|
2082
|
+
var connect = (tenantId, uid, options = {}) => {
|
|
2083
|
+
if (currentTenantId !== null && (currentTenantId !== tenantId || currentUid !== uid || (connectOptions.appName ?? "") !== (options.appName ?? ""))) return reconnect(tenantId, uid, options);
|
|
2084
|
+
return connectInternal(tenantId, uid, options, { resetReconnectAttempts: true });
|
|
2085
|
+
};
|
|
2086
|
+
var disconnect = () => {
|
|
2087
|
+
explicitDisconnect = true;
|
|
2088
|
+
connectionGeneration += 1;
|
|
2089
|
+
rejectPendingConnectionAttempt();
|
|
2090
|
+
clearReconnectTimer();
|
|
2091
|
+
hasEstablishedConnection = false;
|
|
2092
|
+
forceInitialQueryOnNextConnection = false;
|
|
2093
|
+
pendingServerReconnectJitter = false;
|
|
2094
|
+
if (socket) try {
|
|
2095
|
+
socket.close();
|
|
2096
|
+
} catch {}
|
|
2097
|
+
socket = null;
|
|
2098
|
+
socketReadyForMessages = false;
|
|
2099
|
+
connectPromise = null;
|
|
2100
|
+
connectionEpoch = null;
|
|
2101
|
+
cancelPendingWindowRequests();
|
|
2102
|
+
setConnectionStatus("idle");
|
|
2103
|
+
};
|
|
2104
|
+
var reconnect = (tenantId, uid, options = {}) => {
|
|
2105
|
+
const identityChanged = currentTenantId !== null && currentUid !== null && (currentTenantId !== tenantId || currentUid !== uid || (connectOptions.appName ?? "") !== (options.appName ?? ""));
|
|
2106
|
+
if (identityChanged) {
|
|
2107
|
+
cancelPendingWindowRequests();
|
|
2108
|
+
for (const subscription of subscriptions.values()) {
|
|
2109
|
+
subscription.hasRequestedInitialLocalQuery = false;
|
|
2110
|
+
subscription.lastSnapshot = void 0;
|
|
2111
|
+
subscription.lastNetworkVersion = 0;
|
|
2112
|
+
subscription.windowIntentVersion += 1;
|
|
2113
|
+
subscription.requestedCount = subscription.initialWindowSize;
|
|
2114
|
+
delete subscription.localQueryPromise;
|
|
2115
|
+
dispatchQuerySnapshotToSubscription(subscription, {
|
|
2116
|
+
error: null,
|
|
2117
|
+
data: void 0,
|
|
2118
|
+
context: {
|
|
2119
|
+
source: "cache",
|
|
2120
|
+
reset: true,
|
|
2121
|
+
...subscription.initialWindowSize ? { window: { requestedCount: subscription.initialWindowSize } } : {}
|
|
2122
|
+
}
|
|
2123
|
+
});
|
|
2124
|
+
}
|
|
2125
|
+
for (const subscription of countSubscriptions.values()) {
|
|
2126
|
+
subscription.lastSnapshot = void 0;
|
|
2127
|
+
dispatchCountSnapshotToSubscription(subscription, {
|
|
2128
|
+
error: null,
|
|
2129
|
+
count: void 0,
|
|
2130
|
+
context: {
|
|
2131
|
+
source: "cache",
|
|
2132
|
+
reset: true
|
|
2133
|
+
}
|
|
2134
|
+
});
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
forceInitialQueryOnNextConnection = hasEstablishedConnection || identityChanged;
|
|
2138
|
+
explicitDisconnect = true;
|
|
2139
|
+
connectionGeneration += 1;
|
|
2140
|
+
rejectPendingConnectionAttempt();
|
|
2141
|
+
clearReconnectTimer();
|
|
2142
|
+
pendingServerReconnectJitter = false;
|
|
2143
|
+
if (socket) try {
|
|
2144
|
+
socket.close();
|
|
2145
|
+
} catch {}
|
|
2146
|
+
socket = null;
|
|
2147
|
+
socketReadyForMessages = false;
|
|
2148
|
+
connectPromise = null;
|
|
2149
|
+
connectionEpoch = null;
|
|
2150
|
+
return connectInternal(tenantId, uid, options, { resetReconnectAttempts: true });
|
|
2151
|
+
};
|
|
2152
|
+
var getConnectionStatus = () => {
|
|
2153
|
+
return connectionStatus;
|
|
2154
|
+
};
|
|
2155
|
+
var getConnectionError = () => {
|
|
2156
|
+
return connectionError;
|
|
2157
|
+
};
|
|
2158
|
+
var onConnectionStatusChange = (callback) => {
|
|
2159
|
+
connectionStatusCallbacks.add(callback);
|
|
2160
|
+
return () => {
|
|
2161
|
+
connectionStatusCallbacks.delete(callback);
|
|
2162
|
+
};
|
|
2163
|
+
};
|
|
2164
|
+
var registerQuery = (modelName, query, optionsOrCallback, callbackMaybe, behavior) => {
|
|
2165
|
+
let options;
|
|
2166
|
+
let callback;
|
|
2167
|
+
if (typeof optionsOrCallback === "function") {
|
|
2168
|
+
options = {};
|
|
2169
|
+
callback = optionsOrCallback;
|
|
2170
|
+
} else {
|
|
2171
|
+
options = optionsOrCallback ?? {};
|
|
2172
|
+
callback = callbackMaybe;
|
|
2173
|
+
}
|
|
2174
|
+
if (!callback) return void 0;
|
|
2175
|
+
if (typeof modelName !== "string" || modelName.trim().length === 0) throw new Error("registerQuery: modelName must be a non-empty string");
|
|
2176
|
+
const queryKey = computeRtsQueryKey(query, options);
|
|
2177
|
+
const cbKey = `${modelName}.${queryKey}`;
|
|
2178
|
+
const runInitialNetworkQuery = behavior?.runInitialNetworkQuery !== false;
|
|
2179
|
+
const runInitialLocalQuery = behavior?.runInitialLocalQuery !== false;
|
|
2180
|
+
const forceRefreshOnMount = behavior?.forceRefreshOnMount === true;
|
|
2181
|
+
const populateCache = preparePopulateCacheOptions({
|
|
2182
|
+
projection: options.projection,
|
|
2183
|
+
populate: options.populate
|
|
2184
|
+
}, "registerQuery");
|
|
2185
|
+
const initialWindowSize = options.pagination ? normalizeRequestedCount(options.pagination.limit) : void 0;
|
|
2186
|
+
const subscription = subscriptions.get(cbKey) ?? {
|
|
2187
|
+
modelName,
|
|
2188
|
+
query,
|
|
2189
|
+
options,
|
|
2190
|
+
queryKey,
|
|
2191
|
+
callbacks: /* @__PURE__ */ new Set(),
|
|
2192
|
+
runInitialNetworkQuery,
|
|
2193
|
+
runInitialLocalQuery,
|
|
2194
|
+
hasRequestedInitialLocalQuery: false,
|
|
2195
|
+
lastNetworkVersion: 0,
|
|
2196
|
+
windowIntentVersion: 0,
|
|
2197
|
+
...initialWindowSize ? {
|
|
2198
|
+
initialWindowSize,
|
|
2199
|
+
requestedCount: initialWindowSize
|
|
2200
|
+
} : {},
|
|
2201
|
+
...populateCache ? { populateCache } : {}
|
|
2202
|
+
};
|
|
2203
|
+
const hadCallbacks = subscription.callbacks.size > 0;
|
|
2204
|
+
const hadInitialNetworkQuery = subscription.runInitialNetworkQuery;
|
|
2205
|
+
subscription.callbacks.add(callback);
|
|
2206
|
+
if (runInitialNetworkQuery) subscription.runInitialNetworkQuery = true;
|
|
2207
|
+
if (runInitialLocalQuery) subscription.runInitialLocalQuery = true;
|
|
2208
|
+
subscriptions.set(cbKey, subscription);
|
|
2209
|
+
let seeded = false;
|
|
2210
|
+
if (behavior?.seedSnapshot && updateQuerySubscriptionSnapshot(subscription, behavior.seedSnapshot)) {
|
|
2211
|
+
dispatchQuerySnapshotToSubscription(subscription, behavior.seedSnapshot);
|
|
2212
|
+
seeded = true;
|
|
2213
|
+
}
|
|
2214
|
+
if (!seeded && hadCallbacks && subscription.lastSnapshot && !hasSnapshotError(subscription.lastSnapshot)) callback(subscription.lastSnapshot.error, subscription.lastSnapshot.data, subscription.lastSnapshot.context);
|
|
2215
|
+
if (!hadCallbacks) {
|
|
2216
|
+
if (runInitialLocalQuery) subscription.hasRequestedInitialLocalQuery = requestSubscriptionLocalQuery(cbKey, subscription);
|
|
2217
|
+
sendToServer({
|
|
2218
|
+
type: "register-query",
|
|
2219
|
+
modelName,
|
|
2220
|
+
queryKey,
|
|
2221
|
+
query,
|
|
2222
|
+
options: getSubscriptionServerOptions(subscription),
|
|
2223
|
+
runInitialQuery: runInitialNetworkQuery
|
|
2224
|
+
});
|
|
2225
|
+
} else {
|
|
2226
|
+
if (runInitialLocalQuery && !subscription.hasRequestedInitialLocalQuery) subscription.hasRequestedInitialLocalQuery = requestSubscriptionLocalQuery(cbKey, subscription);
|
|
2227
|
+
if (forceRefreshOnMount || runInitialNetworkQuery && (!hadInitialNetworkQuery || hasSnapshotError(subscription.lastSnapshot))) requestRegisteredQueryRefresh(subscription);
|
|
2228
|
+
}
|
|
2229
|
+
return () => {
|
|
2230
|
+
const currentSubscription = subscriptions.get(cbKey);
|
|
2231
|
+
currentSubscription?.callbacks.delete(callback);
|
|
2232
|
+
if (currentSubscription && currentSubscription.callbacks.size === 0) {
|
|
2233
|
+
subscriptions.delete(cbKey);
|
|
2234
|
+
cancelPendingWindowRequests(cbKey);
|
|
2235
|
+
sendToServer({
|
|
2236
|
+
type: "remove-query",
|
|
2237
|
+
modelName,
|
|
2238
|
+
queryKey
|
|
2239
|
+
});
|
|
2240
|
+
}
|
|
2241
|
+
};
|
|
2242
|
+
};
|
|
2243
|
+
var setQueryWindowSize = async ({ modelName, query, options, requestedCount, timeoutMs = 1e4 }) => {
|
|
2244
|
+
const normalizedRequestedCount = normalizeRequestedCount(requestedCount);
|
|
2245
|
+
if (!normalizedRequestedCount || !options.pagination) return false;
|
|
2246
|
+
const queryKey = computeRtsQueryKey(query, options);
|
|
2247
|
+
const cbKey = `${modelName}.${queryKey}`;
|
|
2248
|
+
const subscription = subscriptions.get(cbKey);
|
|
2249
|
+
if (!subscription?.options.pagination) return false;
|
|
2250
|
+
const currentNetworkWindow = subscription.lastSnapshot?.context.source === "network" ? subscription.lastSnapshot.context.window : void 0;
|
|
2251
|
+
if (subscription.requestedCount === normalizedRequestedCount && currentNetworkWindow?.requestedCount === normalizedRequestedCount) return true;
|
|
2252
|
+
const previousRequestedCount = subscription.requestedCount ?? subscription.initialWindowSize ?? normalizedRequestedCount;
|
|
2253
|
+
subscription.windowIntentVersion += 1;
|
|
2254
|
+
subscription.requestedCount = normalizedRequestedCount;
|
|
2255
|
+
let request;
|
|
2256
|
+
const response = new Promise((resolve) => {
|
|
2257
|
+
const timeoutId = setRuntimeTimeout(() => {
|
|
2258
|
+
settlePendingWindowRequests(cbKey, request.requestedCount, false);
|
|
2259
|
+
}, timeoutMs);
|
|
2260
|
+
request = {
|
|
2261
|
+
requestedCount: normalizedRequestedCount,
|
|
2262
|
+
previousRequestedCount,
|
|
2263
|
+
resolve,
|
|
2264
|
+
timeoutId
|
|
2265
|
+
};
|
|
2266
|
+
const pending = pendingWindowRequests.get(cbKey) ?? /* @__PURE__ */ new Set();
|
|
2267
|
+
pending.add(request);
|
|
2268
|
+
pendingWindowRequests.set(cbKey, pending);
|
|
2269
|
+
});
|
|
2270
|
+
try {
|
|
2271
|
+
if (!isSocketReady()) {
|
|
2272
|
+
if (!currentTenantId || !currentUid) {
|
|
2273
|
+
settlePendingWindowRequests(cbKey, normalizedRequestedCount, false);
|
|
2274
|
+
return false;
|
|
2275
|
+
}
|
|
2276
|
+
await connectInternal(currentTenantId, currentUid, connectOptions, { resetReconnectAttempts: false });
|
|
2277
|
+
}
|
|
2278
|
+
sendToServer({
|
|
2279
|
+
type: "set-query-window",
|
|
2280
|
+
modelName,
|
|
2281
|
+
queryKey,
|
|
2282
|
+
requestedCount: normalizedRequestedCount
|
|
2283
|
+
});
|
|
2284
|
+
} catch {
|
|
2285
|
+
settlePendingWindowRequests(cbKey, normalizedRequestedCount, false);
|
|
2286
|
+
return false;
|
|
2287
|
+
}
|
|
2288
|
+
return await response;
|
|
2289
|
+
};
|
|
2290
|
+
var registerCount = (modelName, query, optionsOrCallback, callbackMaybe, behavior) => {
|
|
2291
|
+
let options;
|
|
2292
|
+
let callback;
|
|
2293
|
+
if (typeof optionsOrCallback === "function") {
|
|
2294
|
+
options = {};
|
|
2295
|
+
callback = optionsOrCallback;
|
|
2296
|
+
} else {
|
|
2297
|
+
options = optionsOrCallback ?? {};
|
|
2298
|
+
callback = callbackMaybe;
|
|
2299
|
+
}
|
|
2300
|
+
if (!callback) return void 0;
|
|
2301
|
+
if (typeof modelName !== "string" || modelName.trim().length === 0) throw new Error("registerCount: modelName must be a non-empty string");
|
|
2302
|
+
const queryKey = computeRtsQueryKey(query, options);
|
|
2303
|
+
const cbKey = `${modelName}.${queryKey}`;
|
|
2304
|
+
const runInitialNetworkQuery = behavior?.runInitialNetworkQuery !== false;
|
|
2305
|
+
const forceRefreshOnMount = behavior?.forceRefreshOnMount === true;
|
|
2306
|
+
const subscription = countSubscriptions.get(cbKey) ?? {
|
|
2307
|
+
modelName,
|
|
2308
|
+
query,
|
|
2309
|
+
options,
|
|
2310
|
+
queryKey,
|
|
2311
|
+
callbacks: /* @__PURE__ */ new Set(),
|
|
2312
|
+
runInitialNetworkQuery
|
|
2313
|
+
};
|
|
2314
|
+
const hadCallbacks = subscription.callbacks.size > 0;
|
|
2315
|
+
const hadInitialNetworkQuery = subscription.runInitialNetworkQuery;
|
|
2316
|
+
subscription.callbacks.add(callback);
|
|
2317
|
+
if (runInitialNetworkQuery) subscription.runInitialNetworkQuery = true;
|
|
2318
|
+
countSubscriptions.set(cbKey, subscription);
|
|
2319
|
+
let seeded = false;
|
|
2320
|
+
if (behavior?.seedSnapshot && updateCountSubscriptionSnapshot(subscription, behavior.seedSnapshot)) {
|
|
2321
|
+
dispatchCountSnapshotToSubscription(subscription, behavior.seedSnapshot);
|
|
2322
|
+
seeded = true;
|
|
2323
|
+
}
|
|
2324
|
+
if (!seeded && hadCallbacks && subscription.lastSnapshot && !hasSnapshotError(subscription.lastSnapshot)) callback(subscription.lastSnapshot.error, subscription.lastSnapshot.count, subscription.lastSnapshot.context);
|
|
2325
|
+
if (!hadCallbacks) sendToServer({
|
|
2326
|
+
type: "register-count",
|
|
2327
|
+
modelName,
|
|
2328
|
+
queryKey,
|
|
2329
|
+
query,
|
|
2330
|
+
options,
|
|
2331
|
+
runInitialQuery: runInitialNetworkQuery
|
|
2332
|
+
});
|
|
2333
|
+
else if (forceRefreshOnMount || runInitialNetworkQuery && (!hadInitialNetworkQuery || hasSnapshotError(subscription.lastSnapshot))) requestRegisteredCountRefresh(subscription);
|
|
2334
|
+
return () => {
|
|
2335
|
+
const currentSubscription = countSubscriptions.get(cbKey);
|
|
2336
|
+
currentSubscription?.callbacks.delete(callback);
|
|
2337
|
+
if (currentSubscription && currentSubscription.callbacks.size === 0) {
|
|
2338
|
+
countSubscriptions.delete(cbKey);
|
|
2339
|
+
sendToServer({
|
|
2340
|
+
type: "remove-count",
|
|
2341
|
+
modelName,
|
|
2342
|
+
queryKey
|
|
2343
|
+
});
|
|
2344
|
+
}
|
|
2345
|
+
};
|
|
2346
|
+
};
|
|
2347
|
+
var makeRunQueryKey = () => `run-query.${Date.now().toString(36)}.${Math.random().toString(36).slice(2, 10)}`;
|
|
2348
|
+
var runNetworkQuery = async ({ modelName, query, options = {}, timeoutMs = 1e4 }) => {
|
|
2349
|
+
if (typeof modelName !== "string" || modelName.trim().length === 0) throw new Error("runNetworkQuery: modelName must be a non-empty string");
|
|
2350
|
+
preparePopulateCacheOptions({
|
|
2351
|
+
projection: options.projection,
|
|
2352
|
+
populate: options.populate
|
|
2353
|
+
}, "runNetworkQuery");
|
|
2354
|
+
const hasTimeout = typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0;
|
|
2355
|
+
const timeoutStartedAt = hasTimeout ? Date.now() : 0;
|
|
2356
|
+
if (!isSocketReady()) {
|
|
2357
|
+
if (currentTenantId && currentUid) try {
|
|
2358
|
+
const connectAttempt = connectInternal(currentTenantId, currentUid, connectOptions, { resetReconnectAttempts: false });
|
|
2359
|
+
if (hasTimeout) await new Promise((resolve, reject) => {
|
|
2360
|
+
const timeoutId = setRuntimeTimeout(() => {
|
|
2361
|
+
reject(/* @__PURE__ */ new Error(RUN_NETWORK_QUERY_TIMEOUT_ERROR));
|
|
2362
|
+
}, timeoutMs);
|
|
2363
|
+
connectAttempt.then(() => {
|
|
2364
|
+
clearRuntimeTimeout(timeoutId);
|
|
2365
|
+
resolve();
|
|
2366
|
+
}, (error) => {
|
|
2367
|
+
clearRuntimeTimeout(timeoutId);
|
|
2368
|
+
reject(error);
|
|
2369
|
+
});
|
|
2370
|
+
});
|
|
2371
|
+
else await connectAttempt;
|
|
2372
|
+
} catch {}
|
|
2373
|
+
}
|
|
2374
|
+
if (!isSocketReady()) {
|
|
2375
|
+
if (hasTimeout && Date.now() - timeoutStartedAt >= timeoutMs) throw new Error(RUN_NETWORK_QUERY_TIMEOUT_ERROR);
|
|
2376
|
+
throw new Error("runNetworkQuery: RTS socket is not connected");
|
|
2377
|
+
}
|
|
2378
|
+
const remainingTimeoutMs = hasTimeout ? Math.max(0, timeoutMs - (Date.now() - timeoutStartedAt)) : null;
|
|
2379
|
+
if (remainingTimeoutMs !== null && remainingTimeoutMs <= 0) throw new Error(RUN_NETWORK_QUERY_TIMEOUT_ERROR);
|
|
2380
|
+
const resolvedOptions = {
|
|
2381
|
+
...options,
|
|
2382
|
+
key: makeRunQueryKey()
|
|
2383
|
+
};
|
|
2384
|
+
const queryKey = computeRtsQueryKey(query, resolvedOptions);
|
|
2385
|
+
const cbKey = `${modelName}.${queryKey}`;
|
|
2386
|
+
return await new Promise((resolve, reject) => {
|
|
2387
|
+
let settled = false;
|
|
2388
|
+
let timeoutId = null;
|
|
2389
|
+
const cleanup = () => {
|
|
2390
|
+
const callbacks = queryCallbacks.get(cbKey);
|
|
2391
|
+
callbacks?.delete(callback);
|
|
2392
|
+
if (callbacks && callbacks.size === 0) queryCallbacks.delete(cbKey);
|
|
2393
|
+
if (timeoutId !== null) clearRuntimeTimeout(timeoutId);
|
|
2394
|
+
};
|
|
2395
|
+
const settle = (next) => {
|
|
2396
|
+
if (settled) return;
|
|
2397
|
+
settled = true;
|
|
2398
|
+
cleanup();
|
|
2399
|
+
next();
|
|
2400
|
+
};
|
|
2401
|
+
const callback = (error, data, context) => {
|
|
2402
|
+
if (error) {
|
|
2403
|
+
settle(() => reject(error));
|
|
2404
|
+
return;
|
|
2405
|
+
}
|
|
2406
|
+
settle(() => resolve({
|
|
2407
|
+
data,
|
|
2408
|
+
context
|
|
2409
|
+
}));
|
|
2410
|
+
};
|
|
2411
|
+
const callbacks = queryCallbacks.get(cbKey) ?? /* @__PURE__ */ new Set();
|
|
2412
|
+
callbacks.add(callback);
|
|
2413
|
+
queryCallbacks.set(cbKey, callbacks);
|
|
2414
|
+
if (remainingTimeoutMs !== null) timeoutId = setRuntimeTimeout(() => {
|
|
2415
|
+
settle(() => reject(/* @__PURE__ */ new Error(RUN_NETWORK_QUERY_TIMEOUT_ERROR)));
|
|
2416
|
+
}, remainingTimeoutMs);
|
|
2417
|
+
sendToServer({
|
|
2418
|
+
type: "run-query",
|
|
2419
|
+
modelName,
|
|
2420
|
+
queryKey,
|
|
2421
|
+
query,
|
|
2422
|
+
options: resolvedOptions
|
|
2423
|
+
});
|
|
2424
|
+
});
|
|
2425
|
+
};
|
|
2426
|
+
var runNetworkCount = async ({ modelName, query, options = {}, timeoutMs = 1e4 }) => {
|
|
2427
|
+
if (typeof modelName !== "string" || modelName.trim().length === 0) throw new Error("runNetworkCount: modelName must be a non-empty string");
|
|
2428
|
+
const hasTimeout = typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0;
|
|
2429
|
+
const timeoutStartedAt = hasTimeout ? Date.now() : 0;
|
|
2430
|
+
if (!isSocketReady()) {
|
|
2431
|
+
if (currentTenantId && currentUid) try {
|
|
2432
|
+
const connectAttempt = connectInternal(currentTenantId, currentUid, connectOptions, { resetReconnectAttempts: false });
|
|
2433
|
+
if (hasTimeout) await new Promise((resolve, reject) => {
|
|
2434
|
+
const timeoutId = setRuntimeTimeout(() => {
|
|
2435
|
+
reject(/* @__PURE__ */ new Error(RUN_NETWORK_COUNT_TIMEOUT_ERROR));
|
|
2436
|
+
}, timeoutMs);
|
|
2437
|
+
connectAttempt.then(() => {
|
|
2438
|
+
clearRuntimeTimeout(timeoutId);
|
|
2439
|
+
resolve();
|
|
2440
|
+
}, (error) => {
|
|
2441
|
+
clearRuntimeTimeout(timeoutId);
|
|
2442
|
+
reject(error);
|
|
2443
|
+
});
|
|
2444
|
+
});
|
|
2445
|
+
else await connectAttempt;
|
|
2446
|
+
} catch {}
|
|
2447
|
+
}
|
|
2448
|
+
if (!isSocketReady()) {
|
|
2449
|
+
if (hasTimeout && Date.now() - timeoutStartedAt >= timeoutMs) throw new Error(RUN_NETWORK_COUNT_TIMEOUT_ERROR);
|
|
2450
|
+
throw new Error("runNetworkCount: RTS socket is not connected");
|
|
2451
|
+
}
|
|
2452
|
+
const remainingTimeoutMs = hasTimeout ? Math.max(0, timeoutMs - (Date.now() - timeoutStartedAt)) : null;
|
|
2453
|
+
if (remainingTimeoutMs !== null && remainingTimeoutMs <= 0) throw new Error(RUN_NETWORK_COUNT_TIMEOUT_ERROR);
|
|
2454
|
+
const resolvedOptions = {
|
|
2455
|
+
...options,
|
|
2456
|
+
key: makeRunQueryKey()
|
|
2457
|
+
};
|
|
2458
|
+
const queryKey = computeRtsQueryKey(query, resolvedOptions);
|
|
2459
|
+
const cbKey = `${modelName}.${queryKey}`;
|
|
2460
|
+
return await new Promise((resolve, reject) => {
|
|
2461
|
+
let settled = false;
|
|
2462
|
+
let timeoutId = null;
|
|
2463
|
+
const cleanup = () => {
|
|
2464
|
+
const callbacks = countCallbacks.get(cbKey);
|
|
2465
|
+
callbacks?.delete(callback);
|
|
2466
|
+
if (callbacks && callbacks.size === 0) countCallbacks.delete(cbKey);
|
|
2467
|
+
if (timeoutId !== null) clearRuntimeTimeout(timeoutId);
|
|
2468
|
+
};
|
|
2469
|
+
const settle = (next) => {
|
|
2470
|
+
if (settled) return;
|
|
2471
|
+
settled = true;
|
|
2472
|
+
cleanup();
|
|
2473
|
+
next();
|
|
2474
|
+
};
|
|
2475
|
+
const callback = (error, count, context) => {
|
|
2476
|
+
if (error) {
|
|
2477
|
+
settle(() => reject(typeof error === "string" ? new Error(error) : error));
|
|
2478
|
+
return;
|
|
2479
|
+
}
|
|
2480
|
+
if (count === void 0) {
|
|
2481
|
+
settle(() => reject(/* @__PURE__ */ new Error("runNetworkCount: invalid count payload")));
|
|
2482
|
+
return;
|
|
2483
|
+
}
|
|
2484
|
+
settle(() => resolve({
|
|
2485
|
+
count,
|
|
2486
|
+
context
|
|
2487
|
+
}));
|
|
2488
|
+
};
|
|
2489
|
+
const set = countCallbacks.get(cbKey) ?? /* @__PURE__ */ new Set();
|
|
2490
|
+
set.add(callback);
|
|
2491
|
+
countCallbacks.set(cbKey, set);
|
|
2492
|
+
if (remainingTimeoutMs !== null) timeoutId = setRuntimeTimeout(() => {
|
|
2493
|
+
settle(() => reject(/* @__PURE__ */ new Error(RUN_NETWORK_COUNT_TIMEOUT_ERROR)));
|
|
2494
|
+
}, remainingTimeoutMs);
|
|
2495
|
+
sendToServer({
|
|
2496
|
+
type: "run-count",
|
|
2497
|
+
modelName,
|
|
2498
|
+
queryKey,
|
|
2499
|
+
query,
|
|
2500
|
+
options: resolvedOptions
|
|
2501
|
+
});
|
|
2502
|
+
});
|
|
2503
|
+
};
|
|
2504
|
+
var sendMessage = (event, payload) => {
|
|
2505
|
+
sendToServer({
|
|
2506
|
+
type: "event",
|
|
2507
|
+
event,
|
|
2508
|
+
payload
|
|
2509
|
+
});
|
|
2510
|
+
};
|
|
2511
|
+
var sendRtsMessage = (channel, payload) => {
|
|
2512
|
+
sendToServer({
|
|
2513
|
+
type: "rts-message",
|
|
2514
|
+
channel,
|
|
2515
|
+
payload
|
|
2516
|
+
});
|
|
2517
|
+
};
|
|
2518
|
+
var onMessage = (event, callback) => {
|
|
2519
|
+
const set = messageCallbacks.get(event) ?? /* @__PURE__ */ new Set();
|
|
2520
|
+
set.add(callback);
|
|
2521
|
+
messageCallbacks.set(event, set);
|
|
2522
|
+
return () => {
|
|
2523
|
+
const callbacks = messageCallbacks.get(event);
|
|
2524
|
+
callbacks?.delete(callback);
|
|
2525
|
+
if (callbacks && callbacks.size === 0) messageCallbacks.delete(event);
|
|
2526
|
+
};
|
|
2527
|
+
};
|
|
2528
|
+
var onRtsMessage = (channel, callback) => {
|
|
2529
|
+
const set = rtsMessageCallbacks.get(channel) ?? /* @__PURE__ */ new Set();
|
|
2530
|
+
set.add(callback);
|
|
2531
|
+
rtsMessageCallbacks.set(channel, set);
|
|
2532
|
+
return () => {
|
|
2533
|
+
const callbacks = rtsMessageCallbacks.get(channel);
|
|
2534
|
+
callbacks?.delete(callback);
|
|
2535
|
+
if (callbacks && callbacks.size === 0) rtsMessageCallbacks.delete(channel);
|
|
2536
|
+
};
|
|
2537
|
+
};
|
|
2538
|
+
//#endregion
|
|
2539
|
+
//#region src/rts/useCountQuery.ts
|
|
2540
|
+
var normalizeCount = (value) => {
|
|
2541
|
+
if (typeof value !== "number") return void 0;
|
|
2542
|
+
if (!Number.isFinite(value) || value < 0) return void 0;
|
|
2543
|
+
return Math.floor(value);
|
|
2544
|
+
};
|
|
2545
|
+
var useCountQuery = (modelName, t0, t1) => {
|
|
2546
|
+
const $ = c(54);
|
|
2547
|
+
const query = t0 === void 0 ? {} : t0;
|
|
2548
|
+
let t2;
|
|
2549
|
+
if ($[0] !== t1) {
|
|
2550
|
+
t2 = t1 === void 0 ? {} : t1;
|
|
2551
|
+
$[0] = t1;
|
|
2552
|
+
$[1] = t2;
|
|
2553
|
+
} else t2 = $[1];
|
|
2554
|
+
const options = t2;
|
|
2555
|
+
if (typeof modelName !== "string" || modelName.trim().length === 0) throw new Error("useCountQuery: modelName must be a non-empty string");
|
|
2556
|
+
const enabled = options.enabled ?? true;
|
|
2557
|
+
const ssrEnabled = options.ssr !== false;
|
|
2558
|
+
const refreshOnMount = options.refreshOnMount === true;
|
|
2559
|
+
const key = options.key;
|
|
2560
|
+
const queryJson = serializeRtsQueryValue(query);
|
|
2561
|
+
let t3;
|
|
2562
|
+
if ($[2] !== key) {
|
|
2563
|
+
t3 = key ? { key } : {};
|
|
2564
|
+
$[2] = key;
|
|
2565
|
+
$[3] = t3;
|
|
2566
|
+
} else t3 = $[3];
|
|
2567
|
+
const runtimeOptions = t3;
|
|
2568
|
+
const queryKey = computeRtsQueryKey(query, runtimeOptions);
|
|
2569
|
+
const ssrRuntime = useRtsSsrRuntime();
|
|
2570
|
+
if (enabled && ssrEnabled && ssrRuntime) ssrRuntime.registerCount({
|
|
2571
|
+
modelName,
|
|
2572
|
+
query,
|
|
2573
|
+
options: runtimeOptions,
|
|
2574
|
+
queryKey
|
|
2575
|
+
});
|
|
2576
|
+
let t4;
|
|
2577
|
+
if ($[4] !== enabled || $[5] !== modelName || $[6] !== queryKey || $[7] !== ssrEnabled || $[8] !== ssrRuntime) {
|
|
2578
|
+
t4 = enabled && ssrEnabled ? ssrRuntime ? ssrRuntime.getCount(modelName, queryKey) : peekHydratedRtsCount(modelName, queryKey) : void 0;
|
|
2579
|
+
$[4] = enabled;
|
|
2580
|
+
$[5] = modelName;
|
|
2581
|
+
$[6] = queryKey;
|
|
2582
|
+
$[7] = ssrEnabled;
|
|
2583
|
+
$[8] = ssrRuntime;
|
|
2584
|
+
$[9] = t4;
|
|
2585
|
+
} else t4 = $[9];
|
|
2586
|
+
const seedCountRaw = t4;
|
|
2587
|
+
let t5;
|
|
2588
|
+
if ($[10] !== seedCountRaw) {
|
|
2589
|
+
t5 = normalizeCount(seedCountRaw);
|
|
2590
|
+
$[10] = seedCountRaw;
|
|
2591
|
+
$[11] = t5;
|
|
2592
|
+
} else t5 = $[11];
|
|
2593
|
+
const seedCount = t5;
|
|
2594
|
+
const seedCountStr = seedCount !== void 0 ? String(seedCount) : "";
|
|
2595
|
+
const hasSeedCount = seedCount !== void 0;
|
|
2596
|
+
let t6;
|
|
2597
|
+
if ($[12] !== seedCount) {
|
|
2598
|
+
t6 = () => seedCount;
|
|
2599
|
+
$[12] = seedCount;
|
|
2600
|
+
$[13] = t6;
|
|
2601
|
+
} else t6 = $[13];
|
|
2602
|
+
const [count, setCount] = useState(t6);
|
|
2603
|
+
let t7;
|
|
2604
|
+
if ($[14] !== hasSeedCount) {
|
|
2605
|
+
t7 = () => hasSeedCount ? "cache" : void 0;
|
|
2606
|
+
$[14] = hasSeedCount;
|
|
2607
|
+
$[15] = t7;
|
|
2608
|
+
} else t7 = $[15];
|
|
2609
|
+
const [source, setSource] = useState(t7);
|
|
2610
|
+
const [error, setError] = useState(void 0);
|
|
2611
|
+
const [loading, setLoading] = useState(enabled && !hasSeedCount);
|
|
2612
|
+
const lastCountRef = useRef(seedCount);
|
|
2613
|
+
let t8;
|
|
2614
|
+
if ($[16] !== enabled || $[17] !== hasSeedCount || $[18] !== modelName || $[19] !== queryKey || $[20] !== seedCount || $[21] !== ssrEnabled || $[22] !== ssrRuntime) {
|
|
2615
|
+
t8 = () => {
|
|
2616
|
+
if (!ssrRuntime && enabled && ssrEnabled && hasSeedCount) consumeHydratedRtsCount(modelName, queryKey);
|
|
2617
|
+
lastCountRef.current = seedCount;
|
|
2618
|
+
setError(void 0);
|
|
2619
|
+
if (!enabled) {
|
|
2620
|
+
setCount(void 0);
|
|
2621
|
+
setSource(void 0);
|
|
2622
|
+
setLoading(false);
|
|
2623
|
+
return;
|
|
2624
|
+
}
|
|
2625
|
+
if (hasSeedCount) {
|
|
2626
|
+
setCount(seedCount);
|
|
2627
|
+
setSource("cache");
|
|
2628
|
+
setLoading(false);
|
|
2629
|
+
return;
|
|
2630
|
+
}
|
|
2631
|
+
setCount(void 0);
|
|
2632
|
+
setSource(void 0);
|
|
2633
|
+
setLoading(true);
|
|
2634
|
+
};
|
|
2635
|
+
$[16] = enabled;
|
|
2636
|
+
$[17] = hasSeedCount;
|
|
2637
|
+
$[18] = modelName;
|
|
2638
|
+
$[19] = queryKey;
|
|
2639
|
+
$[20] = seedCount;
|
|
2640
|
+
$[21] = ssrEnabled;
|
|
2641
|
+
$[22] = ssrRuntime;
|
|
2642
|
+
$[23] = t8;
|
|
2643
|
+
} else t8 = $[23];
|
|
2644
|
+
let t9;
|
|
2645
|
+
if ($[24] !== enabled || $[25] !== hasSeedCount || $[26] !== modelName || $[27] !== queryKey || $[28] !== seedCount || $[29] !== seedCountStr || $[30] !== ssrEnabled || $[31] !== ssrRuntime) {
|
|
2646
|
+
t9 = [
|
|
2647
|
+
enabled,
|
|
2648
|
+
ssrEnabled,
|
|
2649
|
+
ssrRuntime,
|
|
2650
|
+
modelName,
|
|
2651
|
+
queryKey,
|
|
2652
|
+
hasSeedCount,
|
|
2653
|
+
seedCount,
|
|
2654
|
+
seedCountStr
|
|
2655
|
+
];
|
|
2656
|
+
$[24] = enabled;
|
|
2657
|
+
$[25] = hasSeedCount;
|
|
2658
|
+
$[26] = modelName;
|
|
2659
|
+
$[27] = queryKey;
|
|
2660
|
+
$[28] = seedCount;
|
|
2661
|
+
$[29] = seedCountStr;
|
|
2662
|
+
$[30] = ssrEnabled;
|
|
2663
|
+
$[31] = ssrRuntime;
|
|
2664
|
+
$[32] = t9;
|
|
2665
|
+
} else t9 = $[32];
|
|
2666
|
+
useEffect(t8, t9);
|
|
2667
|
+
let t10;
|
|
2668
|
+
if ($[33] !== enabled || $[34] !== hasSeedCount || $[35] !== modelName || $[36] !== query || $[37] !== refreshOnMount || $[38] !== runtimeOptions || $[39] !== seedCount) {
|
|
2669
|
+
t10 = () => {
|
|
2670
|
+
if (!enabled) return;
|
|
2671
|
+
const unsubscribe = registerCount(modelName, query, runtimeOptions, (err, nextCount, context) => {
|
|
2672
|
+
if (context.source === "cache" && context.reset) {
|
|
2673
|
+
lastCountRef.current = void 0;
|
|
2674
|
+
setCount(void 0);
|
|
2675
|
+
setSource(void 0);
|
|
2676
|
+
setError(void 0);
|
|
2677
|
+
setLoading(true);
|
|
2678
|
+
return;
|
|
2679
|
+
}
|
|
2680
|
+
setLoading(false);
|
|
2681
|
+
if (err) {
|
|
2682
|
+
setError(err);
|
|
2683
|
+
return;
|
|
2684
|
+
}
|
|
2685
|
+
if (nextCount === void 0) return;
|
|
2686
|
+
setError(void 0);
|
|
2687
|
+
if (lastCountRef.current === nextCount) {
|
|
2688
|
+
setSource(context.source);
|
|
2689
|
+
return;
|
|
2690
|
+
}
|
|
2691
|
+
lastCountRef.current = nextCount;
|
|
2692
|
+
setCount(nextCount);
|
|
2693
|
+
setSource(context.source);
|
|
2694
|
+
}, {
|
|
2695
|
+
runInitialNetworkQuery: refreshOnMount || !hasSeedCount,
|
|
2696
|
+
forceRefreshOnMount: refreshOnMount,
|
|
2697
|
+
...hasSeedCount ? { seedSnapshot: {
|
|
2698
|
+
error: null,
|
|
2699
|
+
count: seedCount,
|
|
2700
|
+
context: { source: "cache" }
|
|
2701
|
+
} } : {}
|
|
2702
|
+
});
|
|
2703
|
+
return () => {
|
|
2704
|
+
unsubscribe?.();
|
|
2705
|
+
};
|
|
2706
|
+
};
|
|
2707
|
+
$[33] = enabled;
|
|
2708
|
+
$[34] = hasSeedCount;
|
|
2709
|
+
$[35] = modelName;
|
|
2710
|
+
$[36] = query;
|
|
2711
|
+
$[37] = refreshOnMount;
|
|
2712
|
+
$[38] = runtimeOptions;
|
|
2713
|
+
$[39] = seedCount;
|
|
2714
|
+
$[40] = t10;
|
|
2715
|
+
} else t10 = $[40];
|
|
2716
|
+
let t11;
|
|
2717
|
+
if ($[41] !== enabled || $[42] !== hasSeedCount || $[43] !== modelName || $[44] !== queryJson || $[45] !== queryKey || $[46] !== refreshOnMount || $[47] !== runtimeOptions) {
|
|
2718
|
+
t11 = [
|
|
2719
|
+
enabled,
|
|
2720
|
+
modelName,
|
|
2721
|
+
queryJson,
|
|
2722
|
+
queryKey,
|
|
2723
|
+
runtimeOptions,
|
|
2724
|
+
hasSeedCount,
|
|
2725
|
+
refreshOnMount
|
|
2726
|
+
];
|
|
2727
|
+
$[41] = enabled;
|
|
2728
|
+
$[42] = hasSeedCount;
|
|
2729
|
+
$[43] = modelName;
|
|
2730
|
+
$[44] = queryJson;
|
|
2731
|
+
$[45] = queryKey;
|
|
2732
|
+
$[46] = refreshOnMount;
|
|
2733
|
+
$[47] = runtimeOptions;
|
|
2734
|
+
$[48] = t11;
|
|
2735
|
+
} else t11 = $[48];
|
|
2736
|
+
useEffect(t10, t11);
|
|
2737
|
+
let t12;
|
|
2738
|
+
if ($[49] !== count || $[50] !== error || $[51] !== loading || $[52] !== source) {
|
|
2739
|
+
t12 = {
|
|
2740
|
+
count,
|
|
2741
|
+
source,
|
|
2742
|
+
error,
|
|
2743
|
+
loading
|
|
2744
|
+
};
|
|
2745
|
+
$[49] = count;
|
|
2746
|
+
$[50] = error;
|
|
2747
|
+
$[51] = loading;
|
|
2748
|
+
$[52] = source;
|
|
2749
|
+
$[53] = t12;
|
|
2750
|
+
} else t12 = $[53];
|
|
2751
|
+
return t12;
|
|
2752
|
+
};
|
|
2753
|
+
//#endregion
|
|
2754
|
+
//#region src/rts/useQuery.ts
|
|
2755
|
+
var normalizePageInfo = (value) => {
|
|
2756
|
+
if (!value || typeof value !== "object") return void 0;
|
|
2757
|
+
if (Array.isArray(value)) return void 0;
|
|
2758
|
+
const raw = value;
|
|
2759
|
+
if (typeof raw.hasNextPage !== "boolean" || typeof raw.hasPrevPage !== "boolean") return void 0;
|
|
2760
|
+
const nextCursor = typeof raw.nextCursor === "string" && raw.nextCursor ? raw.nextCursor : void 0;
|
|
2761
|
+
const prevCursor = typeof raw.prevCursor === "string" && raw.prevCursor ? raw.prevCursor : void 0;
|
|
2762
|
+
return {
|
|
2763
|
+
hasNextPage: raw.hasNextPage,
|
|
2764
|
+
hasPrevPage: raw.hasPrevPage,
|
|
2765
|
+
...nextCursor ? { nextCursor } : {},
|
|
2766
|
+
...prevCursor ? { prevCursor } : {}
|
|
2767
|
+
};
|
|
2768
|
+
};
|
|
2769
|
+
var normalizeTotalCount = (value) => {
|
|
2770
|
+
if (typeof value !== "number") return void 0;
|
|
2771
|
+
if (!Number.isFinite(value) || value < 0) return void 0;
|
|
2772
|
+
return Math.floor(value);
|
|
2773
|
+
};
|
|
2774
|
+
var normalizeWindowSize = (value) => {
|
|
2775
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return 1;
|
|
2776
|
+
return Math.max(1, Math.min(RTS_QUERY_WINDOW_MAX_COUNT, Math.floor(value)));
|
|
2777
|
+
};
|
|
2778
|
+
var assertIncludeOnlyProjection = (projection) => {
|
|
2779
|
+
if (!projection) return;
|
|
2780
|
+
for (const [path, value] of Object.entries(projection)) {
|
|
2781
|
+
if (!path.trim()) continue;
|
|
2782
|
+
if (value !== 1) throw new Error("useQuery: projection must be include-only (value 1); exclusion projection is not supported");
|
|
2783
|
+
}
|
|
2784
|
+
};
|
|
2785
|
+
var useQuery = (modelName, query = {}, options = {}) => {
|
|
2786
|
+
if (typeof modelName !== "string" || modelName.trim().length === 0) throw new Error("useQuery: modelName must be a non-empty string");
|
|
2787
|
+
assertIncludeOnlyProjection(options.projection);
|
|
2788
|
+
const enabled = options.enabled ?? true;
|
|
2789
|
+
const ssrEnabled = options.ssr !== false;
|
|
2790
|
+
const refreshOnMount = options.refreshOnMount === true;
|
|
2791
|
+
const key = options.key;
|
|
2792
|
+
const queryJson = serializeRtsQueryValue(query);
|
|
2793
|
+
const projectionJson = options.projection ? serializeRtsQueryValue(options.projection) : "";
|
|
2794
|
+
const sortJson = options.sort ? serializeRtsQueryValue(options.sort) : "";
|
|
2795
|
+
const limitStr = typeof options.limit === "number" ? String(options.limit) : "";
|
|
2796
|
+
const populateJson = options.populate ? serializeRtsQueryValue(options.populate) : "";
|
|
2797
|
+
const paginationJson = options.pagination ? serializeRtsQueryValue(options.pagination) : "";
|
|
2798
|
+
preparePopulateCacheOptions({
|
|
2799
|
+
projection: options.projection,
|
|
2800
|
+
populate: options.populate
|
|
2801
|
+
}, "useQuery");
|
|
2802
|
+
const isPaginated = Boolean(options.pagination);
|
|
2803
|
+
if (options.pagination && (!Number.isInteger(options.pagination.limit) || options.pagination.limit < 1 || options.pagination.limit > 4096)) throw new Error("useQuery: pagination limit must be an integer between 1 and 4096");
|
|
2804
|
+
if (options.pagination?.cursor || options.pagination?.direction) throw new Error("useQuery: cursor and direction are not supported by virtual query windows");
|
|
2805
|
+
const initialWindowSize = isPaginated ? normalizeWindowSize(options.pagination?.limit) : void 0;
|
|
2806
|
+
const queryKey = computeRtsQueryKey(query, {
|
|
2807
|
+
key,
|
|
2808
|
+
projection: options.projection,
|
|
2809
|
+
sort: options.sort,
|
|
2810
|
+
limit: options.limit,
|
|
2811
|
+
populate: options.populate,
|
|
2812
|
+
pagination: options.pagination
|
|
2813
|
+
});
|
|
2814
|
+
const ssrRuntime = useRtsSsrRuntime();
|
|
2815
|
+
if (enabled && ssrEnabled && ssrRuntime) ssrRuntime.registerQuery({
|
|
2816
|
+
modelName,
|
|
2817
|
+
query,
|
|
2818
|
+
options: {
|
|
2819
|
+
key,
|
|
2820
|
+
projection: options.projection,
|
|
2821
|
+
sort: options.sort,
|
|
2822
|
+
limit: options.limit,
|
|
2823
|
+
populate: options.populate,
|
|
2824
|
+
pagination: options.pagination
|
|
2825
|
+
},
|
|
2826
|
+
queryKey
|
|
2827
|
+
});
|
|
2828
|
+
const seedDataRaw = useMemo(() => enabled && ssrEnabled ? ssrRuntime ? ssrRuntime.getQueryData(modelName, queryKey) : peekHydratedRtsQueryData(modelName, queryKey) : void 0, [
|
|
2829
|
+
enabled,
|
|
2830
|
+
ssrEnabled,
|
|
2831
|
+
ssrRuntime,
|
|
2832
|
+
modelName,
|
|
2833
|
+
queryKey
|
|
2834
|
+
]);
|
|
2835
|
+
const seedPageInfoRaw = useMemo(() => enabled && ssrEnabled ? ssrRuntime ? ssrRuntime.getQueryPageInfo(modelName, queryKey) : peekHydratedRtsQueryPageInfo(modelName, queryKey) : void 0, [
|
|
2836
|
+
enabled,
|
|
2837
|
+
ssrEnabled,
|
|
2838
|
+
ssrRuntime,
|
|
2839
|
+
modelName,
|
|
2840
|
+
queryKey
|
|
2841
|
+
]);
|
|
2842
|
+
const seedTotalCountRaw = useMemo(() => enabled && ssrEnabled ? ssrRuntime ? ssrRuntime.getQueryTotalCount(modelName, queryKey) : peekHydratedRtsQueryTotalCount(modelName, queryKey) : void 0, [
|
|
2843
|
+
enabled,
|
|
2844
|
+
ssrEnabled,
|
|
2845
|
+
ssrRuntime,
|
|
2846
|
+
modelName,
|
|
2847
|
+
queryKey
|
|
2848
|
+
]);
|
|
2849
|
+
const hasSeedData = Array.isArray(seedDataRaw);
|
|
2850
|
+
const seedData = hasSeedData ? seedDataRaw : void 0;
|
|
2851
|
+
const seedPageInfo = useMemo(() => normalizePageInfo(seedPageInfoRaw), [seedPageInfoRaw]);
|
|
2852
|
+
const seedTotalCount = normalizeTotalCount(seedTotalCountRaw);
|
|
2853
|
+
const seedJson = (() => {
|
|
2854
|
+
if (!hasSeedData) return "";
|
|
2855
|
+
try {
|
|
2856
|
+
return JSON.stringify(seedDataRaw);
|
|
2857
|
+
} catch {
|
|
2858
|
+
return "";
|
|
2859
|
+
}
|
|
2860
|
+
})();
|
|
2861
|
+
const seedPageInfoJson = (() => {
|
|
2862
|
+
if (!seedPageInfo) return "";
|
|
2863
|
+
try {
|
|
2864
|
+
return JSON.stringify(seedPageInfo);
|
|
2865
|
+
} catch {
|
|
2866
|
+
return "";
|
|
2867
|
+
}
|
|
2868
|
+
})();
|
|
2869
|
+
const seedTotalCountStr = seedTotalCount !== void 0 ? String(seedTotalCount) : "";
|
|
2870
|
+
const [data, setData] = useState(() => isPaginated ? void 0 : seedData);
|
|
2871
|
+
const [paginatedData, setPaginatedData] = useState(() => isPaginated ? seedData : void 0);
|
|
2872
|
+
const [pageInfo, setPageInfo] = useState(() => isPaginated ? seedPageInfo : void 0);
|
|
2873
|
+
const [totalCount, setTotalCount] = useState(() => isPaginated ? seedTotalCount : void 0);
|
|
2874
|
+
const [source, setSource] = useState(() => hasSeedData ? "cache" : void 0);
|
|
2875
|
+
const [error, setError] = useState(void 0);
|
|
2876
|
+
const [loading, setLoading] = useState(enabled && !hasSeedData);
|
|
2877
|
+
const hasFirstReply = useRef(false);
|
|
2878
|
+
const hasNetworkReply = useRef(false);
|
|
2879
|
+
const lastDataJsonRef = useRef("");
|
|
2880
|
+
const pageInfoRef = useRef(seedPageInfo);
|
|
2881
|
+
const requestedCountRef = useRef(initialWindowSize ?? 1);
|
|
2882
|
+
const pagingRef = useRef(false);
|
|
2883
|
+
useEffect(() => {
|
|
2884
|
+
pageInfoRef.current = pageInfo;
|
|
2885
|
+
}, [pageInfo]);
|
|
2886
|
+
useEffect(() => {
|
|
2887
|
+
if (!ssrRuntime && enabled && ssrEnabled && hasSeedData) consumeHydratedRtsQueryData(modelName, queryKey);
|
|
2888
|
+
hasFirstReply.current = hasSeedData;
|
|
2889
|
+
hasNetworkReply.current = false;
|
|
2890
|
+
lastDataJsonRef.current = seedJson;
|
|
2891
|
+
requestedCountRef.current = initialWindowSize ?? 1;
|
|
2892
|
+
pagingRef.current = false;
|
|
2893
|
+
setError(void 0);
|
|
2894
|
+
if (!enabled) {
|
|
2895
|
+
setLoading(false);
|
|
2896
|
+
setData(void 0);
|
|
2897
|
+
setPaginatedData(void 0);
|
|
2898
|
+
setPageInfo(void 0);
|
|
2899
|
+
setTotalCount(void 0);
|
|
2900
|
+
setSource(void 0);
|
|
2901
|
+
return;
|
|
2902
|
+
}
|
|
2903
|
+
if (hasSeedData) {
|
|
2904
|
+
const nextSeedData = seedData;
|
|
2905
|
+
setLoading(false);
|
|
2906
|
+
setSource("cache");
|
|
2907
|
+
if (isPaginated) {
|
|
2908
|
+
setPaginatedData(nextSeedData);
|
|
2909
|
+
setPageInfo(seedPageInfo);
|
|
2910
|
+
setTotalCount(seedTotalCount);
|
|
2911
|
+
setData(void 0);
|
|
2912
|
+
} else {
|
|
2913
|
+
setData(nextSeedData);
|
|
2914
|
+
setTotalCount(void 0);
|
|
2915
|
+
setPaginatedData(void 0);
|
|
2916
|
+
setPageInfo(void 0);
|
|
2917
|
+
}
|
|
2918
|
+
return;
|
|
2919
|
+
}
|
|
2920
|
+
setData(void 0);
|
|
2921
|
+
setPaginatedData(void 0);
|
|
2922
|
+
setPageInfo(void 0);
|
|
2923
|
+
setTotalCount(void 0);
|
|
2924
|
+
setLoading(true);
|
|
2925
|
+
}, [
|
|
2926
|
+
enabled,
|
|
2927
|
+
ssrEnabled,
|
|
2928
|
+
ssrRuntime,
|
|
2929
|
+
modelName,
|
|
2930
|
+
queryKey,
|
|
2931
|
+
hasSeedData,
|
|
2932
|
+
seedData,
|
|
2933
|
+
seedJson,
|
|
2934
|
+
seedPageInfo,
|
|
2935
|
+
seedPageInfoJson,
|
|
2936
|
+
seedTotalCount,
|
|
2937
|
+
seedTotalCountStr,
|
|
2938
|
+
isPaginated,
|
|
2939
|
+
initialWindowSize
|
|
2940
|
+
]);
|
|
2941
|
+
useEffect(() => {
|
|
2942
|
+
if (!enabled) return;
|
|
2943
|
+
const runInitialNetworkQuery = isPaginated || refreshOnMount || !hasSeedData;
|
|
2944
|
+
const runInitialLocalQuery = !hasSeedData;
|
|
2945
|
+
const unsubscribe = registerQuery(modelName, query, {
|
|
2946
|
+
key,
|
|
2947
|
+
projection: options.projection,
|
|
2948
|
+
sort: options.sort,
|
|
2949
|
+
limit: options.limit,
|
|
2950
|
+
populate: options.populate,
|
|
2951
|
+
pagination: options.pagination
|
|
2952
|
+
}, (err, result, context) => {
|
|
2953
|
+
if (context.source === "cache" && context.reset) {
|
|
2954
|
+
hasFirstReply.current = false;
|
|
2955
|
+
hasNetworkReply.current = false;
|
|
2956
|
+
lastDataJsonRef.current = "";
|
|
2957
|
+
pageInfoRef.current = void 0;
|
|
2958
|
+
requestedCountRef.current = initialWindowSize ?? 1;
|
|
2959
|
+
pagingRef.current = false;
|
|
2960
|
+
setData(void 0);
|
|
2961
|
+
setPaginatedData(void 0);
|
|
2962
|
+
setPageInfo(void 0);
|
|
2963
|
+
setTotalCount(void 0);
|
|
2964
|
+
setSource(void 0);
|
|
2965
|
+
setError(void 0);
|
|
2966
|
+
setLoading(true);
|
|
2967
|
+
return;
|
|
2968
|
+
}
|
|
2969
|
+
if (context.source === "cache" && hasNetworkReply.current && (!isPaginated || !context.window || context.window.requestedCount <= requestedCountRef.current)) return;
|
|
2970
|
+
if (context.source === "network") hasNetworkReply.current = true;
|
|
2971
|
+
setLoading(false);
|
|
2972
|
+
if (err) {
|
|
2973
|
+
setError(err);
|
|
2974
|
+
return;
|
|
2975
|
+
}
|
|
2976
|
+
if (!Array.isArray(result)) return;
|
|
2977
|
+
if (context.source === "network" && context.isLocal && options.skipLocal && hasFirstReply.current) return;
|
|
2978
|
+
hasFirstReply.current = true;
|
|
2979
|
+
const nextPageInfo = context.pageInfo;
|
|
2980
|
+
const nextTotalCount = context.totalCount;
|
|
2981
|
+
const nextRequestedCount = context.window?.requestedCount;
|
|
2982
|
+
if (isPaginated && nextRequestedCount !== void 0) requestedCountRef.current = nextRequestedCount;
|
|
2983
|
+
const payloadForHash = isPaginated ? {
|
|
2984
|
+
result,
|
|
2985
|
+
pageInfo: nextPageInfo,
|
|
2986
|
+
totalCount: nextTotalCount,
|
|
2987
|
+
requestedCount: nextRequestedCount
|
|
2988
|
+
} : result;
|
|
2989
|
+
let nextJson = "";
|
|
2990
|
+
try {
|
|
2991
|
+
nextJson = JSON.stringify(payloadForHash);
|
|
2992
|
+
} catch {
|
|
2993
|
+
nextJson = "";
|
|
2994
|
+
}
|
|
2995
|
+
if (nextJson && nextJson === lastDataJsonRef.current) {
|
|
2996
|
+
setSource(context.source);
|
|
2997
|
+
return;
|
|
2998
|
+
}
|
|
2999
|
+
lastDataJsonRef.current = nextJson;
|
|
3000
|
+
setSource(context.source);
|
|
3001
|
+
setError(void 0);
|
|
3002
|
+
if (isPaginated) {
|
|
3003
|
+
setPaginatedData(result);
|
|
3004
|
+
setPageInfo(nextPageInfo);
|
|
3005
|
+
setTotalCount(nextTotalCount);
|
|
3006
|
+
return;
|
|
3007
|
+
}
|
|
3008
|
+
setData(result);
|
|
3009
|
+
setTotalCount(void 0);
|
|
3010
|
+
}, {
|
|
3011
|
+
runInitialNetworkQuery,
|
|
3012
|
+
runInitialLocalQuery,
|
|
3013
|
+
forceRefreshOnMount: refreshOnMount,
|
|
3014
|
+
...hasSeedData ? { seedSnapshot: {
|
|
3015
|
+
error: null,
|
|
3016
|
+
data: seedData,
|
|
3017
|
+
context: {
|
|
3018
|
+
source: "cache",
|
|
3019
|
+
...seedPageInfo ? { pageInfo: seedPageInfo } : {},
|
|
3020
|
+
...seedTotalCount !== void 0 ? { totalCount: seedTotalCount } : {},
|
|
3021
|
+
...initialWindowSize ? { window: { requestedCount: initialWindowSize } } : {}
|
|
3022
|
+
}
|
|
3023
|
+
} } : {}
|
|
3024
|
+
});
|
|
3025
|
+
return () => {
|
|
3026
|
+
unsubscribe?.();
|
|
3027
|
+
};
|
|
3028
|
+
}, [
|
|
3029
|
+
enabled,
|
|
3030
|
+
modelName,
|
|
3031
|
+
queryKey,
|
|
3032
|
+
queryJson,
|
|
3033
|
+
projectionJson,
|
|
3034
|
+
sortJson,
|
|
3035
|
+
limitStr,
|
|
3036
|
+
populateJson,
|
|
3037
|
+
paginationJson,
|
|
3038
|
+
hasSeedData,
|
|
3039
|
+
refreshOnMount,
|
|
3040
|
+
isPaginated,
|
|
3041
|
+
initialWindowSize
|
|
3042
|
+
]);
|
|
3043
|
+
const fetchNext = async () => {
|
|
3044
|
+
if (!enabled || !isPaginated || !options.pagination) return false;
|
|
3045
|
+
if (pagingRef.current || !pageInfoRef.current?.hasNextPage) return false;
|
|
3046
|
+
const pageSize = initialWindowSize ?? normalizeWindowSize(options.pagination.limit);
|
|
3047
|
+
const nextRequestedCount_0 = Math.min(RTS_QUERY_WINDOW_MAX_COUNT, requestedCountRef.current + pageSize);
|
|
3048
|
+
if (nextRequestedCount_0 === requestedCountRef.current) return false;
|
|
3049
|
+
pagingRef.current = true;
|
|
3050
|
+
setLoading(true);
|
|
3051
|
+
setError(void 0);
|
|
3052
|
+
try {
|
|
3053
|
+
return await setQueryWindowSize({
|
|
3054
|
+
modelName,
|
|
3055
|
+
query,
|
|
3056
|
+
options: {
|
|
3057
|
+
key,
|
|
3058
|
+
projection: options.projection,
|
|
3059
|
+
sort: options.sort,
|
|
3060
|
+
limit: options.limit,
|
|
3061
|
+
populate: options.populate,
|
|
3062
|
+
pagination: options.pagination
|
|
3063
|
+
},
|
|
3064
|
+
requestedCount: nextRequestedCount_0
|
|
3065
|
+
});
|
|
3066
|
+
} catch (err_0) {
|
|
3067
|
+
setError(err_0);
|
|
3068
|
+
return false;
|
|
3069
|
+
} finally {
|
|
3070
|
+
pagingRef.current = false;
|
|
3071
|
+
setLoading(false);
|
|
3072
|
+
}
|
|
3073
|
+
};
|
|
3074
|
+
const fetchPrevious = async () => {
|
|
3075
|
+
return false;
|
|
3076
|
+
};
|
|
3077
|
+
const resetPagination = () => {
|
|
3078
|
+
if (!enabled || !isPaginated || !options.pagination || !initialWindowSize) return;
|
|
3079
|
+
if (pagingRef.current || requestedCountRef.current === initialWindowSize) return;
|
|
3080
|
+
pagingRef.current = true;
|
|
3081
|
+
setLoading(true);
|
|
3082
|
+
setError(void 0);
|
|
3083
|
+
setQueryWindowSize({
|
|
3084
|
+
modelName,
|
|
3085
|
+
query,
|
|
3086
|
+
options: {
|
|
3087
|
+
key,
|
|
3088
|
+
projection: options.projection,
|
|
3089
|
+
sort: options.sort,
|
|
3090
|
+
limit: options.limit,
|
|
3091
|
+
populate: options.populate,
|
|
3092
|
+
pagination: options.pagination
|
|
3093
|
+
},
|
|
3094
|
+
requestedCount: initialWindowSize
|
|
3095
|
+
}).finally(() => {
|
|
3096
|
+
pagingRef.current = false;
|
|
3097
|
+
setLoading(false);
|
|
3098
|
+
});
|
|
3099
|
+
};
|
|
3100
|
+
return {
|
|
3101
|
+
data: isPaginated ? paginatedData : data,
|
|
3102
|
+
pageInfo: isPaginated ? pageInfo : void 0,
|
|
3103
|
+
totalCount: isPaginated ? totalCount : void 0,
|
|
3104
|
+
source,
|
|
3105
|
+
error,
|
|
3106
|
+
loading,
|
|
3107
|
+
fetchNext,
|
|
3108
|
+
fetchPrevious,
|
|
3109
|
+
resetPagination
|
|
3110
|
+
};
|
|
3111
|
+
};
|
|
3112
|
+
//#endregion
|
|
3113
|
+
//#region src/rts/useRtsConnection.ts
|
|
3114
|
+
var activeConsumers = 0;
|
|
3115
|
+
var connectedKey = null;
|
|
3116
|
+
var pendingDisconnectTimer = null;
|
|
3117
|
+
var cancelPendingDisconnect = () => {
|
|
3118
|
+
if (pendingDisconnectTimer !== null) {
|
|
3119
|
+
clearTimeout(pendingDisconnectTimer);
|
|
3120
|
+
pendingDisconnectTimer = null;
|
|
3121
|
+
}
|
|
3122
|
+
};
|
|
3123
|
+
var useRtsConnection = (tenantId, uid, options = {}) => {
|
|
3124
|
+
const tenantIdValue = typeof tenantId === "string" ? tenantId.trim() : "";
|
|
3125
|
+
const uidValue = typeof uid === "string" ? uid.trim() : "";
|
|
3126
|
+
const optionsJson = serializeRtsQueryValue(options);
|
|
3127
|
+
const optionsRef = useRef(options);
|
|
3128
|
+
optionsRef.current = options;
|
|
3129
|
+
useEffect(() => {
|
|
3130
|
+
if (typeof window === "undefined") return;
|
|
3131
|
+
if (!tenantIdValue || !uidValue) return;
|
|
3132
|
+
activeConsumers += 1;
|
|
3133
|
+
cancelPendingDisconnect();
|
|
3134
|
+
const key = `${tenantIdValue}::${uidValue}::${optionsJson}`;
|
|
3135
|
+
if (connectedKey !== key) {
|
|
3136
|
+
connectedKey = key;
|
|
3137
|
+
reconnect(tenantIdValue, uidValue, optionsRef.current).catch((error) => {
|
|
3138
|
+
connectedKey = null;
|
|
3139
|
+
console.error("RTS connection failed", error);
|
|
3140
|
+
});
|
|
3141
|
+
}
|
|
3142
|
+
return () => {
|
|
3143
|
+
activeConsumers -= 1;
|
|
3144
|
+
if (activeConsumers > 0) return;
|
|
3145
|
+
pendingDisconnectTimer = setTimeout(() => {
|
|
3146
|
+
pendingDisconnectTimer = null;
|
|
3147
|
+
if (activeConsumers > 0) return;
|
|
3148
|
+
connectedKey = null;
|
|
3149
|
+
disconnect();
|
|
3150
|
+
}, 0);
|
|
3151
|
+
};
|
|
3152
|
+
}, [
|
|
3153
|
+
tenantIdValue,
|
|
3154
|
+
uidValue,
|
|
3155
|
+
optionsJson
|
|
3156
|
+
]);
|
|
3157
|
+
};
|
|
3158
|
+
//#endregion
|
|
3159
|
+
export { clearHydratedRtsQueryData as A, destroyCollection as C, updateDocs as D, runQuery as E, peekHydratedRtsQueryPageInfo as F, peekHydratedRtsQueryTotalCount as I, hydrateRtsFromWindow as M, peekHydratedRtsCount as N, RtsSsrRuntimeProvider as O, peekHydratedRtsQueryData as P, destroyAllCollections as S, resetRtsPouchStore as T, sendMessage as _, connect as a, configureRtsPouchStore as b, getConnectionStatus as c, onRtsMessage as d, reconnect as f, runNetworkQuery as g, runNetworkCount as h, addLocalTxn as i, consumeHydratedRtsCount as j, STATIC_RPCBASE_RTS_HYDRATION_DATA_KEY as k, onConnectionStatusChange as l, registerQuery as m, useQuery as n, disconnect as o, registerCount as p, useCountQuery as r, getConnectionError as s, useRtsConnection as t, onMessage as u, sendRtsMessage as v, getCollection as w, deleteDocs as x, syncRtsChanges as y };
|
|
3160
|
+
|
|
3161
|
+
//# sourceMappingURL=rts-C9lvbdZX.js.map
|