@loro-dev/streams-crdt 0.6.0 → 0.6.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/dist/flock.d.ts +1 -1
- package/dist/flock.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/loro.d.ts +1 -1
- package/dist/loro.js +1 -1
- package/dist/{stream-id-DBym_2Mj.js → stream-id-0dTFsiaO.js} +245 -130
- package/dist/stream-id-0dTFsiaO.js.map +1 -0
- package/dist/{stream-id-Do4h2lOD.d.ts → stream-id-Dr8ZBpUQ.d.ts} +16 -5
- package/package.json +1 -1
- package/dist/stream-id-DBym_2Mj.js.map +0 -1
package/dist/flock.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as RemoteCursorSaveSource, C as TransportSyncSuccess, D as IndexedDbRemoteCursorStore, E as InMemoryRemoteCursorStore, M as createInitialRemoteCursor, O as IndexedDbRemoteCursorStoreOptions, S as TransportSubscription, T as BeforeRemoteCursorSaveHook, _ as TransportCreateStreamSuccess, a as CrdtAdapter, b as TransportJoinParams, c as JsonObject, d as SnapshotCodec, f as SnapshotTransformHook, g as StreamsCrdtOptions, h as StreamsAuthProvider, i as StreamsCrdt, j as RemoteCursorStore, k as RemoteCursor, l as JsonValue, m as StreamsAuthContext, n as isValidBucketId, o as CrdtUpdateBatch, p as SnapshotUploadOptions, r as isValidRillId, s as IsolatedCrdtAdapter, t as createStreamUrl, u as Result, v as TransportDeleteStreamSuccess, w as BeforeRemoteCursorSaveContext, x as TransportRoomStatus, y as TransportError } from "./stream-id-
|
|
1
|
+
import { A as RemoteCursorSaveSource, C as TransportSyncSuccess, D as IndexedDbRemoteCursorStore, E as InMemoryRemoteCursorStore, M as createInitialRemoteCursor, O as IndexedDbRemoteCursorStoreOptions, S as TransportSubscription, T as BeforeRemoteCursorSaveHook, _ as TransportCreateStreamSuccess, a as CrdtAdapter, b as TransportJoinParams, c as JsonObject, d as SnapshotCodec, f as SnapshotTransformHook, g as StreamsCrdtOptions, h as StreamsAuthProvider, i as StreamsCrdt, j as RemoteCursorStore, k as RemoteCursor, l as JsonValue, m as StreamsAuthContext, n as isValidBucketId, o as CrdtUpdateBatch, p as SnapshotUploadOptions, r as isValidRillId, s as IsolatedCrdtAdapter, t as createStreamUrl, u as Result, v as TransportDeleteStreamSuccess, w as BeforeRemoteCursorSaveContext, x as TransportRoomStatus, y as TransportError } from "./stream-id-Dr8ZBpUQ.js";
|
|
2
2
|
import { ExportBundle, Flock, VersionVector, VersionVector as VersionVector$1 } from "@loro-dev/flock-wasm";
|
|
3
3
|
|
|
4
4
|
//#region src/flock.d.ts
|
package/dist/flock.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as InMemoryRemoteCursorStore, i as StreamsCrdt, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-
|
|
1
|
+
import { a as InMemoryRemoteCursorStore, i as StreamsCrdt, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-0dTFsiaO.js";
|
|
2
2
|
import { Flock } from "@loro-dev/flock-wasm";
|
|
3
3
|
//#region src/flock.ts
|
|
4
4
|
function isAfter(entry, current) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as RemoteCursorSaveSource, C as TransportSyncSuccess, D as IndexedDbRemoteCursorStore, E as InMemoryRemoteCursorStore, M as createInitialRemoteCursor, O as IndexedDbRemoteCursorStoreOptions, S as TransportSubscription, T as BeforeRemoteCursorSaveHook, _ as TransportCreateStreamSuccess, a as CrdtAdapter, b as TransportJoinParams, c as JsonObject, d as SnapshotCodec, f as SnapshotTransformHook, g as StreamsCrdtOptions, h as StreamsAuthProvider, i as StreamsCrdt, j as RemoteCursorStore, k as RemoteCursor, l as JsonValue, m as StreamsAuthContext, n as isValidBucketId, o as CrdtUpdateBatch, p as SnapshotUploadOptions, r as isValidRillId, s as IsolatedCrdtAdapter, t as createStreamUrl, u as Result, v as TransportDeleteStreamSuccess, w as BeforeRemoteCursorSaveContext, x as TransportRoomStatus, y as TransportError } from "./stream-id-
|
|
1
|
+
import { A as RemoteCursorSaveSource, C as TransportSyncSuccess, D as IndexedDbRemoteCursorStore, E as InMemoryRemoteCursorStore, M as createInitialRemoteCursor, O as IndexedDbRemoteCursorStoreOptions, S as TransportSubscription, T as BeforeRemoteCursorSaveHook, _ as TransportCreateStreamSuccess, a as CrdtAdapter, b as TransportJoinParams, c as JsonObject, d as SnapshotCodec, f as SnapshotTransformHook, g as StreamsCrdtOptions, h as StreamsAuthProvider, i as StreamsCrdt, j as RemoteCursorStore, k as RemoteCursor, l as JsonValue, m as StreamsAuthContext, n as isValidBucketId, o as CrdtUpdateBatch, p as SnapshotUploadOptions, r as isValidRillId, s as IsolatedCrdtAdapter, t as createStreamUrl, u as Result, v as TransportDeleteStreamSuccess, w as BeforeRemoteCursorSaveContext, x as TransportRoomStatus, y as TransportError } from "./stream-id-Dr8ZBpUQ.js";
|
|
2
2
|
export { BeforeRemoteCursorSaveContext, BeforeRemoteCursorSaveHook, CrdtAdapter, CrdtUpdateBatch, InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, IndexedDbRemoteCursorStoreOptions, IsolatedCrdtAdapter, JsonObject, JsonValue, RemoteCursor, RemoteCursorSaveSource, RemoteCursorStore, Result, SnapshotCodec, SnapshotTransformHook, SnapshotUploadOptions, StreamsAuthContext, StreamsAuthProvider, StreamsCrdt, StreamsCrdtOptions, TransportCreateStreamSuccess, TransportDeleteStreamSuccess, TransportError, TransportJoinParams, TransportRoomStatus, TransportSubscription, TransportSyncSuccess, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as InMemoryRemoteCursorStore, i as StreamsCrdt, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-
|
|
1
|
+
import { a as InMemoryRemoteCursorStore, i as StreamsCrdt, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-0dTFsiaO.js";
|
|
2
2
|
export { InMemoryRemoteCursorStore, IndexedDbRemoteCursorStore, StreamsCrdt, createInitialRemoteCursor, createStreamUrl, isValidBucketId, isValidRillId };
|
package/dist/loro.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as RemoteCursorSaveSource, C as TransportSyncSuccess, D as IndexedDbRemoteCursorStore, E as InMemoryRemoteCursorStore, M as createInitialRemoteCursor, O as IndexedDbRemoteCursorStoreOptions, S as TransportSubscription, T as BeforeRemoteCursorSaveHook, _ as TransportCreateStreamSuccess, a as CrdtAdapter, b as TransportJoinParams, c as JsonObject, d as SnapshotCodec, f as SnapshotTransformHook, g as StreamsCrdtOptions, h as StreamsAuthProvider, i as StreamsCrdt, j as RemoteCursorStore, k as RemoteCursor, l as JsonValue, m as StreamsAuthContext, n as isValidBucketId, o as CrdtUpdateBatch, p as SnapshotUploadOptions, r as isValidRillId, s as IsolatedCrdtAdapter, t as createStreamUrl, u as Result, v as TransportDeleteStreamSuccess, w as BeforeRemoteCursorSaveContext, x as TransportRoomStatus, y as TransportError } from "./stream-id-
|
|
1
|
+
import { A as RemoteCursorSaveSource, C as TransportSyncSuccess, D as IndexedDbRemoteCursorStore, E as InMemoryRemoteCursorStore, M as createInitialRemoteCursor, O as IndexedDbRemoteCursorStoreOptions, S as TransportSubscription, T as BeforeRemoteCursorSaveHook, _ as TransportCreateStreamSuccess, a as CrdtAdapter, b as TransportJoinParams, c as JsonObject, d as SnapshotCodec, f as SnapshotTransformHook, g as StreamsCrdtOptions, h as StreamsAuthProvider, i as StreamsCrdt, j as RemoteCursorStore, k as RemoteCursor, l as JsonValue, m as StreamsAuthContext, n as isValidBucketId, o as CrdtUpdateBatch, p as SnapshotUploadOptions, r as isValidRillId, s as IsolatedCrdtAdapter, t as createStreamUrl, u as Result, v as TransportDeleteStreamSuccess, w as BeforeRemoteCursorSaveContext, x as TransportRoomStatus, y as TransportError } from "./stream-id-Dr8ZBpUQ.js";
|
|
2
2
|
import { LoroDoc } from "loro-crdt";
|
|
3
3
|
|
|
4
4
|
//#region src/loro.d.ts
|
package/dist/loro.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as InMemoryRemoteCursorStore, i as StreamsCrdt, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-
|
|
1
|
+
import { a as InMemoryRemoteCursorStore, i as StreamsCrdt, n as isValidBucketId, o as IndexedDbRemoteCursorStore, r as isValidRillId, s as createInitialRemoteCursor, t as createStreamUrl } from "./stream-id-0dTFsiaO.js";
|
|
2
2
|
import { LoroDoc, VersionVector, decodeImportBlobMeta } from "loro-crdt";
|
|
3
3
|
//#region src/loro.ts
|
|
4
4
|
function batchImportUpdates(doc, updates) {
|
|
@@ -184,7 +184,8 @@ const DEFAULT_RECONNECT_CONFIG = {
|
|
|
184
184
|
jitterFraction: .2,
|
|
185
185
|
maxAttempts: Infinity,
|
|
186
186
|
connectTimeoutMs: 1e4,
|
|
187
|
-
pollTimeoutMs: 3e4
|
|
187
|
+
pollTimeoutMs: 3e4,
|
|
188
|
+
liveInactivityTimeoutMs: 45e3
|
|
188
189
|
};
|
|
189
190
|
/**
|
|
190
191
|
* Apply jitter of +/- fraction to a base delay.
|
|
@@ -677,13 +678,29 @@ var StreamsTransportClient = class {
|
|
|
677
678
|
async readItemsSse(initialOffset, cursor, onControl, onOpen, signal) {
|
|
678
679
|
const opened = await this.openItemsSseSession(initialOffset, cursor, onOpen, signal);
|
|
679
680
|
if (opened.kind !== "ok") return opened;
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
681
|
+
let abortHandler;
|
|
682
|
+
const abortRace = signal != null && !signal.aborted ? new Promise((_, reject) => {
|
|
683
|
+
abortHandler = () => {
|
|
684
|
+
opened.session.cancel();
|
|
685
|
+
reject(signal.reason ?? new DOMException("The operation was aborted.", "AbortError"));
|
|
686
|
+
};
|
|
687
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
688
|
+
}) : null;
|
|
689
|
+
try {
|
|
690
|
+
if (signal?.aborted) {
|
|
691
|
+
await opened.session.cancel();
|
|
692
|
+
throw signal.reason ?? new DOMException("The operation was aborted.", "AbortError");
|
|
693
|
+
}
|
|
694
|
+
const result = abortRace != null ? await Promise.race([opened.session.continue(onControl), abortRace]) : await opened.session.continue(onControl);
|
|
695
|
+
if (result.paused) await opened.session.cancel();
|
|
696
|
+
return {
|
|
697
|
+
kind: "ok",
|
|
698
|
+
latestOffset: result.latestOffset,
|
|
699
|
+
latestCursor: result.latestCursor
|
|
700
|
+
};
|
|
701
|
+
} finally {
|
|
702
|
+
if (abortHandler != null && signal != null) signal.removeEventListener("abort", abortHandler);
|
|
703
|
+
}
|
|
687
704
|
}
|
|
688
705
|
async openItemsSseSession(initialOffset, cursor, onOpen, signal) {
|
|
689
706
|
const result = await this.client.openSseSession({
|
|
@@ -1074,6 +1091,8 @@ function createJoinState(cursor, liveMode = "sse") {
|
|
|
1074
1091
|
refCount: 0,
|
|
1075
1092
|
closed: false,
|
|
1076
1093
|
status: "connecting",
|
|
1094
|
+
readSubStatus: "connecting",
|
|
1095
|
+
writeSubStatus: "ok",
|
|
1077
1096
|
statusListeners: /* @__PURE__ */ new Set(),
|
|
1078
1097
|
pendingLocal: new Deque(),
|
|
1079
1098
|
flushingLocal: false,
|
|
@@ -1178,22 +1197,31 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
1178
1197
|
* Resets the retry counter and immediately attempts to reconnect.
|
|
1179
1198
|
*
|
|
1180
1199
|
* Use this after device wake or network recovery to skip any pending
|
|
1181
|
-
* backoff delay. No-op when the room is already
|
|
1200
|
+
* backoff delay. No-op when the room is already fully healthy (both
|
|
1201
|
+
* read and write sub-statuses are "ok") or no join is active.
|
|
1182
1202
|
*
|
|
1183
|
-
* If the room reached `"disconnected"`
|
|
1184
|
-
*
|
|
1203
|
+
* If the room reached `"disconnected"` or `"error"` status, this
|
|
1204
|
+
* restarts the affected loop(s).
|
|
1185
1205
|
*/
|
|
1186
1206
|
rejoin() {
|
|
1187
1207
|
const state = this.joinState;
|
|
1188
|
-
if (state == null || state.closed
|
|
1189
|
-
|
|
1208
|
+
if (state == null || state.closed) return;
|
|
1209
|
+
if (state.readSubStatus === "ok" && state.writeSubStatus === "ok") return;
|
|
1190
1210
|
state.retryAttempt = 0;
|
|
1191
1211
|
state.retrySleepController.abort();
|
|
1192
1212
|
state.retrySleepController = new AbortController();
|
|
1193
|
-
|
|
1194
|
-
|
|
1213
|
+
const previousController = state.requestController;
|
|
1214
|
+
state.requestController = new AbortController();
|
|
1215
|
+
previousController?.abort();
|
|
1216
|
+
if (state.readSubStatus === "disconnected" || state.readSubStatus === "error") {
|
|
1217
|
+
this.setReadSubStatus(state, "reconnecting");
|
|
1195
1218
|
this.runReadLoop(state);
|
|
1196
1219
|
}
|
|
1220
|
+
if (state.writeSubStatus === "error") {
|
|
1221
|
+
state.writeSubStatus = state.pendingLocal.length > 0 ? "reconnecting" : "ok";
|
|
1222
|
+
this.recomputeJoinStatus(state);
|
|
1223
|
+
if (state.pendingLocal.length > 0) this.flushPendingLocal(state);
|
|
1224
|
+
}
|
|
1197
1225
|
}
|
|
1198
1226
|
/**
|
|
1199
1227
|
* Reads the latest remote snapshot offset, if the server exposes one.
|
|
@@ -1377,13 +1405,16 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
1377
1405
|
if (state.status === "error" || state.status === "disconnected") throw new Error("join local sync failed before subscription became ready");
|
|
1378
1406
|
}
|
|
1379
1407
|
if (state.closed) return;
|
|
1380
|
-
this.
|
|
1408
|
+
this.setReadSubStatus(state, "ok");
|
|
1409
|
+
this.setWriteSubStatus(state, "ok");
|
|
1381
1410
|
this.runReadLoop(state);
|
|
1382
1411
|
} catch (error) {
|
|
1383
1412
|
if (state.closed) return;
|
|
1384
1413
|
this.snapshotManager.dispose(state);
|
|
1385
1414
|
this.logError("join first sync failed", error, { streamUrl: this.streamUrl });
|
|
1386
|
-
|
|
1415
|
+
state.readSubStatus = "error";
|
|
1416
|
+
state.writeSubStatus = "error";
|
|
1417
|
+
this.recomputeJoinStatus(state);
|
|
1387
1418
|
throw error;
|
|
1388
1419
|
}
|
|
1389
1420
|
}
|
|
@@ -1435,7 +1466,7 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
1435
1466
|
return caughtUp;
|
|
1436
1467
|
} catch (error) {
|
|
1437
1468
|
if (!isStreamNotFoundError(error) || !this.createStreamIfMissing) throw error;
|
|
1438
|
-
await this.
|
|
1469
|
+
await this.ensureStreamExists();
|
|
1439
1470
|
return await this.bootstrapState(this.createInitialCursor());
|
|
1440
1471
|
}
|
|
1441
1472
|
}
|
|
@@ -1500,7 +1531,16 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
1500
1531
|
nextProducerSeq: this.nextProducerSeq
|
|
1501
1532
|
};
|
|
1502
1533
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
1503
|
-
|
|
1534
|
+
let result;
|
|
1535
|
+
try {
|
|
1536
|
+
result = await this.client.appendUpdates(batch.updates, producerSession);
|
|
1537
|
+
} catch (error) {
|
|
1538
|
+
if (isStreamNotFoundError(error) && this.createStreamIfMissing) {
|
|
1539
|
+
await this.ensureStreamExists();
|
|
1540
|
+
continue;
|
|
1541
|
+
}
|
|
1542
|
+
throw error;
|
|
1543
|
+
}
|
|
1504
1544
|
if (result.kind === "ok") {
|
|
1505
1545
|
this.producerEpoch = result.ackEpoch;
|
|
1506
1546
|
this.nextProducerSeq = result.ackSeq + 1;
|
|
@@ -1526,6 +1566,17 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
1526
1566
|
}
|
|
1527
1567
|
throw new Error("append retry budget exhausted");
|
|
1528
1568
|
}
|
|
1569
|
+
/**
|
|
1570
|
+
* Ensures the target stream exists, creating it if necessary.
|
|
1571
|
+
* Tolerates 409 Conflict (stream already created concurrently).
|
|
1572
|
+
*/
|
|
1573
|
+
async ensureStreamExists() {
|
|
1574
|
+
try {
|
|
1575
|
+
await this.client.createStreamRequest();
|
|
1576
|
+
} catch (error) {
|
|
1577
|
+
if (!(error instanceof HttpError && error.status === 409)) throw error;
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1529
1580
|
exportUpdates(fromVersion) {
|
|
1530
1581
|
const exported = this.adapter.exportUpdates(fromVersion);
|
|
1531
1582
|
if (exported == null) return { nextRefVersion: fromVersion };
|
|
@@ -1589,121 +1640,166 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
1589
1640
|
}
|
|
1590
1641
|
async runReadLoop(state) {
|
|
1591
1642
|
state.requestController ??= new AbortController();
|
|
1592
|
-
while (!state.closed)
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1643
|
+
while (!state.closed) {
|
|
1644
|
+
const capturedController = state.requestController;
|
|
1645
|
+
let inactivityTimer = null;
|
|
1646
|
+
let inactivityTriggered = false;
|
|
1647
|
+
const clearInactivity = () => {
|
|
1648
|
+
if (inactivityTimer != null) {
|
|
1649
|
+
clearTimeout(inactivityTimer);
|
|
1650
|
+
inactivityTimer = null;
|
|
1651
|
+
}
|
|
1652
|
+
};
|
|
1653
|
+
const resetInactivity = () => {
|
|
1654
|
+
clearInactivity();
|
|
1655
|
+
const timeoutMs = this.reconnectConfig.liveInactivityTimeoutMs;
|
|
1656
|
+
if (timeoutMs != null && timeoutMs > 0 && !state.closed) inactivityTimer = setTimeout(() => {
|
|
1657
|
+
inactivityTriggered = true;
|
|
1658
|
+
capturedController?.abort();
|
|
1659
|
+
}, timeoutMs);
|
|
1660
|
+
};
|
|
1661
|
+
try {
|
|
1662
|
+
if (state.liveMode === "long-poll") {
|
|
1663
|
+
const requestOffset = state.cursor.nextOffset;
|
|
1664
|
+
const response = await this.client.readFromOffset(requestOffset, state.streamCursor, "long-poll", capturedController?.signal);
|
|
1665
|
+
if (response.kind === "gone") {
|
|
1666
|
+
const bootstrapped = await this.bootstrapState({
|
|
1667
|
+
...state.cursor,
|
|
1668
|
+
nextOffset: "-1",
|
|
1669
|
+
serverLowerBoundVersion: this.adapter.emptyVersion()
|
|
1670
|
+
});
|
|
1671
|
+
state.cursor = bootstrapped.cursor;
|
|
1672
|
+
state.streamCursor = bootstrapped.streamCursor;
|
|
1673
|
+
state.retryAttempt = 0;
|
|
1674
|
+
continue;
|
|
1675
|
+
}
|
|
1676
|
+
const result = response.value;
|
|
1677
|
+
if (isReadNoProgress(requestOffset, result)) throw new Error(`catch-up made no progress at offset ${requestOffset}; server returned empty updates and unchanged next offset with up_to_date=false`);
|
|
1678
|
+
await this.enqueueExclusive(async () => {
|
|
1679
|
+
if (state.closed) return;
|
|
1680
|
+
state.cursor = await this.cursorManager.applyLiveResult(state.cursor, requestOffset, result, (previousVersion, nextVersion) => {
|
|
1681
|
+
this.snapshotManager.observeRemoteVersion(state, previousVersion, nextVersion);
|
|
1682
|
+
});
|
|
1683
|
+
if (result.streamCursor != null) state.streamCursor = result.streamCursor;
|
|
1601
1684
|
});
|
|
1602
|
-
state.
|
|
1603
|
-
state.streamCursor = bootstrapped.streamCursor;
|
|
1685
|
+
if (state.closed) return;
|
|
1604
1686
|
state.retryAttempt = 0;
|
|
1687
|
+
this.setReadSubStatus(state, "ok");
|
|
1688
|
+
await sleep(0);
|
|
1605
1689
|
continue;
|
|
1606
1690
|
}
|
|
1607
|
-
const
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1691
|
+
const sseRequestOffset = state.cursor.nextOffset;
|
|
1692
|
+
const pendingRemote = [];
|
|
1693
|
+
let flushChain = Promise.resolve();
|
|
1694
|
+
resetInactivity();
|
|
1695
|
+
const sseResult = await this.client.readFromOffsetSseItems(sseRequestOffset, state.streamCursor, (updates, nextOffset, _upToDate, streamCursor) => {
|
|
1696
|
+
resetInactivity();
|
|
1697
|
+
pendingRemote.push({
|
|
1698
|
+
updates,
|
|
1699
|
+
nextOffset,
|
|
1700
|
+
streamCursor
|
|
1613
1701
|
});
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
flushChain = flushChain.then(async () => {
|
|
1632
|
-
const events = pendingRemote.splice(0);
|
|
1633
|
-
if (events.length === 0 || state.closed) return;
|
|
1634
|
-
const allUpdates = events.flatMap((e) => e.updates);
|
|
1635
|
-
const lastEvent = events[events.length - 1];
|
|
1636
|
-
await this.enqueueExclusive(async () => {
|
|
1637
|
-
if (state.closed) return;
|
|
1638
|
-
state.cursor = await this.cursorManager.applyLiveResult(state.cursor, sseRequestOffset, {
|
|
1639
|
-
nextOffset: lastEvent.nextOffset,
|
|
1640
|
-
updates: allUpdates
|
|
1641
|
-
}, (previousVersion, nextVersion) => {
|
|
1642
|
-
this.snapshotManager.observeRemoteVersion(state, previousVersion, nextVersion);
|
|
1702
|
+
flushChain = flushChain.then(async () => {
|
|
1703
|
+
const events = pendingRemote.splice(0);
|
|
1704
|
+
if (events.length === 0 || state.closed) return;
|
|
1705
|
+
const allUpdates = events.flatMap((e) => e.updates);
|
|
1706
|
+
const lastEvent = events[events.length - 1];
|
|
1707
|
+
await this.enqueueExclusive(async () => {
|
|
1708
|
+
if (state.closed) return;
|
|
1709
|
+
state.cursor = await this.cursorManager.applyLiveResult(state.cursor, sseRequestOffset, {
|
|
1710
|
+
nextOffset: lastEvent.nextOffset,
|
|
1711
|
+
updates: allUpdates
|
|
1712
|
+
}, (previousVersion, nextVersion) => {
|
|
1713
|
+
this.snapshotManager.observeRemoteVersion(state, previousVersion, nextVersion);
|
|
1714
|
+
});
|
|
1715
|
+
for (let i = events.length - 1; i >= 0; i--) if (events[i].streamCursor != null) {
|
|
1716
|
+
state.streamCursor = events[i].streamCursor;
|
|
1717
|
+
break;
|
|
1718
|
+
}
|
|
1643
1719
|
});
|
|
1644
|
-
|
|
1645
|
-
state.streamCursor = events[i].streamCursor;
|
|
1646
|
-
break;
|
|
1647
|
-
}
|
|
1720
|
+
this.setReadSubStatus(state, "ok");
|
|
1648
1721
|
});
|
|
1649
|
-
|
|
1722
|
+
}, () => {
|
|
1723
|
+
resetInactivity();
|
|
1724
|
+
this.setReadSubStatus(state, "ok");
|
|
1725
|
+
}, capturedController?.signal);
|
|
1726
|
+
clearInactivity();
|
|
1727
|
+
await flushChain;
|
|
1728
|
+
if (state.closed) return;
|
|
1729
|
+
if (sseResult.kind === "ok") {
|
|
1730
|
+
state.retryAttempt = 0;
|
|
1731
|
+
state.streamCursor = sseResult.latestCursor;
|
|
1732
|
+
await sleep(0);
|
|
1733
|
+
continue;
|
|
1734
|
+
}
|
|
1735
|
+
if (sseResult.kind === "unsupported") {
|
|
1736
|
+
state.liveMode = "long-poll";
|
|
1737
|
+
await sleep(0);
|
|
1738
|
+
continue;
|
|
1739
|
+
}
|
|
1740
|
+
if (sseResult.kind === "not-found") {
|
|
1741
|
+
this.logError("live read failed", new HttpError("stream not found", 404), {
|
|
1742
|
+
streamUrl: this.streamUrl,
|
|
1743
|
+
code: "stream_not_found"
|
|
1744
|
+
});
|
|
1745
|
+
this.setReadSubStatus(state, "error");
|
|
1746
|
+
return;
|
|
1747
|
+
}
|
|
1748
|
+
const bootstrapped = await this.bootstrapState({
|
|
1749
|
+
...state.cursor,
|
|
1750
|
+
nextOffset: "-1",
|
|
1751
|
+
serverLowerBoundVersion: this.adapter.emptyVersion()
|
|
1650
1752
|
});
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
}, state.requestController?.signal);
|
|
1654
|
-
await flushChain;
|
|
1655
|
-
if (state.closed) return;
|
|
1656
|
-
if (sseResult.kind === "ok") {
|
|
1753
|
+
state.cursor = bootstrapped.cursor;
|
|
1754
|
+
state.streamCursor = bootstrapped.streamCursor;
|
|
1657
1755
|
state.retryAttempt = 0;
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
}
|
|
1691
|
-
this.
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
}
|
|
1701
|
-
|
|
1702
|
-
|
|
1756
|
+
} catch (error) {
|
|
1757
|
+
clearInactivity();
|
|
1758
|
+
if (state.closed) return;
|
|
1759
|
+
if (capturedController?.signal.aborted) {
|
|
1760
|
+
if (inactivityTriggered) {
|
|
1761
|
+
if (state.requestController === capturedController) state.requestController = new AbortController();
|
|
1762
|
+
this.logDebug("sse inactivity timeout", { streamUrl: this.streamUrl });
|
|
1763
|
+
this.setReadSubStatus(state, "reconnecting");
|
|
1764
|
+
const maxAttempts = this.reconnectConfig.maxAttempts;
|
|
1765
|
+
if (state.retryAttempt >= maxAttempts) {
|
|
1766
|
+
this.logError("live read reconnect exhausted (inactivity)", error, {
|
|
1767
|
+
streamUrl: this.streamUrl,
|
|
1768
|
+
attempts: state.retryAttempt
|
|
1769
|
+
});
|
|
1770
|
+
this.setReadSubStatus(state, "disconnected");
|
|
1771
|
+
return;
|
|
1772
|
+
}
|
|
1773
|
+
state.retryAttempt += 1;
|
|
1774
|
+
continue;
|
|
1775
|
+
}
|
|
1776
|
+
state.retryAttempt = 0;
|
|
1777
|
+
this.setReadSubStatus(state, "reconnecting");
|
|
1778
|
+
continue;
|
|
1779
|
+
}
|
|
1780
|
+
const classified = classifyFetchError(error);
|
|
1781
|
+
if (!classified.retriable || isAuthOrProtocolError(error)) {
|
|
1782
|
+
this.logError("live read failed", error, {
|
|
1783
|
+
streamUrl: this.streamUrl,
|
|
1784
|
+
code: classified.code
|
|
1785
|
+
});
|
|
1786
|
+
this.setReadSubStatus(state, "error");
|
|
1787
|
+
return;
|
|
1788
|
+
}
|
|
1789
|
+
this.setReadSubStatus(state, "reconnecting");
|
|
1790
|
+
const maxAttempts = this.reconnectConfig.maxAttempts;
|
|
1791
|
+
if (state.retryAttempt >= maxAttempts) {
|
|
1792
|
+
this.logError("live read reconnect exhausted", error, {
|
|
1793
|
+
streamUrl: this.streamUrl,
|
|
1794
|
+
attempts: state.retryAttempt
|
|
1795
|
+
});
|
|
1796
|
+
this.setReadSubStatus(state, "disconnected");
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
const delay = computeRetryDelay(state.retryAttempt, this.reconnectConfig);
|
|
1800
|
+
state.retryAttempt += 1;
|
|
1801
|
+
await sleep(delay, state.retrySleepController.signal);
|
|
1703
1802
|
}
|
|
1704
|
-
const delay = computeRetryDelay(state.retryAttempt, this.reconnectConfig);
|
|
1705
|
-
state.retryAttempt += 1;
|
|
1706
|
-
await sleep(delay, state.retrySleepController.signal);
|
|
1707
1803
|
}
|
|
1708
1804
|
}
|
|
1709
1805
|
/** Maximum encoded size (bytes) for a single batched append. */
|
|
@@ -1755,7 +1851,7 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
1755
1851
|
state.cursor = await this.enqueueExclusive(async () => this.appendLocalBatch(state.cursor, drained.batch));
|
|
1756
1852
|
for (let i = 0; i < drained.count; i += 1) state.pendingLocal.popFront();
|
|
1757
1853
|
this.notifySyncWaiters(state, drained.count);
|
|
1758
|
-
this.
|
|
1854
|
+
this.setWriteSubStatus(state, "ok");
|
|
1759
1855
|
this.snapshotManager.schedule(state);
|
|
1760
1856
|
break;
|
|
1761
1857
|
} catch (error) {
|
|
@@ -1766,16 +1862,16 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
1766
1862
|
code: classified.code
|
|
1767
1863
|
});
|
|
1768
1864
|
this.rejectSyncWaiters(state, /* @__PURE__ */ new Error(`local append failed: ${classified.code}`));
|
|
1769
|
-
this.
|
|
1865
|
+
this.setWriteSubStatus(state, "error");
|
|
1770
1866
|
return;
|
|
1771
1867
|
}
|
|
1772
1868
|
const maxAttempts = this.reconnectConfig.maxAttempts;
|
|
1773
1869
|
if (attempts >= maxAttempts) {
|
|
1774
1870
|
this.rejectSyncWaiters(state, /* @__PURE__ */ new Error("local append retry budget exhausted"));
|
|
1775
|
-
this.
|
|
1871
|
+
this.setWriteSubStatus(state, "error");
|
|
1776
1872
|
return;
|
|
1777
1873
|
}
|
|
1778
|
-
this.
|
|
1874
|
+
this.setWriteSubStatus(state, "reconnecting");
|
|
1779
1875
|
const delay = computeRetryDelay(attempts, this.reconnectConfig);
|
|
1780
1876
|
attempts += 1;
|
|
1781
1877
|
await sleep(delay, state.retrySleepController.signal);
|
|
@@ -1812,11 +1908,30 @@ var StreamsCrdt = class StreamsCrdt {
|
|
|
1812
1908
|
beforeRemoteCursorSave: this.beforeRemoteCursorSave
|
|
1813
1909
|
});
|
|
1814
1910
|
}
|
|
1815
|
-
|
|
1911
|
+
setReadSubStatus(state, sub) {
|
|
1912
|
+
if (state.readSubStatus === sub) return;
|
|
1913
|
+
state.readSubStatus = sub;
|
|
1914
|
+
this.recomputeJoinStatus(state);
|
|
1915
|
+
}
|
|
1916
|
+
setWriteSubStatus(state, sub) {
|
|
1917
|
+
if (state.writeSubStatus === sub) return;
|
|
1918
|
+
state.writeSubStatus = sub;
|
|
1919
|
+
this.recomputeJoinStatus(state);
|
|
1920
|
+
}
|
|
1921
|
+
recomputeJoinStatus(state) {
|
|
1922
|
+
const status = this.computeOverallStatus(state);
|
|
1816
1923
|
if (state.status === status) return;
|
|
1817
1924
|
state.status = status;
|
|
1818
1925
|
for (const listener of state.statusListeners) listener(status);
|
|
1819
1926
|
}
|
|
1927
|
+
computeOverallStatus(state) {
|
|
1928
|
+
const { readSubStatus, writeSubStatus } = state;
|
|
1929
|
+
if (readSubStatus === "error" || writeSubStatus === "error") return "error";
|
|
1930
|
+
if (readSubStatus === "disconnected") return "disconnected";
|
|
1931
|
+
if (readSubStatus === "connecting") return "connecting";
|
|
1932
|
+
if (readSubStatus === "reconnecting" || writeSubStatus === "reconnecting") return "reconnecting";
|
|
1933
|
+
return "joined";
|
|
1934
|
+
}
|
|
1820
1935
|
enqueueExclusive(task) {
|
|
1821
1936
|
const run = this.opQueue.then(task, task);
|
|
1822
1937
|
this.opQueue = run.then(() => void 0, () => void 0);
|
|
@@ -1875,4 +1990,4 @@ function createStreamUrl(input) {
|
|
|
1875
1990
|
//#endregion
|
|
1876
1991
|
export { InMemoryRemoteCursorStore as a, StreamsCrdt as i, isValidBucketId as n, IndexedDbRemoteCursorStore as o, isValidRillId as r, createInitialRemoteCursor as s, createStreamUrl as t };
|
|
1877
1992
|
|
|
1878
|
-
//# sourceMappingURL=stream-id-
|
|
1993
|
+
//# sourceMappingURL=stream-id-0dTFsiaO.js.map
|