@korajs/sync 0.1.3 → 0.1.5

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/index.cjs CHANGED
@@ -642,8 +642,10 @@ var WebSocketTransport = class {
642
642
  errorHandler = null;
643
643
  serializer;
644
644
  WebSocketImpl;
645
+ connectTimeout;
645
646
  constructor(options) {
646
647
  this.serializer = options?.serializer ?? new JsonMessageSerializer();
648
+ this.connectTimeout = options?.connectTimeout ?? 1e4;
647
649
  if (options?.WebSocketImpl) {
648
650
  this.WebSocketImpl = options.WebSocketImpl;
649
651
  } else if (typeof globalThis.WebSocket !== "undefined") {
@@ -659,12 +661,38 @@ var WebSocketTransport = class {
659
661
  });
660
662
  }
661
663
  return new Promise((resolve, reject) => {
664
+ let settled = false;
665
+ const settle = (fn) => {
666
+ if (settled) return;
667
+ settled = true;
668
+ clearTimeout(timer);
669
+ fn();
670
+ };
671
+ const timer = setTimeout(() => {
672
+ settle(() => {
673
+ const err = new import_core2.SyncError("WebSocket connection timed out", {
674
+ url,
675
+ timeout: this.connectTimeout
676
+ });
677
+ if (this.ws) {
678
+ try {
679
+ this.ws.onclose = null;
680
+ this.ws.onerror = null;
681
+ this.ws.close();
682
+ } catch {
683
+ }
684
+ this.ws = null;
685
+ }
686
+ this.errorHandler?.(err);
687
+ reject(err);
688
+ });
689
+ }, this.connectTimeout);
662
690
  try {
663
691
  const connectUrl = options?.authToken ? `${url}${url.includes("?") ? "&" : "?"}token=${encodeURIComponent(options.authToken)}` : url;
664
692
  const ws = new this.WebSocketImpl(connectUrl);
665
693
  this.ws = ws;
666
694
  ws.onopen = () => {
667
- resolve();
695
+ settle(() => resolve());
668
696
  };
669
697
  ws.onmessage = (event) => {
670
698
  try {
@@ -688,15 +716,17 @@ var WebSocketTransport = class {
688
716
  this.errorHandler?.(err);
689
717
  if (!this.isConnected()) {
690
718
  this.ws = null;
691
- reject(err);
719
+ settle(() => reject(err));
692
720
  }
693
721
  };
694
722
  } catch (err) {
695
- reject(
696
- err instanceof import_core2.SyncError ? err : new import_core2.SyncError("Failed to create WebSocket", {
697
- url,
698
- error: String(err)
699
- })
723
+ settle(
724
+ () => reject(
725
+ err instanceof import_core2.SyncError ? err : new import_core2.SyncError("Failed to create WebSocket", {
726
+ url,
727
+ error: String(err)
728
+ })
729
+ )
700
730
  );
701
731
  }
702
732
  });
@@ -1213,6 +1243,7 @@ var SyncEngine = class {
1213
1243
  remoteVector = /* @__PURE__ */ new Map();
1214
1244
  lastSyncedAt = null;
1215
1245
  currentBatch = null;
1246
+ reconnecting = false;
1216
1247
  // Track delta exchange state
1217
1248
  deltaBatchesReceived = 0;
1218
1249
  deltaReceiveComplete = false;
@@ -1257,8 +1288,7 @@ var SyncEngine = class {
1257
1288
  };
1258
1289
  this.transport.send(handshake);
1259
1290
  } catch (err) {
1260
- this.transitionTo("error");
1261
- this.transitionTo("disconnected");
1291
+ this.ensureDisconnected();
1262
1292
  throw err;
1263
1293
  }
1264
1294
  }
@@ -1292,6 +1322,15 @@ var SyncEngine = class {
1292
1322
  this.flushQueue();
1293
1323
  }
1294
1324
  }
1325
+ /**
1326
+ * Mark the engine as being in a reconnection loop. When reconnecting,
1327
+ * `getStatus()` returns 'offline' instead of 'syncing' for intermediate
1328
+ * states (connecting, handshaking, syncing), since the user is effectively
1329
+ * disconnected until reconnection succeeds.
1330
+ */
1331
+ setReconnecting(value) {
1332
+ this.reconnecting = value;
1333
+ }
1295
1334
  /**
1296
1335
  * Get the current developer-facing sync status.
1297
1336
  */
@@ -1303,7 +1342,11 @@ var SyncEngine = class {
1303
1342
  case "connecting":
1304
1343
  case "handshaking":
1305
1344
  case "syncing":
1306
- return { status: "syncing", pendingOperations, lastSyncedAt: this.lastSyncedAt };
1345
+ return {
1346
+ status: this.reconnecting ? "offline" : "syncing",
1347
+ pendingOperations,
1348
+ lastSyncedAt: this.lastSyncedAt
1349
+ };
1307
1350
  case "streaming":
1308
1351
  return {
1309
1352
  status: pendingOperations > 0 ? "syncing" : "synced",
@@ -1620,6 +1663,7 @@ var ReconnectionManager = class {
1620
1663
  attempt = 0;
1621
1664
  timer = null;
1622
1665
  stopped = false;
1666
+ running = false;
1623
1667
  waitResolve = null;
1624
1668
  constructor(config) {
1625
1669
  this.initialDelay = config?.initialDelay ?? 1e3;
@@ -1637,25 +1681,30 @@ var ReconnectionManager = class {
1637
1681
  */
1638
1682
  async start(onReconnect) {
1639
1683
  this.stopped = false;
1684
+ this.running = true;
1640
1685
  this.attempt = 0;
1641
- while (!this.stopped) {
1642
- if (this.maxAttempts > 0 && this.attempt >= this.maxAttempts) {
1643
- return false;
1644
- }
1645
- const delay = this.getNextDelay();
1646
- this.attempt++;
1647
- await this.wait(delay);
1648
- if (this.stopped) return false;
1649
- try {
1650
- const success = await onReconnect();
1651
- if (success) {
1652
- this.reset();
1653
- return true;
1686
+ try {
1687
+ while (!this.stopped) {
1688
+ if (this.maxAttempts > 0 && this.attempt >= this.maxAttempts) {
1689
+ return false;
1690
+ }
1691
+ const delay = this.getNextDelay();
1692
+ this.attempt++;
1693
+ await this.wait(delay);
1694
+ if (this.stopped) return false;
1695
+ try {
1696
+ const success = await onReconnect();
1697
+ if (success) {
1698
+ this.reset();
1699
+ return true;
1700
+ }
1701
+ } catch {
1654
1702
  }
1655
- } catch {
1656
1703
  }
1704
+ return false;
1705
+ } finally {
1706
+ this.running = false;
1657
1707
  }
1658
- return false;
1659
1708
  }
1660
1709
  /**
1661
1710
  * Stop any pending reconnection attempt.
@@ -1688,6 +1737,12 @@ var ReconnectionManager = class {
1688
1737
  const jitterOffset = (this.random() - 0.5) * 2 * jitterRange;
1689
1738
  return Math.max(0, Math.round(baseDelay + jitterOffset));
1690
1739
  }
1740
+ /**
1741
+ * Whether the reconnection loop is currently running.
1742
+ */
1743
+ isRunning() {
1744
+ return this.running;
1745
+ }
1691
1746
  /**
1692
1747
  * Current attempt number (for testing).
1693
1748
  */