@rebasepro/client 0.25.0 → 0.26.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/README.md +2 -1
- package/dist/admin.d.ts +8 -0
- package/dist/index.d.ts +36 -6
- package/dist/index.es.js +225 -50
- package/dist/index.es.js.map +1 -1
- package/dist/{offline-BrqCRgih.js → offline-AvuEcg7u.js} +122 -36
- package/dist/offline-AvuEcg7u.js.map +1 -0
- package/dist/offline-lazy.d.ts +1 -0
- package/dist/offline-store-BhYXViha.js.map +1 -1
- package/dist/offline-store.d.ts +10 -0
- package/dist/offline.d.ts +26 -1
- package/dist/realtime-channel.d.ts +8 -0
- package/dist/websocket.d.ts +34 -0
- package/package.json +4 -4
- package/dist/offline-BrqCRgih.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as byIdRead, c as invalidBulkBody, d as RebaseApiError$1, f as buildQueryString, g as rebaseReviver, l as SDKQueryBuilder, n as MemoryOfflineStore, r as createMutationId, s as etagOf, t as IndexedDBOfflineStore, u as RebaseClientError$1 } from "./offline-store-BhYXViha.js";
|
|
2
2
|
import { EntityReference, EntityRelation, GeoPoint, Vector, hasFieldOperation, isUnsupported, parseRelationAggregateSort, toCanonicalOp } from "@rebasepro/types";
|
|
3
|
-
import { collectAllPages, normalizeOrderBy, paginateFind, resolveFindWindow } from "@rebasepro/common";
|
|
3
|
+
import { collectAllPages, normalizeOrderBy, paginateFind, resolveFindWindow, withETag } from "@rebasepro/common";
|
|
4
4
|
import { splitSearchTerms, unref } from "@rebasepro/utils";
|
|
5
5
|
//#region src/offline-codec.ts
|
|
6
6
|
/**
|
|
@@ -903,6 +903,13 @@ function isPlainObject(value) {
|
|
|
903
903
|
const proto = Object.getPrototypeOf(value);
|
|
904
904
|
return proto === null || Object.getPrototypeOf(proto) === null;
|
|
905
905
|
}
|
|
906
|
+
/**
|
|
907
|
+
* The key a folded run is sent under: its first and last member and its
|
|
908
|
+
* length, which fixes its composition.
|
|
909
|
+
*/
|
|
910
|
+
function runKey(run) {
|
|
911
|
+
return `${run[0].mutationId}..${run[run.length - 1].mutationId}:${run.length}`;
|
|
912
|
+
}
|
|
906
913
|
/** Whether a queued write's payload mentions `id` anywhere — see {@link replaceMintedId}. */
|
|
907
914
|
function mentionsMintedId(op, id) {
|
|
908
915
|
return [op.data, ...(op.updates ?? []).map((u) => u.data)].some((payload) => replaceMintedId(payload, id, "").changed);
|
|
@@ -1326,19 +1333,45 @@ var OfflineManager = class {
|
|
|
1326
1333
|
*/
|
|
1327
1334
|
ticket() {
|
|
1328
1335
|
if (this.scopeHold) return void 0;
|
|
1329
|
-
return
|
|
1330
|
-
scope: this.scope,
|
|
1331
|
-
epoch: this.scopeEpoch
|
|
1332
|
-
};
|
|
1336
|
+
return this.currentTicket();
|
|
1333
1337
|
}
|
|
1334
1338
|
/** The ticket of whoever the held scope turns out to be. */
|
|
1335
1339
|
async nextTicket() {
|
|
1336
1340
|
await this.scopeKnown();
|
|
1337
|
-
return
|
|
1341
|
+
return this.currentTicket();
|
|
1342
|
+
}
|
|
1343
|
+
currentTicket() {
|
|
1344
|
+
return this.unverified ? {
|
|
1345
|
+
scope: this.scope,
|
|
1346
|
+
epoch: this.scopeEpoch,
|
|
1347
|
+
unverified: true
|
|
1348
|
+
} : {
|
|
1338
1349
|
scope: this.scope,
|
|
1339
1350
|
epoch: this.scopeEpoch
|
|
1340
1351
|
};
|
|
1341
1352
|
}
|
|
1353
|
+
/**
|
|
1354
|
+
* Whether a write may go to the server now. Not while the scope is the last
|
|
1355
|
+
* user's, unconfirmed: it would go out with no credentials, as nobody. It
|
|
1356
|
+
* queues for its author instead, as it does offline.
|
|
1357
|
+
*/
|
|
1358
|
+
sendsWrites() {
|
|
1359
|
+
return this.connectivity.shouldAttempt() && !this.unverified;
|
|
1360
|
+
}
|
|
1361
|
+
reachedUnconfirmedListeners = /* @__PURE__ */ new Set();
|
|
1362
|
+
/**
|
|
1363
|
+
* `listener` runs when a request made while the scope is unconfirmed
|
|
1364
|
+
* reached the server: the connection is back, so the session can be asked
|
|
1365
|
+
* about. The client wires it to the same confirmation the status listener
|
|
1366
|
+
* runs — the status may never have said the connection was gone.
|
|
1367
|
+
*/
|
|
1368
|
+
onReachedUnconfirmed(listener) {
|
|
1369
|
+
this.reachedUnconfirmedListeners.add(listener);
|
|
1370
|
+
return () => this.reachedUnconfirmedListeners.delete(listener);
|
|
1371
|
+
}
|
|
1372
|
+
reachedUnconfirmed() {
|
|
1373
|
+
for (const listener of this.reachedUnconfirmedListeners) callSafely(listener);
|
|
1374
|
+
}
|
|
1342
1375
|
/** Whether an operation's user is still the signed-in one. */
|
|
1343
1376
|
isCurrent(ticket) {
|
|
1344
1377
|
return ticket.epoch === this.scopeEpoch;
|
|
@@ -1395,15 +1428,18 @@ var OfflineManager = class {
|
|
|
1395
1428
|
const res = await inner.find(params);
|
|
1396
1429
|
this.connectivity.markSuccess();
|
|
1397
1430
|
if (!this.isCurrent(ticket)) return res;
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1431
|
+
if (ticket.unverified) this.reachedUnconfirmed();
|
|
1432
|
+
else {
|
|
1433
|
+
await this.ingest(slug, res.data ?? [], ticket, isProjection(params));
|
|
1434
|
+
if (!this.isCurrent(ticket)) return res;
|
|
1435
|
+
const snapshot = this.recordSnapshot(slug, params, res, ticket);
|
|
1436
|
+
const answer = this.answer(slug, params, snapshot);
|
|
1437
|
+
this.notifyCollection(slug, false);
|
|
1438
|
+
return {
|
|
1439
|
+
data: answer.data,
|
|
1440
|
+
meta: answer.meta
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1407
1443
|
} catch (error) {
|
|
1408
1444
|
if (!isNetworkError(error)) {
|
|
1409
1445
|
if (isRetryableError(error) && this.hasLocalAnswer(state, slug, params) && !(walk && !this.snapshotFor(slug, params))) {
|
|
@@ -1437,10 +1473,16 @@ var OfflineManager = class {
|
|
|
1437
1473
|
const row = await inner.findById(id, options);
|
|
1438
1474
|
this.connectivity.markSuccess();
|
|
1439
1475
|
if (!this.isCurrent(ticket)) return row;
|
|
1476
|
+
if (ticket.unverified) {
|
|
1477
|
+
this.reachedUnconfirmed();
|
|
1478
|
+
return this.localRow(slug, id);
|
|
1479
|
+
}
|
|
1440
1480
|
if (row !== void 0) await this.ingest(slug, [row], ticket);
|
|
1441
1481
|
else if (!this.hasPending(slug, id)) this.removeLocalRow(slug, id, ticket, true);
|
|
1442
1482
|
this.notifyCollection(slug, false);
|
|
1443
|
-
|
|
1483
|
+
const local = this.localRow(slug, id);
|
|
1484
|
+
const etag = etagOf(row);
|
|
1485
|
+
return etag && local !== void 0 && !this.hasPending(slug, id) ? withETag({ ...local }, etag) : local;
|
|
1444
1486
|
} catch (error) {
|
|
1445
1487
|
if (!isNetworkError(error)) throw error;
|
|
1446
1488
|
this.connectivity.markFailure();
|
|
@@ -1463,7 +1505,7 @@ var OfflineManager = class {
|
|
|
1463
1505
|
await this.ensureCollection(slug);
|
|
1464
1506
|
const idempotencyKey = options?.idempotencyKey ?? createMutationId();
|
|
1465
1507
|
let sent = false;
|
|
1466
|
-
if (this.
|
|
1508
|
+
if (this.sendsWrites()) try {
|
|
1467
1509
|
const row = await inner.create(data, id, {
|
|
1468
1510
|
...options,
|
|
1469
1511
|
idempotencyKey
|
|
@@ -1505,7 +1547,7 @@ var OfflineManager = class {
|
|
|
1505
1547
|
if (data.length === 0) return [];
|
|
1506
1548
|
const idempotencyKey = options?.idempotencyKey ?? createMutationId();
|
|
1507
1549
|
let sent = false;
|
|
1508
|
-
if (this.
|
|
1550
|
+
if (this.sendsWrites()) try {
|
|
1509
1551
|
const rows = await inner.createMany(data, {
|
|
1510
1552
|
...options,
|
|
1511
1553
|
idempotencyKey
|
|
@@ -1557,7 +1599,7 @@ var OfflineManager = class {
|
|
|
1557
1599
|
upsert: async (data, options) => {
|
|
1558
1600
|
const ticket = this.ticket() ?? await this.nextTicket();
|
|
1559
1601
|
await this.ensureCollection(slug);
|
|
1560
|
-
if (this.
|
|
1602
|
+
if (this.sendsWrites()) try {
|
|
1561
1603
|
const row = await inner.upsert(data, options);
|
|
1562
1604
|
this.connectivity.markSuccess();
|
|
1563
1605
|
await this.ingest(slug, [row], ticket);
|
|
@@ -1588,7 +1630,7 @@ var OfflineManager = class {
|
|
|
1588
1630
|
if (!Array.isArray(updates)) throw invalidBulkBody("updateMany expects an array of { id, data } entries.");
|
|
1589
1631
|
if (updates.length === 0) return [];
|
|
1590
1632
|
const anyPending = updates.some((u) => this.hasPending(slug, u.id));
|
|
1591
|
-
if (this.
|
|
1633
|
+
if (this.sendsWrites() && !anyPending) try {
|
|
1592
1634
|
const rows = await inner.updateMany(updates, options);
|
|
1593
1635
|
this.connectivity.markSuccess();
|
|
1594
1636
|
await this.ingest(slug, rows, ticket);
|
|
@@ -1627,7 +1669,7 @@ var OfflineManager = class {
|
|
|
1627
1669
|
if (!Array.isArray(ids)) throw invalidBulkBody("deleteMany expects an array of ids.");
|
|
1628
1670
|
if (ids.length === 0) return;
|
|
1629
1671
|
const anyPending = ids.some((id) => this.hasPending(slug, id));
|
|
1630
|
-
if (this.
|
|
1672
|
+
if (this.sendsWrites() && !anyPending) try {
|
|
1631
1673
|
await inner.deleteMany(ids, options);
|
|
1632
1674
|
this.connectivity.markSuccess();
|
|
1633
1675
|
for (const id of ids) this.removeLocalRow(slug, id, ticket, true);
|
|
@@ -1655,7 +1697,7 @@ var OfflineManager = class {
|
|
|
1655
1697
|
const idempotencyKey = options?.idempotencyKey ?? createMutationId();
|
|
1656
1698
|
let sent = false;
|
|
1657
1699
|
let sendFailure;
|
|
1658
|
-
if (this.
|
|
1700
|
+
if (this.sendsWrites() && !this.hasPending(slug, id)) try {
|
|
1659
1701
|
const row = await inner.update(id, data, {
|
|
1660
1702
|
...options,
|
|
1661
1703
|
idempotencyKey
|
|
@@ -1680,7 +1722,7 @@ var OfflineManager = class {
|
|
|
1680
1722
|
throw new RebaseClientError$1(`Cannot apply a field operation to "${slug}" while offline: it is evaluated against the stored value, which this device does not have a current copy of. Send the value itself, or retry when the connection is back.`, { code: "OFFLINE_FIELD_OP_UNSUPPORTED" });
|
|
1681
1723
|
}
|
|
1682
1724
|
const base = this.rawLocalRow(slug, id);
|
|
1683
|
-
const ifMatch =
|
|
1725
|
+
const ifMatch = options?.ifMatch ?? (this.onConflict && !this.hasPending(slug, id) ? this.etagFor(slug, id) : void 0);
|
|
1684
1726
|
await this.enqueue({
|
|
1685
1727
|
collection: slug,
|
|
1686
1728
|
type: "update",
|
|
@@ -1703,7 +1745,7 @@ var OfflineManager = class {
|
|
|
1703
1745
|
await this.ensureCollection(slug);
|
|
1704
1746
|
const idempotencyKey = options?.idempotencyKey ?? createMutationId();
|
|
1705
1747
|
let sent = false;
|
|
1706
|
-
if (this.
|
|
1748
|
+
if (this.sendsWrites() && !this.hasPending(slug, id)) try {
|
|
1707
1749
|
await inner.delete(id, {
|
|
1708
1750
|
...options,
|
|
1709
1751
|
idempotencyKey
|
|
@@ -1723,6 +1765,7 @@ var OfflineManager = class {
|
|
|
1723
1765
|
type: "delete",
|
|
1724
1766
|
id,
|
|
1725
1767
|
...sent ? { sent: { idempotencyKey } } : {},
|
|
1768
|
+
...options?.ifMatch ? { ifMatch: options.ifMatch } : {},
|
|
1726
1769
|
rollback: { rows: { [String(id)]: this.rawLocalRow(slug, id) ?? null } }
|
|
1727
1770
|
}, ticket);
|
|
1728
1771
|
this.removeLocalRow(slug, id, ticket);
|
|
@@ -1735,6 +1778,12 @@ var OfflineManager = class {
|
|
|
1735
1778
|
const n = await inner.count(params);
|
|
1736
1779
|
this.connectivity.markSuccess();
|
|
1737
1780
|
if (!this.isCurrent(ticket)) return n;
|
|
1781
|
+
if (ticket.unverified) {
|
|
1782
|
+
this.reachedUnconfirmed();
|
|
1783
|
+
const cached = await this.readCache(this.countKey(slug, params));
|
|
1784
|
+
if (cached !== void 0) return Math.max(0, cached + this.pendingDelta(slug, params));
|
|
1785
|
+
throw offlineError(`No count of "${slug}" for the last user is cached, and their session is not confirmed yet.`);
|
|
1786
|
+
}
|
|
1738
1787
|
this.writeCache(this.countKey(slug, params), n);
|
|
1739
1788
|
return Math.max(0, n + this.pendingDelta(slug, params));
|
|
1740
1789
|
} catch (error) {
|
|
@@ -1773,6 +1822,15 @@ var OfflineManager = class {
|
|
|
1773
1822
|
onUpdate(response);
|
|
1774
1823
|
return;
|
|
1775
1824
|
}
|
|
1825
|
+
if (ticket.unverified) {
|
|
1826
|
+
const answer = this.localFind(slug, params);
|
|
1827
|
+
onUpdate({
|
|
1828
|
+
...response,
|
|
1829
|
+
data: answer.data,
|
|
1830
|
+
meta: answer.meta
|
|
1831
|
+
});
|
|
1832
|
+
return;
|
|
1833
|
+
}
|
|
1776
1834
|
this.ingest(slug, response.data ?? [], ticket, isProjection(params)).then(() => {
|
|
1777
1835
|
if (!this.isCurrent(ticket)) return;
|
|
1778
1836
|
const snapshot = this.recordSnapshot(slug, params, response, ticket);
|
|
@@ -1791,6 +1849,10 @@ var OfflineManager = class {
|
|
|
1791
1849
|
onUpdate(row);
|
|
1792
1850
|
return;
|
|
1793
1851
|
}
|
|
1852
|
+
if (ticket.unverified) {
|
|
1853
|
+
onUpdate(this.localRow(slug, id));
|
|
1854
|
+
return;
|
|
1855
|
+
}
|
|
1794
1856
|
if (!row) {
|
|
1795
1857
|
if (!this.hasPending(slug, id)) this.removeLocalRow(slug, id, ticket, true);
|
|
1796
1858
|
this.notifyCollection(slug, false);
|
|
@@ -1822,7 +1884,7 @@ var OfflineManager = class {
|
|
|
1822
1884
|
wrapTrash(slug, inner) {
|
|
1823
1885
|
const online = async (what, call) => {
|
|
1824
1886
|
const message = `Offline: ${what} on the trash of "${slug}" needs the server; the trash is never cached on this device.`;
|
|
1825
|
-
if (!this.
|
|
1887
|
+
if (!this.sendsWrites()) throw offlineError(message);
|
|
1826
1888
|
try {
|
|
1827
1889
|
const result = await call();
|
|
1828
1890
|
this.connectivity.markSuccess();
|
|
@@ -1936,7 +1998,7 @@ var OfflineManager = class {
|
|
|
1936
1998
|
})();
|
|
1937
1999
|
if (options?.realtime !== false && !isUnsupported(inner.listen)) unlisten = inner.listen(params, (response) => {
|
|
1938
2000
|
const ticket = this.ticket();
|
|
1939
|
-
if (!ticket) return;
|
|
2001
|
+
if (!ticket || ticket.unverified) return;
|
|
1940
2002
|
this.ingest(slug, response.data ?? [], ticket, isProjection(params)).then(() => {
|
|
1941
2003
|
this.recordSnapshot(slug, params, response, ticket);
|
|
1942
2004
|
this.notifyCollection(slug, false);
|
|
@@ -1991,7 +2053,7 @@ var OfflineManager = class {
|
|
|
1991
2053
|
})();
|
|
1992
2054
|
if (options?.realtime !== false && !isUnsupported(inner.listenById)) unlisten = inner.listenById(id, (row) => {
|
|
1993
2055
|
const ticket = this.ticket();
|
|
1994
|
-
if (!ticket) return;
|
|
2056
|
+
if (!ticket || ticket.unverified) return;
|
|
1995
2057
|
if (!row) {
|
|
1996
2058
|
if (!this.hasPending(slug, id)) this.removeLocalRow(slug, id, ticket, true);
|
|
1997
2059
|
this.notifyCollection(slug, false);
|
|
@@ -2316,6 +2378,7 @@ var OfflineManager = class {
|
|
|
2316
2378
|
*/
|
|
2317
2379
|
removeLocalRow(slug, id, ticket, known = false) {
|
|
2318
2380
|
if (!this.isCurrent(ticket)) return;
|
|
2381
|
+
if (known && ticket.unverified) return;
|
|
2319
2382
|
const state = this.collectionState(slug);
|
|
2320
2383
|
const key = String(id);
|
|
2321
2384
|
const existed = state.rows.delete(key);
|
|
@@ -2348,7 +2411,7 @@ var OfflineManager = class {
|
|
|
2348
2411
|
* dropdown elsewhere asked for titles.
|
|
2349
2412
|
*/
|
|
2350
2413
|
async ingest(slug, rows, ticket, projection = false) {
|
|
2351
|
-
if (rows.length === 0 || !this.isCurrent(ticket)) return;
|
|
2414
|
+
if (rows.length === 0 || !this.isCurrent(ticket) || ticket.unverified) return;
|
|
2352
2415
|
const state = await this.ensureCollection(slug);
|
|
2353
2416
|
if (!this.isCurrent(ticket)) return;
|
|
2354
2417
|
const cachedAt = Date.now();
|
|
@@ -2452,7 +2515,7 @@ var OfflineManager = class {
|
|
|
2452
2515
|
hasMore: meta.hasMore ?? false,
|
|
2453
2516
|
...isProjection(params) ? { rows: (result.data ?? []).map((row) => ({ ...row })) } : {}
|
|
2454
2517
|
};
|
|
2455
|
-
if (!this.isCurrent(ticket)) return snapshot;
|
|
2518
|
+
if (!this.isCurrent(ticket) || ticket.unverified) return snapshot;
|
|
2456
2519
|
const state = this.collectionState(slug);
|
|
2457
2520
|
const key = buildQueryString(params);
|
|
2458
2521
|
state.snapshots.set(key, snapshot);
|
|
@@ -2550,7 +2613,7 @@ var OfflineManager = class {
|
|
|
2550
2613
|
}
|
|
2551
2614
|
if (mutation.type === "update") {
|
|
2552
2615
|
const tail = this.queue[this.queue.length - 1];
|
|
2553
|
-
if (tail && !this.inFlight.has(tail.mutationId) && tail.sent === void 0 && (tail.schemaVersion ?? 1) <= 1 && tail.collection === mutation.collection && (tail.type === "create" || tail.type === "update") && tail.id === mutation.id) {
|
|
2616
|
+
if (tail && !this.inFlight.has(tail.mutationId) && tail.sent === void 0 && (tail.schemaVersion ?? 1) <= 1 && tail.collection === mutation.collection && (tail.type === "create" || tail.type === "update") && tail.id === mutation.id && (mutation.ifMatch === void 0 || mutation.ifMatch === tail.ifMatch)) {
|
|
2554
2617
|
if (tail.type === "update") tail.parts = [...tail.parts ?? [{ ...tail.data }], { ...mutation.data }];
|
|
2555
2618
|
tail.data = {
|
|
2556
2619
|
...tail.data,
|
|
@@ -2731,7 +2794,7 @@ var OfflineManager = class {
|
|
|
2731
2794
|
this.connectivity.markFailure();
|
|
2732
2795
|
break;
|
|
2733
2796
|
}
|
|
2734
|
-
if (op.type === "update" && op.ifMatch && isPreconditionFailed(error)) {
|
|
2797
|
+
if ((op.type === "update" || op.type === "delete") && op.ifMatch && this.onConflict && isPreconditionFailed(error)) {
|
|
2735
2798
|
this.connectivity.markSuccess();
|
|
2736
2799
|
await this.resolveConflict(op, error, ticket);
|
|
2737
2800
|
continue;
|
|
@@ -2802,16 +2865,18 @@ var OfflineManager = class {
|
|
|
2802
2865
|
*/
|
|
2803
2866
|
foldableRun(unbatched) {
|
|
2804
2867
|
if (this.replayBatchSize < 2) return [];
|
|
2805
|
-
const foldable = (op, collection) => op !== void 0 && op.type === "create" && op.generatedId === true && typeof op.id === "string" && op.sent === void 0 && !op.upsert && (op.schemaVersion ?? 1) <= 1 && !unbatched.has(op.mutationId) && (collection === void 0 || op.collection === collection);
|
|
2806
2868
|
const head = this.queue[0];
|
|
2869
|
+
const resent = head?.sent?.run;
|
|
2870
|
+
const foldable = (op, collection) => op !== void 0 && op.type === "create" && op.generatedId === true && typeof op.id === "string" && (resent === void 0 ? op.sent === void 0 : op.sent?.run === resent) && !op.upsert && (op.schemaVersion ?? 1) <= 1 && !unbatched.has(op.mutationId) && (collection === void 0 || op.collection === collection);
|
|
2807
2871
|
if (!foldable(head)) return [];
|
|
2808
2872
|
const run = [head];
|
|
2809
|
-
for (let i = 1; i < this.queue.length && run.length < this.replayBatchSize; i++) {
|
|
2873
|
+
for (let i = 1; i < this.queue.length && (resent !== void 0 || run.length < this.replayBatchSize); i++) {
|
|
2810
2874
|
const next = this.queue[i];
|
|
2811
2875
|
if (!foldable(next, head.collection)) break;
|
|
2812
2876
|
if (run.some((member) => mentionsMintedId(next, member.id))) break;
|
|
2813
2877
|
run.push(next);
|
|
2814
2878
|
}
|
|
2879
|
+
if (resent !== void 0 && runKey(run) !== resent) return [];
|
|
2815
2880
|
return run;
|
|
2816
2881
|
}
|
|
2817
2882
|
/**
|
|
@@ -2832,9 +2897,10 @@ var OfflineManager = class {
|
|
|
2832
2897
|
*/
|
|
2833
2898
|
async replayRun(run, ticket) {
|
|
2834
2899
|
const inner = this.innerFor(run[0].collection);
|
|
2835
|
-
const idempotencyKey =
|
|
2900
|
+
const idempotencyKey = runKey(run);
|
|
2836
2901
|
for (const member of run) this.inFlight.add(member.mutationId);
|
|
2837
2902
|
try {
|
|
2903
|
+
for (const member of run) await this.markCreateSent(member, ticket, idempotencyKey);
|
|
2838
2904
|
let rows;
|
|
2839
2905
|
try {
|
|
2840
2906
|
rows = await inner.createMany(run.map((member) => member.data), { idempotencyKey });
|
|
@@ -2856,6 +2922,7 @@ var OfflineManager = class {
|
|
|
2856
2922
|
async replay(op, ticket) {
|
|
2857
2923
|
const inner = this.innerFor(op.collection);
|
|
2858
2924
|
if (op.type === "create") {
|
|
2925
|
+
await this.markCreateSent(op, ticket);
|
|
2859
2926
|
let row;
|
|
2860
2927
|
try {
|
|
2861
2928
|
row = await this.sendQueuedWrite(op, (idempotencyKey) => inner.create(op.data, void 0, { idempotencyKey }), (sent) => inner.create(sent.data, sent.id, { idempotencyKey: sent.idempotencyKey }));
|
|
@@ -2908,11 +2975,30 @@ var OfflineManager = class {
|
|
|
2908
2975
|
}
|
|
2909
2976
|
for (const id of ids) this.removeLocalRow(op.collection, id, ticket, true);
|
|
2910
2977
|
} else if (op.type === "delete") {
|
|
2911
|
-
await inner.delete(op.id, {
|
|
2978
|
+
await inner.delete(op.id, {
|
|
2979
|
+
idempotencyKey: op.sent?.idempotencyKey ?? op.mutationId,
|
|
2980
|
+
...op.ifMatch ? { ifMatch: op.ifMatch } : {}
|
|
2981
|
+
});
|
|
2912
2982
|
this.removeLocalRow(op.collection, op.id, ticket, true);
|
|
2913
2983
|
}
|
|
2914
2984
|
}
|
|
2915
2985
|
/**
|
|
2986
|
+
* Record a queued create as sent, with the request about to go out, and
|
|
2987
|
+
* persist it — before the request, so a lost answer cannot leave it
|
|
2988
|
+
* unmarked. One already marked (sent online before it was queued, or by
|
|
2989
|
+
* an earlier replay) keeps what it holds: that is the request the server
|
|
2990
|
+
* may have stored.
|
|
2991
|
+
*/
|
|
2992
|
+
async markCreateSent(op, ticket, run) {
|
|
2993
|
+
if (op.sent) return;
|
|
2994
|
+
op.sent = {
|
|
2995
|
+
idempotencyKey: op.mutationId,
|
|
2996
|
+
data: { ...op.data },
|
|
2997
|
+
...run ? { run } : {}
|
|
2998
|
+
};
|
|
2999
|
+
await this.persistMutation(`${ticket.scope}|${op.mutationId}`, op).catch(() => void 0);
|
|
3000
|
+
}
|
|
3001
|
+
/**
|
|
2916
3002
|
* Send a queued write under its key.
|
|
2917
3003
|
*
|
|
2918
3004
|
* A write the queue sent once already, before it was queued, carries that
|
|
@@ -3304,4 +3390,4 @@ var OfflineManager = class {
|
|
|
3304
3390
|
//#endregion
|
|
3305
3391
|
export { OfflineManager };
|
|
3306
3392
|
|
|
3307
|
-
//# sourceMappingURL=offline-
|
|
3393
|
+
//# sourceMappingURL=offline-AvuEcg7u.js.map
|