@olane/o-node 0.7.13-alpha.0 → 0.7.13-alpha.2

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.
Files changed (60) hide show
  1. package/dist/src/connection/interfaces/o-node-connection-manager.config.d.ts +1 -0
  2. package/dist/src/connection/interfaces/o-node-connection-manager.config.d.ts.map +1 -1
  3. package/dist/src/connection/o-node-connection.d.ts +0 -1
  4. package/dist/src/connection/o-node-connection.d.ts.map +1 -1
  5. package/dist/src/connection/o-node-connection.js +0 -8
  6. package/dist/src/connection/o-node-connection.manager.d.ts +41 -4
  7. package/dist/src/connection/o-node-connection.manager.d.ts.map +1 -1
  8. package/dist/src/connection/o-node-connection.manager.js +187 -45
  9. package/dist/src/connection/stream-handler.d.ts.map +1 -1
  10. package/dist/src/connection/stream-handler.js +0 -2
  11. package/dist/src/managers/o-connection-heartbeat.manager.d.ts.map +1 -1
  12. package/dist/src/managers/o-connection-heartbeat.manager.js +15 -1
  13. package/dist/src/managers/o-reconnection.manager.d.ts.map +1 -1
  14. package/dist/src/managers/o-reconnection.manager.js +12 -7
  15. package/dist/src/o-node.d.ts +19 -0
  16. package/dist/src/o-node.d.ts.map +1 -1
  17. package/dist/src/o-node.js +89 -11
  18. package/dist/src/o-node.tool.d.ts.map +1 -1
  19. package/dist/src/o-node.tool.js +5 -0
  20. package/dist/src/router/o-node.router.d.ts.map +1 -1
  21. package/dist/src/router/o-node.router.js +16 -6
  22. package/dist/src/router/o-node.routing-policy.d.ts.map +1 -1
  23. package/dist/src/router/o-node.routing-policy.js +4 -0
  24. package/dist/test/connection-management.spec.d.ts +2 -0
  25. package/dist/test/connection-management.spec.d.ts.map +1 -0
  26. package/dist/test/connection-management.spec.js +370 -0
  27. package/dist/test/helpers/connection-spy.d.ts +124 -0
  28. package/dist/test/helpers/connection-spy.d.ts.map +1 -0
  29. package/dist/test/helpers/connection-spy.js +229 -0
  30. package/dist/test/helpers/index.d.ts +6 -0
  31. package/dist/test/helpers/index.d.ts.map +1 -0
  32. package/dist/test/helpers/index.js +12 -0
  33. package/dist/test/helpers/network-builder.d.ts +109 -0
  34. package/dist/test/helpers/network-builder.d.ts.map +1 -0
  35. package/dist/test/helpers/network-builder.js +309 -0
  36. package/dist/test/helpers/simple-node-builder.d.ts +50 -0
  37. package/dist/test/helpers/simple-node-builder.d.ts.map +1 -0
  38. package/dist/test/helpers/simple-node-builder.js +66 -0
  39. package/dist/test/helpers/test-environment.d.ts +140 -0
  40. package/dist/test/helpers/test-environment.d.ts.map +1 -0
  41. package/dist/test/helpers/test-environment.js +184 -0
  42. package/dist/test/helpers/test-node.tool.d.ts +31 -0
  43. package/dist/test/helpers/test-node.tool.d.ts.map +1 -1
  44. package/dist/test/helpers/test-node.tool.js +49 -0
  45. package/dist/test/leader-transport-validation.spec.d.ts +2 -0
  46. package/dist/test/leader-transport-validation.spec.d.ts.map +1 -0
  47. package/dist/test/leader-transport-validation.spec.js +177 -0
  48. package/dist/test/network-communication.spec.d.ts +2 -0
  49. package/dist/test/network-communication.spec.d.ts.map +1 -0
  50. package/dist/test/network-communication.spec.js +256 -0
  51. package/dist/test/o-node.spec.d.ts +2 -0
  52. package/dist/test/o-node.spec.d.ts.map +1 -0
  53. package/dist/test/o-node.spec.js +247 -0
  54. package/dist/test/parent-child-registration.spec.d.ts +2 -0
  55. package/dist/test/parent-child-registration.spec.d.ts.map +1 -0
  56. package/dist/test/parent-child-registration.spec.js +177 -0
  57. package/dist/test/search-resolver.spec.d.ts +2 -0
  58. package/dist/test/search-resolver.spec.d.ts.map +1 -0
  59. package/dist/test/search-resolver.spec.js +648 -0
  60. package/package.json +12 -7
@@ -3,5 +3,6 @@ import { Libp2p } from '@olane/o-config';
3
3
  export interface oNodeConnectionManagerConfig extends oConnectionManagerConfig {
4
4
  p2pNode: Libp2p;
5
5
  runOnLimitedConnection?: boolean;
6
+ originAddress?: string;
6
7
  }
7
8
  //# sourceMappingURL=o-node-connection-manager.config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"o-node-connection-manager.config.d.ts","sourceRoot":"","sources":["../../../../src/connection/interfaces/o-node-connection-manager.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,MAAM,WAAW,4BAA6B,SAAQ,wBAAwB;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC"}
1
+ {"version":3,"file":"o-node-connection-manager.config.d.ts","sourceRoot":"","sources":["../../../../src/connection/interfaces/o-node-connection-manager.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,MAAM,WAAW,4BAA6B,SAAQ,wBAAwB;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB"}
@@ -7,7 +7,6 @@ export declare class oNodeConnection extends oConnection {
7
7
  p2pConnection: Connection;
8
8
  protected streamHandler: StreamHandler;
9
9
  constructor(config: oNodeConnectionConfig);
10
- setupConnectionListeners(): void;
11
10
  validate(stream?: Stream): void;
12
11
  getOrCreateStream(): Promise<Stream>;
13
12
  transmit(request: oRequest): Promise<oResponse>;
@@ -1 +1 @@
1
- {"version":3,"file":"o-node-connection.d.ts","sourceRoot":"","sources":["../../../src/connection/o-node-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAEL,WAAW,EAGX,QAAQ,EACR,SAAS,EACV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD,qBAAa,eAAgB,SAAQ,WAAW;IAIlC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,qBAAqB;IAHrD,aAAa,EAAE,UAAU,CAAC;IACjC,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;gBAER,MAAM,EAAE,qBAAqB;IAO5D,wBAAwB;IAYxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM;IAmBlB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IAkBpC,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IA0C/C,YAAY,CAAC,MAAM,EAAE,MAAM;IAQ3B,KAAK,CAAC,KAAK,EAAE,KAAK;IAMlB,KAAK;CAKZ"}
1
+ {"version":3,"file":"o-node-connection.d.ts","sourceRoot":"","sources":["../../../src/connection/o-node-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAEL,WAAW,EAGX,QAAQ,EACR,SAAS,EACV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD,qBAAa,eAAgB,SAAQ,WAAW;IAIlC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,qBAAqB;IAHrD,aAAa,EAAE,UAAU,CAAC;IACjC,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;gBAER,MAAM,EAAE,qBAAqB;IAM5D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM;IAmBlB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IAkBpC,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IA0C/C,YAAY,CAAC,MAAM,EAAE,MAAM;IAQ3B,KAAK,CAAC,KAAK,EAAE,KAAK;IAMlB,KAAK;CAKZ"}
@@ -6,14 +6,6 @@ export class oNodeConnection extends oConnection {
6
6
  this.config = config;
7
7
  this.p2pConnection = config.p2pConnection;
8
8
  this.streamHandler = new StreamHandler(this.logger);
9
- this.setupConnectionListeners();
10
- }
11
- setupConnectionListeners() {
12
- this.logger.debug('Setting up connection listeners for address: ' +
13
- this.nextHopAddress.toString());
14
- this.p2pConnection?.addEventListener('close', () => {
15
- this.logger.debug('Connection closed for address: ' + this.nextHopAddress.toString());
16
- });
17
9
  }
18
10
  validate(stream) {
19
11
  if (this.config.p2pConnection.status !== 'open') {
@@ -7,9 +7,29 @@ export declare class oNodeConnectionManager extends oConnectionManager {
7
7
  protected p2pNode: Libp2p;
8
8
  private defaultReadTimeoutMs?;
9
9
  private defaultDrainTimeoutMs?;
10
- private connections;
10
+ private connectionsByTransportKey;
11
+ private pendingDialsByTransportKey;
11
12
  constructor(config: oNodeConnectionManagerConfig);
13
+ /**
14
+ * Set up listeners to maintain connection cache state
15
+ */
16
+ private setupConnectionListeners;
17
+ /**
18
+ * Build a stable cache key from the libp2p transports on an address.
19
+ *
20
+ * We intentionally key the cache by transports (multiaddrs) instead of peer IDs
21
+ * to avoid ambiguity when multiple peers may share a peer ID or when addresses
22
+ * change but the libp2p transports are the true dial targets.
23
+ */
24
+ private getTransportKeyFromAddress;
25
+ /**
26
+ * Extract peer ID string from an address
27
+ * @param address - The address to extract peer ID from
28
+ * @returns The peer ID string or null if not found
29
+ */
30
+ private getPeerIdFromAddress;
12
31
  getOrCreateConnection(nextHopAddress: oAddress, address: oAddress): Promise<Connection>;
32
+ private performDial;
13
33
  /**
14
34
  * Connect to a given address, reusing libp2p connections when possible
15
35
  * @param config - Connection configuration
@@ -17,16 +37,33 @@ export declare class oNodeConnectionManager extends oConnectionManager {
17
37
  */
18
38
  connect(config: oConnectionConfig): Promise<oNodeConnection>;
19
39
  /**
20
- * Check if libp2p has an active connection to the target peer
40
+ * Check if we have an active connection to the target peer
21
41
  * @param address - The address to check
22
42
  * @returns true if an active connection exists
23
43
  */
24
44
  isCached(address: oAddress): boolean;
25
45
  /**
26
- * Get an existing libp2p connection to the target peer
46
+ * Get an existing connection to the target peer (from cache or libp2p)
27
47
  * @param address - The address to get a connection for
28
- * @returns The libp2p Connection object or null if not found
48
+ * @returns The Connection object or null if not found
29
49
  */
30
50
  getCachedLibp2pConnection(address: oAddress): Connection | null;
51
+ /**
52
+ * Get cache statistics for monitoring and debugging
53
+ * @returns Object containing cache statistics
54
+ */
55
+ getCacheStats(): {
56
+ cachedConnections: number;
57
+ pendingDials: number;
58
+ connectionsByPeer: Array<{
59
+ peerId: string;
60
+ status: string;
61
+ }>;
62
+ };
63
+ /**
64
+ * Clean up all stale (non-open) connections from cache
65
+ * @returns Number of connections removed
66
+ */
67
+ cleanupStaleConnections(): number;
31
68
  }
32
69
  //# sourceMappingURL=o-node-connection.manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"o-node-connection.manager.d.ts","sourceRoot":"","sources":["../../../src/connection/o-node-connection.manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,MAAM,EAAE,UAAU,EAAU,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,4BAA4B,EAAE,MAAM,kDAAkD,CAAC;AAEhG,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,qBAAa,sBAAuB,SAAQ,kBAAkB;IAMhD,QAAQ,CAAC,MAAM,EAAE,4BAA4B;IALzD,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,oBAAoB,CAAC,CAAS;IACtC,OAAO,CAAC,qBAAqB,CAAC,CAAS;IACvC,OAAO,CAAC,WAAW,CAAsC;gBAEpC,MAAM,EAAE,4BAA4B;IAOnD,qBAAqB,CACzB,cAAc,EAAE,QAAQ,EACxB,OAAO,EAAE,QAAQ,GAChB,OAAO,CAAC,UAAU,CAAC;IAoCtB;;;;OAIG;IACG,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,eAAe,CAAC;IA8BlE;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO;IA4BpC;;;;OAIG;IACH,yBAAyB,CAAC,OAAO,EAAE,QAAQ,GAAG,UAAU,GAAG,IAAI;CAmChE"}
1
+ {"version":3,"file":"o-node-connection.manager.d.ts","sourceRoot":"","sources":["../../../src/connection/o-node-connection.manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,MAAM,EAAE,UAAU,EAAU,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,4BAA4B,EAAE,MAAM,kDAAkD,CAAC;AAEhG,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,qBAAa,sBAAuB,SAAQ,kBAAkB;IAOhD,QAAQ,CAAC,MAAM,EAAE,4BAA4B;IANzD,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,oBAAoB,CAAC,CAAS;IACtC,OAAO,CAAC,qBAAqB,CAAC,CAAS;IACvC,OAAO,CAAC,yBAAyB,CAAsC;IACvE,OAAO,CAAC,0BAA0B,CAA+C;gBAE5D,MAAM,EAAE,4BAA4B;IAWzD;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAmBhC;;;;;;OAMG;IACH,OAAO,CAAC,0BAA0B;IAiBlC;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IAatB,qBAAqB,CACzB,cAAc,EAAE,QAAQ,EACxB,OAAO,EAAE,QAAQ,GAChB,OAAO,CAAC,UAAU,CAAC;YAsDR,WAAW;IAwBzB;;;;OAIG;IACG,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,eAAe,CAAC;IA8BlE;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO;IAoCpC;;;;OAIG;IACH,yBAAyB,CAAC,OAAO,EAAE,QAAQ,GAAG,UAAU,GAAG,IAAI;IA+C/D;;;OAGG;IACH,aAAa,IAAI;QACf,iBAAiB,EAAE,MAAM,CAAC;QAC1B,YAAY,EAAE,MAAM,CAAC;QACrB,iBAAiB,EAAE,KAAK,CAAC;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAC9D;IAaD;;;OAGG;IACH,uBAAuB,IAAI,MAAM;CAalC"}
@@ -4,32 +4,128 @@ export class oNodeConnectionManager extends oConnectionManager {
4
4
  constructor(config) {
5
5
  super(config);
6
6
  this.config = config;
7
- this.connections = new Map();
7
+ this.connectionsByTransportKey = new Map();
8
+ this.pendingDialsByTransportKey = new Map();
8
9
  this.p2pNode = config.p2pNode;
9
10
  this.defaultReadTimeoutMs = config.defaultReadTimeoutMs;
10
11
  this.defaultDrainTimeoutMs = config.defaultDrainTimeoutMs;
12
+ this.logger.setNamespace(`oNodeConnectionManager[${config.originAddress}]`);
13
+ // Set up connection lifecycle listeners for cache management
14
+ this.setupConnectionListeners();
11
15
  }
12
- async getOrCreateConnection(nextHopAddress, address) {
13
- // Check if libp2p already has an active connection to this peer
14
- const existingConnection = this.getCachedLibp2pConnection(nextHopAddress);
15
- let p2pConnection;
16
- if (existingConnection && existingConnection.status === 'open') {
17
- this.logger.debug('Reusing existing libp2p connection for address: ' +
18
- nextHopAddress.toString());
19
- p2pConnection = existingConnection;
20
- }
21
- else {
22
- // No existing connection or connection is closed, dial a new one
23
- this.logger.debug('Dialing new connection for address: ' + address.toString());
24
- p2pConnection = await this.p2pNode.dial(nextHopAddress.libp2pTransports.map((ma) => ma.toMultiaddr()));
25
- }
26
- if (nextHopAddress.libp2pTransports.length) {
27
- const peerIdString = nextHopAddress.libp2pTransports[0].toPeerId();
28
- if (peerIdString) {
29
- this.connections.set(peerIdString, p2pConnection);
16
+ /**
17
+ * Set up listeners to maintain connection cache state
18
+ */
19
+ setupConnectionListeners() {
20
+ this.p2pNode.addEventListener('connection:close', (event) => {
21
+ const connection = event.detail;
22
+ if (!connection) {
23
+ return;
24
+ }
25
+ for (const [transportKey, cachedConnection] of this.connectionsByTransportKey.entries()) {
26
+ if (cachedConnection === connection) {
27
+ this.logger.debug('Connection closed, removing from cache for transport key:', transportKey);
28
+ this.connectionsByTransportKey.delete(transportKey);
29
+ }
30
+ }
31
+ });
32
+ }
33
+ /**
34
+ * Build a stable cache key from the libp2p transports on an address.
35
+ *
36
+ * We intentionally key the cache by transports (multiaddrs) instead of peer IDs
37
+ * to avoid ambiguity when multiple peers may share a peer ID or when addresses
38
+ * change but the libp2p transports are the true dial targets.
39
+ */
40
+ getTransportKeyFromAddress(address) {
41
+ try {
42
+ const nodeAddress = address;
43
+ const transports = nodeAddress.libp2pTransports;
44
+ if (!transports?.length) {
45
+ return null;
30
46
  }
47
+ // Sort to ensure deterministic keys regardless of transport ordering.
48
+ const parts = transports.map((t) => t.toString()).sort();
49
+ return parts.join('|');
50
+ }
51
+ catch (error) {
52
+ this.logger.debug('Error extracting transport key from address:', error);
53
+ return null;
31
54
  }
32
- return p2pConnection;
55
+ }
56
+ /**
57
+ * Extract peer ID string from an address
58
+ * @param address - The address to extract peer ID from
59
+ * @returns The peer ID string or null if not found
60
+ */
61
+ getPeerIdFromAddress(address) {
62
+ try {
63
+ const nodeAddress = address;
64
+ if (!nodeAddress.libp2pTransports?.length) {
65
+ return null;
66
+ }
67
+ return nodeAddress.libp2pTransports[0].toPeerId() || null;
68
+ }
69
+ catch (error) {
70
+ this.logger.debug('Error extracting peer ID from address:', error);
71
+ return null;
72
+ }
73
+ }
74
+ async getOrCreateConnection(nextHopAddress, address) {
75
+ // Build a transport-based cache key from the next hop address
76
+ const transportKey = this.getTransportKeyFromAddress(nextHopAddress);
77
+ if (!transportKey) {
78
+ throw new Error(`Unable to extract libp2p transports from address: ${nextHopAddress.toString()}`);
79
+ }
80
+ // Check if we have a cached connection by transport key
81
+ const cachedConnection = this.connectionsByTransportKey.get(transportKey);
82
+ if (cachedConnection && cachedConnection.status === 'open') {
83
+ this.logger.debug('Reusing cached connection for transports:', transportKey);
84
+ return cachedConnection;
85
+ }
86
+ // Clean up stale connection if it exists but is not open
87
+ if (cachedConnection && cachedConnection.status !== 'open') {
88
+ this.logger.debug('Removing stale connection for transports:', transportKey);
89
+ this.connectionsByTransportKey.delete(transportKey);
90
+ }
91
+ // Check if libp2p has an active connection for this address
92
+ const libp2pConnection = this.getCachedLibp2pConnection(nextHopAddress);
93
+ if (libp2pConnection && libp2pConnection.status === 'open') {
94
+ this.logger.debug('Caching existing libp2p connection for transports:', transportKey);
95
+ this.connectionsByTransportKey.set(transportKey, libp2pConnection);
96
+ return libp2pConnection;
97
+ }
98
+ // Check if dial is already in progress for this transport key
99
+ const pendingDial = this.pendingDialsByTransportKey.get(transportKey);
100
+ if (pendingDial) {
101
+ this.logger.debug('Awaiting existing dial for transports:', transportKey);
102
+ return pendingDial;
103
+ }
104
+ // Start new dial and cache the promise by transport key
105
+ const dialPromise = this.performDial(nextHopAddress, transportKey);
106
+ this.pendingDialsByTransportKey.set(transportKey, dialPromise);
107
+ try {
108
+ const connection = await dialPromise;
109
+ // Cache the established connection by transport key
110
+ this.connectionsByTransportKey.set(transportKey, connection);
111
+ return connection;
112
+ }
113
+ finally {
114
+ this.pendingDialsByTransportKey.delete(transportKey);
115
+ }
116
+ }
117
+ async performDial(nextHopAddress, transportKey) {
118
+ this.logger.debug('Dialing new connection', {
119
+ address: nextHopAddress.value,
120
+ transportKey,
121
+ });
122
+ const connection = await this.p2pNode.dial(nextHopAddress.libp2pTransports.map((ma) => ma.toMultiaddr()));
123
+ this.logger.debug('Successfully dialed connection', {
124
+ transportKey,
125
+ status: connection.status,
126
+ remotePeer: connection.remotePeer?.toString(),
127
+ });
128
+ return connection;
33
129
  }
34
130
  /**
35
131
  * Connect to a given address, reusing libp2p connections when possible
@@ -54,27 +150,35 @@ export class oNodeConnectionManager extends oConnectionManager {
54
150
  return connection;
55
151
  }
56
152
  /**
57
- * Check if libp2p has an active connection to the target peer
153
+ * Check if we have an active connection to the target peer
58
154
  * @param address - The address to check
59
155
  * @returns true if an active connection exists
60
156
  */
61
157
  isCached(address) {
62
158
  try {
63
- const nodeAddress = address;
64
- if (!nodeAddress.libp2pTransports ||
65
- nodeAddress.libp2pTransports.length === 0) {
159
+ const transportKey = this.getTransportKeyFromAddress(address);
160
+ if (!transportKey) {
66
161
  return false;
67
162
  }
68
- // Extract peer ID from the first transport
69
- const peerIdString = nodeAddress.libp2pTransports[0].toPeerId();
70
- if (!peerIdString) {
71
- return false;
163
+ // Check our transport-based cache first
164
+ const cachedConnection = this.connectionsByTransportKey.get(transportKey);
165
+ if (cachedConnection?.status === 'open') {
166
+ return true;
72
167
  }
73
- this.logger.debug('Peer ID string:', peerIdString);
168
+ // Fall back to checking libp2p's connections
169
+ const peerId = this.getPeerIdFromAddress(address);
74
170
  // the following works since the peer id param is not really required: https://github.com/libp2p/js-libp2p/blob/0bbf5021b53938b2bffcffca6c13c479a95c2a60/packages/libp2p/src/connection-manager/index.ts#L508
75
- const connections = this.p2pNode.getConnections(peerIdString); // ignore since converting to a proper peer id breaks the browser implementation
171
+ const connections = this.p2pNode.getConnections(peerId); // ignore since converting to a proper peer id breaks the browser implementation
76
172
  // Check if we have at least one open connection
77
- return connections.some((conn) => conn.status === 'open');
173
+ const hasOpenConnection = connections.some((conn) => conn.status === 'open');
174
+ // If libp2p has an open connection, update our cache
175
+ if (hasOpenConnection) {
176
+ const openConnection = connections.find((conn) => conn.status === 'open');
177
+ if (openConnection) {
178
+ this.connectionsByTransportKey.set(transportKey, openConnection);
179
+ }
180
+ }
181
+ return hasOpenConnection;
78
182
  }
79
183
  catch (error) {
80
184
  this.logger.debug('Error checking cached connection:', error);
@@ -82,37 +186,75 @@ export class oNodeConnectionManager extends oConnectionManager {
82
186
  }
83
187
  }
84
188
  /**
85
- * Get an existing libp2p connection to the target peer
189
+ * Get an existing connection to the target peer (from cache or libp2p)
86
190
  * @param address - The address to get a connection for
87
- * @returns The libp2p Connection object or null if not found
191
+ * @returns The Connection object or null if not found
88
192
  */
89
193
  getCachedLibp2pConnection(address) {
90
194
  try {
91
- const nodeAddress = address;
92
- if (!nodeAddress.libp2pTransports.length) {
195
+ const transportKey = this.getTransportKeyFromAddress(address);
196
+ if (!transportKey) {
93
197
  return null;
94
198
  }
95
- const peerIdString = nodeAddress.libp2pTransports[0].toPeerId();
96
- if (!peerIdString) {
199
+ // Check transport-based cache first
200
+ const cachedConnection = this.connectionsByTransportKey.get(transportKey);
201
+ if (cachedConnection?.status === 'open') {
202
+ return cachedConnection;
203
+ }
204
+ const peerId = this.getPeerIdFromAddress(address);
205
+ if (!peerId) {
97
206
  return null;
98
207
  }
99
- const connections = this.p2pNode.getConnections(); // ignore since converting to a proper peer id breaks the browser implementation
100
- const filteredConnections = connections.filter((conn) => conn.remotePeer?.toString() === peerIdString);
101
- // Return the first open connection, or null if none exist
208
+ // Query libp2p for connections to this peer
209
+ const connections = this.p2pNode.getConnections();
210
+ const filteredConnections = connections.filter((conn) => conn.remotePeer?.toString() === peerId);
211
+ // Find the first open connection
102
212
  const openConnection = filteredConnections.find((conn) => conn.status === 'open');
213
+ // If we found an open connection in libp2p, cache it by transport key
103
214
  if (openConnection) {
215
+ this.connectionsByTransportKey.set(transportKey, openConnection);
104
216
  return openConnection;
105
217
  }
106
- const connection = this.connections.get(peerIdString);
107
- if (connection?.status === 'open') {
108
- return connection;
218
+ // Clean up stale cache entry if connection is no longer open
219
+ if (cachedConnection) {
220
+ this.connectionsByTransportKey.delete(transportKey);
109
221
  }
110
- // Return the first open connection, or null if none exist
111
- return connection ?? null;
222
+ return null;
112
223
  }
113
224
  catch (error) {
114
225
  this.logger.debug('Error getting cached connection:', error);
115
226
  return null;
116
227
  }
117
228
  }
229
+ /**
230
+ * Get cache statistics for monitoring and debugging
231
+ * @returns Object containing cache statistics
232
+ */
233
+ getCacheStats() {
234
+ return {
235
+ cachedConnections: this.connectionsByTransportKey.size,
236
+ pendingDials: this.pendingDialsByTransportKey.size,
237
+ connectionsByPeer: Array.from(this.connectionsByTransportKey.values()).map((conn) => ({
238
+ peerId: conn.remotePeer?.toString() ?? 'unknown',
239
+ status: conn.status,
240
+ })),
241
+ };
242
+ }
243
+ /**
244
+ * Clean up all stale (non-open) connections from cache
245
+ * @returns Number of connections removed
246
+ */
247
+ cleanupStaleConnections() {
248
+ let removed = 0;
249
+ for (const [transportKey, connection] of this.connectionsByTransportKey.entries()) {
250
+ if (connection.status !== 'open') {
251
+ this.connectionsByTransportKey.delete(transportKey);
252
+ removed++;
253
+ }
254
+ }
255
+ if (removed > 0) {
256
+ this.logger.debug(`Cleaned up ${removed} stale connections`);
257
+ }
258
+ return removed;
259
+ }
118
260
  }
@@ -1 +1 @@
1
- {"version":3,"file":"stream-handler.d.ts","sourceRoot":"","sources":["../../../src/connection/stream-handler.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EACL,QAAQ,EACR,SAAS,EAIT,MAAM,EAEP,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAGtE;;;;;;;GAOG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,CAAC,EAAE,MAAM;IAI3B;;;OAGG;IACH,SAAS,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO;IAIhC;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO;IAIjC;;OAEG;IACG,aAAa,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAI7C;;;;;;OAMG;IACG,iBAAiB,CACrB,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,mBAAwB,GAC/B,OAAO,CAAC,MAAM,CAAC;IAsClB;;;;;;OAMG;IACG,IAAI,CACR,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,EAChB,MAAM,GAAE,mBAAwB,GAC/B,OAAO,CAAC,IAAI,CAAC;IAiBhB;;;;;OAKG;IACG,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB5E;;;;;;;;OAQG;IACG,oBAAoB,CACxB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,GACtE,OAAO,CAAC,IAAI,CAAC;IAuChB;;;;;;OAMG;YACW,oBAAoB;IA4BlC;;;;;;;;;;;OAWG;IACG,oBAAoB,CACxB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,YAAY,EACrB,MAAM,GAAE,mBAAwB,EAChC,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,EAC1E,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAC1B,OAAO,CAAC,SAAS,CAAC;IA+GrB;;;;;;;OAOG;IACG,cAAc,CAClB,OAAO,EAAE,cAAc,EACvB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,GAChD,OAAO,CAAC,IAAI,CAAC;CAyBjB"}
1
+ {"version":3,"file":"stream-handler.d.ts","sourceRoot":"","sources":["../../../src/connection/stream-handler.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EACL,QAAQ,EACR,SAAS,EAIT,MAAM,EAEP,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAGtE;;;;;;;GAOG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,CAAC,EAAE,MAAM;IAI3B;;;OAGG;IACH,SAAS,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO;IAIhC;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO;IAIjC;;OAEG;IACG,aAAa,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAI7C;;;;;;OAMG;IACG,iBAAiB,CACrB,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,mBAAwB,GAC/B,OAAO,CAAC,MAAM,CAAC;IAoClB;;;;;;OAMG;IACG,IAAI,CACR,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,EAChB,MAAM,GAAE,mBAAwB,GAC/B,OAAO,CAAC,IAAI,CAAC;IAiBhB;;;;;OAKG;IACG,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB5E;;;;;;;;OAQG;IACG,oBAAoB,CACxB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,GACtE,OAAO,CAAC,IAAI,CAAC;IAuChB;;;;;;OAMG;YACW,oBAAoB;IA4BlC;;;;;;;;;;;OAWG;IACG,oBAAoB,CACxB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,YAAY,EACrB,MAAM,GAAE,mBAAwB,EAChC,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,EAC1E,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAC1B,OAAO,CAAC,SAAS,CAAC;IA+GrB;;;;;;;OAOG;IACG,cAAc,CAClB,OAAO,EAAE,cAAc,EACvB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,GAChD,OAAO,CAAC,IAAI,CAAC;CAyBjB"}
@@ -43,7 +43,6 @@ export class StreamHandler {
43
43
  throw new oError(oErrorCodes.INVALID_STATE, 'Connection not open');
44
44
  }
45
45
  const reusePolicy = config.reusePolicy ?? 'none';
46
- this.logger.debug('Reuse policy:', reusePolicy);
47
46
  // Check for existing stream if reuse is enabled
48
47
  if (reusePolicy === 'reuse') {
49
48
  const existingStream = connection.streams.find((stream) => stream.status === 'open' &&
@@ -56,7 +55,6 @@ export class StreamHandler {
56
55
  }
57
56
  }
58
57
  // Create new stream
59
- this.logger.debug('Creating new stream');
60
58
  const stream = await connection.newStream(protocol, {
61
59
  signal: config.signal,
62
60
  maxOutboundStreams: config.maxOutboundStreams ?? 1000,
@@ -1 +1 @@
1
- {"version":3,"file":"o-connection-heartbeat.manager.d.ts","sourceRoot":"","sources":["../../../src/managers/o-connection-heartbeat.manager.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EAOR,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAE3E,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;CACzC;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,2BAA4B,SAAQ,OAAO;IAMpD,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,MAAM;IANhB,OAAO,CAAC,iBAAiB,CAAC,CAAiB;IAC3C,OAAO,CAAC,SAAS,CAAuC;IACxD,OAAO,CAAC,SAAS,CAAS;gBAGhB,IAAI,EAAE,kBAAkB,EACxB,MAAM,EAAE,eAAe;IAK3B,KAAK;IAqBL,IAAI;YAQI,uBAAuB;IAgDrC;;;OAGG;IACH,OAAO,CAAC,qBAAqB;YA8Bf,WAAW;IAoEzB,OAAO,CAAC,oBAAoB;IAyD5B,OAAO,CAAC,2BAA2B;IAmBnC,OAAO,CAAC,4BAA4B;IAiBpC;;OAEG;IACH,eAAe,IAAI,gBAAgB,EAAE;IAIrC;;OAEG;IACH,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,gBAAgB,GAAG,SAAS;IAIxE;;OAEG;IACH,SAAS,IAAI,eAAe;CAG7B"}
1
+ {"version":3,"file":"o-connection-heartbeat.manager.d.ts","sourceRoot":"","sources":["../../../src/managers/o-connection-heartbeat.manager.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EAOR,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAE3E,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;CACzC;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,2BAA4B,SAAQ,OAAO;IAMpD,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,MAAM;IANhB,OAAO,CAAC,iBAAiB,CAAC,CAAiB;IAC3C,OAAO,CAAC,SAAS,CAAuC;IACxD,OAAO,CAAC,SAAS,CAAS;gBAGhB,IAAI,EAAE,kBAAkB,EACxB,MAAM,EAAE,eAAe;IAK3B,KAAK;IAqBL,IAAI;YAQI,uBAAuB;IAgDrC;;;OAGG;IACH,OAAO,CAAC,qBAAqB;YA8Cf,WAAW;IAoEzB,OAAO,CAAC,oBAAoB;IAyD5B,OAAO,CAAC,2BAA2B;IAmBnC,OAAO,CAAC,4BAA4B;IAiBpC;;OAEG;IACH,eAAe,IAAI,gBAAgB,EAAE;IAIrC;;OAEG;IACH,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,gBAAgB,GAAG,SAAS;IAIxE;;OAEG;IACH,SAAS,IAAI,eAAe;CAG7B"}
@@ -95,7 +95,21 @@ export class oConnectionHeartbeatManager extends oObject {
95
95
  // Note: Using 'as any' since converting to proper PeerId breaks browser implementation
96
96
  const connections = this.node.p2pNode.getConnections(peerIdString);
97
97
  // Check if any connection is open
98
- return connections.some((conn) => conn.status === 'open');
98
+ const existing = connections.some((conn) => conn.status === 'open');
99
+ if (existing) {
100
+ return true;
101
+ }
102
+ // check via ping
103
+ const pingResponse = this.node.use(address, {
104
+ method: 'ping'
105
+ }).catch((err) => {
106
+ if (err.message === 'Can not dial self') {
107
+ return true;
108
+ }
109
+ this.logger.warn('Could not reach address in heartbeat:' + address.value, err);
110
+ return null;
111
+ });
112
+ return !!pingResponse;
99
113
  }
100
114
  catch (error) {
101
115
  this.logger.debug(`Error checking connection status for ${address}`, error);
@@ -1 +1 @@
1
- {"version":3,"file":"o-reconnection.manager.d.ts","sourceRoot":"","sources":["../../../src/managers/o-reconnection.manager.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EASR,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAI3E,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,yBAAyB,EAAE,MAAM,CAAC;IAClC,yBAAyB,EAAE,MAAM,CAAC;CACnC;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,oBAAqB,SAAQ,OAAO;IAI7C,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,MAAM;IAJhB,OAAO,CAAC,YAAY,CAAS;gBAGnB,IAAI,EAAE,kBAAkB,EACxB,MAAM,EAAE,kBAAkB;IAMpC,OAAO,CAAC,mBAAmB;IAyBrB,mBAAmB,CAAC,KAAK,EAAE,GAAG;YAatB,wBAAwB;YAaxB,wBAAwB;YAexB,wBAAwB;IAehC,mBAAmB;YAgDX,2BAA2B;YAiB3B,iBAAiB;IAkB/B;;;OAGG;YACW,yBAAyB;IAiFvC;;OAEG;IACG,yBAAyB;IAkG/B,OAAO,CAAC,yBAAyB;IAajC,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,KAAK;CAGd"}
1
+ {"version":3,"file":"o-reconnection.manager.d.ts","sourceRoot":"","sources":["../../../src/managers/o-reconnection.manager.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EASR,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAI3E,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,yBAAyB,EAAE,MAAM,CAAC;IAClC,yBAAyB,EAAE,MAAM,CAAC;CACnC;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,oBAAqB,SAAQ,OAAO;IAI7C,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,MAAM;IAJhB,OAAO,CAAC,YAAY,CAAS;gBAGnB,IAAI,EAAE,kBAAkB,EACxB,MAAM,EAAE,kBAAkB;IAMpC,OAAO,CAAC,mBAAmB;IAyBrB,mBAAmB,CAAC,KAAK,EAAE,GAAG;YAatB,wBAAwB;YAaxB,wBAAwB;YAexB,wBAAwB;IAehC,mBAAmB;YAgDX,2BAA2B;YAiB3B,iBAAiB;IAkB/B;;;OAGG;YACW,yBAAyB;IAiFvC;;OAEG;IACG,yBAAyB;IAwG/B,OAAO,CAAC,yBAAyB;IAajC,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,KAAK;CAGd"}
@@ -189,6 +189,9 @@ export class oReconnectionManager extends oObject {
189
189
  const startTime = Date.now();
190
190
  let attempt = 0;
191
191
  let currentDelay = this.config.parentDiscoveryIntervalMs;
192
+ if (!this.node.config.leader) {
193
+ throw new Error('Cannot connect to parent without leader');
194
+ }
192
195
  // Infinite retry loop - keep trying until parent is found
193
196
  while (true) {
194
197
  attempt++;
@@ -196,18 +199,20 @@ export class oReconnectionManager extends oObject {
196
199
  this.logger.info(`Parent discovery attempt ${attempt} (elapsed: ${elapsedMinutes}m)`);
197
200
  try {
198
201
  // Query registry for parent by its known address
199
- const response = await this.node.use(oAddress.registry(), {
200
- method: 'find_available_parent',
201
- params: {
202
- parentAddress: this.node.config.parent?.toString(),
203
- },
202
+ if (!this.node.config.parent) {
203
+ throw new Error('Invalid parent definition');
204
+ }
205
+ const response = await this.node.use(this.node.config.parent, {
206
+ method: 'identify',
207
+ params: {},
204
208
  });
205
- const { parentAddress, parentTransports } = response.result.data;
209
+ this.logger.debug('Identify parent response:', response.result.data);
210
+ const { address: parentAddress, transports: parentTransports } = response.result.data;
206
211
  // Check if parent was found in registry
207
212
  if (parentAddress && parentTransports && parentTransports.length > 0) {
208
213
  this.logger.info(`Parent found in registry: ${parentAddress} with ${parentTransports.length} transports`);
209
214
  // Update parent reference with fresh transports
210
- this.node.config.parent = new oNodeAddress(parentAddress, parentTransports.map((t) => new oNodeTransport(t.value)));
215
+ this.node.config.parent = new oNodeAddress(parentAddress, parentTransports.map((value) => new oNodeTransport(value)));
211
216
  // Attempt to register with parent and re-register with registry
212
217
  try {
213
218
  await this.tryDirectParentReconnection();
@@ -49,11 +49,30 @@ export declare class oNode extends oToolBase {
49
49
  initConnectionManager(): Promise<void>;
50
50
  hookInitializeFinished(): Promise<void>;
51
51
  hookStartFinished(): Promise<void>;
52
+ /**
53
+ * Validates that if a leader address is defined, it has associated transports.
54
+ * This is critical for non-leader nodes to be able to connect to their leader.
55
+ * @throws Error if leader is defined but has no transports
56
+ */
57
+ private validateLeaderTransports;
58
+ /**
59
+ * oNode-specific validation that runs before core start() logic.
60
+ *
61
+ * This ensures that leader transport configuration errors are
62
+ * surfaced quickly and before any libp2p nodes or other heavy
63
+ * resources are created.
64
+ */
65
+ protected validate(): Promise<void>;
52
66
  initialize(): Promise<void>;
53
67
  /**
54
68
  * Override use() to wrap leader/registry requests with retry logic
55
69
  */
56
70
  teardown(): Promise<void>;
71
+ /**
72
+ * Reset node state to allow restart after stop
73
+ * Called at the end of teardown()
74
+ */
75
+ protected resetState(): void;
57
76
  getLeaders(): oNodeAddress[];
58
77
  getParents(): oNodeAddress[];
59
78
  getChildren(): oNodeAddress[];
@@ -1 +1 @@
1
- {"version":3,"file":"o-node.d.ts","sourceRoot":"","sources":["../../src/o-node.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"o-node.d.ts","sourceRoot":"","sources":["../../src/o-node.ts"],"names":[],"mappings":"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"}