@olane/o-node 0.7.12-alpha.6 → 0.7.12-alpha.61

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 (74) 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 +7 -2
  6. package/dist/src/connection/o-node-connection.d.ts.map +1 -1
  7. package/dist/src/connection/o-node-connection.js +55 -32
  8. package/dist/src/connection/o-node-connection.manager.d.ts +19 -5
  9. package/dist/src/connection/o-node-connection.manager.d.ts.map +1 -1
  10. package/dist/src/connection/o-node-connection.manager.js +81 -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/connection/stream-handler.config.d.ts +46 -0
  15. package/dist/src/connection/stream-handler.config.d.ts.map +1 -0
  16. package/dist/src/connection/stream-handler.config.js +1 -0
  17. package/dist/src/connection/stream-handler.d.ts +97 -0
  18. package/dist/src/connection/stream-handler.d.ts.map +1 -0
  19. package/dist/src/connection/stream-handler.js +303 -0
  20. package/dist/src/index.d.ts +2 -1
  21. package/dist/src/index.d.ts.map +1 -1
  22. package/dist/src/index.js +2 -1
  23. package/dist/src/interfaces/i-heartbeatable-node.d.ts +49 -0
  24. package/dist/src/interfaces/i-heartbeatable-node.d.ts.map +1 -0
  25. package/dist/src/interfaces/i-heartbeatable-node.js +1 -0
  26. package/dist/src/interfaces/i-reconnectable-node.d.ts +5 -0
  27. package/dist/src/interfaces/i-reconnectable-node.d.ts.map +1 -1
  28. package/dist/src/interfaces/o-node.config.d.ts +16 -8
  29. package/dist/src/interfaces/o-node.config.d.ts.map +1 -1
  30. package/dist/src/managers/o-connection-heartbeat.manager.d.ts +5 -9
  31. package/dist/src/managers/o-connection-heartbeat.manager.d.ts.map +1 -1
  32. package/dist/src/managers/o-connection-heartbeat.manager.js +51 -47
  33. package/dist/src/managers/o-reconnection.manager.d.ts +12 -0
  34. package/dist/src/managers/o-reconnection.manager.d.ts.map +1 -1
  35. package/dist/src/managers/o-reconnection.manager.js +138 -22
  36. package/dist/src/o-node.d.ts +22 -11
  37. package/dist/src/o-node.d.ts.map +1 -1
  38. package/dist/src/o-node.js +202 -72
  39. package/dist/src/o-node.notification-manager.d.ts.map +1 -1
  40. package/dist/src/o-node.notification-manager.js +17 -12
  41. package/dist/src/o-node.tool.d.ts +1 -0
  42. package/dist/src/o-node.tool.d.ts.map +1 -1
  43. package/dist/src/o-node.tool.js +18 -34
  44. package/dist/src/router/o-node.router.d.ts +1 -0
  45. package/dist/src/router/o-node.router.d.ts.map +1 -1
  46. package/dist/src/router/o-node.router.js +62 -9
  47. package/dist/src/router/o-node.routing-policy.d.ts.map +1 -1
  48. package/dist/src/router/o-node.routing-policy.js +7 -2
  49. package/dist/src/router/resolvers/o-node.resolver.d.ts.map +1 -1
  50. package/dist/src/router/resolvers/o-node.resolver.js +5 -1
  51. package/dist/src/router/resolvers/o-node.search-resolver.d.ts.map +1 -1
  52. package/dist/src/router/resolvers/o-node.search-resolver.js +34 -9
  53. package/dist/src/utils/index.d.ts +3 -0
  54. package/dist/src/utils/index.d.ts.map +1 -0
  55. package/dist/src/utils/index.js +2 -0
  56. package/dist/src/utils/stream.utils.d.ts +6 -0
  57. package/dist/src/utils/stream.utils.d.ts.map +1 -0
  58. package/dist/src/utils/stream.utils.js +31 -0
  59. package/dist/test/helpers/test-node.tool.d.ts +15 -0
  60. package/dist/test/helpers/test-node.tool.d.ts.map +1 -0
  61. package/dist/test/helpers/test-node.tool.js +27 -0
  62. package/package.json +6 -6
  63. package/dist/src/router/resolvers/o-node.child-resolver.d.ts +0 -11
  64. package/dist/src/router/resolvers/o-node.child-resolver.d.ts.map +0 -1
  65. package/dist/src/router/resolvers/o-node.child-resolver.js +0 -58
  66. package/dist/src/utils/leader-request-wrapper.d.ts +0 -45
  67. package/dist/src/utils/leader-request-wrapper.d.ts.map +0 -1
  68. package/dist/src/utils/leader-request-wrapper.js +0 -89
  69. package/dist/test/o-node.spec.d.ts +0 -2
  70. package/dist/test/o-node.spec.d.ts.map +0 -1
  71. package/dist/test/o-node.spec.js +0 -20
  72. package/dist/test/search-resolver.spec.d.ts +0 -2
  73. package/dist/test/search-resolver.spec.d.ts.map +0 -1
  74. package/dist/test/search-resolver.spec.js +0 -693
@@ -12,7 +12,6 @@ import { oLeaderResolverFallback } from './router/index.js';
12
12
  import { oNodeNotificationManager } from './o-node.notification-manager.js';
13
13
  import { oConnectionHeartbeatManager } from './managers/o-connection-heartbeat.manager.js';
14
14
  import { oReconnectionManager } from './managers/o-reconnection.manager.js';
15
- import { LeaderRequestWrapper } from './utils/leader-request-wrapper.js';
16
15
  export class oNode extends oToolBase {
17
16
  constructor(config) {
18
17
  super(config);
@@ -103,18 +102,30 @@ export class oNode extends oToolBase {
103
102
  peerId: this.peerId.toString(),
104
103
  },
105
104
  };
106
- await this.use(address, params);
105
+ this.use(address, params).catch((error) => {
106
+ this.logger.error('Failed to unregister from network:', error);
107
+ });
107
108
  }
108
109
  async registerParent() {
109
110
  if (this.type === NodeType.LEADER) {
110
111
  this.logger.debug('Skipping parent registration, node is leader');
111
112
  return;
112
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
+ }
113
124
  // if no parent transports, register with the parent to get them
114
125
  // TODO: should we remove the transports check to make this more consistent?
115
- if (this.config.parent && this.config.parent.transports.length === 0) {
116
- this.logger.debug('Registering node with parent...', this.config.parent);
117
- 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, {
118
129
  method: 'child_register',
119
130
  params: {
120
131
  address: this.address.toString(),
@@ -123,11 +134,22 @@ export class oNode extends oToolBase {
123
134
  _token: this.config.joinToken,
124
135
  },
125
136
  });
126
- const { parentTransports } = parentRegistration.result.data;
127
- // update the parent transports
128
- this.config.parent.setTransports(parentTransports.map((t) => new oNodeTransport(t)));
129
137
  }
130
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
+ }
131
153
  async register() {
132
154
  if (this.type === NodeType.LEADER) {
133
155
  this.logger.debug('Skipping registration, node is leader');
@@ -148,18 +170,7 @@ export class oNode extends oToolBase {
148
170
  this.logger.debug('Registering node with leader...');
149
171
  }
150
172
  await this.registerParent();
151
- const address = oAddress.registry();
152
- const params = {
153
- method: 'commit',
154
- params: {
155
- peerId: this.peerId.toString(),
156
- address: this.address.toString(),
157
- protocols: this.p2pNode.getProtocols(),
158
- transports: this.transports,
159
- staticAddress: this.staticAddress.toString(),
160
- },
161
- };
162
- await this.use(address, params);
173
+ await this.registerLeader();
163
174
  this.logger.debug('Registration successful');
164
175
  }
165
176
  extractMethod(address) {
@@ -171,11 +182,6 @@ export class oNode extends oToolBase {
171
182
  if (this.connectionHeartbeatManager) {
172
183
  await this.connectionHeartbeatManager.start();
173
184
  }
174
- // await NetworkUtils.advertiseToNetwork(
175
- // this.address,
176
- // this.staticAddress,
177
- // this.p2pNode,
178
- // );
179
185
  }
180
186
  async validateJoinRequest(request) {
181
187
  return true;
@@ -225,12 +231,32 @@ export class oNode extends oToolBase {
225
231
  // ];
226
232
  // // let's make sure we only allow communication through the parent transports
227
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
+ },
228
253
  // who can call us?
229
254
  denyInboundEncryptedConnection: (peerId, maConn) => {
230
255
  // deny all inbound connections unless they are from a parent transport
231
256
  if (this.parentPeerId === peerId.toString()) {
232
257
  return false;
233
258
  }
259
+ // allow connections from children (for ping)
234
260
  if (this.hierarchyManager.children.some((c) => c.libp2pTransports.some((t) => t.toPeerId() === peerId.toString()))) {
235
261
  return false;
236
262
  }
@@ -258,17 +284,13 @@ export class oNode extends oToolBase {
258
284
  this.p2pNode = await createNode(params);
259
285
  return this.p2pNode;
260
286
  }
261
- async connect(nextHopAddress, targetAddress) {
287
+ async connect(config) {
262
288
  if (!this.connectionManager) {
263
289
  this.logger.error('Connection manager not initialized');
264
290
  throw new Error('Node is not ready to connect to other nodes');
265
291
  }
266
292
  const connection = await this.connectionManager
267
- .connect({
268
- address: targetAddress,
269
- nextHopAddress,
270
- callerAddress: this.address,
271
- })
293
+ .connect(config)
272
294
  .catch((error) => {
273
295
  // TODO: we need to handle this better and document
274
296
  if (error.message === 'Can not dial self') {
@@ -281,6 +303,43 @@ export class oNode extends oToolBase {
281
303
  }
282
304
  return connection;
283
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
+ }
284
343
  async initialize() {
285
344
  this.logger.debug('Initializing node...');
286
345
  if (this.p2pNode && this.state !== NodeState.STOPPED) {
@@ -297,19 +356,7 @@ export class oNode extends oToolBase {
297
356
  this.address.setTransports(this.transports);
298
357
  this.peerId = this.p2pNode.peerId;
299
358
  // initialize connection manager
300
- this.connectionManager = new oNodeConnectionManager({
301
- p2pNode: this.p2pNode,
302
- });
303
- // Initialize leader request wrapper
304
- this.leaderRequestWrapper = new LeaderRequestWrapper({
305
- enabled: this.config.leaderRetry?.enabled ?? true,
306
- maxAttempts: this.config.leaderRetry?.maxAttempts ?? 20,
307
- baseDelayMs: this.config.leaderRetry?.baseDelayMs ?? 2000,
308
- maxDelayMs: this.config.leaderRetry?.maxDelayMs ?? 30000,
309
- timeoutMs: this.config.leaderRetry?.timeoutMs ?? 10000,
310
- });
311
- this.logger.info(`Leader retry config: enabled=${this.leaderRequestWrapper.getConfig().enabled}, ` +
312
- `maxAttempts=${this.leaderRequestWrapper.getConfig().maxAttempts}`);
359
+ await this.initConnectionManager();
313
360
  // initialize address resolution
314
361
  this.router.addResolver(new oMethodResolver(this.address));
315
362
  this.router.addResolver(new oNodeResolver(this.address));
@@ -318,38 +365,27 @@ export class oNode extends oToolBase {
318
365
  this.logger.debug('Adding leader resolver fallback...');
319
366
  this.router.addResolver(new oLeaderResolverFallback(this.address));
320
367
  }
321
- // Read ENABLE_LEADER_HEARTBEAT environment variable
322
- const enableLeaderHeartbeat = process.env.ENABLE_LEADER_HEARTBEAT === 'true';
323
- // Initialize connection heartbeat manager
324
- this.connectionHeartbeatManager = new oConnectionHeartbeatManager(this.p2pNode, this.hierarchyManager, this.notificationManager, this.address, {
325
- enabled: this.config.connectionHeartbeat?.enabled ?? true,
326
- intervalMs: this.config.connectionHeartbeat?.intervalMs ?? 15000,
327
- timeoutMs: this.config.connectionHeartbeat?.timeoutMs ?? 5000,
328
- failureThreshold: this.config.connectionHeartbeat?.failureThreshold ?? 3,
329
- checkChildren: this.config.connectionHeartbeat?.checkChildren ?? true,
330
- checkParent: this.config.connectionHeartbeat?.checkParent ?? true,
331
- checkLeader: this.config.connectionHeartbeat?.checkLeader ?? enableLeaderHeartbeat,
332
- });
333
- this.logger.info(`Connection heartbeat config: leader=${this.connectionHeartbeatManager.getConfig().checkLeader}, ` +
334
- `parent=${this.connectionHeartbeatManager.getConfig().checkParent}`);
335
- // Initialize reconnection manager
336
- if (this.config.reconnection?.enabled !== false) {
337
- this.reconnectionManager = new oReconnectionManager(this, {
338
- enabled: true,
339
- maxAttempts: this.config.reconnection?.maxAttempts ?? 10,
340
- baseDelayMs: this.config.reconnection?.baseDelayMs ?? 5000,
341
- maxDelayMs: this.config.reconnection?.maxDelayMs ?? 60000,
342
- useLeaderFallback: this.config.reconnection?.useLeaderFallback ?? true,
343
- });
344
- }
368
+ // initialize reconnection manager
369
+ await this.initReconnectionManager();
370
+ await this.hookInitializeFinished();
345
371
  }
346
372
  /**
347
373
  * Override use() to wrap leader/registry requests with retry logic
348
374
  */
349
- async use(address, data) {
350
- // Wrap leader/registry requests with retry logic
351
- return this.leaderRequestWrapper.execute(() => super.use(address, data), address, data?.method);
352
- }
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
+ // }
353
389
  async teardown() {
354
390
  // Stop heartbeat before parent teardown
355
391
  if (this.connectionHeartbeatManager) {
@@ -361,4 +397,98 @@ export class oNode extends oToolBase {
361
397
  await this.p2pNode.stop();
362
398
  }
363
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
+ }
364
494
  }
@@ -1 +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;IAiBhC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAgDzB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAkD5B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAmB3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAK5B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAK7B;;;OAGG;IACH,OAAO,CAAC,eAAe;IAkCvB;;OAEG;IACH,OAAO,CAAC,OAAO;IAMf;;OAEG;IACH,OAAO,CAAC,QAAQ;CAKjB"}
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"}
@@ -17,7 +17,10 @@ export class oNodeNotificationManager extends oNotificationManager {
17
17
  this.logger.debug('Setting up libp2p event listeners...');
18
18
  // Peer connection events
19
19
  this.p2pNode.addEventListener('peer:connect', this.handlePeerConnect.bind(this));
20
- this.p2pNode.addEventListener('peer:disconnect', this.handlePeerDisconnect.bind(this));
20
+ // this.p2pNode.addEventListener(
21
+ // 'peer:disconnect',
22
+ // this.handlePeerDisconnect.bind(this),
23
+ // );
21
24
  // Peer discovery events
22
25
  this.p2pNode.addEventListener('peer:discovery', this.handlePeerDiscovery.bind(this));
23
26
  // Connection events
@@ -30,11 +33,13 @@ export class oNodeNotificationManager extends oNotificationManager {
30
33
  */
31
34
  handlePeerConnect(evt) {
32
35
  const peerId = evt.detail;
33
- this.logger.debug(`Peer connected: ${peerId.toString()}`);
36
+ // this.logger.debug(`Peer connected: ${peerId.toString()}`);
34
37
  // Try to resolve peer ID to Olane address
35
38
  const nodeAddress = this.peerIdToAddress(peerId.toString());
36
39
  if (!nodeAddress) {
37
- this.logger.debug(`Could not resolve peer ID ${peerId.toString()} to address`);
40
+ // this.logger.debug(
41
+ // `Could not resolve peer ID ${peerId.toString()} to address`,
42
+ // );
38
43
  return;
39
44
  }
40
45
  // Emit generic node connected event
@@ -48,7 +53,7 @@ export class oNodeNotificationManager extends oNotificationManager {
48
53
  }));
49
54
  // Check if this is a child node
50
55
  if (this.isChild(nodeAddress)) {
51
- this.logger.debug(`Child node connected: ${nodeAddress.toString()}`);
56
+ // this.logger.debug(`Child node connected: ${nodeAddress.toString()}`);
52
57
  this.emit(new ChildJoinedEvent({
53
58
  source: this.address,
54
59
  childAddress: nodeAddress,
@@ -57,7 +62,7 @@ export class oNodeNotificationManager extends oNotificationManager {
57
62
  }
58
63
  // Check if this is a parent node
59
64
  if (this.isParent(nodeAddress)) {
60
- this.logger.debug(`Parent node connected: ${nodeAddress.toString()}`);
65
+ // this.logger.debug(`Parent node connected: ${nodeAddress.toString()}`);
61
66
  this.emit(new ParentConnectedEvent({
62
67
  source: this.address,
63
68
  parentAddress: nodeAddress,
@@ -69,11 +74,13 @@ export class oNodeNotificationManager extends oNotificationManager {
69
74
  */
70
75
  handlePeerDisconnect(evt) {
71
76
  const peerId = evt.detail;
72
- this.logger.debug(`Peer disconnected: ${peerId.toString()}`);
77
+ // this.logger.debug(`Peer disconnected: ${peerId.toString()}`);
73
78
  // Try to resolve peer ID to Olane address
74
79
  const nodeAddress = this.peerIdToAddress(peerId.toString());
75
80
  if (!nodeAddress) {
76
- this.logger.debug(`Could not resolve peer ID ${peerId.toString()} to address`);
81
+ // this.logger.debug(
82
+ // `Could not resolve peer ID ${peerId.toString()} to address`,
83
+ // );
77
84
  return;
78
85
  }
79
86
  // Emit generic node disconnected event
@@ -109,7 +116,7 @@ export class oNodeNotificationManager extends oNotificationManager {
109
116
  */
110
117
  handlePeerDiscovery(evt) {
111
118
  const peerInfo = evt.detail;
112
- this.logger.debug(`Peer discovered: ${peerInfo.id.toString()}`);
119
+ // this.logger.debug(`Peer discovered: ${peerInfo.id.toString()}`);
113
120
  // Try to resolve peer ID to Olane address
114
121
  const nodeAddress = this.peerIdToAddress(peerInfo.id.toString());
115
122
  if (!nodeAddress) {
@@ -124,15 +131,13 @@ export class oNodeNotificationManager extends oNotificationManager {
124
131
  * Handle connection open event from libp2p
125
132
  */
126
133
  handleConnectionOpen(evt) {
127
- const remotePeer = evt.detail.remotePeer;
128
- this.logger.debug(`Connection opened to: ${remotePeer.toString()}`);
134
+ // do nothing for now
129
135
  }
130
136
  /**
131
137
  * Handle connection close event from libp2p
132
138
  */
133
139
  handleConnectionClose(evt) {
134
- const remotePeer = evt.detail.remotePeer;
135
- this.logger.debug(`Connection closed to: ${remotePeer.toString()}`);
140
+ // do nothing for now
136
141
  }
137
142
  /**
138
143
  * Try to resolve a libp2p peer ID to an Olane address
@@ -8,6 +8,7 @@ declare const oNodeTool_base: typeof oServerNode;
8
8
  * @returns A new class that extends the base class and implements the oTool interface
9
9
  */
10
10
  export declare class oNodeTool extends oNodeTool_base {
11
+ private streamHandler;
11
12
  handleProtocol(address: oAddress): Promise<void>;
12
13
  initialize(): Promise<void>;
13
14
  handleStream(stream: Stream, connection: Connection): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"o-node.tool.d.ts","sourceRoot":"","sources":["../../src/o-node.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,QAAQ,EAGR,QAAQ,EAGT,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;;AAIrD;;;;GAIG;AACH,qBAAa,SAAU,SAAQ,cAAkB;IACzC,cAAc,CAAC,OAAO,EAAE,QAAQ;IAQhC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAW3B,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAiDnE,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;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,8 +1,9 @@
1
- import { CoreUtils, oError, oErrorCodes, oRequest, ChildJoinedEvent, } from '@olane/o-core';
1
+ import { ChildJoinedEvent, } from '@olane/o-core';
2
2
  import { oTool } from '@olane/o-tool';
3
3
  import { oServerNode } from './nodes/server.node.js';
4
4
  import { oNodeTransport } from './router/o-node.transport.js';
5
5
  import { oNodeAddress } from './router/o-node.address.js';
6
+ import { StreamHandler } from './connection/stream-handler.js';
6
7
  /**
7
8
  * oTool is a mixin that extends the base class and implements the oTool interface
8
9
  * @param Base - The base class to extend
@@ -12,12 +13,15 @@ export class oNodeTool extends oTool(oServerNode) {
12
13
  async handleProtocol(address) {
13
14
  this.logger.debug('Handling protocol: ' + address.protocol);
14
15
  await this.p2pNode.handle(address.protocol, this.handleStream.bind(this), {
15
- maxInboundStreams: Infinity,
16
- maxOutboundStreams: Infinity,
16
+ maxInboundStreams: 10000,
17
+ maxOutboundStreams: process.env.MAX_OUTBOUND_STREAMS
18
+ ? parseInt(process.env.MAX_OUTBOUND_STREAMS)
19
+ : 1000,
17
20
  });
18
21
  }
19
22
  async initialize() {
20
23
  await super.initialize();
24
+ this.streamHandler = new StreamHandler(this.logger);
21
25
  await this.handleProtocol(this.address);
22
26
  if (this.staticAddress &&
23
27
  this.staticAddress?.toString() !== this.address.toString()) {
@@ -25,40 +29,20 @@ export class oNodeTool extends oTool(oServerNode) {
25
29
  }
26
30
  }
27
31
  async handleStream(stream, connection) {
28
- // CRITICAL: Attach message listener immediately to prevent buffer overflow (libp2p v3)
29
- // Per libp2p migration guide: "If no message event handler is added, streams will
30
- // buffer incoming data until a pre-configured limit is reached, after which the stream will be reset."
31
- const messageHandler = async (event) => {
32
- if (!event.data) {
33
- this.logger.warn('Malformed event data');
34
- return;
32
+ this.logger.debug('Handling connection: ', connection.id);
33
+ // Use StreamHandler for consistent stream handling
34
+ // This follows libp2p v3 best practices for immediate message listener attachment
35
+ await this.streamHandler.handleIncomingStream(stream, connection, async (request, stream) => {
36
+ try {
37
+ const result = await this.execute(request, stream);
38
+ // Return the raw result - StreamHandler will build and send the response
39
+ return result;
35
40
  }
36
- const requestConfig = await CoreUtils.processStream(event);
37
- const request = new oRequest(requestConfig);
38
- let success = true;
39
- const result = await this.execute(request, stream).catch((error) => {
41
+ catch (error) {
40
42
  this.logger.error('Error executing tool: ', request.toString(), error, typeof error);
41
- success = false;
42
- const responseError = error instanceof oError
43
- ? error
44
- : new oError(oErrorCodes.UNKNOWN, error.message);
45
- return {
46
- error: responseError.toJSON(),
47
- };
48
- });
49
- if (success) {
50
- this.metrics.successCount++;
43
+ throw error; // StreamHandler will handle error response building
51
44
  }
52
- else {
53
- this.metrics.errorCount++;
54
- }
55
- // compose the response & add the expected connection + request fields
56
- const response = CoreUtils.buildResponse(request, result, result?.error);
57
- // add the request method to the response
58
- await CoreUtils.sendResponse(response, stream);
59
- };
60
- // Attach listener synchronously before any async operations
61
- stream.addEventListener('message', messageHandler);
45
+ });
62
46
  }
63
47
  async _tool_identify() {
64
48
  return {
@@ -17,6 +17,7 @@ export declare class oNodeRouter extends oToolRouter {
17
17
  protected forward(address: oNodeAddress, request: oRouterRequest, node: oNode): Promise<any>;
18
18
  /**
19
19
  * Executes a request locally when routing to self.
20
+ * Now uses ResponseBuilder for consistency with useSelf() behavior.
20
21
  */
21
22
  private executeSelfRouting;
22
23
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"o-node.router.d.ts","sourceRoot":"","sources":["../../../src/router/o-node.router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAML,cAAc,EACd,aAAa,EACd,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAK5C,qBAAa,WAAY,SAAQ,WAAW;IAC1C,OAAO,CAAC,aAAa,CAAqB;;IAO1C;;;;;;;;OAQG;cACa,OAAO,CACrB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,KAAK,GACV,OAAO,CAAC,GAAG,CAAC;IA0Bf;;OAEG;YACW,kBAAkB;IAgBhC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAS5B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAUhC;;OAEG;YACW,eAAe;IA2B7B;;;OAGG;IACG,SAAS,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC,aAAa,CAAC;IAyB3E;;;OAGG;IACH,UAAU,CAAC,qBAAqB,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,GAAG,OAAO;CAGtE"}
1
+ {"version":3,"file":"o-node.router.d.ts","sourceRoot":"","sources":["../../../src/router/o-node.router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAML,cAAc,EAGd,aAAa,EACd,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAM5C,qBAAa,WAAY,SAAQ,WAAW;IAC1C,OAAO,CAAC,aAAa,CAAqB;;IAO1C;;;;;;;;OAQG;cACa,OAAO,CACrB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,KAAK,GACV,OAAO,CAAC,GAAG,CAAC;IA6Bf;;;OAGG;YACW,kBAAkB;IA8DhC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAS5B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAWhC;;OAEG;YACW,eAAe;IAsC7B;;;OAGG;IACG,SAAS,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC,aAAa,CAAC;IAyB3E;;;OAGG;IACH,UAAU,CAAC,qBAAqB,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,GAAG,OAAO;CAGtE"}