@aexol/spectral 0.9.113 → 0.9.114
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/relay/client.d.ts +6 -0
- package/dist/relay/client.d.ts.map +1 -1
- package/dist/relay/client.js +44 -1
- package/package.json +1 -1
package/dist/relay/client.d.ts
CHANGED
|
@@ -96,6 +96,12 @@ export declare class RelayClient extends EventEmitter {
|
|
|
96
96
|
private ws;
|
|
97
97
|
private disposed;
|
|
98
98
|
private reconnectAttempt;
|
|
99
|
+
/** Timestamp of the last `open` event; 0 when socket is closed. */
|
|
100
|
+
private openedAtMs;
|
|
101
|
+
/** Consecutive closes where the socket was open < STABLE_OPEN_MS. */
|
|
102
|
+
private consecutiveRapidCloses;
|
|
103
|
+
/** Start of the current rapid-close counting window (ms epoch). */
|
|
104
|
+
private tightLoopWindowStart;
|
|
99
105
|
private reconnectTimer;
|
|
100
106
|
private sendQueue;
|
|
101
107
|
/** Set true when the WS error handler sees an HTTP 401/403 upgrade rejection. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/relay/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,SAAS,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/relay/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,SAAS,MAAM,IAAI,CAAC;AA4B3B,MAAM,WAAW,kBAAkB;IACjC,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,SAAS,CAAC;IACjC;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB;;OAEG;IACH,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IACjD,2DAA2D;IAC3D,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C;;;;OAIG;IACH,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC;CAChC;AAED,6EAA6E;AAC7E,MAAM,WAAW,UAAU;IACzB,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,qBAAa,WAAY,SAAQ,YAAY;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAmB;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0C;IACjE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA0B;IAC/C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+C;IAE3E,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,gBAAgB,CAAK;IAC7B,mEAAmE;IACnE,OAAO,CAAC,UAAU,CAAK;IACvB,qEAAqE;IACrE,OAAO,CAAC,sBAAsB,CAAK;IACnC,mEAAmE;IACnE,OAAO,CAAC,oBAAoB,CAAK;IACjC,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,SAAS,CAAgB;IACjC,iFAAiF;IACjF,OAAO,CAAC,UAAU,CAAS;IAE3B,qEAAqE;IACrE,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,aAAa,CAA+B;gBAExC,IAAI,EAAE,kBAAkB;IAYpC,kEAAkE;IAClE,OAAO,IAAI,IAAI;IAMf;;;;;;;OAOG;IACH,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,OAAO;IAqBzC;;;OAGG;IACH,OAAO,IAAI,IAAI;IA2Bf,OAAO,CAAC,UAAU;IA4LlB,OAAO,CAAC,aAAa;IAsBrB,OAAO,CAAC,YAAY;IAOpB;;;;;OAKG;YACW,WAAW;YA8BX,iBAAiB;CAmDhC"}
|
package/dist/relay/client.js
CHANGED
|
@@ -49,6 +49,11 @@ const SEND_QUEUE_CAP = 100;
|
|
|
49
49
|
const RECONNECT_SCHEDULE = [1000, 2000, 5000, 15000, 30000];
|
|
50
50
|
/** ±20% jitter on each scheduled delay. */
|
|
51
51
|
const JITTER_RATIO = 0.2;
|
|
52
|
+
/** Minimum socket open duration to consider the connection stable and reset backoff. */
|
|
53
|
+
const STABLE_OPEN_MS = 5_000;
|
|
54
|
+
/** Max rapid closes within TIGHT_LOOP_WINDOW_MS before giving up. */
|
|
55
|
+
const TIGHT_LOOP_MAX_CLOSES = 5;
|
|
56
|
+
const TIGHT_LOOP_WINDOW_MS = 60_000;
|
|
52
57
|
/**
|
|
53
58
|
* Watchdog interval (ms). Every `WATCHDOG_INTERVAL_MS` we check whether
|
|
54
59
|
* the socket has been silent longer than `WATCHDOG_SILENCE_MS`.
|
|
@@ -73,6 +78,12 @@ export class RelayClient extends EventEmitter {
|
|
|
73
78
|
ws = null;
|
|
74
79
|
disposed = false;
|
|
75
80
|
reconnectAttempt = 0;
|
|
81
|
+
/** Timestamp of the last `open` event; 0 when socket is closed. */
|
|
82
|
+
openedAtMs = 0;
|
|
83
|
+
/** Consecutive closes where the socket was open < STABLE_OPEN_MS. */
|
|
84
|
+
consecutiveRapidCloses = 0;
|
|
85
|
+
/** Start of the current rapid-close counting window (ms epoch). */
|
|
86
|
+
tightLoopWindowStart = 0;
|
|
76
87
|
reconnectTimer = null;
|
|
77
88
|
sendQueue = [];
|
|
78
89
|
/** Set true when the WS error handler sees an HTTP 401/403 upgrade rejection. */
|
|
@@ -173,7 +184,7 @@ export class RelayClient extends EventEmitter {
|
|
|
173
184
|
this.startWatchdog();
|
|
174
185
|
ws.on("open", () => {
|
|
175
186
|
this.lastActivityMs = Date.now();
|
|
176
|
-
this.
|
|
187
|
+
this.openedAtMs = Date.now();
|
|
177
188
|
// Flush any queued frames.
|
|
178
189
|
const queued = this.sendQueue;
|
|
179
190
|
this.sendQueue = [];
|
|
@@ -237,6 +248,38 @@ export class RelayClient extends EventEmitter {
|
|
|
237
248
|
ws.on("close", (code, reason) => {
|
|
238
249
|
this.ws = null;
|
|
239
250
|
this.stopWatchdog();
|
|
251
|
+
// Stable-open gate: only reset backoff if the socket was open long
|
|
252
|
+
// enough to be considered healthy. A connect→immediate-die loop (1006,
|
|
253
|
+
// proxy kill, duplicate instance) must escalate through the full
|
|
254
|
+
// schedule instead of pinning at RECONNECT_SCHEDULE[0] = 1s forever.
|
|
255
|
+
const openDuration = this.openedAtMs > 0 ? Date.now() - this.openedAtMs : 0;
|
|
256
|
+
this.openedAtMs = 0;
|
|
257
|
+
if (openDuration >= STABLE_OPEN_MS) {
|
|
258
|
+
this.reconnectAttempt = 0;
|
|
259
|
+
this.consecutiveRapidCloses = 0;
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
// Tight-loop detection: if the socket dies repeatedly right after
|
|
263
|
+
// open, bail out with an actionable message instead of reconnecting
|
|
264
|
+
// forever.
|
|
265
|
+
const now = Date.now();
|
|
266
|
+
if (now - this.tightLoopWindowStart > TIGHT_LOOP_WINDOW_MS) {
|
|
267
|
+
this.tightLoopWindowStart = now;
|
|
268
|
+
this.consecutiveRapidCloses = 1;
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
this.consecutiveRapidCloses++;
|
|
272
|
+
}
|
|
273
|
+
if (this.consecutiveRapidCloses >= TIGHT_LOOP_MAX_CLOSES) {
|
|
274
|
+
this.logger.error("\n✗ Relay connection dropped repeatedly right after opening.");
|
|
275
|
+
this.logger.error(" This usually means another `spectral serve` process is running for the same machine,");
|
|
276
|
+
this.logger.error(" or a reverse proxy / firewall is closing the WebSocket immediately.");
|
|
277
|
+
this.logger.error(' Check: `ps aux | grep "spectral serve"` and your proxy WS timeout settings.\n');
|
|
278
|
+
this.dispose();
|
|
279
|
+
this.exit(1);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
240
283
|
const reasonStr = reason?.toString() ?? "";
|
|
241
284
|
this.emit("close", { code, reason: reasonStr });
|
|
242
285
|
if (this.disposed)
|