@olane/o-node 0.7.12-alpha.5 → 0.7.12-alpha.50

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/interfaces/o-node-connection.config.d.ts +1 -0
  4. package/dist/src/connection/interfaces/o-node-connection.config.d.ts.map +1 -1
  5. package/dist/src/connection/o-node-connection.d.ts +3 -2
  6. package/dist/src/connection/o-node-connection.d.ts.map +1 -1
  7. package/dist/src/connection/o-node-connection.js +58 -18
  8. package/dist/src/connection/o-node-connection.manager.d.ts +17 -4
  9. package/dist/src/connection/o-node-connection.manager.d.ts.map +1 -1
  10. package/dist/src/connection/o-node-connection.manager.js +75 -65
  11. package/dist/src/connection/o-stream.request.d.ts +11 -0
  12. package/dist/src/connection/o-stream.request.d.ts.map +1 -0
  13. package/dist/src/connection/o-stream.request.js +7 -0
  14. package/dist/src/index.d.ts +2 -1
  15. package/dist/src/index.d.ts.map +1 -1
  16. package/dist/src/index.js +2 -1
  17. package/dist/src/interfaces/i-heartbeatable-node.d.ts +49 -0
  18. package/dist/src/interfaces/i-heartbeatable-node.d.ts.map +1 -0
  19. package/dist/src/interfaces/i-heartbeatable-node.js +1 -0
  20. package/dist/src/interfaces/i-reconnectable-node.d.ts +46 -0
  21. package/dist/src/interfaces/i-reconnectable-node.d.ts.map +1 -0
  22. package/dist/src/interfaces/i-reconnectable-node.js +1 -0
  23. package/dist/src/interfaces/o-node.config.d.ts +43 -0
  24. package/dist/src/interfaces/o-node.config.d.ts.map +1 -1
  25. package/dist/src/managers/o-connection-heartbeat.manager.d.ts +63 -0
  26. package/dist/src/managers/o-connection-heartbeat.manager.d.ts.map +1 -0
  27. package/dist/src/managers/o-connection-heartbeat.manager.js +227 -0
  28. package/dist/src/managers/o-reconnection.manager.d.ts +51 -0
  29. package/dist/src/managers/o-reconnection.manager.d.ts.map +1 -0
  30. package/dist/src/managers/o-reconnection.manager.js +266 -0
  31. package/dist/src/o-node.d.ts +30 -2
  32. package/dist/src/o-node.d.ts.map +1 -1
  33. package/dist/src/o-node.js +245 -33
  34. package/dist/src/o-node.notification-manager.d.ts +52 -0
  35. package/dist/src/o-node.notification-manager.d.ts.map +1 -0
  36. package/dist/src/o-node.notification-manager.js +188 -0
  37. package/dist/src/o-node.tool.d.ts.map +1 -1
  38. package/dist/src/o-node.tool.js +27 -22
  39. package/dist/src/router/o-node.router.d.ts +1 -0
  40. package/dist/src/router/o-node.router.d.ts.map +1 -1
  41. package/dist/src/router/o-node.router.js +62 -9
  42. package/dist/src/router/o-node.routing-policy.d.ts.map +1 -1
  43. package/dist/src/router/o-node.routing-policy.js +7 -2
  44. package/dist/src/router/resolvers/o-node.resolver.d.ts.map +1 -1
  45. package/dist/src/router/resolvers/o-node.resolver.js +5 -1
  46. package/dist/src/router/resolvers/o-node.search-resolver.d.ts.map +1 -1
  47. package/dist/src/router/resolvers/o-node.search-resolver.js +34 -9
  48. package/dist/src/utils/index.d.ts +3 -0
  49. package/dist/src/utils/index.d.ts.map +1 -0
  50. package/dist/src/utils/index.js +2 -0
  51. package/dist/src/utils/stream.utils.d.ts +6 -0
  52. package/dist/src/utils/stream.utils.d.ts.map +1 -0
  53. package/dist/src/utils/stream.utils.js +31 -0
  54. package/dist/test/helpers/test-node.tool.d.ts +15 -0
  55. package/dist/test/helpers/test-node.tool.d.ts.map +1 -0
  56. package/dist/test/helpers/test-node.tool.js +27 -0
  57. package/package.json +6 -6
  58. package/dist/src/router/resolvers/o-node.child-resolver.d.ts +0 -11
  59. package/dist/src/router/resolvers/o-node.child-resolver.d.ts.map +0 -1
  60. package/dist/src/router/resolvers/o-node.child-resolver.js +0 -58
@@ -3,11 +3,14 @@ import { PeerId } from '@olane/o-config';
3
3
  import { oNodeHierarchyManager } from './o-node.hierarchy-manager.js';
4
4
  import { oNodeConfig } from './interfaces/o-node.config.js';
5
5
  import { oNodeTransport } from './router/o-node.transport.js';
6
- import { oRequest } from '@olane/o-core';
6
+ import { oRequest, oNotificationManager } from '@olane/o-core';
7
7
  import { oNodeAddress } from './router/o-node.address.js';
8
8
  import { oNodeConnection } from './connection/o-node-connection.js';
9
9
  import { oNodeConnectionManager } from './connection/o-node-connection.manager.js';
10
10
  import { oToolBase } from '@olane/o-tool';
11
+ import { oConnectionHeartbeatManager } from './managers/o-connection-heartbeat.manager.js';
12
+ import { oReconnectionManager } from './managers/o-reconnection.manager.js';
13
+ import { oNodeConnectionConfig } from './connection/index.js';
11
14
  export declare class oNode extends oToolBase {
12
15
  peerId: PeerId;
13
16
  p2pNode: Libp2p;
@@ -15,6 +18,8 @@ export declare class oNode extends oToolBase {
15
18
  config: oNodeConfig;
16
19
  connectionManager: oNodeConnectionManager;
17
20
  hierarchyManager: oNodeHierarchyManager;
21
+ connectionHeartbeatManager?: oConnectionHeartbeatManager;
22
+ reconnectionManager?: oReconnectionManager;
18
23
  protected didRegister: boolean;
19
24
  constructor(config: oNodeConfig);
20
25
  get leader(): oNodeAddress | null;
@@ -22,11 +27,13 @@ export declare class oNode extends oToolBase {
22
27
  get parentPeerId(): string | null;
23
28
  configureTransports(): any[];
24
29
  initializeRouter(): Promise<void>;
30
+ protected createNotificationManager(): oNotificationManager;
25
31
  get staticAddress(): oNodeAddress;
26
32
  get parentTransports(): oNodeTransport[];
27
33
  get transports(): oNodeTransport[];
28
34
  unregister(): Promise<void>;
29
35
  registerParent(): Promise<void>;
36
+ registerLeader(): Promise<void>;
30
37
  register(): Promise<void>;
31
38
  extractMethod(address: oNodeAddress): string;
32
39
  start(): Promise<void>;
@@ -37,8 +44,29 @@ export declare class oNode extends oToolBase {
37
44
  */
38
45
  configure(): Promise<Libp2pConfig>;
39
46
  protected createNode(): Promise<Libp2p>;
40
- connect(nextHopAddress: oNodeAddress, targetAddress: oNodeAddress): Promise<oNodeConnection>;
47
+ connect(config: oNodeConnectionConfig): Promise<oNodeConnection>;
48
+ initConnectionManager(): Promise<void>;
49
+ initReconnectionManager(): Promise<void>;
50
+ hookInitializeFinished(): Promise<void>;
51
+ hookStartFinished(): Promise<void>;
41
52
  initialize(): Promise<void>;
53
+ /**
54
+ * Override use() to wrap leader/registry requests with retry logic
55
+ */
42
56
  teardown(): Promise<void>;
57
+ getLeaders(): oNodeAddress[];
58
+ getParents(): oNodeAddress[];
59
+ getChildren(): oNodeAddress[];
60
+ removeChild(childAddress: oNodeAddress): void;
61
+ /**
62
+ * Get the total number of active streams across all connections
63
+ * @returns Total count of active streams
64
+ */
65
+ getStreamCount(): number;
66
+ /**
67
+ * Get libp2p metrics for this node
68
+ * Tool method that can be called remotely by monitoring systems
69
+ */
70
+ _tool_get_libp2p_metrics(request: oRequest): Promise<any>;
43
71
  }
44
72
  //# sourceMappingURL=o-node.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"o-node.d.ts","sourceRoot":"","sources":["../../src/o-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,MAAM,EACN,YAAY,EACb,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAKL,QAAQ,EAET,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AAGnF,OAAO,EAAmB,SAAS,EAAE,MAAM,eAAe,CAAC;AAI3D,qBAAa,KAAM,SAAQ,SAAS;IAC3B,MAAM,EAAG,MAAM,CAAC;IAChB,OAAO,EAAG,MAAM,CAAC;IACjB,OAAO,EAAG,YAAY,CAAC;IACvB,MAAM,EAAE,WAAW,CAAC;IACpB,iBAAiB,EAAG,sBAAsB,CAAC;IAC3C,gBAAgB,EAAG,qBAAqB,CAAC;IAChD,SAAS,CAAC,WAAW,EAAE,OAAO,CAAS;gBAE3B,MAAM,EAAE,WAAW;IAK/B,IAAI,MAAM,IAAI,YAAY,GAAG,IAAI,CAEhC;IAED,IAAI,aAAa,IAAI,YAAY,CAKhC;IAED,IAAI,YAAY,IAAI,MAAM,GAAG,IAAI,CAOhC;IAED,mBAAmB,IAAI,GAAG,EAAE;IAItB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IASvC,IAAI,aAAa,IAAI,YAAY,CAEhC;IAED,IAAI,gBAAgB,IAAI,cAAc,EAAE,CAEvC;IAED,IAAI,UAAU,IAAI,cAAc,EAAE,CAIjC;IAEK,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB3B,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B/B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAwC/B,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM;IAItC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,mBAAmB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;IAG1D;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC;cA0FxB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAMvC,OAAO,CACX,cAAc,EAAE,YAAY,EAC5B,aAAa,EAAE,YAAY,GAC1B,OAAO,CAAC,eAAe,CAAC;IA0BrB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC3B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAOhC"}
1
+ {"version":3,"file":"o-node.d.ts","sourceRoot":"","sources":["../../src/o-node.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,9 @@ import { oNodeConnectionManager } from './connection/o-node-connection.manager.j
9
9
  import { oNodeResolver } from './router/resolvers/o-node.resolver.js';
10
10
  import { oMethodResolver, oToolBase } from '@olane/o-tool';
11
11
  import { oLeaderResolverFallback } from './router/index.js';
12
+ import { oNodeNotificationManager } from './o-node.notification-manager.js';
13
+ import { oConnectionHeartbeatManager } from './managers/o-connection-heartbeat.manager.js';
14
+ import { oReconnectionManager } from './managers/o-reconnection.manager.js';
12
15
  export class oNode extends oToolBase {
13
16
  constructor(config) {
14
17
  super(config);
@@ -43,6 +46,9 @@ export class oNode extends oToolBase {
43
46
  });
44
47
  this.router = new oNodeRouter();
45
48
  }
49
+ createNotificationManager() {
50
+ return new oNodeNotificationManager(this.p2pNode, this.hierarchyManager, this.address);
51
+ }
46
52
  get staticAddress() {
47
53
  return this.config.address;
48
54
  }
@@ -60,6 +66,30 @@ export class oNode extends oToolBase {
60
66
  this.logger.debug('Skipping unregistration, node is leader');
61
67
  return;
62
68
  }
69
+ // Notify parent we're stopping (best-effort, 2s timeout)
70
+ if (this.config.parent) {
71
+ try {
72
+ await Promise.race([
73
+ this.use(this.config.parent, {
74
+ method: 'notify',
75
+ params: {
76
+ eventType: 'node:stopping',
77
+ eventData: {
78
+ address: this.address.toString(),
79
+ reason: 'graceful_shutdown',
80
+ expectedDowntime: null,
81
+ },
82
+ source: this.address.toString(),
83
+ },
84
+ }),
85
+ new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 2000)),
86
+ ]);
87
+ this.logger.debug('Notified parent of shutdown');
88
+ }
89
+ catch (error) {
90
+ this.logger.warn('Failed to notify parent (will be detected by heartbeat):', error instanceof Error ? error.message : error);
91
+ }
92
+ }
63
93
  if (!this.config.leader) {
64
94
  this.logger.debug('No leader found, skipping unregistration');
65
95
  return;
@@ -72,18 +102,30 @@ export class oNode extends oToolBase {
72
102
  peerId: this.peerId.toString(),
73
103
  },
74
104
  };
75
- await this.use(address, params);
105
+ this.use(address, params).catch((error) => {
106
+ this.logger.error('Failed to unregister from network:', error);
107
+ });
76
108
  }
77
109
  async registerParent() {
78
110
  if (this.type === NodeType.LEADER) {
79
111
  this.logger.debug('Skipping parent registration, node is leader');
80
112
  return;
81
113
  }
114
+ if (!this.parent?.libp2pTransports?.length) {
115
+ this.logger.debug('Parent has no transports, waiting for reconnection & leader ack');
116
+ if (this.parent?.toString() === oAddress.leader().toString()) {
117
+ this.parent.setTransports(this.leader?.libp2pTransports || []);
118
+ }
119
+ else {
120
+ this.logger.debug('Waiting for parent and reconnecting...');
121
+ await this.reconnectionManager?.waitForParentAndReconnect();
122
+ }
123
+ }
82
124
  // if no parent transports, register with the parent to get them
83
125
  // TODO: should we remove the transports check to make this more consistent?
84
- if (this.config.parent && this.config.parent.transports.length === 0) {
85
- this.logger.debug('Registering node with parent...', this.config.parent);
86
- const parentRegistration = await this.use(this.config.parent, {
126
+ if (this.config.parent) {
127
+ this.logger.debug('Registering node with parent...', this.config.parent?.toString());
128
+ await this.use(this.config.parent, {
87
129
  method: 'child_register',
88
130
  params: {
89
131
  address: this.address.toString(),
@@ -92,11 +134,22 @@ export class oNode extends oToolBase {
92
134
  _token: this.config.joinToken,
93
135
  },
94
136
  });
95
- const { parentTransports } = parentRegistration.result.data;
96
- // update the parent transports
97
- this.config.parent.setTransports(parentTransports.map((t) => new oNodeTransport(t)));
98
137
  }
99
138
  }
139
+ async registerLeader() {
140
+ const address = oAddress.registry();
141
+ const params = {
142
+ method: 'commit',
143
+ params: {
144
+ peerId: this.peerId.toString(),
145
+ address: this.address.toString(),
146
+ protocols: this.p2pNode.getProtocols(),
147
+ transports: this.transports,
148
+ staticAddress: this.staticAddress.toString(),
149
+ },
150
+ };
151
+ await this.use(address, params);
152
+ }
100
153
  async register() {
101
154
  if (this.type === NodeType.LEADER) {
102
155
  this.logger.debug('Skipping registration, node is leader');
@@ -117,18 +170,7 @@ export class oNode extends oToolBase {
117
170
  this.logger.debug('Registering node with leader...');
118
171
  }
119
172
  await this.registerParent();
120
- const address = oAddress.registry();
121
- const params = {
122
- method: 'commit',
123
- params: {
124
- peerId: this.peerId.toString(),
125
- address: this.address.toString(),
126
- protocols: this.p2pNode.getProtocols(),
127
- transports: this.transports,
128
- staticAddress: this.staticAddress.toString(),
129
- },
130
- };
131
- await this.use(address, params);
173
+ await this.registerLeader();
132
174
  this.logger.debug('Registration successful');
133
175
  }
134
176
  extractMethod(address) {
@@ -136,11 +178,10 @@ export class oNode extends oToolBase {
136
178
  }
137
179
  async start() {
138
180
  await super.start();
139
- // await NetworkUtils.advertiseToNetwork(
140
- // this.address,
141
- // this.staticAddress,
142
- // this.p2pNode,
143
- // );
181
+ // Start heartbeat after node is running
182
+ if (this.connectionHeartbeatManager) {
183
+ await this.connectionHeartbeatManager.start();
184
+ }
144
185
  }
145
186
  async validateJoinRequest(request) {
146
187
  return true;
@@ -190,12 +231,32 @@ export class oNode extends oToolBase {
190
231
  // ];
191
232
  // // let's make sure we only allow communication through the parent transports
192
233
  params.connectionGater = {
234
+ denyDialPeer: (peerId) => {
235
+ // we can call the leader
236
+ if (this.config.leader?.libp2pTransports.some((t) => t.toPeerId() === peerId.toString())) {
237
+ return false;
238
+ }
239
+ // we can call our parent
240
+ if (this.parentPeerId === peerId.toString()) {
241
+ return false;
242
+ }
243
+ // we can call our children
244
+ if (this.hierarchyManager.children.some((c) => c.libp2pTransports.some((t) => t.toPeerId() === peerId.toString()))) {
245
+ return false;
246
+ }
247
+ // check for standalone node
248
+ if (!this.config.parent && !this.config.leader) {
249
+ return false;
250
+ }
251
+ return true;
252
+ },
193
253
  // who can call us?
194
254
  denyInboundEncryptedConnection: (peerId, maConn) => {
195
255
  // deny all inbound connections unless they are from a parent transport
196
256
  if (this.parentPeerId === peerId.toString()) {
197
257
  return false;
198
258
  }
259
+ // allow connections from children (for ping)
199
260
  if (this.hierarchyManager.children.some((c) => c.libp2pTransports.some((t) => t.toPeerId() === peerId.toString()))) {
200
261
  return false;
201
262
  }
@@ -223,17 +284,13 @@ export class oNode extends oToolBase {
223
284
  this.p2pNode = await createNode(params);
224
285
  return this.p2pNode;
225
286
  }
226
- async connect(nextHopAddress, targetAddress) {
287
+ async connect(config) {
227
288
  if (!this.connectionManager) {
228
289
  this.logger.error('Connection manager not initialized');
229
290
  throw new Error('Node is not ready to connect to other nodes');
230
291
  }
231
292
  const connection = await this.connectionManager
232
- .connect({
233
- address: targetAddress,
234
- nextHopAddress,
235
- callerAddress: this.address,
236
- })
293
+ .connect(config)
237
294
  .catch((error) => {
238
295
  // TODO: we need to handle this better and document
239
296
  if (error.message === 'Can not dial self') {
@@ -246,6 +303,43 @@ export class oNode extends oToolBase {
246
303
  }
247
304
  return connection;
248
305
  }
306
+ async initConnectionManager() {
307
+ this.connectionManager = new oNodeConnectionManager({
308
+ p2pNode: this.p2pNode,
309
+ defaultReadTimeoutMs: this.config.connectionTimeouts?.readTimeoutMs,
310
+ defaultDrainTimeoutMs: this.config.connectionTimeouts?.drainTimeoutMs,
311
+ runOnLimitedConnection: this.config.runOnLimitedConnection ?? false,
312
+ });
313
+ }
314
+ async initReconnectionManager() {
315
+ // Initialize reconnection manager
316
+ if (this.config.reconnection?.enabled !== false) {
317
+ this.reconnectionManager = new oReconnectionManager(this, {
318
+ enabled: true,
319
+ maxAttempts: this.config.reconnection?.maxAttempts ?? 10,
320
+ baseDelayMs: this.config.reconnection?.baseDelayMs ?? 5000,
321
+ maxDelayMs: this.config.reconnection?.maxDelayMs ?? 60000,
322
+ useLeaderFallback: this.config.reconnection?.useLeaderFallback ?? true,
323
+ parentDiscoveryIntervalMs: this.config.reconnection?.parentDiscoveryIntervalMs ?? 10000,
324
+ parentDiscoveryMaxDelayMs: this.config.reconnection?.parentDiscoveryMaxDelayMs ?? 60000,
325
+ });
326
+ }
327
+ }
328
+ async hookInitializeFinished() { }
329
+ async hookStartFinished() {
330
+ // Initialize connection heartbeat manager
331
+ this.connectionHeartbeatManager = new oConnectionHeartbeatManager(this, {
332
+ enabled: this.config.connectionHeartbeat?.enabled ?? true,
333
+ intervalMs: this.config.connectionHeartbeat?.intervalMs ?? 15000,
334
+ timeoutMs: this.config.connectionHeartbeat?.timeoutMs ?? 15000,
335
+ failureThreshold: this.config.connectionHeartbeat?.failureThreshold ?? 3,
336
+ checkChildren: this.config.connectionHeartbeat?.checkChildren ?? false,
337
+ checkParent: this.config.connectionHeartbeat?.checkParent ?? true,
338
+ checkLeader: true,
339
+ });
340
+ this.logger.info(`Connection heartbeat config: leader=${this.connectionHeartbeatManager.getConfig().checkLeader}, ` +
341
+ `parent=${this.connectionHeartbeatManager.getConfig().checkParent}`);
342
+ }
249
343
  async initialize() {
250
344
  this.logger.debug('Initializing node...');
251
345
  if (this.p2pNode && this.state !== NodeState.STOPPED) {
@@ -256,13 +350,13 @@ export class oNode extends oToolBase {
256
350
  }
257
351
  await this.createNode();
258
352
  await this.initializeRouter();
353
+ // need to wait until our libpp2 node is initialized before calling super.initialize
354
+ await super.initialize();
259
355
  this.logger.debug('Node initialized!', this.transports.map((t) => t.toString()));
260
356
  this.address.setTransports(this.transports);
261
357
  this.peerId = this.p2pNode.peerId;
262
358
  // initialize connection manager
263
- this.connectionManager = new oNodeConnectionManager({
264
- p2pNode: this.p2pNode,
265
- });
359
+ await this.initConnectionManager();
266
360
  // initialize address resolution
267
361
  this.router.addResolver(new oMethodResolver(this.address));
268
362
  this.router.addResolver(new oNodeResolver(this.address));
@@ -271,12 +365,130 @@ export class oNode extends oToolBase {
271
365
  this.logger.debug('Adding leader resolver fallback...');
272
366
  this.router.addResolver(new oLeaderResolverFallback(this.address));
273
367
  }
368
+ // initialize reconnection manager
369
+ await this.initReconnectionManager();
370
+ await this.hookInitializeFinished();
274
371
  }
372
+ /**
373
+ * Override use() to wrap leader/registry requests with retry logic
374
+ */
375
+ // async use(
376
+ // address: oAddress,
377
+ // data?: {
378
+ // method?: string;
379
+ // params?: { [key: string]: any };
380
+ // id?: string;
381
+ // },
382
+ // options?: UseOptions,
383
+ // ): Promise<any> {
384
+ // // Wrap leader/registry requests with retry logic
385
+ // return super.use(address, data, options),
386
+ // address,
387
+ // data?.method,
388
+ // }
275
389
  async teardown() {
390
+ // Stop heartbeat before parent teardown
391
+ if (this.connectionHeartbeatManager) {
392
+ await this.connectionHeartbeatManager.stop();
393
+ }
276
394
  await this.unregister();
277
395
  await super.teardown();
278
396
  if (this.p2pNode) {
279
397
  await this.p2pNode.stop();
280
398
  }
281
399
  }
400
+ // IHeartbeatableNode interface methods
401
+ getLeaders() {
402
+ return [this.leader];
403
+ }
404
+ getParents() {
405
+ return this.hierarchyManager.getParents();
406
+ }
407
+ getChildren() {
408
+ return this.hierarchyManager.getChildren();
409
+ }
410
+ removeChild(childAddress) {
411
+ this.hierarchyManager.removeChild(childAddress);
412
+ }
413
+ /**
414
+ * Get the total number of active streams across all connections
415
+ * @returns Total count of active streams
416
+ */
417
+ getStreamCount() {
418
+ if (!this.p2pNode) {
419
+ return 0;
420
+ }
421
+ const connections = this.p2pNode.getConnections();
422
+ return connections.reduce((count, conn) => {
423
+ return count + (conn.streams?.length || 0);
424
+ }, 0);
425
+ }
426
+ /**
427
+ * Get libp2p metrics for this node
428
+ * Tool method that can be called remotely by monitoring systems
429
+ */
430
+ async _tool_get_libp2p_metrics(request) {
431
+ if (!this.p2pNode) {
432
+ return {
433
+ error: 'libp2p node not available',
434
+ available: false,
435
+ };
436
+ }
437
+ try {
438
+ // Get basic connection stats
439
+ const connections = this.p2pNode.getConnections();
440
+ const peers = await this.p2pNode.peerStore.all();
441
+ const inbound = connections.filter((c) => c.direction === 'inbound').length;
442
+ const outbound = connections.filter((c) => c.direction === 'outbound').length;
443
+ // Get DHT info if available
444
+ const services = this.p2pNode.services;
445
+ const dht = services?.dht;
446
+ const routingTable = dht?.routingTable;
447
+ const kBuckets = routingTable?.kb || null;
448
+ let routingTableSize = 0;
449
+ if (kBuckets) {
450
+ // Handle both array and object-like structures
451
+ if (Array.isArray(kBuckets)) {
452
+ for (const bucket of kBuckets) {
453
+ routingTableSize += bucket.peers?.length || 0;
454
+ }
455
+ }
456
+ else if (typeof kBuckets === 'object') {
457
+ // If it's an object, iterate over its values
458
+ for (const bucket of Object.values(kBuckets)) {
459
+ routingTableSize += bucket?.peers?.length || 0;
460
+ }
461
+ }
462
+ }
463
+ // Calculate total stream count across all connections
464
+ const streamCount = connections.reduce((count, conn) => {
465
+ return count + (conn.streams?.length || 0);
466
+ }, 0);
467
+ return {
468
+ available: true,
469
+ timestamp: Date.now(),
470
+ nodeAddress: this.address.toString(),
471
+ peerCount: peers.length,
472
+ connectionCount: connections.length,
473
+ inboundConnections: inbound,
474
+ outboundConnections: outbound,
475
+ streamCount,
476
+ dhtEnabled: !!dht,
477
+ dhtMode: dht?.clientMode ? 'client' : 'server',
478
+ dhtRoutingTableSize: routingTableSize,
479
+ protocols: Array.from(this.p2pNode.getProtocols()),
480
+ selfPeerId: this.p2pNode.peerId.toString(),
481
+ multiaddrs: this.p2pNode
482
+ .getMultiaddrs()
483
+ .map((ma) => ma.toString()),
484
+ };
485
+ }
486
+ catch (error) {
487
+ return {
488
+ error: `Failed to collect libp2p metrics: ${error.message}`,
489
+ available: false,
490
+ nodeAddress: this.address.toString(),
491
+ };
492
+ }
493
+ }
282
494
  }
@@ -0,0 +1,52 @@
1
+ import { Libp2p } from '@olane/o-config';
2
+ import { oNotificationManager } from '@olane/o-core';
3
+ import { oNodeAddress } from './router/o-node.address.js';
4
+ import { oNodeHierarchyManager } from './o-node.hierarchy-manager.js';
5
+ /**
6
+ * libp2p-specific implementation of oNotificationManager
7
+ * Wraps libp2p events and enriches them with Olane context
8
+ */
9
+ export declare class oNodeNotificationManager extends oNotificationManager {
10
+ private p2pNode;
11
+ private hierarchyManager;
12
+ private address;
13
+ constructor(p2pNode: Libp2p, hierarchyManager: oNodeHierarchyManager, address: oNodeAddress);
14
+ /**
15
+ * Wire up libp2p event listeners
16
+ */
17
+ protected setupListeners(): void;
18
+ /**
19
+ * Handle peer connect event from libp2p
20
+ */
21
+ private handlePeerConnect;
22
+ /**
23
+ * Handle peer disconnect event from libp2p
24
+ */
25
+ private handlePeerDisconnect;
26
+ /**
27
+ * Handle peer discovery event from libp2p
28
+ */
29
+ private handlePeerDiscovery;
30
+ /**
31
+ * Handle connection open event from libp2p
32
+ */
33
+ private handleConnectionOpen;
34
+ /**
35
+ * Handle connection close event from libp2p
36
+ */
37
+ private handleConnectionClose;
38
+ /**
39
+ * Try to resolve a libp2p peer ID to an Olane address
40
+ * Checks hierarchy manager for known peers
41
+ */
42
+ private peerIdToAddress;
43
+ /**
44
+ * Check if an address is a direct child
45
+ */
46
+ private isChild;
47
+ /**
48
+ * Check if an address is a parent
49
+ */
50
+ private isParent;
51
+ }
52
+ //# sourceMappingURL=o-node.notification-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"o-node.notification-manager.d.ts","sourceRoot":"","sources":["../../src/o-node.notification-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EACL,oBAAoB,EAQrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAEtE;;;GAGG;AACH,qBAAa,wBAAyB,SAAQ,oBAAoB;IAE9D,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,gBAAgB;IACxB,OAAO,CAAC,OAAO;gBAFP,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,qBAAqB,EACvC,OAAO,EAAE,YAAY;IAK/B;;OAEG;IACH,SAAS,CAAC,cAAc,IAAI,IAAI;IAgChC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAkDzB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAoD5B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAmB3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAI5B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAI7B;;;OAGG;IACH,OAAO,CAAC,eAAe;IAkCvB;;OAEG;IACH,OAAO,CAAC,OAAO;IAMf;;OAEG;IACH,OAAO,CAAC,QAAQ;CAKjB"}