@relayfile/sdk 0.8.26 → 0.8.27
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/sync.d.ts +3 -0
- package/dist/sync.js +59 -0
- package/package.json +6 -6
package/dist/sync.d.ts
CHANGED
|
@@ -106,6 +106,8 @@ export declare class RelayFileSync {
|
|
|
106
106
|
private lastFrameAt;
|
|
107
107
|
private lastPingSentAt;
|
|
108
108
|
private reconnectAttempts;
|
|
109
|
+
private pollingReprobeAttempts;
|
|
110
|
+
private pollingReprobeEnabled;
|
|
109
111
|
private readonly abortHandler?;
|
|
110
112
|
constructor(options: RelayFileSyncOptions);
|
|
111
113
|
static connect(options: RelayFileSyncOptions): RelayFileSync;
|
|
@@ -126,6 +128,7 @@ export declare class RelayFileSync {
|
|
|
126
128
|
private forceReconnect;
|
|
127
129
|
private scheduleReconnect;
|
|
128
130
|
private computeReconnectDelayMs;
|
|
131
|
+
private computeReprobeDelayMs;
|
|
129
132
|
private sleep;
|
|
130
133
|
private isAbortError;
|
|
131
134
|
private setState;
|
package/dist/sync.js
CHANGED
|
@@ -44,6 +44,16 @@ const DEFAULT_POLL_INTERVAL_MS = 5000;
|
|
|
44
44
|
const DEFAULT_PING_INTERVAL_MS = 30000;
|
|
45
45
|
const DEFAULT_RECONNECT_MIN_DELAY_MS = 250;
|
|
46
46
|
const DEFAULT_RECONNECT_MAX_DELAY_MS = 5000;
|
|
47
|
+
// When involuntary polling is active but the WebSocket transport is still
|
|
48
|
+
// viable (baseUrl present, reconnect enabled, not caller-forced), the poll
|
|
49
|
+
// loop yields after a backoff window so the SDK can re-probe the WS and climb
|
|
50
|
+
// back to push delivery. Backoff grows on repeated pre-open failures so we
|
|
51
|
+
// never tight-loop a handshake that keeps rejecting, yet always recover once
|
|
52
|
+
// the transient (cold DO, proxy blip, brief auth gap) clears. Without this a
|
|
53
|
+
// single pre-open failure latches the process into polling forever — fatal
|
|
54
|
+
// for long-running consumers like the factory daemon.
|
|
55
|
+
const DEFAULT_WS_REPROBE_MIN_DELAY_MS = 5000;
|
|
56
|
+
const DEFAULT_WS_REPROBE_MAX_DELAY_MS = 300000;
|
|
47
57
|
// Cap on the dedupe cache used in polling mode. Sized large enough that no
|
|
48
58
|
// realistic workspace burst can churn through it within one poll interval
|
|
49
59
|
// (the events API is itself capped at ~1000 per page), small enough to keep
|
|
@@ -202,6 +212,14 @@ export class RelayFileSync {
|
|
|
202
212
|
// (no broadcasts and no pings yet) would falsely trip the watchdog.
|
|
203
213
|
lastPingSentAt = 0;
|
|
204
214
|
reconnectAttempts = 0;
|
|
215
|
+
// Tracks how many times involuntary polling has yielded to re-probe the
|
|
216
|
+
// WebSocket without a successful open in between. Drives re-probe backoff;
|
|
217
|
+
// reset to 0 once a socket actually reaches OPEN.
|
|
218
|
+
pollingReprobeAttempts = 0;
|
|
219
|
+
// Whether the *current* polling session should periodically yield to
|
|
220
|
+
// re-probe the WS. False for explicit/caller-forced polling (preferPolling
|
|
221
|
+
// or no baseUrl) — that polling is intentional and stays put.
|
|
222
|
+
pollingReprobeEnabled = false;
|
|
205
223
|
abortHandler;
|
|
206
224
|
constructor(options) {
|
|
207
225
|
this.client = options.client;
|
|
@@ -399,6 +417,9 @@ export class RelayFileSync {
|
|
|
399
417
|
}
|
|
400
418
|
this.currentSocketHasOpened = true;
|
|
401
419
|
this.reconnectAttempts = 0;
|
|
420
|
+
// A real OPEN means the transport is healthy again — start the next
|
|
421
|
+
// potential degradation from a fresh (short) re-probe backoff.
|
|
422
|
+
this.pollingReprobeAttempts = 0;
|
|
402
423
|
// Reset frame/ping bookkeeping for the freshly opened socket so the
|
|
403
424
|
// watchdog has a clean baseline. lastPingSentAt=0 disables the pong
|
|
404
425
|
// timeout until we actually send our first ping.
|
|
@@ -515,6 +536,15 @@ export class RelayFileSync {
|
|
|
515
536
|
}
|
|
516
537
|
}
|
|
517
538
|
this.setState("polling");
|
|
539
|
+
// Involuntary polling (anything but "explicit") should not be a one-way
|
|
540
|
+
// door: if the WS transport is viable, the poll loop will yield after a
|
|
541
|
+
// backoff window so the .finally() below can re-open the socket. Explicit
|
|
542
|
+
// polling (preferPolling / no baseUrl) is intentional and stays put.
|
|
543
|
+
this.pollingReprobeEnabled =
|
|
544
|
+
reason !== "explicit" &&
|
|
545
|
+
!!this.baseUrl &&
|
|
546
|
+
this.reconnect.enabled &&
|
|
547
|
+
!this.preferPolling;
|
|
518
548
|
this.pollingPromise = this.pollLoop().finally(() => {
|
|
519
549
|
this.pollingPromise = undefined;
|
|
520
550
|
if (!this.stopped && !this.shouldUsePolling()) {
|
|
@@ -524,10 +554,31 @@ export class RelayFileSync {
|
|
|
524
554
|
}
|
|
525
555
|
async pollLoop() {
|
|
526
556
|
let retryAttempt = 0;
|
|
557
|
+
// When re-probing is enabled, fix a deadline at which this poll session
|
|
558
|
+
// yields back to the WebSocket. Computed once per session from the running
|
|
559
|
+
// attempt count so repeated failures back off (5s, 10s, 20s … capped 5m).
|
|
560
|
+
// `undefined` => never yield (explicit polling, or WS not viable).
|
|
561
|
+
const reprobeDeadlineAt = this.pollingReprobeEnabled
|
|
562
|
+
? Date.now() + this.computeReprobeDelayMs(this.pollingReprobeAttempts)
|
|
563
|
+
: undefined;
|
|
527
564
|
while (!this.stopped) {
|
|
528
565
|
if (this.signal?.aborted) {
|
|
529
566
|
throw createAbortError();
|
|
530
567
|
}
|
|
568
|
+
// Backoff window elapsed: exit the loop so startPolling()'s .finally
|
|
569
|
+
// runs scheduleReconnect() → openWebSocket(). The cursor advanced by
|
|
570
|
+
// this session carries into the WS URL, so the re-opened socket resumes
|
|
571
|
+
// exactly where polling left off (no gap, no replay). If the re-open
|
|
572
|
+
// fails pre-open again, forceReconnect → startPolling restarts this loop
|
|
573
|
+
// with an incremented attempt and a longer window.
|
|
574
|
+
if (reprobeDeadlineAt !== undefined && Date.now() >= reprobeDeadlineAt) {
|
|
575
|
+
this.pollingReprobeAttempts += 1;
|
|
576
|
+
debugLog("polling yielding to WS re-probe", {
|
|
577
|
+
workspaceId: this.workspaceId,
|
|
578
|
+
attempt: this.pollingReprobeAttempts,
|
|
579
|
+
});
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
531
582
|
try {
|
|
532
583
|
// The current server implementation paginates events from oldest to
|
|
533
584
|
// newest. Empty cursor starts at index 0, and nextCursor advances
|
|
@@ -773,6 +824,14 @@ export class RelayFileSync {
|
|
|
773
824
|
const uncapped = this.reconnect.minDelayMs * Math.pow(2, Math.max(0, attempt - 1));
|
|
774
825
|
return Math.min(this.reconnect.maxDelayMs, uncapped);
|
|
775
826
|
}
|
|
827
|
+
// How long involuntary polling runs before yielding to re-probe the WS.
|
|
828
|
+
// attempt 0 → 5s, 1 → 10s, 2 → 20s … capped at 5m. The first re-probe after
|
|
829
|
+
// a fresh degradation is fast (transients usually clear within seconds); a
|
|
830
|
+
// persistently rejecting handshake backs off so we never hammer it.
|
|
831
|
+
computeReprobeDelayMs(attempt) {
|
|
832
|
+
const uncapped = DEFAULT_WS_REPROBE_MIN_DELAY_MS * Math.pow(2, Math.max(0, attempt));
|
|
833
|
+
return Math.min(DEFAULT_WS_REPROBE_MAX_DELAY_MS, uncapped);
|
|
834
|
+
}
|
|
776
835
|
async sleep(delayMs) {
|
|
777
836
|
if (delayMs <= 0) {
|
|
778
837
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@relayfile/sdk",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.27",
|
|
4
4
|
"description": "TypeScript SDK for relayfile — real-time filesystem for humans and agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -59,15 +59,15 @@
|
|
|
59
59
|
"prepublishOnly": "npm run build"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@relayfile/core": "0.8.
|
|
62
|
+
"@relayfile/core": "0.8.27",
|
|
63
63
|
"ignore": "^7.0.5",
|
|
64
64
|
"tar": "^7.5.10"
|
|
65
65
|
},
|
|
66
66
|
"optionalDependencies": {
|
|
67
|
-
"@relayfile/mount-darwin-arm64": "0.8.
|
|
68
|
-
"@relayfile/mount-darwin-x64": "0.8.
|
|
69
|
-
"@relayfile/mount-linux-arm64": "0.8.
|
|
70
|
-
"@relayfile/mount-linux-x64": "0.8.
|
|
67
|
+
"@relayfile/mount-darwin-arm64": "0.8.27",
|
|
68
|
+
"@relayfile/mount-darwin-x64": "0.8.27",
|
|
69
|
+
"@relayfile/mount-linux-arm64": "0.8.27",
|
|
70
|
+
"@relayfile/mount-linux-x64": "0.8.27"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {},
|
|
73
73
|
"devDependencies": {
|