hypha-rpc 0.21.46 → 0.21.47
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/coverage/html/index.html +1 -1
- package/dist/hypha-rpc-websocket.js +63 -2
- package/dist/hypha-rpc-websocket.js.map +1 -1
- package/dist/hypha-rpc-websocket.min.js +1 -1
- package/dist/hypha-rpc-websocket.min.js.map +1 -1
- package/dist/hypha-rpc-websocket.min.mjs +1 -1
- package/dist/hypha-rpc-websocket.min.mjs.map +1 -1
- package/dist/hypha-rpc-websocket.mjs +63 -2
- package/dist/hypha-rpc-websocket.mjs.map +1 -1
- package/package.json +1 -1
- package/src/websocket-client.js +63 -2
package/coverage/html/index.html
CHANGED
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
87
87
|
Code coverage generated by
|
|
88
88
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
89
|
-
at 2026-
|
|
89
|
+
at 2026-08-31T13:24:20.113Z
|
|
90
90
|
</div>
|
|
91
91
|
<script src="prettify.js"></script>
|
|
92
92
|
<script>
|
|
@@ -10425,6 +10425,16 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
10425
10425
|
|
|
10426
10426
|
const MAX_RETRY = 1000000;
|
|
10427
10427
|
|
|
10428
|
+
// Reconnect flap control: a connection that stays up for less than
|
|
10429
|
+
// RECONNECT_FLAP_WINDOW_MS after connecting is treated as a "flap" (e.g. the
|
|
10430
|
+
// server accepts the reconnect then immediately supersedes/closes it because a
|
|
10431
|
+
// prior session with the same client_id is still active). Flaps escalate the
|
|
10432
|
+
// backoff across reconnect cycles — instead of resetting to an immediate retry
|
|
10433
|
+
// each time — and never reconnect faster than RECONNECT_MIN_DELAY_MS while
|
|
10434
|
+
// flapping. This prevents a tight succeed→superseded→succeed reconnect storm.
|
|
10435
|
+
const RECONNECT_FLAP_WINDOW_MS = 5000;
|
|
10436
|
+
const RECONNECT_MIN_DELAY_MS = 1000;
|
|
10437
|
+
|
|
10428
10438
|
// When the socket errors/closes during the handshake, wait briefly before
|
|
10429
10439
|
// rejecting with the generic reason: the server usually sends a descriptive
|
|
10430
10440
|
// {type:"error"} message (e.g. token/workspace mismatch) just before closing,
|
|
@@ -10482,6 +10492,8 @@ class WebsocketRPCConnection {
|
|
|
10482
10492
|
this._reconnecting = false; // Mutex to prevent overlapping reconnection attempts
|
|
10483
10493
|
this._closedDuringReconnect = false; // Flag for close events during reconnection
|
|
10484
10494
|
this._disconnectedNotified = false;
|
|
10495
|
+
this._lastConnectedTime = 0; // ms timestamp of last successful connection_info
|
|
10496
|
+
this._reconnectFlapCount = 0; // consecutive short-lived (flapping) connections
|
|
10485
10497
|
}
|
|
10486
10498
|
|
|
10487
10499
|
/**
|
|
@@ -10634,6 +10646,9 @@ class WebsocketRPCConnection {
|
|
|
10634
10646
|
const first_message = JSON.parse(data);
|
|
10635
10647
|
if (first_message.type == "connection_info") {
|
|
10636
10648
|
settled = true;
|
|
10649
|
+
// Mark when a connection is fully established; used for flap
|
|
10650
|
+
// detection when the connection later closes.
|
|
10651
|
+
this._lastConnectedTime = Date.now();
|
|
10637
10652
|
this.connection_info = first_message;
|
|
10638
10653
|
if (this._workspace) {
|
|
10639
10654
|
(0,_utils_index_js__WEBPACK_IMPORTED_MODULE_2__.assert)(
|
|
@@ -10841,7 +10856,25 @@ class WebsocketRPCConnection {
|
|
|
10841
10856
|
}
|
|
10842
10857
|
this._reconnecting = true;
|
|
10843
10858
|
|
|
10844
|
-
|
|
10859
|
+
// Flap detection: if the connection that just closed was short-lived,
|
|
10860
|
+
// escalate the backoff across reconnect cycles instead of resetting to
|
|
10861
|
+
// an immediate retry. A healthy long-lived connection resets the count
|
|
10862
|
+
// so genuine disconnects still recover fast.
|
|
10863
|
+
const aliveMs = this._lastConnectedTime
|
|
10864
|
+
? Date.now() - this._lastConnectedTime
|
|
10865
|
+
: Infinity;
|
|
10866
|
+
if (aliveMs < RECONNECT_FLAP_WINDOW_MS) {
|
|
10867
|
+
this._reconnectFlapCount += 1;
|
|
10868
|
+
} else {
|
|
10869
|
+
this._reconnectFlapCount = 0;
|
|
10870
|
+
}
|
|
10871
|
+
|
|
10872
|
+
// Seed the retry counter from the flap history so a REPEATED flap
|
|
10873
|
+
// escalates the backoff. The first flap is free (retry 0 → immediate)
|
|
10874
|
+
// so a genuine single quick disconnect still recovers fast; only the
|
|
10875
|
+
// 2nd+ consecutive short-lived connection is delayed, which is the
|
|
10876
|
+
// succeed→superseded storm signature.
|
|
10877
|
+
let retry = Math.max(0, this._reconnectFlapCount - 1);
|
|
10845
10878
|
const baseDelay = 1000; // Start with 1 second
|
|
10846
10879
|
const maxDelay = 60000; // Maximum delay of 60 seconds
|
|
10847
10880
|
const maxJitter = 0.1; // Maximum jitter factor
|
|
@@ -10981,7 +11014,35 @@ class WebsocketRPCConnection {
|
|
|
10981
11014
|
this._reconnect_timeouts.add(timeoutId);
|
|
10982
11015
|
}
|
|
10983
11016
|
};
|
|
10984
|
-
|
|
11017
|
+
|
|
11018
|
+
// Kick off the reconnect loop. When flapping (retry seeded > 0), delay
|
|
11019
|
+
// the first attempt with the same exponential backoff + jitter and a
|
|
11020
|
+
// minimum floor, so a succeed→superseded loop cannot hammer the server.
|
|
11021
|
+
// A genuine disconnect after a healthy connection (retry === 0) still
|
|
11022
|
+
// reconnects immediately.
|
|
11023
|
+
if (retry > 0) {
|
|
11024
|
+
const delay = Math.min(baseDelay * Math.pow(2, retry - 1), maxDelay);
|
|
11025
|
+
const jitter = (Math.random() * 2 - 1) * maxJitter * delay;
|
|
11026
|
+
const finalDelay = Math.max(RECONNECT_MIN_DELAY_MS, delay + jitter);
|
|
11027
|
+
const timeoutId = setTimeout(() => {
|
|
11028
|
+
this._reconnect_timeouts.delete(timeoutId);
|
|
11029
|
+
if (this._closed) {
|
|
11030
|
+
this._reconnecting = false;
|
|
11031
|
+
return;
|
|
11032
|
+
}
|
|
11033
|
+
if (
|
|
11034
|
+
this._websocket &&
|
|
11035
|
+
this._websocket.readyState === WebSocket.OPEN
|
|
11036
|
+
) {
|
|
11037
|
+
this._reconnecting = false;
|
|
11038
|
+
return;
|
|
11039
|
+
}
|
|
11040
|
+
reconnect();
|
|
11041
|
+
}, finalDelay);
|
|
11042
|
+
this._reconnect_timeouts.add(timeoutId);
|
|
11043
|
+
} else {
|
|
11044
|
+
reconnect();
|
|
11045
|
+
}
|
|
10985
11046
|
}
|
|
10986
11047
|
} else {
|
|
10987
11048
|
// Clean up timers in all cases
|