@leofcoin/chain 1.7.104 → 1.7.106

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.
@@ -5405,27 +5405,12 @@ class ConnectionMonitor {
5405
5405
  const connectedPeers = this.connectedPeers;
5406
5406
  const compatiblePeers = this.compatiblePeers;
5407
5407
  console.log(`🔍 Health check: ${connectedPeers.length} connected, ${compatiblePeers.length} compatible`);
5408
- if (connectedPeers.length === 0) {
5409
- console.warn('⚠️ No peer connections detected');
5410
- await this.#attemptReconnection();
5411
- }
5412
- else if (compatiblePeers.length === 0) {
5413
- console.warn('⚠️ No compatible peers found');
5414
- await this.#attemptReconnection();
5415
- // Could attempt to find compatible peers or trigger version negotiation
5416
- }
5417
- // Log disconnected peers
5418
- // const disconnectedPeers = this.disconnectedPeers
5419
- // if (disconnectedPeers.length > 0) {
5420
- // console.warn(`⚠️ Disconnected peers: ${disconnectedPeers.map((peer) => peer.peerId).join(', ')}`)
5421
- // // Attempt to reconnect each disconnected peer sequentially to avoid racing signaling/state
5422
- // for (const peer of disconnectedPeers) {
5423
- // // small spacing between attempts to reduce signaling races
5424
- // // eslint-disable-next-line no-await-in-loop
5425
- // await new Promise((r) => setTimeout(r, 150))
5426
- // // eslint-disable-next-line no-await-in-loop
5427
- // await this.#attemptPeerReconnection(peer)
5428
- // }
5408
+ // if (connectedPeers.length === 0) {
5409
+ console.warn('⚠️ No peer connections detected');
5410
+ // await this.#attemptReconnection()
5411
+ // } else if (compatiblePeers.length === 0) {
5412
+ console.warn('⚠️ No compatible peers found');
5413
+ // await this.#attemptReconnection()
5429
5414
  // }
5430
5415
  // Publish connection status
5431
5416
  globalThis.pubsub?.publish('connection-status', {
@@ -5434,59 +5419,74 @@ class ConnectionMonitor {
5434
5419
  healthy: compatiblePeers.length > 0
5435
5420
  });
5436
5421
  }
5437
- // helper: try to close/destroy a single peer connection and remove it from peernet's map
5438
- async #cleanupPeerState(peerId, peernet) {
5439
- try {
5440
- const conns = peernet.connections || {};
5441
- const conn = conns[peerId] || conns[Object.keys(conns).find((k) => k.includes(peerId) || peerId.includes(k))];
5442
- if (!conn)
5443
- return;
5444
- // close underlying RTCPeerConnection if exposed
5445
- try {
5446
- if (conn.pc && typeof conn.pc.close === 'function') {
5447
- conn.pc.close();
5448
- }
5422
+ // lightweight TCP probe to detect internet connectivity in Node.js
5423
+ async #isOnLine(timeout = 1500) {
5424
+ // If not running in Node, fallback to navigator.onLine if available, otherwise assume online
5425
+ if (typeof process === 'undefined') {
5426
+ if (navigator?.onLine !== undefined) {
5427
+ return navigator.onLine;
5449
5428
  }
5450
- catch (e) {
5451
- // ignore
5452
- }
5453
- // call any destroy/cleanup API on the connection object
5429
+ return true;
5430
+ }
5431
+ return new Promise(async (resolve) => {
5454
5432
  try {
5455
- if (typeof conn.destroy === 'function') {
5456
- conn.destroy();
5457
- }
5458
- else if (typeof conn.close === 'function') {
5459
- conn.close();
5460
- }
5433
+ // lazy require so bundlers / browser builds don't break
5434
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
5435
+ const net = await import('net');
5436
+ const socket = new net.Socket();
5437
+ let finished = false;
5438
+ const finish = (val) => {
5439
+ if (finished)
5440
+ return;
5441
+ finished = true;
5442
+ try {
5443
+ socket.destroy();
5444
+ }
5445
+ catch (e) {
5446
+ // ignore
5447
+ }
5448
+ resolve(val);
5449
+ };
5450
+ socket.setTimeout(timeout);
5451
+ socket.once('connect', () => finish(true));
5452
+ socket.once('error', () => finish(false));
5453
+ socket.once('timeout', () => finish(false));
5454
+ // connect to Cloudflare DNS (1.1.1.1) on TCP/53 — fast and reliable
5455
+ socket.connect(53, '1.1.1.1');
5461
5456
  }
5462
5457
  catch (e) {
5463
- // ignore
5464
- }
5465
- // remove reference so reconnect path will create a fresh one
5466
- try {
5467
- delete peernet.connections[peerId];
5458
+ resolve(false);
5468
5459
  }
5469
- catch (e) {
5470
- // ignore
5460
+ });
5461
+ }
5462
+ // Called on visibility/online/resume events
5463
+ async #restoreNetwork() {
5464
+ console.log('🔁 Restoring network');
5465
+ try {
5466
+ const online = await this.#isOnLine(1500);
5467
+ if (!online) {
5468
+ console.warn('⚠️ No internet detected, skipping restore');
5469
+ return;
5471
5470
  }
5472
- // small pause to let underlying sockets/RTCs settle
5473
- await new Promise((r) => setTimeout(r, 100));
5474
5471
  }
5475
5472
  catch (e) {
5476
- // ignore cleanup errors
5473
+ // If the probe failed for any reason, continue with restore attempt
5474
+ console.warn('⚠️ Online probe failed, proceeding with restore', e?.message || e);
5475
+ }
5476
+ try {
5477
+ // prefer safe client reinit if available
5478
+ console.log('🔄 Attempting to reinitialize peernet client');
5479
+ await globalThis.peernet.client.reinit();
5480
+ }
5481
+ catch (e) {
5482
+ console.warn('⚠️ peernet.client.reinit failed, falling back to peernet.start if available', e?.message || e);
5483
+ try {
5484
+ await globalThis.peernet.start();
5485
+ }
5486
+ catch (err) {
5487
+ console.error('❌ peernet.start also failed during restore', err?.message || err);
5488
+ }
5477
5489
  }
5478
- }
5479
- // Called on visibility/online/resume events
5480
- async #restoreNetwork() {
5481
- console.log('🔁 Restoring network after resume/wake');
5482
- globalThis.peernet.client.reinit();
5483
- // If there is a peernet instance, try a safe restore
5484
- // if (globalThis.peernet) {
5485
- // await this.#performFullRestart(globalThis.peernet)
5486
- // } else {
5487
- // // If no global peernet, attempt a normal reconnection flow
5488
- // await this.#attemptReconnection()
5489
- // }
5490
5490
  }
5491
5491
  async waitForPeers(timeoutMs = 30000) {
5492
5492
  return new Promise((resolve) => {
@@ -5505,9 +5505,23 @@ class ConnectionMonitor {
5505
5505
  checkPeers();
5506
5506
  });
5507
5507
  }
5508
- // New: attempt reconnection flow (gentle start + sequential per-peer reconnect)
5509
5508
  async #attemptReconnection() {
5510
- console.warn('⚠️ Attempting to reconnect to peers...');
5509
+ try {
5510
+ await this.#restoreNetwork();
5511
+ }
5512
+ catch (error) {
5513
+ console.error('❌ Reconnection failed:', error?.message || error);
5514
+ if (this.#reconnectDelay >= 30000) {
5515
+ console.warn('⚠️ Reconnection delay reached maximum, resetting to 5 seconds');
5516
+ this.#reconnectDelay = 5000;
5517
+ }
5518
+ else {
5519
+ // exponential-ish backoff
5520
+ this.#reconnectDelay = Math.min(this.#reconnectDelay * 1.5, 30000);
5521
+ console.warn(`⚠️ Increasing reconnection delay to ${this.#reconnectDelay} ms`);
5522
+ }
5523
+ setTimeout(() => this.#attemptReconnection(), this.#reconnectDelay);
5524
+ }
5511
5525
  }
5512
5526
  }
5513
5527
 
package/exports/chain.js CHANGED
@@ -1551,27 +1551,12 @@ class ConnectionMonitor {
1551
1551
  const connectedPeers = this.connectedPeers;
1552
1552
  const compatiblePeers = this.compatiblePeers;
1553
1553
  console.log(`🔍 Health check: ${connectedPeers.length} connected, ${compatiblePeers.length} compatible`);
1554
- if (connectedPeers.length === 0) {
1555
- console.warn('⚠️ No peer connections detected');
1556
- await this.#attemptReconnection();
1557
- }
1558
- else if (compatiblePeers.length === 0) {
1559
- console.warn('⚠️ No compatible peers found');
1560
- await this.#attemptReconnection();
1561
- // Could attempt to find compatible peers or trigger version negotiation
1562
- }
1563
- // Log disconnected peers
1564
- // const disconnectedPeers = this.disconnectedPeers
1565
- // if (disconnectedPeers.length > 0) {
1566
- // console.warn(`⚠️ Disconnected peers: ${disconnectedPeers.map((peer) => peer.peerId).join(', ')}`)
1567
- // // Attempt to reconnect each disconnected peer sequentially to avoid racing signaling/state
1568
- // for (const peer of disconnectedPeers) {
1569
- // // small spacing between attempts to reduce signaling races
1570
- // // eslint-disable-next-line no-await-in-loop
1571
- // await new Promise((r) => setTimeout(r, 150))
1572
- // // eslint-disable-next-line no-await-in-loop
1573
- // await this.#attemptPeerReconnection(peer)
1574
- // }
1554
+ // if (connectedPeers.length === 0) {
1555
+ console.warn('⚠️ No peer connections detected');
1556
+ // await this.#attemptReconnection()
1557
+ // } else if (compatiblePeers.length === 0) {
1558
+ console.warn('⚠️ No compatible peers found');
1559
+ // await this.#attemptReconnection()
1575
1560
  // }
1576
1561
  // Publish connection status
1577
1562
  globalThis.pubsub?.publish('connection-status', {
@@ -1580,59 +1565,74 @@ class ConnectionMonitor {
1580
1565
  healthy: compatiblePeers.length > 0
1581
1566
  });
1582
1567
  }
1583
- // helper: try to close/destroy a single peer connection and remove it from peernet's map
1584
- async #cleanupPeerState(peerId, peernet) {
1585
- try {
1586
- const conns = peernet.connections || {};
1587
- const conn = conns[peerId] || conns[Object.keys(conns).find((k) => k.includes(peerId) || peerId.includes(k))];
1588
- if (!conn)
1589
- return;
1590
- // close underlying RTCPeerConnection if exposed
1591
- try {
1592
- if (conn.pc && typeof conn.pc.close === 'function') {
1593
- conn.pc.close();
1594
- }
1595
- }
1596
- catch (e) {
1597
- // ignore
1568
+ // lightweight TCP probe to detect internet connectivity in Node.js
1569
+ async #isOnLine(timeout = 1500) {
1570
+ // If not running in Node, fallback to navigator.onLine if available, otherwise assume online
1571
+ if (typeof process === 'undefined') {
1572
+ if (navigator?.onLine !== undefined) {
1573
+ return navigator.onLine;
1598
1574
  }
1599
- // call any destroy/cleanup API on the connection object
1575
+ return true;
1576
+ }
1577
+ return new Promise(async (resolve) => {
1600
1578
  try {
1601
- if (typeof conn.destroy === 'function') {
1602
- conn.destroy();
1603
- }
1604
- else if (typeof conn.close === 'function') {
1605
- conn.close();
1606
- }
1579
+ // lazy require so bundlers / browser builds don't break
1580
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
1581
+ const net = await import('net');
1582
+ const socket = new net.Socket();
1583
+ let finished = false;
1584
+ const finish = (val) => {
1585
+ if (finished)
1586
+ return;
1587
+ finished = true;
1588
+ try {
1589
+ socket.destroy();
1590
+ }
1591
+ catch (e) {
1592
+ // ignore
1593
+ }
1594
+ resolve(val);
1595
+ };
1596
+ socket.setTimeout(timeout);
1597
+ socket.once('connect', () => finish(true));
1598
+ socket.once('error', () => finish(false));
1599
+ socket.once('timeout', () => finish(false));
1600
+ // connect to Cloudflare DNS (1.1.1.1) on TCP/53 — fast and reliable
1601
+ socket.connect(53, '1.1.1.1');
1607
1602
  }
1608
1603
  catch (e) {
1609
- // ignore
1610
- }
1611
- // remove reference so reconnect path will create a fresh one
1612
- try {
1613
- delete peernet.connections[peerId];
1604
+ resolve(false);
1614
1605
  }
1615
- catch (e) {
1616
- // ignore
1606
+ });
1607
+ }
1608
+ // Called on visibility/online/resume events
1609
+ async #restoreNetwork() {
1610
+ console.log('🔁 Restoring network');
1611
+ try {
1612
+ const online = await this.#isOnLine(1500);
1613
+ if (!online) {
1614
+ console.warn('⚠️ No internet detected, skipping restore');
1615
+ return;
1617
1616
  }
1618
- // small pause to let underlying sockets/RTCs settle
1619
- await new Promise((r) => setTimeout(r, 100));
1620
1617
  }
1621
1618
  catch (e) {
1622
- // ignore cleanup errors
1619
+ // If the probe failed for any reason, continue with restore attempt
1620
+ console.warn('⚠️ Online probe failed, proceeding with restore', e?.message || e);
1621
+ }
1622
+ try {
1623
+ // prefer safe client reinit if available
1624
+ console.log('🔄 Attempting to reinitialize peernet client');
1625
+ await globalThis.peernet.client.reinit();
1626
+ }
1627
+ catch (e) {
1628
+ console.warn('⚠️ peernet.client.reinit failed, falling back to peernet.start if available', e?.message || e);
1629
+ try {
1630
+ await globalThis.peernet.start();
1631
+ }
1632
+ catch (err) {
1633
+ console.error('❌ peernet.start also failed during restore', err?.message || err);
1634
+ }
1623
1635
  }
1624
- }
1625
- // Called on visibility/online/resume events
1626
- async #restoreNetwork() {
1627
- console.log('🔁 Restoring network after resume/wake');
1628
- globalThis.peernet.client.reinit();
1629
- // If there is a peernet instance, try a safe restore
1630
- // if (globalThis.peernet) {
1631
- // await this.#performFullRestart(globalThis.peernet)
1632
- // } else {
1633
- // // If no global peernet, attempt a normal reconnection flow
1634
- // await this.#attemptReconnection()
1635
- // }
1636
1636
  }
1637
1637
  async waitForPeers(timeoutMs = 30000) {
1638
1638
  return new Promise((resolve) => {
@@ -1651,9 +1651,23 @@ class ConnectionMonitor {
1651
1651
  checkPeers();
1652
1652
  });
1653
1653
  }
1654
- // New: attempt reconnection flow (gentle start + sequential per-peer reconnect)
1655
1654
  async #attemptReconnection() {
1656
- console.warn('⚠️ Attempting to reconnect to peers...');
1655
+ try {
1656
+ await this.#restoreNetwork();
1657
+ }
1658
+ catch (error) {
1659
+ console.error('❌ Reconnection failed:', error?.message || error);
1660
+ if (this.#reconnectDelay >= 30000) {
1661
+ console.warn('⚠️ Reconnection delay reached maximum, resetting to 5 seconds');
1662
+ this.#reconnectDelay = 5000;
1663
+ }
1664
+ else {
1665
+ // exponential-ish backoff
1666
+ this.#reconnectDelay = Math.min(this.#reconnectDelay * 1.5, 30000);
1667
+ console.warn(`⚠️ Increasing reconnection delay to ${this.#reconnectDelay} ms`);
1668
+ }
1669
+ setTimeout(() => this.#attemptReconnection(), this.#reconnectDelay);
1670
+ }
1657
1671
  }
1658
1672
  }
1659
1673
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leofcoin/chain",
3
- "version": "1.7.104",
3
+ "version": "1.7.106",
4
4
  "description": "Official javascript implementation",
5
5
  "private": false,
6
6
  "exports": {