powersync-bridge 0.0.1-alpha.0 → 0.0.1-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -20
- package/dist/{chunk-TANEBEGU.js → chunk-KAR4QIIU.js} +64 -64
- package/dist/chunk-KAR4QIIU.js.map +1 -0
- package/dist/{clock-BAt_Za0Q.d.cts → clock-DANrBsZH.d.cts} +2 -2
- package/dist/{clock-BAt_Za0Q.d.ts → clock-DANrBsZH.d.ts} +2 -2
- package/dist/index.cjs +645 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +300 -171
- package/dist/index.d.ts +300 -171
- package/dist/index.js +583 -14
- package/dist/index.js.map +1 -1
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-TANEBEGU.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -24,6 +24,68 @@ async function withTimeout(clock, p, ms, signal) {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
// src/keyset.ts
|
|
28
|
+
function keyColumns(orderKey) {
|
|
29
|
+
if (typeof orderKey === "string") return [orderKey, "id"];
|
|
30
|
+
return [orderKey[0], orderKey[1]];
|
|
31
|
+
}
|
|
32
|
+
function idOf(row) {
|
|
33
|
+
const r = row;
|
|
34
|
+
if (r == null || r.id === void 0 || r.id === null) {
|
|
35
|
+
throw new Error("powersync-bridge: every row must have an `id`");
|
|
36
|
+
}
|
|
37
|
+
return String(r.id);
|
|
38
|
+
}
|
|
39
|
+
function compareValues(a, b) {
|
|
40
|
+
if (a === b) return 0;
|
|
41
|
+
if (a === null || a === void 0) return 1;
|
|
42
|
+
if (b === null || b === void 0) return -1;
|
|
43
|
+
if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();
|
|
44
|
+
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
45
|
+
if (typeof a === "boolean" && typeof b === "boolean") return Number(a) - Number(b);
|
|
46
|
+
const sa = String(a);
|
|
47
|
+
const sb = String(b);
|
|
48
|
+
return sa < sb ? -1 : sa > sb ? 1 : 0;
|
|
49
|
+
}
|
|
50
|
+
function rowComparator(orderKey, direction = "asc") {
|
|
51
|
+
const [k1, k2] = keyColumns(orderKey);
|
|
52
|
+
const sign = direction === "desc" ? -1 : 1;
|
|
53
|
+
return (a, b) => {
|
|
54
|
+
const c1 = compareValues(a[k1], b[k1]);
|
|
55
|
+
if (c1 !== 0) return c1 * sign;
|
|
56
|
+
return compareValues(a[k2], b[k2]) * sign;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function cursorOfRow(row, orderKey) {
|
|
60
|
+
const r = row;
|
|
61
|
+
if (typeof orderKey === "string") return r[orderKey];
|
|
62
|
+
return [r[orderKey[0]], r[orderKey[1]]];
|
|
63
|
+
}
|
|
64
|
+
function isAfterCursor(row, cursor, orderKey, direction = "asc") {
|
|
65
|
+
const [k1, k2] = keyColumns(orderKey);
|
|
66
|
+
const r = row;
|
|
67
|
+
const sign = direction === "desc" ? -1 : 1;
|
|
68
|
+
const [c1, c2] = typeof orderKey === "string" ? [cursor, void 0] : cursor;
|
|
69
|
+
const d1 = compareValues(r[k1], c1) * sign;
|
|
70
|
+
if (d1 !== 0) return d1 > 0;
|
|
71
|
+
if (c2 === void 0) return false;
|
|
72
|
+
return compareValues(r[k2], c2) * sign > 0;
|
|
73
|
+
}
|
|
74
|
+
function toBase64(s) {
|
|
75
|
+
if (typeof btoa === "function") return btoa(unescape(encodeURIComponent(s)));
|
|
76
|
+
return Buffer.from(s, "utf8").toString("base64");
|
|
77
|
+
}
|
|
78
|
+
function fromBase64(s) {
|
|
79
|
+
if (typeof atob === "function") return decodeURIComponent(escape(atob(s)));
|
|
80
|
+
return Buffer.from(s, "base64").toString("utf8");
|
|
81
|
+
}
|
|
82
|
+
function encodeCursor(cursor) {
|
|
83
|
+
return toBase64(JSON.stringify(cursor));
|
|
84
|
+
}
|
|
85
|
+
function decodeCursor(opaque) {
|
|
86
|
+
return JSON.parse(fromBase64(opaque));
|
|
87
|
+
}
|
|
88
|
+
|
|
27
89
|
// src/removal.ts
|
|
28
90
|
var PROBE_CHUNK = 500;
|
|
29
91
|
async function probePresence(driver, table, ids) {
|
|
@@ -75,13 +137,14 @@ var SnapshotStore = class {
|
|
|
75
137
|
constructor(initial) {
|
|
76
138
|
this.snapshot = initial;
|
|
77
139
|
}
|
|
78
|
-
|
|
140
|
+
// React invokes external-store callbacks as standalone functions.
|
|
141
|
+
getSnapshot = () => {
|
|
79
142
|
return this.snapshot;
|
|
80
|
-
}
|
|
81
|
-
subscribe(listener) {
|
|
143
|
+
};
|
|
144
|
+
subscribe = (listener) => {
|
|
82
145
|
this.listeners.add(listener);
|
|
83
146
|
return () => this.listeners.delete(listener);
|
|
84
|
-
}
|
|
147
|
+
};
|
|
85
148
|
replace(next) {
|
|
86
149
|
if (Object.is(next, this.snapshot)) return;
|
|
87
150
|
this.snapshot = next;
|
|
@@ -112,6 +175,496 @@ function shallowEqualResult(a, b) {
|
|
|
112
175
|
return true;
|
|
113
176
|
}
|
|
114
177
|
|
|
178
|
+
// src/query.ts
|
|
179
|
+
var infiniteQueryKey = (options) => JSON.stringify([
|
|
180
|
+
options.queryKey,
|
|
181
|
+
options.orderBy,
|
|
182
|
+
options.direction ?? "desc",
|
|
183
|
+
options.pageSize ?? 25,
|
|
184
|
+
options.maxRows ?? 500,
|
|
185
|
+
options.enabled !== false
|
|
186
|
+
]);
|
|
187
|
+
var OfflineError = class extends Error {
|
|
188
|
+
name = "OfflineError";
|
|
189
|
+
constructor() {
|
|
190
|
+
super("Additional records require a connection.");
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
var InfiniteQueryStore = class {
|
|
194
|
+
constructor(deps, options) {
|
|
195
|
+
this.deps = deps;
|
|
196
|
+
this.options = options;
|
|
197
|
+
this.pageSize = options.pageSize ?? 25;
|
|
198
|
+
this.maxRows = options.maxRows ?? 500;
|
|
199
|
+
if (!Number.isInteger(this.pageSize) || this.pageSize < 1 || !Number.isInteger(this.maxRows) || this.maxRows < this.pageSize)
|
|
200
|
+
throw new Error("powersync-bridge: pageSize must be positive and maxRows must be at least pageSize");
|
|
201
|
+
this.target = this.pageSize;
|
|
202
|
+
this.compare = rowComparator(options.orderBy, options.direction ?? "desc");
|
|
203
|
+
this.table = options.table;
|
|
204
|
+
this.offline = this.readOffline();
|
|
205
|
+
this.store = new SnapshotStore(this.result());
|
|
206
|
+
}
|
|
207
|
+
deps;
|
|
208
|
+
options;
|
|
209
|
+
store;
|
|
210
|
+
pageSize;
|
|
211
|
+
maxRows;
|
|
212
|
+
compare;
|
|
213
|
+
table;
|
|
214
|
+
target;
|
|
215
|
+
anchor;
|
|
216
|
+
revision = 0;
|
|
217
|
+
local = /* @__PURE__ */ new Map();
|
|
218
|
+
retained = /* @__PURE__ */ new Map();
|
|
219
|
+
tombstones = /* @__PURE__ */ new Set();
|
|
220
|
+
filtered = /* @__PURE__ */ new Set();
|
|
221
|
+
inSession = /* @__PURE__ */ new Set();
|
|
222
|
+
pages = [];
|
|
223
|
+
localPending = true;
|
|
224
|
+
localStamp = Number.NEGATIVE_INFINITY;
|
|
225
|
+
localError;
|
|
226
|
+
apiError;
|
|
227
|
+
apiPending = false;
|
|
228
|
+
apiFailed = false;
|
|
229
|
+
offline;
|
|
230
|
+
generation = 0;
|
|
231
|
+
watchGeneration = 0;
|
|
232
|
+
lastLocalWatch = 0;
|
|
233
|
+
refs = 0;
|
|
234
|
+
ready = false;
|
|
235
|
+
disposed = false;
|
|
236
|
+
unwatch;
|
|
237
|
+
stops = [];
|
|
238
|
+
request;
|
|
239
|
+
requestKey;
|
|
240
|
+
get isDisposed() {
|
|
241
|
+
return this.disposed || this.refs <= 0 && this.unwatch !== void 0;
|
|
242
|
+
}
|
|
243
|
+
retain() {
|
|
244
|
+
this.refs += 1;
|
|
245
|
+
if (this.refs !== 1 || this.unwatch || this.options.enabled === false) return;
|
|
246
|
+
this.stops = [
|
|
247
|
+
this.deps.crud.onChange(() => this.render()),
|
|
248
|
+
this.deps.freshness.onCompletion(() => void this.checkpoint()),
|
|
249
|
+
this.deps.driver.onStatusChanged(() => this.networkChanged()),
|
|
250
|
+
this.deps.connectivity.subscribe(() => this.networkChanged())
|
|
251
|
+
];
|
|
252
|
+
this.watch();
|
|
253
|
+
void this.deps.crud.ready().then(() => {
|
|
254
|
+
this.ready = true;
|
|
255
|
+
this.pump();
|
|
256
|
+
}).catch((error) => {
|
|
257
|
+
if (this.disposed) return;
|
|
258
|
+
this.localPending = false;
|
|
259
|
+
this.localError = error instanceof Error ? error : new Error(String(error));
|
|
260
|
+
this.render();
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
release() {
|
|
264
|
+
this.refs -= 1;
|
|
265
|
+
if (this.refs <= 0)
|
|
266
|
+
queueMicrotask(() => {
|
|
267
|
+
if (this.refs <= 0) this.dispose();
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
swapRoot() {
|
|
271
|
+
this.disposed = true;
|
|
272
|
+
this.local.clear();
|
|
273
|
+
this.retained.clear();
|
|
274
|
+
this.clearPages();
|
|
275
|
+
this.render(true);
|
|
276
|
+
}
|
|
277
|
+
dispose() {
|
|
278
|
+
this.disposed = true;
|
|
279
|
+
this.generation += 1;
|
|
280
|
+
this.watchGeneration += 1;
|
|
281
|
+
this.cancelRequest();
|
|
282
|
+
this.unwatch?.();
|
|
283
|
+
this.unwatch = void 0;
|
|
284
|
+
for (const stop of this.stops) stop();
|
|
285
|
+
this.stops = [];
|
|
286
|
+
this.clearPages();
|
|
287
|
+
this.local.clear();
|
|
288
|
+
this.retained.clear();
|
|
289
|
+
this.tombstones.clear();
|
|
290
|
+
this.filtered.clear();
|
|
291
|
+
this.inSession.clear();
|
|
292
|
+
this.render(true);
|
|
293
|
+
}
|
|
294
|
+
readOffline() {
|
|
295
|
+
return (this.deps.connectivity.getSnapshot() ?? this.deps.driver.onlineHint()) === false;
|
|
296
|
+
}
|
|
297
|
+
cancelRequest() {
|
|
298
|
+
this.request?.abort();
|
|
299
|
+
if (this.requestKey) this.deps.snapshots.invalidate(this.requestKey);
|
|
300
|
+
this.requestKey = void 0;
|
|
301
|
+
}
|
|
302
|
+
networkChanged() {
|
|
303
|
+
if (this.disposed) return;
|
|
304
|
+
const offline = this.readOffline();
|
|
305
|
+
const reconnect = this.offline && !offline;
|
|
306
|
+
this.offline = offline;
|
|
307
|
+
if (offline && this.request) {
|
|
308
|
+
this.cancelRequest();
|
|
309
|
+
this.generation += 1;
|
|
310
|
+
this.apiPending = false;
|
|
311
|
+
this.request = void 0;
|
|
312
|
+
}
|
|
313
|
+
if (reconnect) {
|
|
314
|
+
this.apiFailed = false;
|
|
315
|
+
this.apiError = void 0;
|
|
316
|
+
this.pump();
|
|
317
|
+
}
|
|
318
|
+
this.render();
|
|
319
|
+
}
|
|
320
|
+
identity(row) {
|
|
321
|
+
if (this.options.getRowIdentity) return this.options.getRowIdentity(row);
|
|
322
|
+
if (!this.table)
|
|
323
|
+
throw new Error(
|
|
324
|
+
"powersync-bridge: provide table or getRowIdentity when the adapter cannot infer the business table"
|
|
325
|
+
);
|
|
326
|
+
return { table: this.table, id: idOf(row) };
|
|
327
|
+
}
|
|
328
|
+
pending(row) {
|
|
329
|
+
const { table, id } = this.identity(row);
|
|
330
|
+
return Boolean(
|
|
331
|
+
this.deps.crud.pendingOp(table, id) || this.pages.some((page) => this.deps.crud.pendingOp(table, id, page.window))
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
localBuilder() {
|
|
335
|
+
return this.options.query({ cursor: this.anchor, limit: this.target + 1 });
|
|
336
|
+
}
|
|
337
|
+
watch() {
|
|
338
|
+
this.unwatch?.();
|
|
339
|
+
const token = ++this.watchGeneration;
|
|
340
|
+
const generation = this.generation;
|
|
341
|
+
this.localPending = true;
|
|
342
|
+
let prepared;
|
|
343
|
+
try {
|
|
344
|
+
prepared = this.deps.adapter.prepare(this.localBuilder());
|
|
345
|
+
if (!this.table && prepared.tables.length === 1) this.table = prepared.tables[0];
|
|
346
|
+
if (!this.table && !this.options.getRowIdentity)
|
|
347
|
+
throw new Error(
|
|
348
|
+
"powersync-bridge: provide table or getRowIdentity when the adapter cannot infer the business table"
|
|
349
|
+
);
|
|
350
|
+
} catch (error) {
|
|
351
|
+
this.localPending = false;
|
|
352
|
+
this.localError = error instanceof Error ? error : new Error(String(error));
|
|
353
|
+
this.render();
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
this.unwatch = this.deps.driver.diffs(prepared).subscribe(
|
|
357
|
+
(diff) => {
|
|
358
|
+
void this.acceptLocal(diff.all, token, generation).catch((error) => {
|
|
359
|
+
if (token !== this.watchGeneration || generation !== this.generation || this.disposed) return;
|
|
360
|
+
this.localPending = false;
|
|
361
|
+
this.localError = error instanceof Error ? error : new Error(String(error));
|
|
362
|
+
this.render();
|
|
363
|
+
});
|
|
364
|
+
},
|
|
365
|
+
(error) => {
|
|
366
|
+
if (token !== this.watchGeneration || generation !== this.generation || this.disposed) return;
|
|
367
|
+
this.localPending = false;
|
|
368
|
+
this.localError = error;
|
|
369
|
+
this.render();
|
|
370
|
+
}
|
|
371
|
+
);
|
|
372
|
+
this.render();
|
|
373
|
+
}
|
|
374
|
+
async acceptLocal(rows, token, generation) {
|
|
375
|
+
await this.deps.crud.resync();
|
|
376
|
+
if (token !== this.watchGeneration || generation !== this.generation || this.disposed) return;
|
|
377
|
+
const previous = this.local;
|
|
378
|
+
const firstEmission = this.lastLocalWatch !== token;
|
|
379
|
+
this.lastLocalWatch = token;
|
|
380
|
+
const next = new Map(rows.map((row) => [idOf(row), row]));
|
|
381
|
+
const removed = /* @__PURE__ */ new Map();
|
|
382
|
+
for (const [id, row] of previous)
|
|
383
|
+
if (!next.has(id)) {
|
|
384
|
+
removed.set(id, row);
|
|
385
|
+
this.tombstones.add(id);
|
|
386
|
+
}
|
|
387
|
+
for (const [id, row] of next) {
|
|
388
|
+
if (previous.size && (!firstEmission || previous.has(id)) && JSON.stringify(previous.get(id)) !== JSON.stringify(row) && !this.pending(row))
|
|
389
|
+
this.inSession.add(id);
|
|
390
|
+
this.tombstones.delete(id);
|
|
391
|
+
this.filtered.delete(id);
|
|
392
|
+
this.retained.delete(id);
|
|
393
|
+
}
|
|
394
|
+
this.local = next;
|
|
395
|
+
this.localPending = false;
|
|
396
|
+
this.localError = void 0;
|
|
397
|
+
this.localStamp = this.deps.freshness.freshAtFor(void 0);
|
|
398
|
+
this.render();
|
|
399
|
+
const groups = /* @__PURE__ */ new Map();
|
|
400
|
+
for (const row of removed.values()) {
|
|
401
|
+
const identity = this.identity(row);
|
|
402
|
+
const group = groups.get(identity.table) ?? /* @__PURE__ */ new Map();
|
|
403
|
+
group.set(identity.id, row);
|
|
404
|
+
groups.set(identity.table, group);
|
|
405
|
+
}
|
|
406
|
+
try {
|
|
407
|
+
for (const [table, group] of groups) {
|
|
408
|
+
const classes = await classifyRemovals(
|
|
409
|
+
{
|
|
410
|
+
driver: this.deps.driver,
|
|
411
|
+
clock: this.deps.clock,
|
|
412
|
+
table,
|
|
413
|
+
provenance: () => void 0,
|
|
414
|
+
isInWindow: this.options.isInWindow
|
|
415
|
+
},
|
|
416
|
+
group
|
|
417
|
+
);
|
|
418
|
+
if (token !== this.watchGeneration || generation !== this.generation || this.disposed) return;
|
|
419
|
+
for (const row of removed.values()) {
|
|
420
|
+
const identity = this.identity(row);
|
|
421
|
+
if (identity.table !== table || this.local.has(row.id)) continue;
|
|
422
|
+
const classification = classes.get(identity.id);
|
|
423
|
+
if (!classification) continue;
|
|
424
|
+
if (classification === "delete") this.tombstones.add(row.id);
|
|
425
|
+
else if (classification === "filtered") {
|
|
426
|
+
this.tombstones.delete(row.id);
|
|
427
|
+
this.filtered.add(row.id);
|
|
428
|
+
} else {
|
|
429
|
+
this.tombstones.delete(row.id);
|
|
430
|
+
this.retained.set(row.id, row);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
} catch (error) {
|
|
435
|
+
if (token !== this.watchGeneration || generation !== this.generation || this.disposed) return;
|
|
436
|
+
this.localError = error instanceof Error ? error : new Error(String(error));
|
|
437
|
+
}
|
|
438
|
+
const retained = [...this.retained.values()].sort(this.compare).slice(0, this.maxRows);
|
|
439
|
+
this.retained.clear();
|
|
440
|
+
for (const row of retained) this.retained.set(row.id, row);
|
|
441
|
+
const known = /* @__PURE__ */ new Set([
|
|
442
|
+
...this.local.keys(),
|
|
443
|
+
...this.retained.keys(),
|
|
444
|
+
...this.pages.flatMap((page) => page.rows.map((row) => row.id))
|
|
445
|
+
]);
|
|
446
|
+
for (const set of [this.inSession, this.tombstones, this.filtered]) {
|
|
447
|
+
if (set.size > this.maxRows * 4) {
|
|
448
|
+
for (const id of set) if (!known.has(id)) set.delete(id);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
this.render();
|
|
452
|
+
}
|
|
453
|
+
async checkpoint() {
|
|
454
|
+
if (this.disposed || !this.unwatch) return;
|
|
455
|
+
const token = this.watchGeneration;
|
|
456
|
+
const generation = this.generation;
|
|
457
|
+
try {
|
|
458
|
+
const rows = await this.deps.driver.query(this.deps.adapter.prepare(this.localBuilder()));
|
|
459
|
+
await this.acceptLocal(rows, token, generation);
|
|
460
|
+
} catch (error) {
|
|
461
|
+
if (token !== this.watchGeneration || generation !== this.generation || this.disposed) return;
|
|
462
|
+
this.localError = error instanceof Error ? error : new Error(String(error));
|
|
463
|
+
this.render();
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
clearPages() {
|
|
467
|
+
for (const page of this.pages) this.deps.crud.closeWindow(page.window);
|
|
468
|
+
this.pages = [];
|
|
469
|
+
}
|
|
470
|
+
apiEnd() {
|
|
471
|
+
return this.pages.at(-1)?.end === true;
|
|
472
|
+
}
|
|
473
|
+
apiCount() {
|
|
474
|
+
return this.pages.reduce((count, page) => count + page.rows.length, 0);
|
|
475
|
+
}
|
|
476
|
+
apiCursor() {
|
|
477
|
+
const last = this.pages.at(-1)?.rows.at(-1);
|
|
478
|
+
return last ? last : this.anchor;
|
|
479
|
+
}
|
|
480
|
+
cursorKey(row) {
|
|
481
|
+
if (!row) return void 0;
|
|
482
|
+
const [first, second] = keyColumns(this.options.orderBy);
|
|
483
|
+
return [row[first], row[second]];
|
|
484
|
+
}
|
|
485
|
+
pump() {
|
|
486
|
+
if (!this.ready || this.disposed || this.options.enabled === false || this.offline || this.apiPending || this.apiFailed || this.apiEnd())
|
|
487
|
+
return;
|
|
488
|
+
if (!this.table && !this.options.getRowIdentity) return;
|
|
489
|
+
if (this.pages.length >= Math.ceil(this.maxRows / this.pageSize) + 1) return;
|
|
490
|
+
if (this.apiCount() >= this.target && this.entries().length >= this.target) return;
|
|
491
|
+
const cursor = this.apiCursor();
|
|
492
|
+
const generation = this.generation;
|
|
493
|
+
const request = new AbortController();
|
|
494
|
+
this.request = request;
|
|
495
|
+
const window = this.deps.crud.openWindow();
|
|
496
|
+
this.apiPending = true;
|
|
497
|
+
this.render();
|
|
498
|
+
const key = [
|
|
499
|
+
"powersync-bridge.query",
|
|
500
|
+
this.deps.identityKey,
|
|
501
|
+
this.deps.epoch,
|
|
502
|
+
infiniteQueryKey(this.options),
|
|
503
|
+
this.cursorKey(this.anchor),
|
|
504
|
+
this.revision,
|
|
505
|
+
generation,
|
|
506
|
+
this.cursorKey(cursor)
|
|
507
|
+
];
|
|
508
|
+
this.requestKey = key;
|
|
509
|
+
void this.deps.snapshots.fetch(key, this.options.staleTime ?? 6e4, async (signal) => {
|
|
510
|
+
const abort = () => request.abort();
|
|
511
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
512
|
+
try {
|
|
513
|
+
return await withTimeout(
|
|
514
|
+
this.deps.clock,
|
|
515
|
+
this.options.fetchPage({ cursor, limit: this.pageSize + 1, signal: request.signal }).then((rows) => ({ rows })),
|
|
516
|
+
3e4,
|
|
517
|
+
request.signal
|
|
518
|
+
);
|
|
519
|
+
} finally {
|
|
520
|
+
signal.removeEventListener("abort", abort);
|
|
521
|
+
}
|
|
522
|
+
}).then((record) => {
|
|
523
|
+
if (this.disposed || generation !== this.generation || request.signal.aborted) {
|
|
524
|
+
this.deps.crud.closeWindow(window);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
const rows = [...record.rows];
|
|
528
|
+
for (let index = 1; index < rows.length; index++) {
|
|
529
|
+
const before = rows[index - 1];
|
|
530
|
+
const row = rows[index];
|
|
531
|
+
if (before && row && this.compare(before, row) >= 0)
|
|
532
|
+
throw new Error("powersync-bridge: API rows must use a strict, stable order with an id tiebreaker");
|
|
533
|
+
}
|
|
534
|
+
if (cursor !== void 0 && rows.some((row) => this.compare(row, cursor) <= 0))
|
|
535
|
+
throw new Error("powersync-bridge: API page must advance beyond the supplied cursor");
|
|
536
|
+
this.pages.push({
|
|
537
|
+
cursor,
|
|
538
|
+
rows: rows.slice(0, this.pageSize),
|
|
539
|
+
end: rows.length <= this.pageSize,
|
|
540
|
+
fetchedAt: record.fetchedAt,
|
|
541
|
+
window
|
|
542
|
+
});
|
|
543
|
+
this.apiError = void 0;
|
|
544
|
+
}).catch((error) => {
|
|
545
|
+
this.deps.crud.closeWindow(window);
|
|
546
|
+
if (this.disposed || generation !== this.generation || request.signal.aborted) return;
|
|
547
|
+
this.apiFailed = true;
|
|
548
|
+
this.apiError = error instanceof Error ? error : new Error(String(error));
|
|
549
|
+
request.abort();
|
|
550
|
+
}).finally(() => {
|
|
551
|
+
if (generation !== this.generation || this.disposed) return;
|
|
552
|
+
this.apiPending = false;
|
|
553
|
+
this.request = void 0;
|
|
554
|
+
this.requestKey = void 0;
|
|
555
|
+
this.render();
|
|
556
|
+
this.pump();
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
entries() {
|
|
560
|
+
const api = /* @__PURE__ */ new Map();
|
|
561
|
+
for (const page of this.pages) for (const row of page.rows) api.set(row.id, { row, page });
|
|
562
|
+
const ids = /* @__PURE__ */ new Set([...this.local.keys(), ...this.retained.keys(), ...api.keys()]);
|
|
563
|
+
const entries = [];
|
|
564
|
+
for (const id of ids) {
|
|
565
|
+
if (this.tombstones.has(id) || this.filtered.has(id)) continue;
|
|
566
|
+
const local = this.local.get(id);
|
|
567
|
+
const remote = api.get(id);
|
|
568
|
+
const row = local ?? remote?.row ?? this.retained.get(id);
|
|
569
|
+
if (!row) continue;
|
|
570
|
+
if (this.pending(row)) {
|
|
571
|
+
if (local) entries.push({ row: local, origin: "local", presentLocally: true });
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
if (remote) {
|
|
575
|
+
entries.push(
|
|
576
|
+
local && this.localStamp > remote.page.fetchedAt ? { row: local, origin: "local", presentLocally: true } : { row: remote.row, origin: "api", presentLocally: Boolean(local) }
|
|
577
|
+
);
|
|
578
|
+
} else if (local) {
|
|
579
|
+
const covering = this.pages.find((page) => {
|
|
580
|
+
if (page.cursor !== void 0 && this.compare(local, page.cursor) <= 0) return false;
|
|
581
|
+
const last = page.rows.at(-1);
|
|
582
|
+
return page.end || Boolean(last && this.compare(local, last) <= 0);
|
|
583
|
+
});
|
|
584
|
+
if (!covering || this.inSession.has(id) || this.localStamp > covering.fetchedAt)
|
|
585
|
+
entries.push({ row: local, origin: "local", presentLocally: true });
|
|
586
|
+
} else entries.push({ row, origin: "local", presentLocally: false });
|
|
587
|
+
}
|
|
588
|
+
return entries.sort((a, b) => this.compare(a.row, b.row));
|
|
589
|
+
}
|
|
590
|
+
result(empty = false) {
|
|
591
|
+
const enabled = !empty && this.options.enabled !== false;
|
|
592
|
+
const entries = enabled ? this.entries() : [];
|
|
593
|
+
const items = entries.slice(0, this.target);
|
|
594
|
+
const more = entries.length > this.target || !this.apiEnd();
|
|
595
|
+
const missing = items.length < this.target;
|
|
596
|
+
const errors = {};
|
|
597
|
+
if (this.localError) errors.local = this.localError;
|
|
598
|
+
if (this.apiError) errors.api = this.apiError;
|
|
599
|
+
const offlineError = this.offline && missing && !this.localPending ? new OfflineError() : void 0;
|
|
600
|
+
const error = enabled && missing && !this.localPending && !this.apiPending ? this.localError ?? this.apiError ?? offlineError : void 0;
|
|
601
|
+
return {
|
|
602
|
+
data: items.map((item) => item.row),
|
|
603
|
+
items,
|
|
604
|
+
isLoading: enabled && items.length === 0 && !error && (this.localPending || this.apiPending),
|
|
605
|
+
isFetching: enabled && (this.localPending || this.apiPending),
|
|
606
|
+
isFetchingNextPage: enabled && this.target > this.pageSize && (this.localPending || this.apiPending),
|
|
607
|
+
hasNextPage: enabled && this.target < this.maxRows && more,
|
|
608
|
+
needsOlderWindow: enabled && items.length >= this.maxRows && more,
|
|
609
|
+
error,
|
|
610
|
+
errors,
|
|
611
|
+
isError: Boolean(error),
|
|
612
|
+
windowRevision: this.revision
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
render(empty = false) {
|
|
616
|
+
if (this.disposed && !empty) return;
|
|
617
|
+
const result = this.result(empty);
|
|
618
|
+
if (!shallowEqualResult(
|
|
619
|
+
this.store.getSnapshot(),
|
|
620
|
+
result
|
|
621
|
+
))
|
|
622
|
+
this.store.replace(result);
|
|
623
|
+
}
|
|
624
|
+
fetchNextPage = () => {
|
|
625
|
+
if (this.disposed || this.options.enabled === false || this.target >= this.maxRows) return;
|
|
626
|
+
if (!this.store.getSnapshot().hasNextPage) return;
|
|
627
|
+
this.target = Math.min(this.target + this.pageSize, this.maxRows);
|
|
628
|
+
this.watch();
|
|
629
|
+
this.pump();
|
|
630
|
+
};
|
|
631
|
+
retry = () => {
|
|
632
|
+
if (this.disposed || this.options.enabled === false) return;
|
|
633
|
+
this.apiFailed = false;
|
|
634
|
+
this.apiError = void 0;
|
|
635
|
+
this.watch();
|
|
636
|
+
this.pump();
|
|
637
|
+
};
|
|
638
|
+
refresh = () => {
|
|
639
|
+
this.reset(this.anchor);
|
|
640
|
+
};
|
|
641
|
+
continueOlder = () => {
|
|
642
|
+
const last = this.store.getSnapshot().data.at(-1);
|
|
643
|
+
if (last) this.reset(last);
|
|
644
|
+
};
|
|
645
|
+
reset(anchor) {
|
|
646
|
+
if (this.disposed || this.options.enabled === false) return;
|
|
647
|
+
this.generation += 1;
|
|
648
|
+
this.cancelRequest();
|
|
649
|
+
this.request = void 0;
|
|
650
|
+
this.apiPending = false;
|
|
651
|
+
this.apiFailed = false;
|
|
652
|
+
this.apiError = void 0;
|
|
653
|
+
this.localError = void 0;
|
|
654
|
+
this.clearPages();
|
|
655
|
+
this.local.clear();
|
|
656
|
+
this.retained.clear();
|
|
657
|
+
this.inSession.clear();
|
|
658
|
+
this.tombstones.clear();
|
|
659
|
+
this.filtered.clear();
|
|
660
|
+
this.target = this.pageSize;
|
|
661
|
+
this.anchor = anchor;
|
|
662
|
+
this.revision += 1;
|
|
663
|
+
this.watch();
|
|
664
|
+
this.pump();
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
|
|
115
668
|
// src/flagger.ts
|
|
116
669
|
var AutoSyncFlagStore = class {
|
|
117
670
|
constructor(driver, clock, config, ids) {
|
|
@@ -391,68 +944,6 @@ var IdentityTracker = class {
|
|
|
391
944
|
}
|
|
392
945
|
};
|
|
393
946
|
|
|
394
|
-
// src/keyset.ts
|
|
395
|
-
function keyColumns(orderKey) {
|
|
396
|
-
if (typeof orderKey === "string") return [orderKey, "id"];
|
|
397
|
-
return [orderKey[0], orderKey[1]];
|
|
398
|
-
}
|
|
399
|
-
function idOf(row) {
|
|
400
|
-
const r = row;
|
|
401
|
-
if (r == null || r.id === void 0 || r.id === null) {
|
|
402
|
-
throw new Error("powersync-bridge: every row must have an `id`");
|
|
403
|
-
}
|
|
404
|
-
return String(r.id);
|
|
405
|
-
}
|
|
406
|
-
function compareValues(a, b) {
|
|
407
|
-
if (a === b) return 0;
|
|
408
|
-
if (a === null || a === void 0) return 1;
|
|
409
|
-
if (b === null || b === void 0) return -1;
|
|
410
|
-
if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();
|
|
411
|
-
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
412
|
-
if (typeof a === "boolean" && typeof b === "boolean") return Number(a) - Number(b);
|
|
413
|
-
const sa = String(a);
|
|
414
|
-
const sb = String(b);
|
|
415
|
-
return sa < sb ? -1 : sa > sb ? 1 : 0;
|
|
416
|
-
}
|
|
417
|
-
function rowComparator(orderKey, direction = "asc") {
|
|
418
|
-
const [k1, k2] = keyColumns(orderKey);
|
|
419
|
-
const sign = direction === "desc" ? -1 : 1;
|
|
420
|
-
return (a, b) => {
|
|
421
|
-
const c1 = compareValues(a[k1], b[k1]);
|
|
422
|
-
if (c1 !== 0) return c1 * sign;
|
|
423
|
-
return compareValues(a[k2], b[k2]) * sign;
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
function cursorOfRow(row, orderKey) {
|
|
427
|
-
const r = row;
|
|
428
|
-
if (typeof orderKey === "string") return r[orderKey];
|
|
429
|
-
return [r[orderKey[0]], r[orderKey[1]]];
|
|
430
|
-
}
|
|
431
|
-
function isAfterCursor(row, cursor, orderKey, direction = "asc") {
|
|
432
|
-
const [k1, k2] = keyColumns(orderKey);
|
|
433
|
-
const r = row;
|
|
434
|
-
const sign = direction === "desc" ? -1 : 1;
|
|
435
|
-
const [c1, c2] = typeof orderKey === "string" ? [cursor, void 0] : cursor;
|
|
436
|
-
const d1 = compareValues(r[k1], c1) * sign;
|
|
437
|
-
if (d1 !== 0) return d1 > 0;
|
|
438
|
-
if (c2 === void 0) return false;
|
|
439
|
-
return compareValues(r[k2], c2) * sign > 0;
|
|
440
|
-
}
|
|
441
|
-
function toBase64(s) {
|
|
442
|
-
if (typeof btoa === "function") return btoa(unescape(encodeURIComponent(s)));
|
|
443
|
-
return Buffer.from(s, "utf8").toString("base64");
|
|
444
|
-
}
|
|
445
|
-
function fromBase64(s) {
|
|
446
|
-
if (typeof atob === "function") return decodeURIComponent(escape(atob(s)));
|
|
447
|
-
return Buffer.from(s, "base64").toString("utf8");
|
|
448
|
-
}
|
|
449
|
-
function encodeCursor(cursor) {
|
|
450
|
-
return toBase64(JSON.stringify(cursor));
|
|
451
|
-
}
|
|
452
|
-
function decodeCursor(opaque) {
|
|
453
|
-
return JSON.parse(fromBase64(opaque));
|
|
454
|
-
}
|
|
455
|
-
|
|
456
947
|
// src/infinite.ts
|
|
457
948
|
var EMPTY = Object.freeze({
|
|
458
949
|
items: Object.freeze([]),
|
|
@@ -503,6 +994,7 @@ var InfiniteListStore = class {
|
|
|
503
994
|
reason;
|
|
504
995
|
awaitingSync = false;
|
|
505
996
|
loading = false;
|
|
997
|
+
localPagePending = false;
|
|
506
998
|
errors = {};
|
|
507
999
|
warnings = [];
|
|
508
1000
|
errorCount = 0;
|
|
@@ -511,14 +1003,16 @@ var InfiniteListStore = class {
|
|
|
511
1003
|
refs = 0;
|
|
512
1004
|
retain() {
|
|
513
1005
|
this.refs += 1;
|
|
514
|
-
if (this.refs === 1) this.subscribeLocal();
|
|
1006
|
+
if (this.refs === 1 && this.unsubDiffs === null) this.subscribeLocal();
|
|
515
1007
|
}
|
|
516
1008
|
release() {
|
|
517
1009
|
this.refs -= 1;
|
|
518
|
-
if (this.refs <= 0)
|
|
1010
|
+
if (this.refs <= 0) queueMicrotask(() => {
|
|
1011
|
+
if (this.refs <= 0) this.dispose();
|
|
1012
|
+
});
|
|
519
1013
|
}
|
|
520
1014
|
get isDisposed() {
|
|
521
|
-
return this.disposed;
|
|
1015
|
+
return this.disposed || this.refs <= 0 && this.unsubDiffs !== null;
|
|
522
1016
|
}
|
|
523
1017
|
swapRoot() {
|
|
524
1018
|
this.store.replace(emptyInfiniteResult());
|
|
@@ -532,6 +1026,11 @@ var InfiniteListStore = class {
|
|
|
532
1026
|
for (const h of this.subs.values()) h.unsubscribe();
|
|
533
1027
|
this.subs.clear();
|
|
534
1028
|
this.lru.length = 0;
|
|
1029
|
+
this.local.clear();
|
|
1030
|
+
this.apiRows.clear();
|
|
1031
|
+
this.retained.clear();
|
|
1032
|
+
this.tombstones.clear();
|
|
1033
|
+
this.store.replace(emptyInfiniteResult());
|
|
535
1034
|
}
|
|
536
1035
|
// ---------------------------------------------------------------------------
|
|
537
1036
|
subscribeLocal() {
|
|
@@ -543,6 +1042,10 @@ var InfiniteListStore = class {
|
|
|
543
1042
|
(diff) => void this.onDiff(diff),
|
|
544
1043
|
(err) => {
|
|
545
1044
|
this.localError = err;
|
|
1045
|
+
if (this.localPagePending) {
|
|
1046
|
+
this.localPagePending = false;
|
|
1047
|
+
this.loading = false;
|
|
1048
|
+
}
|
|
546
1049
|
this.render();
|
|
547
1050
|
}
|
|
548
1051
|
);
|
|
@@ -562,6 +1065,11 @@ var InfiniteListStore = class {
|
|
|
562
1065
|
this.apiRows.delete(id);
|
|
563
1066
|
}
|
|
564
1067
|
this.local = next;
|
|
1068
|
+
this.localError = void 0;
|
|
1069
|
+
if (this.localPagePending) {
|
|
1070
|
+
this.localPagePending = false;
|
|
1071
|
+
this.loading = false;
|
|
1072
|
+
}
|
|
565
1073
|
this.emissionSeen = true;
|
|
566
1074
|
const removed = /* @__PURE__ */ new Map();
|
|
567
1075
|
for (const [id, row] of prev) if (!next.has(id)) removed.set(id, row);
|
|
@@ -616,6 +1124,8 @@ var InfiniteListStore = class {
|
|
|
616
1124
|
async loadMore() {
|
|
617
1125
|
if (this.disposed || this.loading || this.boundary === "end") return;
|
|
618
1126
|
if (this.lookahead) {
|
|
1127
|
+
this.localPagePending = true;
|
|
1128
|
+
this.loading = true;
|
|
619
1129
|
this.pages += 1;
|
|
620
1130
|
this.boundary = "more";
|
|
621
1131
|
this.reason = void 0;
|
|
@@ -770,6 +1280,8 @@ var InfiniteListStore = class {
|
|
|
770
1280
|
}
|
|
771
1281
|
}
|
|
772
1282
|
absorbApi(rows) {
|
|
1283
|
+
const { api: _previousApiError, ...remainingErrors } = this.errors;
|
|
1284
|
+
this.errors = remainingErrors;
|
|
773
1285
|
this.errorCount = 0;
|
|
774
1286
|
this.nextRetryAt = 0;
|
|
775
1287
|
for (const row of rows) {
|
|
@@ -777,6 +1289,11 @@ var InfiniteListStore = class {
|
|
|
777
1289
|
if (this.local.has(id) || this.apiRows.has(id) || this.tombstones.has(id)) continue;
|
|
778
1290
|
this.apiRows.set(id, row);
|
|
779
1291
|
}
|
|
1292
|
+
const loadedPages = Math.max(this.pages, Math.ceil(this.mergedEntries().length / this.def.pageSize));
|
|
1293
|
+
if (loadedPages > this.pages) {
|
|
1294
|
+
this.pages = loadedPages;
|
|
1295
|
+
this.subscribeLocal();
|
|
1296
|
+
}
|
|
780
1297
|
if (rows.length === 0) {
|
|
781
1298
|
this.boundary = "end";
|
|
782
1299
|
this.reason = void 0;
|
|
@@ -944,7 +1461,7 @@ var CrudMirror = class {
|
|
|
944
1461
|
for (const r of refs) next.set(refKey(r.table, r.id), r);
|
|
945
1462
|
const added = [];
|
|
946
1463
|
const completed = [];
|
|
947
|
-
for (const [k, r] of next) if (
|
|
1464
|
+
for (const [k, r] of next) if (this.live.get(k)?.op !== r.op) added.push(r);
|
|
948
1465
|
for (const [k, r] of this.live) if (!next.has(k)) completed.push(r);
|
|
949
1466
|
if (added.length || completed.length) this.apply({ added, completed });
|
|
950
1467
|
}
|
|
@@ -1045,14 +1562,16 @@ var RacedListStore = class {
|
|
|
1045
1562
|
apiMs = null;
|
|
1046
1563
|
retain() {
|
|
1047
1564
|
this.refs += 1;
|
|
1048
|
-
if (this.refs === 1) this.start();
|
|
1565
|
+
if (this.refs === 1 && this.unsubDiffs === null) this.start();
|
|
1049
1566
|
}
|
|
1050
1567
|
release() {
|
|
1051
1568
|
this.refs -= 1;
|
|
1052
|
-
if (this.refs <= 0)
|
|
1569
|
+
if (this.refs <= 0) queueMicrotask(() => {
|
|
1570
|
+
if (this.refs <= 0) this.dispose();
|
|
1571
|
+
});
|
|
1053
1572
|
}
|
|
1054
1573
|
get isDisposed() {
|
|
1055
|
-
return this.disposed;
|
|
1574
|
+
return this.disposed || this.refs <= 0 && this.unsubDiffs !== null;
|
|
1056
1575
|
}
|
|
1057
1576
|
/** Synchronous part of an identity swap: the snapshot becomes empty 'pending' at once (R15). */
|
|
1058
1577
|
swapRoot() {
|
|
@@ -1067,6 +1586,14 @@ var RacedListStore = class {
|
|
|
1067
1586
|
this.unsubFresh?.();
|
|
1068
1587
|
if (this.snapWindow) this.deps.crud.closeWindow(this.snapWindow);
|
|
1069
1588
|
this.snapWindow = null;
|
|
1589
|
+
this.local?.clear();
|
|
1590
|
+
this.local = null;
|
|
1591
|
+
this.snap = null;
|
|
1592
|
+
this.prepared = null;
|
|
1593
|
+
this.overlay.clear();
|
|
1594
|
+
this.inSession.clear();
|
|
1595
|
+
this.tombstones.clear();
|
|
1596
|
+
this.store.replace(emptyRacedResult());
|
|
1070
1597
|
}
|
|
1071
1598
|
// ---------------------------------------------------------------------------
|
|
1072
1599
|
start() {
|
|
@@ -1360,6 +1887,17 @@ var SnapshotCache = class {
|
|
|
1360
1887
|
if (existing && (existing.result === null || now - existing.result.fetchedAt < staleTime)) {
|
|
1361
1888
|
return existing.promise;
|
|
1362
1889
|
}
|
|
1890
|
+
for (const [entryKey, entry2] of this.entries) {
|
|
1891
|
+
if (entry2.result && now - entry2.result.fetchedAt >= staleTime) this.entries.delete(entryKey);
|
|
1892
|
+
}
|
|
1893
|
+
if (this.entries.size >= 64) {
|
|
1894
|
+
for (const [entryKey, entry2] of this.entries) {
|
|
1895
|
+
if (entry2.result) {
|
|
1896
|
+
this.entries.delete(entryKey);
|
|
1897
|
+
break;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1363
1901
|
const controller = new AbortController();
|
|
1364
1902
|
const run = async (signal) => {
|
|
1365
1903
|
const res = await fetcher(signal);
|
|
@@ -1379,6 +1917,12 @@ var SnapshotCache = class {
|
|
|
1379
1917
|
return promise;
|
|
1380
1918
|
}
|
|
1381
1919
|
/** Cancel every in-flight fetch under a key prefix (identity swap). */
|
|
1920
|
+
invalidate(key) {
|
|
1921
|
+
const encoded = JSON.stringify(key);
|
|
1922
|
+
this.entries.get(encoded)?.controller.abort(new Error("invalidated"));
|
|
1923
|
+
this.entries.delete(encoded);
|
|
1924
|
+
}
|
|
1925
|
+
/** Cancel every in-flight fetch under a key prefix (identity swap). */
|
|
1382
1926
|
abortAll(prefix) {
|
|
1383
1927
|
const p = JSON.stringify(prefix).slice(0, -1);
|
|
1384
1928
|
for (const [k, e] of this.entries) {
|
|
@@ -1409,6 +1953,7 @@ var Bridge = class {
|
|
|
1409
1953
|
clock;
|
|
1410
1954
|
/** Increments on every identity swap; hooks subscribe to re-acquire their stores. */
|
|
1411
1955
|
epochStore;
|
|
1956
|
+
connectivity = new SnapshotStore(void 0);
|
|
1412
1957
|
identity = new IdentityTracker();
|
|
1413
1958
|
freshness;
|
|
1414
1959
|
crud;
|
|
@@ -1420,6 +1965,7 @@ var Bridge = class {
|
|
|
1420
1965
|
identityOpts;
|
|
1421
1966
|
onEvicted;
|
|
1422
1967
|
disposed = false;
|
|
1968
|
+
onNetwork = () => this.setOnline(globalThis.navigator?.onLine);
|
|
1423
1969
|
constructor(opts) {
|
|
1424
1970
|
this.driver = opts.driver;
|
|
1425
1971
|
this.adapter = opts.adapter;
|
|
@@ -1438,6 +1984,8 @@ var Bridge = class {
|
|
|
1438
1984
|
this.crud.start();
|
|
1439
1985
|
this.freshness.onClear(() => this.identity.signalClear());
|
|
1440
1986
|
this.identity.onChange(() => this.swapEpoch());
|
|
1987
|
+
globalThis.addEventListener?.("online", this.onNetwork);
|
|
1988
|
+
globalThis.addEventListener?.("offline", this.onNetwork);
|
|
1441
1989
|
}
|
|
1442
1990
|
get epoch() {
|
|
1443
1991
|
return this.identity.epoch;
|
|
@@ -1445,6 +1993,25 @@ var Bridge = class {
|
|
|
1445
1993
|
get identityKey() {
|
|
1446
1994
|
return this.identity.currentKey;
|
|
1447
1995
|
}
|
|
1996
|
+
setOnline(online) {
|
|
1997
|
+
this.connectivity.replace(online);
|
|
1998
|
+
}
|
|
1999
|
+
query(options) {
|
|
2000
|
+
const key = `query:${this.epoch}:${infiniteQueryKey(options)}`;
|
|
2001
|
+
return this.acquire(key, () => new InfiniteQueryStore({
|
|
2002
|
+
driver: this.driver,
|
|
2003
|
+
adapter: this.adapter,
|
|
2004
|
+
crud: this.crud,
|
|
2005
|
+
freshness: this.freshness,
|
|
2006
|
+
snapshots: this.snapshots,
|
|
2007
|
+
clock: this.clock,
|
|
2008
|
+
epoch: this.epoch,
|
|
2009
|
+
identityKey: this.identityKey,
|
|
2010
|
+
apiLegEnabled: true,
|
|
2011
|
+
measure: this.measure,
|
|
2012
|
+
connectivity: this.connectivity
|
|
2013
|
+
}, options));
|
|
2014
|
+
}
|
|
1448
2015
|
/** Resolves once the initial upload-queue read has landed (tests). */
|
|
1449
2016
|
ready() {
|
|
1450
2017
|
return this.crud.ready();
|
|
@@ -1555,6 +2122,8 @@ var Bridge = class {
|
|
|
1555
2122
|
dispose() {
|
|
1556
2123
|
if (this.disposed) return;
|
|
1557
2124
|
this.disposed = true;
|
|
2125
|
+
globalThis.removeEventListener?.("online", this.onNetwork);
|
|
2126
|
+
globalThis.removeEventListener?.("offline", this.onNetwork);
|
|
1558
2127
|
for (const s of this.stores.values()) s.dispose();
|
|
1559
2128
|
this.stores.clear();
|
|
1560
2129
|
this.snapshots.clear();
|
|
@@ -1578,6 +2147,8 @@ exports.AutoSyncFlagStore = AutoSyncFlagStore;
|
|
|
1578
2147
|
exports.Bridge = Bridge;
|
|
1579
2148
|
exports.DEFAULT_VOLATILE_CLAIMS = DEFAULT_VOLATILE_CLAIMS;
|
|
1580
2149
|
exports.InfiniteListStore = InfiniteListStore;
|
|
2150
|
+
exports.InfiniteQueryStore = InfiniteQueryStore;
|
|
2151
|
+
exports.OfflineError = OfflineError;
|
|
1581
2152
|
exports.OnDemandStreamStore = OnDemandStreamStore;
|
|
1582
2153
|
exports.RacedListStore = RacedListStore;
|
|
1583
2154
|
exports.SnapshotStore = SnapshotStore;
|
|
@@ -1593,6 +2164,7 @@ exports.emptyRacedResult = emptyRacedResult;
|
|
|
1593
2164
|
exports.encodeCursor = encodeCursor;
|
|
1594
2165
|
exports.identityKeyFromClaims = identityKeyFromClaims;
|
|
1595
2166
|
exports.identityKeyFromToken = identityKeyFromToken;
|
|
2167
|
+
exports.infiniteQueryKey = infiniteQueryKey;
|
|
1596
2168
|
exports.keyColumns = keyColumns;
|
|
1597
2169
|
exports.probePresence = probePresence;
|
|
1598
2170
|
exports.realClock = realClock;
|