@leofcoin/chain 1.7.104 → 1.7.105

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