@rljson/network 0.0.3 → 0.0.4

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/network.js CHANGED
@@ -1156,6 +1156,8 @@ class ProbeScheduler {
1156
1156
  _wasReachable = /* @__PURE__ */ new Map();
1157
1157
  /** Consecutive failure count per peer, for flap dampening */
1158
1158
  _failCount = /* @__PURE__ */ new Map();
1159
+ /** Peers that have been successfully probed at least once */
1160
+ _everReachable = /* @__PURE__ */ new Set();
1159
1161
  /** Event listeners */
1160
1162
  _listeners = /* @__PURE__ */ new Map();
1161
1163
  /** The probe function — real TCP by default, injectable for tests */
@@ -1208,6 +1210,7 @@ class ProbeScheduler {
1208
1210
  this._probes.clear();
1209
1211
  this._wasReachable.clear();
1210
1212
  this._failCount.clear();
1213
+ this._everReachable.clear();
1211
1214
  this._peers = [];
1212
1215
  }
1213
1216
  /** Whether the scheduler is currently running */
@@ -1250,6 +1253,14 @@ class ProbeScheduler {
1250
1253
  async runOnce() {
1251
1254
  return this._runCycle();
1252
1255
  }
1256
+ /**
1257
+ * Whether a peer has ever been successfully probed.
1258
+ * Used to distinguish "new peer (startup race)" from "crashed peer".
1259
+ * @param nodeId - The peer's nodeId
1260
+ */
1261
+ hasEverBeenReachable(nodeId) {
1262
+ return this._everReachable.has(nodeId);
1263
+ }
1253
1264
  // .........................................................................
1254
1265
  // Events
1255
1266
  // .........................................................................
@@ -1304,6 +1315,7 @@ class ProbeScheduler {
1304
1315
  if (probe.reachable) {
1305
1316
  this._failCount.set(probe.toNodeId, 0);
1306
1317
  this._wasReachable.set(probe.toNodeId, true);
1318
+ this._everReachable.add(probe.toNodeId);
1307
1319
  if (previous !== void 0 && !previous) {
1308
1320
  this._emit("peer-reachable", probe.toNodeId, probe);
1309
1321
  }
@@ -1587,6 +1599,17 @@ class NetworkManager {
1587
1599
  );
1588
1600
  /* v8 ignore else -- @preserve */
1589
1601
  if (result.hubId) {
1602
+ if (result.hubId === this._identity.nodeId && result.reason !== "incumbent") {
1603
+ const selfInfo = this._identity.toNodeInfo();
1604
+ const broadcastPeers = this._broadcastLayer.getPeers();
1605
+ const hasUntestedEarlierPeer = broadcastPeers.some(
1606
+ (p) => !this._probeScheduler.hasEverBeenReachable(p.nodeId) && (p.startedAt < selfInfo.startedAt || /* v8 ignore next -- @preserve */
1607
+ p.startedAt === selfInfo.startedAt && p.nodeId < selfInfo.nodeId)
1608
+ );
1609
+ if (hasUntestedEarlierPeer) {
1610
+ return { hubId: null, formedBy: "election" };
1611
+ }
1612
+ }
1590
1613
  const formedBy = this._broadcastLayer.isActive() && this._broadcastLayer.getPeers().length > 0 ? "broadcast" : "election";
1591
1614
  return { hubId: result.hubId, formedBy };
1592
1615
  }
@@ -50,6 +50,8 @@ export declare class ProbeScheduler {
50
50
  private _wasReachable;
51
51
  /** Consecutive failure count per peer, for flap dampening */
52
52
  private _failCount;
53
+ /** Peers that have been successfully probed at least once */
54
+ private _everReachable;
53
55
  /** Event listeners */
54
56
  private _listeners;
55
57
  /** The probe function — real TCP by default, injectable for tests */
@@ -94,6 +96,12 @@ export declare class ProbeScheduler {
94
96
  * Useful for tests that need immediate results without waiting.
95
97
  */
96
98
  runOnce(): Promise<PeerProbe[]>;
99
+ /**
100
+ * Whether a peer has ever been successfully probed.
101
+ * Used to distinguish "new peer (startup race)" from "crashed peer".
102
+ * @param nodeId - The peer's nodeId
103
+ */
104
+ hasEverBeenReachable(nodeId: NodeId): boolean;
97
105
  /**
98
106
  * Subscribe to scheduler events.
99
107
  * @param event - Event name
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rljson/network",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Networking middle layer providing basic advertising and discovery as well as cloud fall backs and basic config.",
5
5
  "homepage": "https://github.com/rljson/network",
6
6
  "bugs": "https://github.com/rljson/network/issues",