@fairfox/polly 0.64.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.js
CHANGED
|
@@ -2364,7 +2364,9 @@ function serialiseSlotView(slot) {
|
|
|
2364
2364
|
selectedCandidatePair: slot.transport.selectedCandidatePair ? { ...slot.transport.selectedCandidatePair } : undefined
|
|
2365
2365
|
} : undefined,
|
|
2366
2366
|
lastSyncHandshakeAttempt: { ...slot.lastSyncHandshakeAttempt },
|
|
2367
|
-
handles
|
|
2367
|
+
handles,
|
|
2368
|
+
createdAt: slot.createdAt,
|
|
2369
|
+
lastInboundAt: slot.lastInboundAt
|
|
2368
2370
|
};
|
|
2369
2371
|
}
|
|
2370
2372
|
function emptySyncHandshakeAttempt() {
|
|
@@ -2391,6 +2393,9 @@ var DEFAULT_ICE_SERVERS = [
|
|
|
2391
2393
|
{ urls: "stun:stun.l.google.com:19302" },
|
|
2392
2394
|
{ urls: "stun:stun1.l.google.com:19302" }
|
|
2393
2395
|
];
|
|
2396
|
+
var SLOT_NEVER_CONNECTED_TIMEOUT_MS = 30000;
|
|
2397
|
+
var SLOT_IDLE_TIMEOUT_MS = 120000;
|
|
2398
|
+
var SLOT_WATCHDOG_INTERVAL_MS = 5000;
|
|
2394
2399
|
function emptyHandleSyncSnapshot() {
|
|
2395
2400
|
return {
|
|
2396
2401
|
lastSyncMessageOutAt: undefined,
|
|
@@ -2421,6 +2426,10 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
|
|
|
2421
2426
|
lastSlotInitiationDecisions = new Map;
|
|
2422
2427
|
sweepRunCount = 0;
|
|
2423
2428
|
lastSweepAt;
|
|
2429
|
+
slotWatchdogTimer;
|
|
2430
|
+
slotNeverConnectedTimeoutMs;
|
|
2431
|
+
slotIdleTimeoutMs;
|
|
2432
|
+
slotWatchdogIntervalMs;
|
|
2424
2433
|
get knownPeerIds() {
|
|
2425
2434
|
if (this.keyringSource !== undefined) {
|
|
2426
2435
|
return [...this.keyringSource().knownPeers.keys()].filter((id) => id !== this.localPeerId);
|
|
@@ -2446,6 +2455,9 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
|
|
|
2446
2455
|
this.knownPeersRefreshIntervalMs = options.knownPeersRefreshIntervalMs ?? 2000;
|
|
2447
2456
|
this.syncYieldEnabled = options.syncYieldEnabled ?? true;
|
|
2448
2457
|
this.syncFragmentChunkSize = options.syncFragmentChunkSizeOverride ?? SYNC_FRAGMENT_CHUNK_SIZE;
|
|
2458
|
+
this.slotNeverConnectedTimeoutMs = options.slotNeverConnectedTimeoutMs ?? SLOT_NEVER_CONNECTED_TIMEOUT_MS;
|
|
2459
|
+
this.slotIdleTimeoutMs = options.slotIdleTimeoutMs ?? SLOT_IDLE_TIMEOUT_MS;
|
|
2460
|
+
this.slotWatchdogIntervalMs = options.slotWatchdogIntervalMs ?? SLOT_WATCHDOG_INTERVAL_MS;
|
|
2449
2461
|
this.localPeerId = options.peerId;
|
|
2450
2462
|
const PC = options.RTCPeerConnection ?? globalThis.RTCPeerConnection;
|
|
2451
2463
|
if (typeof PC !== "function") {
|
|
@@ -2603,9 +2615,11 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
|
|
|
2603
2615
|
this.ready = true;
|
|
2604
2616
|
this.readyResolver?.();
|
|
2605
2617
|
this.startKnownPeersSweep();
|
|
2618
|
+
this.startSlotWatchdog();
|
|
2606
2619
|
}
|
|
2607
2620
|
disconnect() {
|
|
2608
2621
|
this.stopKnownPeersSweep();
|
|
2622
|
+
this.stopSlotWatchdog();
|
|
2609
2623
|
for (const slot of this.slots.values()) {
|
|
2610
2624
|
slot.channel?.close();
|
|
2611
2625
|
slot.connection.close();
|
|
@@ -2636,6 +2650,70 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
|
|
|
2636
2650
|
clearInterval(this.knownPeersRefreshTimer);
|
|
2637
2651
|
this.knownPeersRefreshTimer = undefined;
|
|
2638
2652
|
}
|
|
2653
|
+
tearDownWedgedSlot(peerId) {
|
|
2654
|
+
const slot = this.slots.get(peerId);
|
|
2655
|
+
if (!slot)
|
|
2656
|
+
return;
|
|
2657
|
+
this.slots.delete(peerId);
|
|
2658
|
+
try {
|
|
2659
|
+
slot.channel?.close();
|
|
2660
|
+
} catch {}
|
|
2661
|
+
try {
|
|
2662
|
+
slot.connection.close();
|
|
2663
|
+
} catch {}
|
|
2664
|
+
this.emit("peer-disconnected", { peerId });
|
|
2665
|
+
}
|
|
2666
|
+
startSlotWatchdog() {
|
|
2667
|
+
if (this.slotWatchdogIntervalMs <= 0)
|
|
2668
|
+
return;
|
|
2669
|
+
if (this.slotWatchdogTimer !== undefined)
|
|
2670
|
+
return;
|
|
2671
|
+
this.slotWatchdogTimer = setInterval(() => {
|
|
2672
|
+
try {
|
|
2673
|
+
this.sweepWedgedSlots();
|
|
2674
|
+
} catch {}
|
|
2675
|
+
}, this.slotWatchdogIntervalMs);
|
|
2676
|
+
}
|
|
2677
|
+
stopSlotWatchdog() {
|
|
2678
|
+
if (this.slotWatchdogTimer === undefined)
|
|
2679
|
+
return;
|
|
2680
|
+
clearInterval(this.slotWatchdogTimer);
|
|
2681
|
+
this.slotWatchdogTimer = undefined;
|
|
2682
|
+
}
|
|
2683
|
+
sweepWedgedSlots() {
|
|
2684
|
+
const now = performance.now();
|
|
2685
|
+
const peerIds = [...this.slots.keys()];
|
|
2686
|
+
for (const peerId of peerIds) {
|
|
2687
|
+
const slot = this.slots.get(peerId);
|
|
2688
|
+
if (!slot)
|
|
2689
|
+
continue;
|
|
2690
|
+
const reason = this.classifyWedgedSlot(slot, now);
|
|
2691
|
+
if (!reason)
|
|
2692
|
+
continue;
|
|
2693
|
+
this.lastSlotInitiationDecisions.set(peerId, {
|
|
2694
|
+
decision: "rejected",
|
|
2695
|
+
reason: "fatal-error",
|
|
2696
|
+
error: reason,
|
|
2697
|
+
at: now
|
|
2698
|
+
});
|
|
2699
|
+
this.tearDownWedgedSlot(peerId);
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
classifyWedgedSlot(slot, now) {
|
|
2703
|
+
const state = slot.connection.connectionState;
|
|
2704
|
+
const ageMs = now - slot.createdAt;
|
|
2705
|
+
if (this.slotNeverConnectedTimeoutMs > 0 && (state === "new" || state === "connecting") && ageMs > this.slotNeverConnectedTimeoutMs) {
|
|
2706
|
+
return `slot never reached connected (state=${state}) after ${Math.round(ageMs)}ms`;
|
|
2707
|
+
}
|
|
2708
|
+
if (this.slotIdleTimeoutMs > 0 && state === "connected" && slot.channel?.readyState === "open") {
|
|
2709
|
+
const lastInbound = slot.lastInboundAt ?? slot.createdAt;
|
|
2710
|
+
const idleMs = now - lastInbound;
|
|
2711
|
+
if (idleMs > this.slotIdleTimeoutMs) {
|
|
2712
|
+
return `slot idle: no inbound bytes for ${Math.round(idleMs)}ms (state=connected, dc=open)`;
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
return;
|
|
2716
|
+
}
|
|
2639
2717
|
send(message) {
|
|
2640
2718
|
const targetId = message.targetId;
|
|
2641
2719
|
const bytes = this.serialiseMessage(message);
|
|
@@ -2771,12 +2849,23 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
|
|
|
2771
2849
|
transport: undefined,
|
|
2772
2850
|
lastDataChannelError: undefined,
|
|
2773
2851
|
lastSyncHandshakeAttempt: emptySyncHandshakeAttempt(),
|
|
2774
|
-
handles: new Map
|
|
2852
|
+
handles: new Map,
|
|
2853
|
+
createdAt: performance.now(),
|
|
2854
|
+
lastInboundAt: undefined
|
|
2775
2855
|
};
|
|
2776
2856
|
this.slots.set(targetId, slot);
|
|
2777
2857
|
this.wireConnection(targetId, connection);
|
|
2778
2858
|
this.wireDataChannel(targetId, channel);
|
|
2779
|
-
this.initiateOffer(targetId, connection)
|
|
2859
|
+
this.initiateOffer(targetId, connection).catch((err) => {
|
|
2860
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2861
|
+
this.lastSlotInitiationDecisions.set(targetId, {
|
|
2862
|
+
decision: "rejected",
|
|
2863
|
+
reason: "fatal-error",
|
|
2864
|
+
error: message,
|
|
2865
|
+
at: performance.now()
|
|
2866
|
+
});
|
|
2867
|
+
this.tearDownWedgedSlot(targetId);
|
|
2868
|
+
});
|
|
2780
2869
|
return slot;
|
|
2781
2870
|
}
|
|
2782
2871
|
async initiateOffer(targetId, connection) {
|
|
@@ -2814,7 +2903,9 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
|
|
|
2814
2903
|
transport: undefined,
|
|
2815
2904
|
lastDataChannelError: undefined,
|
|
2816
2905
|
lastSyncHandshakeAttempt: emptySyncHandshakeAttempt(),
|
|
2817
|
-
handles: new Map
|
|
2906
|
+
handles: new Map,
|
|
2907
|
+
createdAt: performance.now(),
|
|
2908
|
+
lastInboundAt: undefined
|
|
2818
2909
|
};
|
|
2819
2910
|
this.slots.set(fromPeerId, slot);
|
|
2820
2911
|
this.wireConnection(fromPeerId, connection);
|
|
@@ -2886,6 +2977,8 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
|
|
|
2886
2977
|
if (state === "connected") {
|
|
2887
2978
|
this.emitPeerCandidateOnce(peerId);
|
|
2888
2979
|
} else if (state === "disconnected" || state === "failed" || state === "closed") {
|
|
2980
|
+
if (!this.slots.has(peerId))
|
|
2981
|
+
return;
|
|
2889
2982
|
this.slots.delete(peerId);
|
|
2890
2983
|
this.emit("peer-disconnected", { peerId });
|
|
2891
2984
|
}
|
|
@@ -2916,6 +3009,9 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
|
|
|
2916
3009
|
slot.pendingSends = [];
|
|
2917
3010
|
};
|
|
2918
3011
|
channel.onmessage = (event) => {
|
|
3012
|
+
const liveSlot = this.slots.get(peerId);
|
|
3013
|
+
if (liveSlot)
|
|
3014
|
+
liveSlot.lastInboundAt = performance.now();
|
|
2919
3015
|
const data = event.data;
|
|
2920
3016
|
if (data instanceof ArrayBuffer) {
|
|
2921
3017
|
this.dispatchMessage(peerId, new Uint8Array(data));
|
|
@@ -3807,4 +3903,4 @@ export {
|
|
|
3807
3903
|
$meshCounter
|
|
3808
3904
|
};
|
|
3809
3905
|
|
|
3810
|
-
//# debugId=
|
|
3906
|
+
//# debugId=9FE5660EB3793A1964756E2164756E21
|