@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 +80 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +80 -25
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
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.
|
|
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 {
|
|
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
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
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
|
*/
|