@fairfox/polly 0.63.0 → 0.65.0
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/src/mesh.d.ts +1 -1
- package/dist/src/mesh.js +244 -35
- package/dist/src/mesh.js.map +6 -6
- package/dist/src/peer.js +88 -20
- package/dist/src/peer.js.map +3 -3
- package/dist/src/shared/lib/crdt-state.d.ts +14 -0
- package/dist/src/shared/lib/mesh-state.d.ts +37 -1
- package/dist/src/shared/lib/mesh-webrtc-adapter.d.ts +89 -1
- package/package.json +1 -1
|
@@ -51,6 +51,30 @@ import type { MeshSignalingClient } from "./mesh-signaling-client";
|
|
|
51
51
|
* need TURN fallback for peers behind symmetric NATs should replace this
|
|
52
52
|
* with their own ICE server list. */
|
|
53
53
|
export declare const DEFAULT_ICE_SERVERS: RTCIceServer[];
|
|
54
|
+
/** Polly issue #109: how long a slot can sit at `connectionState` in
|
|
55
|
+
* `new` or `connecting` before the watchdog tears it down so the
|
|
56
|
+
* recovery sweep can re-attempt. Healthy ICE completes in single-digit
|
|
57
|
+
* seconds; 30s is well past anything legitimate and short enough that
|
|
58
|
+
* a silent `createOffer`/`setLocalDescription` rejection no longer
|
|
59
|
+
* leaves an unrecoverable wedged slot. */
|
|
60
|
+
export declare const SLOT_NEVER_CONNECTED_TIMEOUT_MS = 30000;
|
|
61
|
+
/** Polly issue #110: how long a slot whose data channel is open and
|
|
62
|
+
* whose `connectionState` is `connected` can have no inbound bytes
|
|
63
|
+
* before the watchdog assumes the remote process is dead and tears
|
|
64
|
+
* the slot down. The ICE keepalive timer can take minutes to notice a
|
|
65
|
+
* remote SIGKILL or network partition; an application-layer
|
|
66
|
+
* liveness check that runs an order of magnitude faster keeps
|
|
67
|
+
* paired devices from sending sync traffic into the void after the
|
|
68
|
+
* remote daemon restarts. 120s is conservative — Automerge's idle
|
|
69
|
+
* cadence is well below this, so a healthy slot never crosses the
|
|
70
|
+
* threshold. */
|
|
71
|
+
export declare const SLOT_IDLE_TIMEOUT_MS = 120000;
|
|
72
|
+
/** Polly issue #109/#110: how often the watchdog evaluates teardown
|
|
73
|
+
* decisions across every slot. 5s is well below either threshold so
|
|
74
|
+
* teardown happens promptly after a deadline lapses without
|
|
75
|
+
* dominating runtime cost (one `connectionState` read per slot per
|
|
76
|
+
* tick). */
|
|
77
|
+
export declare const SLOT_WATCHDOG_INTERVAL_MS = 5000;
|
|
54
78
|
/** Options for constructing a {@link MeshWebRTCAdapter}. */
|
|
55
79
|
export interface MeshWebRTCAdapterOptions {
|
|
56
80
|
/** The signalling client the adapter uses to exchange SDP and ICE
|
|
@@ -154,6 +178,22 @@ export interface MeshWebRTCAdapterOptions {
|
|
|
154
178
|
* `POLLY_104_DISABLE_FIX=1` falsification path. Production callers
|
|
155
179
|
* should leave this at the default. */
|
|
156
180
|
syncFragmentChunkSizeOverride?: number;
|
|
181
|
+
/** How long a slot can sit at `connectionState` in `new` or
|
|
182
|
+
* `connecting` before the watchdog tears it down. Defaults to
|
|
183
|
+
* {@link SLOT_NEVER_CONNECTED_TIMEOUT_MS}. Set to 0 to disable the
|
|
184
|
+
* gate. Polly issue #109. */
|
|
185
|
+
slotNeverConnectedTimeoutMs?: number;
|
|
186
|
+
/** How long a connected slot can have no inbound bytes before the
|
|
187
|
+
* watchdog tears it down as idle. Defaults to
|
|
188
|
+
* {@link SLOT_IDLE_TIMEOUT_MS}. Set to 0 to disable the gate.
|
|
189
|
+
* Polly issue #110. */
|
|
190
|
+
slotIdleTimeoutMs?: number;
|
|
191
|
+
/** How often the slot watchdog evaluates teardown decisions.
|
|
192
|
+
* Defaults to {@link SLOT_WATCHDOG_INTERVAL_MS}; tests override to
|
|
193
|
+
* tens of milliseconds. Set to 0 to disable the watchdog entirely
|
|
194
|
+
* (the pre-#109/#110 behaviour, kept only for migration and the
|
|
195
|
+
* falsification path). */
|
|
196
|
+
slotWatchdogIntervalMs?: number;
|
|
157
197
|
}
|
|
158
198
|
/** Payload of the polly-specific `"sync-progress"` event emitted by
|
|
159
199
|
* {@link MeshWebRTCAdapter}. Consumers can subscribe via the adapter's
|
|
@@ -239,7 +279,10 @@ export interface TransportSnapshot {
|
|
|
239
279
|
* negotiation state.
|
|
240
280
|
* - `fatal-error`: an exception was thrown while attempting to build
|
|
241
281
|
* the slot. The accompanying {@link SlotInitiationDecision.error}
|
|
242
|
-
* string carries the message.
|
|
282
|
+
* string carries the message. This is also stamped when the slot
|
|
283
|
+
* watchdog tears a wedged slot down (polly#109's silent throw, or
|
|
284
|
+
* polly#110's idle-but-still-`connected` post-mortem), so the next
|
|
285
|
+
* sweep tick finds no slot and retries.
|
|
243
286
|
*/
|
|
244
287
|
export type SlotInitiationRejectionReason = "self" | "not-in-keyring" | "not-present" | "tie-break-other-side" | "slot-already-exists" | "fatal-error";
|
|
245
288
|
/** Most-recent slot-initiation decision for a peer. Computed at
|
|
@@ -478,6 +521,14 @@ export declare class MeshWebRTCAdapter extends NetworkAdapter {
|
|
|
478
521
|
* {@link sweepRunCount} so a stalled sweep is visible at a glance
|
|
479
522
|
* via the snapshot's `sweep` block. */
|
|
480
523
|
private lastSweepAt;
|
|
524
|
+
/** Watchdog interval (polly#109 + polly#110). Tears down slots that
|
|
525
|
+
* never reach `connected`, or that look connected but have not
|
|
526
|
+
* received bytes for {@link slotIdleTimeoutMs}, so the recovery
|
|
527
|
+
* sweep can re-attempt. Cleared in {@link disconnect}. */
|
|
528
|
+
private slotWatchdogTimer;
|
|
529
|
+
private readonly slotNeverConnectedTimeoutMs;
|
|
530
|
+
private readonly slotIdleTimeoutMs;
|
|
531
|
+
private readonly slotWatchdogIntervalMs;
|
|
481
532
|
/** The peers this adapter will dial. Backward-compatible read accessor
|
|
482
533
|
* for callers that previously iterated the `knownPeerIds` array. With
|
|
483
534
|
* a {@link MeshWebRTCAdapterOptions.keyringSource} configured, the
|
|
@@ -543,6 +594,8 @@ export declare class MeshWebRTCAdapter extends NetworkAdapter {
|
|
|
543
594
|
transport: TransportSnapshot | undefined;
|
|
544
595
|
lastSyncHandshakeAttempt: SyncHandshakeAttemptSnapshot;
|
|
545
596
|
handles: Record<string, HandleSyncSnapshot>;
|
|
597
|
+
createdAt: number;
|
|
598
|
+
lastInboundAt: number | undefined;
|
|
546
599
|
};
|
|
547
600
|
}>;
|
|
548
601
|
};
|
|
@@ -664,6 +717,41 @@ export declare class MeshWebRTCAdapter extends NetworkAdapter {
|
|
|
664
717
|
* #106 item 7. */
|
|
665
718
|
private startKnownPeersSweep;
|
|
666
719
|
private stopKnownPeersSweep;
|
|
720
|
+
/** Close a slot's data channel and connection, remove it from the
|
|
721
|
+
* map, and emit `peer-disconnected` upward so Automerge stops
|
|
722
|
+
* routing through the dead pair. Used by both the polly#109
|
|
723
|
+
* `.catch` in {@link createInitiatingSlot} and the polly#109/#110
|
|
724
|
+
* watchdog in {@link sweepWedgedSlots}. */
|
|
725
|
+
private tearDownWedgedSlot;
|
|
726
|
+
/** Start the slot watchdog (polly#109 + polly#110). Walks every
|
|
727
|
+
* active slot every {@link slotWatchdogIntervalMs} and tears down
|
|
728
|
+
* the two named wedge shapes:
|
|
729
|
+
*
|
|
730
|
+
* - `connectionState` in `new`/`connecting` for longer than
|
|
731
|
+
* {@link slotNeverConnectedTimeoutMs} (polly#109: silent
|
|
732
|
+
* `createOffer`/`setLocalDescription` rejection, or a network
|
|
733
|
+
* condition under which ICE never gathers).
|
|
734
|
+
*
|
|
735
|
+
* - `connectionState === "connected"` AND data channel `open` AND
|
|
736
|
+
* no inbound bytes for {@link slotIdleTimeoutMs} (polly#110:
|
|
737
|
+
* remote process killed without OS-layer FIN — ICE keepalives
|
|
738
|
+
* take many minutes to fail, the slot sends sync traffic into
|
|
739
|
+
* the void until then).
|
|
740
|
+
*
|
|
741
|
+
* Each teardown stamps `fatal-error` on the per-peer decision so
|
|
742
|
+
* the named gate is visible on the next snapshot, then emits
|
|
743
|
+
* `peer-disconnected` so Automerge stops routing through the dead
|
|
744
|
+
* slot. The next sweep tick re-evaluates and the recovery path
|
|
745
|
+
* creates a fresh slot. No-op when configured to 0. */
|
|
746
|
+
private startSlotWatchdog;
|
|
747
|
+
private stopSlotWatchdog;
|
|
748
|
+
private sweepWedgedSlots;
|
|
749
|
+
/** Decide whether a slot is wedged at the moment of inspection,
|
|
750
|
+
* returning the human-readable diagnosis when it is. Pulled out of
|
|
751
|
+
* {@link sweepWedgedSlots} so the per-slot decision stays under
|
|
752
|
+
* biome's cognitive-complexity ceiling and so a future test can
|
|
753
|
+
* exercise the gates directly. */
|
|
754
|
+
private classifyWedgedSlot;
|
|
667
755
|
/**
|
|
668
756
|
* Send a sync message to a specific remote peer. If no RTCPeerConnection
|
|
669
757
|
* exists yet, the adapter initiates one by producing an SDP offer and
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fairfox/polly",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.65.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Multi-execution-context framework with reactive state and cross-context messaging for Chrome extensions, PWAs, and worker-based applications",
|