@loro-dev/streams-crdt 0.1.2 → 0.2.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.
@@ -360,24 +360,28 @@ function parseHeaderU64(value) {
360
360
  return parsed;
361
361
  }
362
362
  function decodeBase64Bytes(encoded) {
363
- if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(encoded, "base64"));
363
+ const normalized = encoded.replace(/[\r\n]/g, "");
364
+ if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(normalized, "base64"));
364
365
  if (typeof atob === "function") {
365
- const binary = atob(encoded);
366
+ const binary = atob(normalized);
366
367
  const out = new Uint8Array(binary.length);
367
368
  for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);
368
369
  return out;
369
370
  }
370
371
  throw new Error("base64 decoding is unavailable");
371
372
  }
372
- function decodeSseRawPayload(data) {
373
- try {
374
- const parsed = JSON.parse(data);
375
- if (isObject(parsed) && parsed.encoding === "base64" && typeof parsed.payload === "string") return decodeBase64Bytes(parsed.payload);
376
- } catch {}
373
+ function normalizeSseDataEncoding(dataEncoding) {
374
+ const normalized = dataEncoding?.trim().toLowerCase();
375
+ if (normalized == null || normalized.length === 0) return;
376
+ if (normalized === "base64") return "base64";
377
+ throw new ProtocolError(`unsupported stream-sse-data-encoding '${dataEncoding}'`);
378
+ }
379
+ function decodeSseRawPayload(data, dataEncoding) {
380
+ if (dataEncoding === "base64") return decodeBase64Bytes(data);
377
381
  return new TextEncoder().encode(data);
378
382
  }
379
- function decodeSseItemPayloads(data) {
380
- const payload = decodeSseRawPayload(data);
383
+ function decodeSseItemPayloads(data, dataEncoding) {
384
+ const payload = decodeSseRawPayload(data, normalizeSseDataEncoding(dataEncoding));
381
385
  if (payload.byteLength === 0) return [payload];
382
386
  try {
383
387
  return decodeItems(payload);
@@ -571,8 +575,8 @@ function normalizeAuthProvider(auth) {
571
575
  const token = auth.trim();
572
576
  return async () => token.length === 0 ? void 0 : token;
573
577
  }
574
- return async () => {
575
- const token = (await auth())?.trim() ?? "";
578
+ return async (context) => {
579
+ const token = (await auth(context))?.trim() ?? "";
576
580
  return token.length === 0 ? void 0 : token;
577
581
  };
578
582
  }
@@ -650,6 +654,7 @@ var StreamsTransportClient = class {
650
654
  authProvider;
651
655
  fetchImpl;
652
656
  reconnectConfig;
657
+ authRefreshInFlight;
653
658
  constructor(options) {
654
659
  this.streamUrl = options.streamUrl;
655
660
  this.adapter = options.adapter;
@@ -659,21 +664,18 @@ var StreamsTransportClient = class {
659
664
  }
660
665
  async createStreamRequest() {
661
666
  const adapterHeaders = this.adapter.getCreateStreamHeaders ? await this.adapter.getCreateStreamHeaders() : void 0;
662
- const response = await this.fetchWithTimeout(this.streamUrl, {
667
+ const response = await this.fetchAuthorized(this.streamUrl, {
663
668
  method: "PUT",
664
- headers: await this.resolveHeaders({
669
+ headers: {
665
670
  "Content-Type": BINARY_CONTENT_TYPE,
666
671
  ...adapterHeaders
667
- })
672
+ }
668
673
  }, this.reconnectConfig.connectTimeoutMs, "connect");
669
674
  if (response.status === 200 || response.status === 201) return response.status === 201;
670
675
  throw new HttpError(`create stream failed with status ${response.status}`, response.status);
671
676
  }
672
677
  async deleteStreamRequest() {
673
- const response = await this.fetchWithTimeout(this.streamUrl, {
674
- method: "DELETE",
675
- headers: await this.resolveHeaders()
676
- }, this.reconnectConfig.connectTimeoutMs, "connect");
678
+ const response = await this.fetchAuthorized(this.streamUrl, { method: "DELETE" }, this.reconnectConfig.connectTimeoutMs, "connect");
677
679
  if (response.status === 200 || response.status === 204) return true;
678
680
  if (response.status === 404) return false;
679
681
  throw new HttpError(`delete stream failed with status ${response.status}`, response.status);
@@ -685,49 +687,14 @@ var StreamsTransportClient = class {
685
687
  });
686
688
  return this.readItemsSse(withCursorQuery(`${this.streamUrl}?${query.toString()}`, cursor), offset, onControl, onOpen, signal);
687
689
  }
688
- async openBootstrapSseSession(signal) {
689
- const query = new URLSearchParams({ live: "sse" });
690
- return await this.openItemsSseSession(`${this.streamUrl}/bootstrap?${query.toString()}`, "-1", void 0, signal);
691
- }
692
- async readBootstrapLongPoll(cursor, signal) {
693
- const query = new URLSearchParams({ live: "long-poll" });
694
- if (cursor != null && cursor.length > 0) query.set("cursor", cursor);
695
- const response = await this.fetchWithTimeout(`${this.streamUrl}/bootstrap?${query.toString()}`, {
696
- method: "GET",
697
- headers: await this.resolveHeaders(),
698
- signal
699
- }, this.reconnectConfig.pollTimeoutMs, "poll");
700
- if (response.status === 410) return { kind: "gone" };
701
- if (!response.ok) throw new HttpError(`bootstrap long-poll failed with status ${response.status}`, response.status);
702
- const nextOffset = getHeader(response.headers, "stream-next-offset", "Stream-Next-Offset");
703
- if (nextOffset == null) throw missingRequiredHeaderError("bootstrap", "Stream-Next-Offset", response.headers);
704
- const upToDate = getHeader(response.headers, "stream-up-to-date", "Stream-Up-To-Date") === "true";
705
- const streamCursor = getHeader(response.headers, "stream-cursor", "Stream-Cursor") ?? void 0;
706
- const body = new Uint8Array(await response.arrayBuffer());
707
- const boundary = extractBoundary(response.headers.get("Content-Type") ?? "");
708
- let items;
709
- if (boundary != null && body.byteLength > 0) items = decodeMultipartMixed(boundary, body).flatMap((part) => decodeMultipartPartItems(part.body));
710
- else if (body.byteLength === 0) items = [];
711
- else throw new ProtocolError("bootstrap long-poll returned a non-multipart body");
712
- return {
713
- kind: "ok",
714
- value: {
715
- nextOffset,
716
- upToDate,
717
- items,
718
- streamCursor
719
- }
720
- };
721
- }
722
690
  async readFromOffset(offset, cursor, live, signal) {
723
691
  const query = new URLSearchParams({ offset });
724
692
  if (live != null) query.set("live", live);
725
693
  if (cursor != null && cursor.length > 0) query.set("cursor", cursor);
726
694
  const timeoutMs = live === "long-poll" ? this.reconnectConfig.pollTimeoutMs : this.reconnectConfig.connectTimeoutMs;
727
695
  const timeoutType = live === "long-poll" ? "poll" : "connect";
728
- const response = await this.fetchWithTimeout(`${this.streamUrl}?${query.toString()}`, {
696
+ const response = await this.fetchAuthorized(`${this.streamUrl}?${query.toString()}`, {
729
697
  method: "GET",
730
- headers: await this.resolveHeaders(),
731
698
  signal
732
699
  }, timeoutMs, timeoutType);
733
700
  if (response.status === 410) return { kind: "gone" };
@@ -748,10 +715,7 @@ var StreamsTransportClient = class {
748
715
  };
749
716
  }
750
717
  async bootstrap() {
751
- const response = await this.fetchWithTimeout(`${this.streamUrl}/bootstrap`, {
752
- method: "GET",
753
- headers: await this.resolveHeaders()
754
- }, this.reconnectConfig.connectTimeoutMs, "connect");
718
+ const response = await this.fetchAuthorized(`${this.streamUrl}/bootstrap`, { method: "GET" }, this.reconnectConfig.connectTimeoutMs, "connect");
755
719
  if (!response.ok) throw new HttpError(`bootstrap failed with status ${response.status}`, response.status);
756
720
  const nextOffset = getHeader(response.headers, "stream-next-offset", "Stream-Next-Offset");
757
721
  if (nextOffset == null) throw missingRequiredHeaderError("bootstrap", "Stream-Next-Offset", response.headers);
@@ -779,35 +743,32 @@ var StreamsTransportClient = class {
779
743
  };
780
744
  }
781
745
  async readSnapshotOffsetValue() {
782
- const response = await this.fetchWithTimeout(this.streamUrl, {
783
- method: "HEAD",
784
- headers: await this.resolveHeaders()
785
- }, this.reconnectConfig.connectTimeoutMs, "connect");
746
+ const response = await this.fetchAuthorized(this.streamUrl, { method: "HEAD" }, this.reconnectConfig.connectTimeoutMs, "connect");
786
747
  if (response.status === 404) return null;
787
748
  if (!response.ok) throw new HttpError(`head snapshot offset failed with status ${response.status}`, response.status);
788
749
  const snapshotOffset = getHeader(response.headers, "stream-snapshot-offset", "Stream-Snapshot-Offset");
789
750
  return snapshotOffset == null || snapshotOffset.length === 0 ? null : snapshotOffset;
790
751
  }
791
752
  async putSnapshotAt(snapshotOffset, snapshot) {
792
- const response = await this.fetchWithTimeout(`${this.streamUrl}/snapshot/${encodeURIComponent(snapshotOffset)}`, {
753
+ const response = await this.fetchAuthorized(`${this.streamUrl}/snapshot/${encodeURIComponent(snapshotOffset)}`, {
793
754
  method: "PUT",
794
- headers: await this.resolveHeaders({ "Content-Type": "application/octet-stream" }),
755
+ headers: { "Content-Type": "application/octet-stream" },
795
756
  body: bytesToArrayBuffer(snapshot)
796
757
  }, this.reconnectConfig.connectTimeoutMs, "connect");
797
758
  if (response.status === 200 || response.status === 204) return;
798
759
  throw new HttpError(`snapshot upload failed with status ${response.status}`, response.status);
799
760
  }
800
761
  async appendUpdates(updates, producerSession) {
801
- const response = await this.fetchWithTimeout(this.streamUrl, {
762
+ const response = await this.fetchAuthorized(this.streamUrl, {
802
763
  method: "POST",
803
- headers: await this.resolveHeaders({
764
+ headers: {
804
765
  "Content-Type": BINARY_CONTENT_TYPE,
805
766
  "Producer-Id": producerSession.producerId,
806
767
  "Producer-Epoch": producerSession.producerEpoch.toString(),
807
768
  "Producer-Seq": producerSession.nextProducerSeq.toString()
808
- }),
769
+ },
809
770
  body: bytesToArrayBuffer(encodeItems(updates))
810
- }, this.reconnectConfig.connectTimeoutMs, "connect");
771
+ }, this.reconnectConfig.connectTimeoutMs, "connect", { shouldRetryAuthFailure: (response) => response.status === 401 || response.status === 403 && parseHeaderU64(getHeader(response.headers, "producer-epoch", "Producer-Epoch")) == null });
811
772
  if (response.status === 200 || response.status === 204) {
812
773
  const nextOffset = getHeader(response.headers, "stream-next-offset", "Stream-Next-Offset");
813
774
  if (nextOffset == null) throw missingRequiredHeaderError("append", "Stream-Next-Offset", response.headers);
@@ -834,10 +795,42 @@ var StreamsTransportClient = class {
834
795
  }
835
796
  throw new HttpError(`append failed with status ${response.status}`, response.status);
836
797
  }
837
- async resolveHeaders(extra) {
838
- const headers = new Headers(extra);
839
- const token = this.authProvider == null ? void 0 : await this.authProvider();
840
- if (token != null && token.length > 0) headers.set("Authorization", `Bearer ${token}`);
798
+ async resolveToken(context) {
799
+ if (this.authProvider == null) return;
800
+ return await this.authProvider(context);
801
+ }
802
+ async refreshTokenAfterAuthFailure(status, previousToken) {
803
+ if (this.authProvider == null) return;
804
+ if (this.authRefreshInFlight == null) this.authRefreshInFlight = this.resolveToken({
805
+ reason: "unauthorized",
806
+ status,
807
+ previousToken
808
+ }).finally(() => {
809
+ this.authRefreshInFlight = void 0;
810
+ });
811
+ return await this.authRefreshInFlight;
812
+ }
813
+ async fetchAuthorized(input, init, timeoutMs, timeoutType, options) {
814
+ const firstToken = await this.resolveToken({ reason: "request" });
815
+ const firstResponse = await this.fetchWithTimeout(input, {
816
+ ...init,
817
+ headers: this.attachAuthorizationHeader(init.headers, firstToken)
818
+ }, timeoutMs, timeoutType);
819
+ const shouldRetryAuthFailure = options?.shouldRetryAuthFailure ?? ((response) => response.status === 401 || response.status === 403);
820
+ if (this.authProvider == null || !shouldRetryAuthFailure(firstResponse)) return firstResponse;
821
+ const refreshedToken = await this.refreshTokenAfterAuthFailure(firstResponse.status, firstToken);
822
+ return await this.fetchWithTimeout(input, {
823
+ ...init,
824
+ headers: this.attachAuthorizationHeader(init.headers, refreshedToken)
825
+ }, timeoutMs, timeoutType);
826
+ }
827
+ attachAuthorizationHeader(base, token) {
828
+ const headers = new Headers(base);
829
+ if (token == null || token.length === 0) {
830
+ headers.delete("Authorization");
831
+ return headers;
832
+ }
833
+ headers.set("Authorization", `Bearer ${token}`);
841
834
  return headers;
842
835
  }
843
836
  async fetchWithTimeout(input, init, timeoutMs, timeoutType) {
@@ -871,9 +864,8 @@ var StreamsTransportClient = class {
871
864
  };
872
865
  }
873
866
  async openItemsSseSession(url, initialOffset, onOpen, signal) {
874
- const response = await this.fetchWithTimeout(url, {
867
+ const response = await this.fetchAuthorized(url, {
875
868
  method: "GET",
876
- headers: await this.resolveHeaders(),
877
869
  signal
878
870
  }, this.reconnectConfig.connectTimeoutMs, "connect");
879
871
  if (response.status === 410) return { kind: "gone" };
@@ -885,6 +877,7 @@ var StreamsTransportClient = class {
885
877
  }
886
878
  if (!response.ok) throw new HttpError(`sse read failed with status ${response.status}`, response.status);
887
879
  if (!(response.headers.get("Content-Type") ?? "").toLowerCase().includes("text/event-stream")) throw new ProtocolError("sse read response must use text/event-stream");
880
+ const sseDataEncoding = response.headers.get("stream-sse-data-encoding");
888
881
  onOpen?.();
889
882
  if (response.body == null) return {
890
883
  kind: "ok",
@@ -934,7 +927,7 @@ var StreamsTransportClient = class {
934
927
  if (eventType !== "control") return;
935
928
  const control = parseSseControlData(data);
936
929
  const updates = [];
937
- for (const eventData of pendingDataEvents) for (const payload of decodeSseItemPayloads(eventData)) updates.push(payload);
930
+ for (const eventData of pendingDataEvents) for (const payload of decodeSseItemPayloads(eventData, sseDataEncoding)) updates.push(payload);
938
931
  pendingDataEvents = [];
939
932
  const handlerResult = await nextOnControl(updates, control.nextOffset, control.upToDate, control.streamCursor);
940
933
  latestOffset = control.nextOffset;
@@ -1335,8 +1328,6 @@ function createJoinState(cursor, liveMode = "sse") {
1335
1328
  async function disposeJoinState(state, options) {
1336
1329
  state.closed = true;
1337
1330
  options.snapshotManager.dispose(state);
1338
- const liveSseSession = state.liveSseSession;
1339
- state.liveSseSession = void 0;
1340
1331
  const requestController = state.requestController;
1341
1332
  state.requestController = void 0;
1342
1333
  const unsubscribeLocal = state.unsubscribeLocal;
@@ -1344,7 +1335,6 @@ async function disposeJoinState(state, options) {
1344
1335
  requestController?.abort();
1345
1336
  unsubscribeLocal?.();
1346
1337
  options.onDisposed?.();
1347
- await liveSseSession?.cancel();
1348
1338
  }
1349
1339
  //#endregion
1350
1340
  //#region src/transport-runtime.ts
@@ -1568,11 +1558,10 @@ var StreamsCrdt = class StreamsCrdt {
1568
1558
  async startJoin(state) {
1569
1559
  try {
1570
1560
  state.requestController ??= new AbortController();
1571
- const joined = await this.performInitialJoinSync(state.liveMode, state.requestController.signal);
1561
+ const joined = await this.performInitialJoinSync(state.liveMode);
1572
1562
  state.cursor = joined.cursor;
1573
1563
  state.streamCursor = joined.streamCursor;
1574
1564
  state.liveMode = joined.liveMode;
1575
- state.liveSseSession = joined.liveSseSession;
1576
1565
  const pendingLocal = this.exportUpdates(joined.localExportRefVersion);
1577
1566
  const hasQueuedPreSubscribeBatch = pendingLocal.batch != null;
1578
1567
  if (pendingLocal.batch != null) state.pendingLocal.pushBack(pendingLocal.batch);
@@ -1622,7 +1611,7 @@ var StreamsCrdt = class StreamsCrdt {
1622
1611
  }
1623
1612
  return cursor;
1624
1613
  }
1625
- async performInitialJoinSync(preferredLiveMode, signal) {
1614
+ async performInitialJoinSync(preferredLiveMode) {
1626
1615
  const loaded = await this.remoteCursorStore.load(this.streamUrl);
1627
1616
  let cursor = loaded ?? createInitialRemoteCursor({
1628
1617
  streamUrl: this.streamUrl,
@@ -1632,15 +1621,9 @@ var StreamsCrdt = class StreamsCrdt {
1632
1621
  let localExportRefVersion = cursor.serverLowerBoundVersion;
1633
1622
  let streamCursor;
1634
1623
  let liveMode = preferredLiveMode;
1635
- let liveSseSession;
1636
1624
  try {
1637
- if (loaded == null) {
1638
- const joined = await this.bootstrapIntoJoinedState(cursor, liveMode, signal);
1639
- cursor = joined.cursor;
1640
- streamCursor = joined.streamCursor;
1641
- liveMode = joined.liveMode;
1642
- liveSseSession = joined.liveSseSession;
1643
- } else {
1625
+ if (loaded == null) cursor = await this.bootstrapIntoState(cursor);
1626
+ else {
1644
1627
  const caughtUp = await this.catchup(cursor);
1645
1628
  cursor = caughtUp.cursor;
1646
1629
  streamCursor = caughtUp.streamCursor;
@@ -1648,38 +1631,26 @@ var StreamsCrdt = class StreamsCrdt {
1648
1631
  } catch (error) {
1649
1632
  if (!isStreamNotFoundError(error) || !this.createStreamIfMissing) throw error;
1650
1633
  await this.createStreamRequest();
1651
- const joined = await this.bootstrapIntoJoinedState({
1634
+ cursor = await this.bootstrapIntoState({
1652
1635
  ...cursor,
1653
1636
  nextOffset: "-1",
1654
1637
  serverLowerBoundVersion: this.adapter.emptyVersion()
1655
- }, liveMode, signal);
1656
- cursor = joined.cursor;
1657
- streamCursor = joined.streamCursor;
1658
- liveMode = joined.liveMode;
1659
- liveSseSession = joined.liveSseSession;
1638
+ });
1660
1639
  }
1661
1640
  localExportRefVersion = cursor.serverLowerBoundVersion;
1662
1641
  const exportedLocal = this.exportUpdates(localExportRefVersion);
1663
1642
  localExportRefVersion = exportedLocal.nextRefVersion;
1664
1643
  if (exportedLocal.batch != null) {
1665
1644
  cursor = await this.appendLocalBatch(cursor, exportedLocal.batch);
1666
- if (liveSseSession != null) {
1667
- const continued = await this.continueJoinedSseSessionUntilCaughtUp(liveSseSession, cursor);
1668
- cursor = continued.cursor;
1669
- streamCursor = continued.streamCursor;
1670
- liveSseSession = continued.liveSseSession;
1671
- } else {
1672
- const caughtUp = await this.catchup(cursor);
1673
- cursor = caughtUp.cursor;
1674
- streamCursor = caughtUp.streamCursor;
1675
- }
1645
+ const caughtUp = await this.catchup(cursor);
1646
+ cursor = caughtUp.cursor;
1647
+ streamCursor = caughtUp.streamCursor;
1676
1648
  }
1677
1649
  localExportRefVersion = cursor.serverLowerBoundVersion;
1678
1650
  return {
1679
1651
  cursor,
1680
1652
  streamCursor,
1681
1653
  liveMode,
1682
- liveSseSession,
1683
1654
  localExportRefVersion
1684
1655
  };
1685
1656
  }
@@ -1689,111 +1660,6 @@ var StreamsCrdt = class StreamsCrdt {
1689
1660
  if (!result.upToDate) return (await this.catchup(saved)).cursor;
1690
1661
  return saved;
1691
1662
  }
1692
- async bootstrapIntoJoinedState(cursor, preferredLiveMode, signal) {
1693
- if (preferredLiveMode === "sse") {
1694
- const sseResult = await this.bootstrapIntoJoinedStateViaSse(cursor, signal);
1695
- if (sseResult.kind === "ok") return {
1696
- ...sseResult,
1697
- liveMode: "sse"
1698
- };
1699
- }
1700
- return {
1701
- ...await this.bootstrapIntoJoinedStateViaLongPoll(cursor),
1702
- liveMode: "long-poll"
1703
- };
1704
- }
1705
- async bootstrapIntoJoinedStateViaSse(cursor, signal) {
1706
- let current = cursor;
1707
- let currentStreamCursor;
1708
- let expectBootstrapFrame = true;
1709
- let caughtUp = false;
1710
- const opened = await this.openBootstrapSseSession(this.streamUrl, signal);
1711
- if (opened.kind === "unsupported") return { kind: "unsupported" };
1712
- if (opened.kind === "not-found") throw new HttpError("bootstrap live sse failed with status 404", 404);
1713
- if (opened.kind === "gone") return {
1714
- kind: "ok",
1715
- cursor: await this.bootstrapIntoState({
1716
- ...current,
1717
- nextOffset: "-1",
1718
- serverLowerBoundVersion: this.adapter.emptyVersion()
1719
- })
1720
- };
1721
- const result = await opened.session.continue(async (items, nextOffset, upToDate, streamCursor) => {
1722
- if (expectBootstrapFrame) {
1723
- current = await this.applyBootstrapItems(current, items, nextOffset);
1724
- expectBootstrapFrame = false;
1725
- } else current = await this.applyRemoteItems(current, items, nextOffset);
1726
- if (streamCursor != null) currentStreamCursor = streamCursor;
1727
- if (upToDate) {
1728
- caughtUp = true;
1729
- return "pause";
1730
- }
1731
- });
1732
- if (!caughtUp || !result.paused) {
1733
- const resumed = await this.catchup(current, void 0, currentStreamCursor);
1734
- await opened.session.cancel();
1735
- return {
1736
- kind: "ok",
1737
- cursor: resumed.cursor,
1738
- streamCursor: resumed.streamCursor
1739
- };
1740
- }
1741
- return {
1742
- kind: "ok",
1743
- cursor: current,
1744
- streamCursor: currentStreamCursor,
1745
- liveSseSession: opened.session
1746
- };
1747
- }
1748
- async continueJoinedSseSessionUntilCaughtUp(session, cursor) {
1749
- let current = cursor;
1750
- let currentStreamCursor;
1751
- let caughtUp = false;
1752
- const result = await session.continue(async (items, nextOffset, upToDate, streamCursor) => {
1753
- current = await this.applyRemoteItems(current, items, nextOffset);
1754
- if (streamCursor != null) currentStreamCursor = streamCursor;
1755
- if (upToDate) {
1756
- caughtUp = true;
1757
- return "pause";
1758
- }
1759
- });
1760
- if (!caughtUp || !result.paused) {
1761
- const resumed = await this.catchup(current, void 0, currentStreamCursor);
1762
- await session.cancel();
1763
- return {
1764
- cursor: resumed.cursor,
1765
- streamCursor: resumed.streamCursor
1766
- };
1767
- }
1768
- return {
1769
- cursor: current,
1770
- streamCursor: currentStreamCursor,
1771
- liveSseSession: session
1772
- };
1773
- }
1774
- async bootstrapIntoJoinedStateViaLongPoll(cursor) {
1775
- let current = cursor;
1776
- let currentStreamCursor;
1777
- let expectBootstrapFrame = true;
1778
- while (true) {
1779
- const response = await this.readBootstrapLongPoll(this.streamUrl, currentStreamCursor);
1780
- if (response.kind === "gone") return { cursor: await this.bootstrapIntoState({
1781
- ...current,
1782
- nextOffset: "-1",
1783
- serverLowerBoundVersion: this.adapter.emptyVersion()
1784
- }) };
1785
- const result = response.value;
1786
- if (expectBootstrapFrame) {
1787
- current = await this.applyBootstrapItems(current, result.items, result.nextOffset);
1788
- expectBootstrapFrame = false;
1789
- } else current = await this.applyRemoteItems(current, result.items, result.nextOffset);
1790
- currentStreamCursor = result.streamCursor;
1791
- if (result.upToDate) return {
1792
- cursor: current,
1793
- streamCursor: currentStreamCursor
1794
- };
1795
- }
1796
- }
1797
1663
  async catchup(cursor, live, streamCursor, onRemoteVersionApplied) {
1798
1664
  let current = cursor;
1799
1665
  let currentStreamCursor = streamCursor;
@@ -1895,29 +1761,6 @@ var StreamsCrdt = class StreamsCrdt {
1895
1761
  await sleep(0);
1896
1762
  continue;
1897
1763
  }
1898
- if (state.liveSseSession != null) {
1899
- const session = state.liveSseSession;
1900
- const sseRequestOffset = state.cursor.nextOffset;
1901
- const sseResult = await session.continue(async (updates, nextOffset, _upToDate, streamCursor) => {
1902
- await this.enqueueExclusive(async () => {
1903
- if (state.closed) return;
1904
- state.cursor = await this.cursorManager.applyLiveResult(state.cursor, sseRequestOffset, {
1905
- nextOffset,
1906
- updates
1907
- }, (previousVersion, nextVersion) => {
1908
- this.snapshotManager.observeRemoteVersion(state, previousVersion, nextVersion);
1909
- });
1910
- if (streamCursor != null) state.streamCursor = streamCursor;
1911
- });
1912
- this.setJoinStatus(state, "joined");
1913
- });
1914
- if (state.closed) return;
1915
- state.liveSseSession = void 0;
1916
- state.retryAttempt = 0;
1917
- state.streamCursor = sseResult.latestCursor;
1918
- await sleep(0);
1919
- continue;
1920
- }
1921
1764
  const sseRequestOffset = state.cursor.nextOffset;
1922
1765
  const sseResult = await this.readFromOffsetSseItems(this.streamUrl, sseRequestOffset, state.streamCursor, async (updates, nextOffset, _upToDate, streamCursor) => {
1923
1766
  await this.enqueueExclusive(async () => {
@@ -1963,9 +1806,6 @@ var StreamsCrdt = class StreamsCrdt {
1963
1806
  state.retryAttempt = 0;
1964
1807
  } catch (error) {
1965
1808
  if (state.closed) return;
1966
- const liveSseSession = state.liveSseSession;
1967
- state.liveSseSession = void 0;
1968
- await liveSseSession?.cancel();
1969
1809
  const classified = classifyFetchError(error);
1970
1810
  if (!classified.retriable || isAuthOrProtocolError(error)) {
1971
1811
  this.logError("live read failed", error, {
@@ -2088,12 +1928,6 @@ var StreamsCrdt = class StreamsCrdt {
2088
1928
  async readFromOffsetSseItems(_streamUrl, offset, cursor, onControl, onOpen, signal) {
2089
1929
  return await this.client.readFromOffsetSseItems(offset, cursor, onControl, onOpen, signal);
2090
1930
  }
2091
- async openBootstrapSseSession(_streamUrl, signal) {
2092
- return await this.client.openBootstrapSseSession(signal);
2093
- }
2094
- async readBootstrapLongPoll(_streamUrl, cursor, signal) {
2095
- return await this.client.readBootstrapLongPoll(cursor, signal);
2096
- }
2097
1931
  async readFromOffset(_streamUrl, offset, cursor, live, signal) {
2098
1932
  return await this.client.readFromOffset(offset, cursor, live, signal);
2099
1933
  }
@@ -2167,4 +2001,4 @@ function createStreamUrl(input) {
2167
2001
  //#endregion
2168
2002
  export { InMemoryRemoteCursorStore as a, StreamsCrdt as i, isValidBucketId as n, IndexedDbRemoteCursorStore as o, isValidRillId as r, createInitialRemoteCursor as s, createStreamUrl as t };
2169
2003
 
2170
- //# sourceMappingURL=stream-id-YOFZ4I6Y.js.map
2004
+ //# sourceMappingURL=stream-id-kM9JM1F7.js.map