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

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 (57) 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 +33 -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 +153 -44
  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 +5 -0
  16. package/dist/src/o-node.d.ts.map +1 -1
  17. package/dist/src/o-node.js +46 -8
  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/network-communication.spec.d.ts +2 -0
  46. package/dist/test/network-communication.spec.d.ts.map +1 -0
  47. package/dist/test/network-communication.spec.js +256 -0
  48. package/dist/test/o-node.spec.d.ts +2 -0
  49. package/dist/test/o-node.spec.d.ts.map +1 -0
  50. package/dist/test/o-node.spec.js +247 -0
  51. package/dist/test/parent-child-registration.spec.d.ts +2 -0
  52. package/dist/test/parent-child-registration.spec.d.ts.map +1 -0
  53. package/dist/test/parent-child-registration.spec.js +177 -0
  54. package/dist/test/search-resolver.spec.d.ts +2 -0
  55. package/dist/test/search-resolver.spec.d.ts.map +1 -0
  56. package/dist/test/search-resolver.spec.js +648 -0
  57. 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,21 @@ export declare class oNodeConnectionManager extends oConnectionManager {
7
7
  protected p2pNode: Libp2p;
8
8
  private defaultReadTimeoutMs?;
9
9
  private defaultDrainTimeoutMs?;
10
- private connections;
10
+ private connectionsByPeerId;
11
+ private pendingDialsByPeerId;
11
12
  constructor(config: oNodeConnectionManagerConfig);
13
+ /**
14
+ * Set up listeners to maintain connection cache state
15
+ */
16
+ private setupConnectionListeners;
17
+ /**
18
+ * Extract peer ID string from an address
19
+ * @param address - The address to extract peer ID from
20
+ * @returns The peer ID string or null if not found
21
+ */
22
+ private getPeerIdFromAddress;
12
23
  getOrCreateConnection(nextHopAddress: oAddress, address: oAddress): Promise<Connection>;
24
+ private performDial;
13
25
  /**
14
26
  * Connect to a given address, reusing libp2p connections when possible
15
27
  * @param config - Connection configuration
@@ -17,16 +29,33 @@ export declare class oNodeConnectionManager extends oConnectionManager {
17
29
  */
18
30
  connect(config: oConnectionConfig): Promise<oNodeConnection>;
19
31
  /**
20
- * Check if libp2p has an active connection to the target peer
32
+ * Check if we have an active connection to the target peer
21
33
  * @param address - The address to check
22
34
  * @returns true if an active connection exists
23
35
  */
24
36
  isCached(address: oAddress): boolean;
25
37
  /**
26
- * Get an existing libp2p connection to the target peer
38
+ * Get an existing connection to the target peer (from cache or libp2p)
27
39
  * @param address - The address to get a connection for
28
- * @returns The libp2p Connection object or null if not found
40
+ * @returns The Connection object or null if not found
29
41
  */
30
42
  getCachedLibp2pConnection(address: oAddress): Connection | null;
43
+ /**
44
+ * Get cache statistics for monitoring and debugging
45
+ * @returns Object containing cache statistics
46
+ */
47
+ getCacheStats(): {
48
+ cachedConnections: number;
49
+ pendingDials: number;
50
+ connectionsByPeer: Array<{
51
+ peerId: string;
52
+ status: string;
53
+ }>;
54
+ };
55
+ /**
56
+ * Clean up all stale (non-open) connections from cache
57
+ * @returns Number of connections removed
58
+ */
59
+ cleanupStaleConnections(): number;
31
60
  }
32
61
  //# 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,mBAAmB,CAAsC;IACjE,OAAO,CAAC,oBAAoB,CAA+C;gBAEtD,MAAM,EAAE,4BAA4B;IAWzD;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAUhC;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IAatB,qBAAqB,CACzB,cAAc,EAAE,QAAQ,EACxB,OAAO,EAAE,QAAQ,GAChB,OAAO,CAAC,UAAU,CAAC;YAmDR,WAAW;IAwBzB;;;;OAIG;IACG,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,eAAe,CAAC;IA8BlE;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO;IAmCpC;;;;OAIG;IACH,yBAAyB,CAAC,OAAO,EAAE,QAAQ,GAAG,UAAU,GAAG,IAAI;IA0C/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,100 @@ 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.connectionsByPeerId = new Map();
8
+ this.pendingDialsByPeerId = 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();
15
+ }
16
+ /**
17
+ * Set up listeners to maintain connection cache state
18
+ */
19
+ setupConnectionListeners() {
20
+ this.p2pNode.addEventListener('connection:close', (event) => {
21
+ const peerId = event.detail?.remotePeer?.toString();
22
+ if (peerId && this.connectionsByPeerId.has(peerId)) {
23
+ this.logger.debug('Connection closed, removing from cache:', peerId);
24
+ this.connectionsByPeerId.delete(peerId);
25
+ }
26
+ });
27
+ }
28
+ /**
29
+ * Extract peer ID string from an address
30
+ * @param address - The address to extract peer ID from
31
+ * @returns The peer ID string or null if not found
32
+ */
33
+ getPeerIdFromAddress(address) {
34
+ try {
35
+ const nodeAddress = address;
36
+ if (!nodeAddress.libp2pTransports?.length) {
37
+ return null;
38
+ }
39
+ return nodeAddress.libp2pTransports[0].toPeerId() || null;
40
+ }
41
+ catch (error) {
42
+ this.logger.debug('Error extracting peer ID from address:', error);
43
+ return null;
44
+ }
11
45
  }
12
46
  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;
47
+ // Extract peer ID as the cache key
48
+ const peerId = this.getPeerIdFromAddress(nextHopAddress);
49
+ if (!peerId) {
50
+ throw new Error(`Unable to extract peer ID from address: ${nextHopAddress.toString()}`);
20
51
  }
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()));
52
+ // Check if we have a cached connection by peer ID
53
+ const cachedConnection = this.connectionsByPeerId.get(peerId);
54
+ if (cachedConnection && cachedConnection.status === 'open') {
55
+ this.logger.debug('Reusing cached connection for peer:', peerId);
56
+ return cachedConnection;
25
57
  }
26
- if (nextHopAddress.libp2pTransports.length) {
27
- const peerIdString = nextHopAddress.libp2pTransports[0].toPeerId();
28
- if (peerIdString) {
29
- this.connections.set(peerIdString, p2pConnection);
30
- }
58
+ // Clean up stale connection if it exists but is not open
59
+ if (cachedConnection && cachedConnection.status !== 'open') {
60
+ this.logger.debug('Removing stale connection for peer:', peerId);
61
+ this.connectionsByPeerId.delete(peerId);
62
+ }
63
+ // Check if libp2p has an active connection to this peer
64
+ const libp2pConnection = this.getCachedLibp2pConnection(nextHopAddress);
65
+ if (libp2pConnection && libp2pConnection.status === 'open') {
66
+ this.logger.debug('Caching existing libp2p connection for peer:', peerId);
67
+ this.connectionsByPeerId.set(peerId, libp2pConnection);
68
+ return libp2pConnection;
69
+ }
70
+ // Check if dial is already in progress for this peer
71
+ const pendingDial = this.pendingDialsByPeerId.get(peerId);
72
+ if (pendingDial) {
73
+ this.logger.debug('Awaiting existing dial for peer:', peerId);
74
+ return pendingDial;
75
+ }
76
+ // Start new dial and cache the promise by peer ID
77
+ const dialPromise = this.performDial(nextHopAddress, peerId);
78
+ this.pendingDialsByPeerId.set(peerId, dialPromise);
79
+ try {
80
+ const connection = await dialPromise;
81
+ // Cache the established connection by peer ID
82
+ this.connectionsByPeerId.set(peerId, connection);
83
+ return connection;
31
84
  }
32
- return p2pConnection;
85
+ finally {
86
+ this.pendingDialsByPeerId.delete(peerId);
87
+ }
88
+ }
89
+ async performDial(nextHopAddress, peerId) {
90
+ this.logger.debug('Dialing new connection', {
91
+ address: nextHopAddress.value,
92
+ peerId,
93
+ });
94
+ const connection = await this.p2pNode.dial(nextHopAddress.libp2pTransports.map((ma) => ma.toMultiaddr()));
95
+ this.logger.debug('Successfully dialed connection', {
96
+ peerId,
97
+ status: connection.status,
98
+ remotePeer: connection.remotePeer?.toString(),
99
+ });
100
+ return connection;
33
101
  }
34
102
  /**
35
103
  * Connect to a given address, reusing libp2p connections when possible
@@ -54,27 +122,34 @@ export class oNodeConnectionManager extends oConnectionManager {
54
122
  return connection;
55
123
  }
56
124
  /**
57
- * Check if libp2p has an active connection to the target peer
125
+ * Check if we have an active connection to the target peer
58
126
  * @param address - The address to check
59
127
  * @returns true if an active connection exists
60
128
  */
61
129
  isCached(address) {
62
130
  try {
63
- const nodeAddress = address;
64
- if (!nodeAddress.libp2pTransports ||
65
- nodeAddress.libp2pTransports.length === 0) {
131
+ const peerId = this.getPeerIdFromAddress(address);
132
+ if (!peerId) {
66
133
  return false;
67
134
  }
68
- // Extract peer ID from the first transport
69
- const peerIdString = nodeAddress.libp2pTransports[0].toPeerId();
70
- if (!peerIdString) {
71
- return false;
135
+ // Check our peer ID-based cache first
136
+ const cachedConnection = this.connectionsByPeerId.get(peerId);
137
+ if (cachedConnection?.status === 'open') {
138
+ return true;
72
139
  }
73
- this.logger.debug('Peer ID string:', peerIdString);
140
+ // Fall back to checking libp2p's connections
74
141
  // 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
142
+ const connections = this.p2pNode.getConnections(peerId); // ignore since converting to a proper peer id breaks the browser implementation
76
143
  // Check if we have at least one open connection
77
- return connections.some((conn) => conn.status === 'open');
144
+ const hasOpenConnection = connections.some((conn) => conn.status === 'open');
145
+ // If libp2p has an open connection, update our cache
146
+ if (hasOpenConnection) {
147
+ const openConnection = connections.find((conn) => conn.status === 'open');
148
+ if (openConnection) {
149
+ this.connectionsByPeerId.set(peerId, openConnection);
150
+ }
151
+ }
152
+ return hasOpenConnection;
78
153
  }
79
154
  catch (error) {
80
155
  this.logger.debug('Error checking cached connection:', error);
@@ -82,37 +157,71 @@ export class oNodeConnectionManager extends oConnectionManager {
82
157
  }
83
158
  }
84
159
  /**
85
- * Get an existing libp2p connection to the target peer
160
+ * Get an existing connection to the target peer (from cache or libp2p)
86
161
  * @param address - The address to get a connection for
87
- * @returns The libp2p Connection object or null if not found
162
+ * @returns The Connection object or null if not found
88
163
  */
89
164
  getCachedLibp2pConnection(address) {
90
165
  try {
91
- const nodeAddress = address;
92
- if (!nodeAddress.libp2pTransports.length) {
166
+ const peerId = this.getPeerIdFromAddress(address);
167
+ if (!peerId) {
93
168
  return null;
94
169
  }
95
- const peerIdString = nodeAddress.libp2pTransports[0].toPeerId();
96
- if (!peerIdString) {
97
- return null;
170
+ // Check peer ID-based cache first
171
+ const cachedConnection = this.connectionsByPeerId.get(peerId);
172
+ if (cachedConnection?.status === 'open') {
173
+ return cachedConnection;
98
174
  }
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
175
+ // Query libp2p for connections to this peer
176
+ const connections = this.p2pNode.getConnections();
177
+ const filteredConnections = connections.filter((conn) => conn.remotePeer?.toString() === peerId);
178
+ // Find the first open connection
102
179
  const openConnection = filteredConnections.find((conn) => conn.status === 'open');
180
+ // If we found an open connection in libp2p, cache it by peer ID
103
181
  if (openConnection) {
182
+ this.connectionsByPeerId.set(peerId, openConnection);
104
183
  return openConnection;
105
184
  }
106
- const connection = this.connections.get(peerIdString);
107
- if (connection?.status === 'open') {
108
- return connection;
185
+ // Clean up stale cache entry if connection is no longer open
186
+ if (cachedConnection) {
187
+ this.connectionsByPeerId.delete(peerId);
109
188
  }
110
- // Return the first open connection, or null if none exist
111
- return connection ?? null;
189
+ return null;
112
190
  }
113
191
  catch (error) {
114
192
  this.logger.debug('Error getting cached connection:', error);
115
193
  return null;
116
194
  }
117
195
  }
196
+ /**
197
+ * Get cache statistics for monitoring and debugging
198
+ * @returns Object containing cache statistics
199
+ */
200
+ getCacheStats() {
201
+ return {
202
+ cachedConnections: this.connectionsByPeerId.size,
203
+ pendingDials: this.pendingDialsByPeerId.size,
204
+ connectionsByPeer: Array.from(this.connectionsByPeerId.entries()).map(([peerId, conn]) => ({
205
+ peerId,
206
+ status: conn.status,
207
+ })),
208
+ };
209
+ }
210
+ /**
211
+ * Clean up all stale (non-open) connections from cache
212
+ * @returns Number of connections removed
213
+ */
214
+ cleanupStaleConnections() {
215
+ let removed = 0;
216
+ for (const [peerId, connection] of this.connectionsByPeerId.entries()) {
217
+ if (connection.status !== 'open') {
218
+ this.connectionsByPeerId.delete(peerId);
219
+ removed++;
220
+ }
221
+ }
222
+ if (removed > 0) {
223
+ this.logger.debug(`Cleaned up ${removed} stale connections`);
224
+ }
225
+ return removed;
226
+ }
118
227
  }
@@ -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();
@@ -54,6 +54,11 @@ export declare class oNode extends oToolBase {
54
54
  * Override use() to wrap leader/registry requests with retry logic
55
55
  */
56
56
  teardown(): Promise<void>;
57
+ /**
58
+ * Reset node state to allow restart after stop
59
+ * Called at the end of teardown()
60
+ */
61
+ protected resetState(): void;
57
62
  getLeaders(): oNodeAddress[];
58
63
  getParents(): oNodeAddress[];
59
64
  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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"}
@@ -135,6 +135,10 @@ export class oNode extends oToolBase {
135
135
  this.logger.debug('Skipping parent registration, node is leader');
136
136
  return;
137
137
  }
138
+ if (!this.parent) {
139
+ this.logger.warn('no parent, skipping registration');
140
+ return;
141
+ }
138
142
  if (!this.parent?.libp2pTransports?.length) {
139
143
  this.logger.debug('Parent has no transports, waiting for reconnection & leader ack');
140
144
  if (this.parent?.toString() === oAddress.leader().toString()) {
@@ -149,7 +153,8 @@ export class oNode extends oToolBase {
149
153
  // TODO: should we remove the transports check to make this more consistent?
150
154
  if (this.config.parent) {
151
155
  this.logger.debug('Registering node with parent...', this.config.parent?.toString());
152
- await this.use(this.config.parent, {
156
+ // avoid transports to ensure we do not try direct connection, we need to route via the leader for proper access controls
157
+ await this.use(new oNodeAddress(this.config.parent.value), {
153
158
  method: 'child_register',
154
159
  params: {
155
160
  address: this.address.toString(),
@@ -162,6 +167,11 @@ export class oNode extends oToolBase {
162
167
  }
163
168
  }
164
169
  async registerLeader() {
170
+ this.logger.info('Register leader called...');
171
+ if (!this.leader) {
172
+ this.logger.warn('No leader defined, skipping registration');
173
+ return;
174
+ }
165
175
  const address = oAddress.registry();
166
176
  const params = {
167
177
  method: 'commit',
@@ -187,6 +197,7 @@ export class oNode extends oToolBase {
187
197
  }
188
198
  this.didRegister = true;
189
199
  this.logger.debug('Registering node...');
200
+ await this.registerParent();
190
201
  // register with the leader global registry
191
202
  if (!this.config.leader) {
192
203
  this.logger.warn('No leaders found, skipping registration');
@@ -195,7 +206,6 @@ export class oNode extends oToolBase {
195
206
  else {
196
207
  this.logger.debug('Registering node with leader...');
197
208
  }
198
- await this.registerParent();
199
209
  await this.registerLeader();
200
210
  this.logger.debug('Registration successful');
201
211
  }
@@ -290,7 +300,6 @@ export class oNode extends oToolBase {
290
300
  if (this.config.type === NodeType.LEADER) {
291
301
  return false;
292
302
  }
293
- // deny everything else
294
303
  return true;
295
304
  },
296
305
  // allow the user to override the default connection gater
@@ -305,9 +314,8 @@ export class oNode extends oToolBase {
305
314
  maxParallelReconnects: 10,
306
315
  };
307
316
  // handle the address encapsulation
308
- if (this.config.leader &&
309
- !this.address.protocol.includes(this.config.leader.protocol)) {
310
- const parentAddress = this.config.parent || this.config.leader;
317
+ if (this.config.parent) {
318
+ const parentAddress = this.config.parent;
311
319
  this.address = CoreUtils.childAddress(parentAddress, this.address);
312
320
  }
313
321
  return params;
@@ -342,6 +350,7 @@ export class oNode extends oToolBase {
342
350
  defaultReadTimeoutMs: this.config.connectionTimeouts?.readTimeoutMs,
343
351
  defaultDrainTimeoutMs: this.config.connectionTimeouts?.drainTimeoutMs,
344
352
  runOnLimitedConnection: this.config.runOnLimitedConnection ?? false,
353
+ originAddress: this.address?.value
345
354
  });
346
355
  }
347
356
  async hookInitializeFinished() { }
@@ -360,7 +369,7 @@ export class oNode extends oToolBase {
360
369
  }
361
370
  async initialize() {
362
371
  this.logger.debug('Initializing node...');
363
- if (this.p2pNode && this.state !== NodeState.STOPPED) {
372
+ if (this.state !== NodeState.STOPPED && this.state !== NodeState.STARTING) {
364
373
  throw new Error('Node is not in a valid state to be initialized');
365
374
  }
366
375
  if (!this.address.validate()) {
@@ -379,7 +388,7 @@ export class oNode extends oToolBase {
379
388
  this.router.addResolver(new oMethodResolver(this.address));
380
389
  this.router.addResolver(new oNodeResolver(this.address));
381
390
  // setup a fallback resolver for non-leader nodes
382
- if (this.isLeader === false) {
391
+ if (this.isLeader === false && !!this.leader) {
383
392
  this.logger.debug('Adding leader resolver fallback...');
384
393
  this.router.addResolver(new oLeaderResolverFallback(this.address));
385
394
  }
@@ -421,6 +430,35 @@ export class oNode extends oToolBase {
421
430
  if (this.p2pNode) {
422
431
  await this.p2pNode.stop();
423
432
  }
433
+ // Reset state to allow restart
434
+ this.resetState();
435
+ }
436
+ /**
437
+ * Reset node state to allow restart after stop
438
+ * Called at the end of teardown()
439
+ */
440
+ resetState() {
441
+ // Reset registration flag
442
+ this.didRegister = false;
443
+ // Clear peer references
444
+ this.peerId = undefined;
445
+ this.p2pNode = undefined;
446
+ // Clear managers
447
+ this.connectionManager = undefined;
448
+ this.connectionHeartbeatManager = undefined;
449
+ this.reconnectionManager = undefined;
450
+ // Reset address to staticAddress with no transports
451
+ this.address = new oNodeAddress(this.staticAddress.value, []);
452
+ // Reset hierarchy manager
453
+ this.hierarchyManager = new oNodeHierarchyManager({
454
+ leaders: this.config.leader ? [this.config.leader] : [],
455
+ parents: this.config.parent ? [this.config.parent] : [],
456
+ children: [],
457
+ });
458
+ // Clear router (will be recreated in initialize)
459
+ this.router = undefined;
460
+ // Call parent reset
461
+ super.resetState();
424
462
  }
425
463
  // IHeartbeatableNode interface methods
426
464
  getLeaders() {
@@ -1 +1 @@
1
- {"version":3,"file":"o-node.tool.d.ts","sourceRoot":"","sources":["../../src/o-node.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,QAAQ,EAET,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;;AAKrD;;;;GAIG;AACH,qBAAa,SAAU,SAAQ,cAAkB;IAC/C,OAAO,CAAC,aAAa,CAAiB;IAEhC,cAAc,CAAC,OAAO,EAAE,QAAQ;IAUhC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BnE,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAQ9B,oBAAoB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;CA2B5D"}
1
+ {"version":3,"file":"o-node.tool.d.ts","sourceRoot":"","sources":["../../src/o-node.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,QAAQ,EAET,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;;AAKrD;;;;GAIG;AACH,qBAAa,SAAU,SAAQ,cAAkB;IAC/C,OAAO,CAAC,aAAa,CAAiB;IAEhC,cAAc,CAAC,OAAO,EAAE,QAAQ;IAehC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BnE,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAQ9B,oBAAoB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;CA2B5D"}