@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.
- package/dist/src/connection/interfaces/o-node-connection-manager.config.d.ts +1 -0
- package/dist/src/connection/interfaces/o-node-connection-manager.config.d.ts.map +1 -1
- package/dist/src/connection/interfaces/o-node-connection.config.d.ts +1 -0
- package/dist/src/connection/interfaces/o-node-connection.config.d.ts.map +1 -1
- package/dist/src/connection/o-node-connection.d.ts +7 -2
- package/dist/src/connection/o-node-connection.d.ts.map +1 -1
- package/dist/src/connection/o-node-connection.js +55 -32
- package/dist/src/connection/o-node-connection.manager.d.ts +19 -5
- package/dist/src/connection/o-node-connection.manager.d.ts.map +1 -1
- package/dist/src/connection/o-node-connection.manager.js +81 -65
- package/dist/src/connection/o-stream.request.d.ts +11 -0
- package/dist/src/connection/o-stream.request.d.ts.map +1 -0
- package/dist/src/connection/o-stream.request.js +7 -0
- package/dist/src/connection/stream-handler.config.d.ts +46 -0
- package/dist/src/connection/stream-handler.config.d.ts.map +1 -0
- package/dist/src/connection/stream-handler.config.js +1 -0
- package/dist/src/connection/stream-handler.d.ts +97 -0
- package/dist/src/connection/stream-handler.d.ts.map +1 -0
- package/dist/src/connection/stream-handler.js +303 -0
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -1
- package/dist/src/interfaces/i-heartbeatable-node.d.ts +49 -0
- package/dist/src/interfaces/i-heartbeatable-node.d.ts.map +1 -0
- package/dist/src/interfaces/i-heartbeatable-node.js +1 -0
- package/dist/src/interfaces/i-reconnectable-node.d.ts +5 -0
- package/dist/src/interfaces/i-reconnectable-node.d.ts.map +1 -1
- package/dist/src/interfaces/o-node.config.d.ts +16 -8
- package/dist/src/interfaces/o-node.config.d.ts.map +1 -1
- package/dist/src/managers/o-connection-heartbeat.manager.d.ts +5 -9
- package/dist/src/managers/o-connection-heartbeat.manager.d.ts.map +1 -1
- package/dist/src/managers/o-connection-heartbeat.manager.js +51 -47
- package/dist/src/managers/o-reconnection.manager.d.ts +12 -0
- package/dist/src/managers/o-reconnection.manager.d.ts.map +1 -1
- package/dist/src/managers/o-reconnection.manager.js +138 -22
- package/dist/src/o-node.d.ts +22 -11
- package/dist/src/o-node.d.ts.map +1 -1
- package/dist/src/o-node.js +202 -72
- package/dist/src/o-node.notification-manager.d.ts.map +1 -1
- package/dist/src/o-node.notification-manager.js +17 -12
- package/dist/src/o-node.tool.d.ts +1 -0
- package/dist/src/o-node.tool.d.ts.map +1 -1
- package/dist/src/o-node.tool.js +18 -34
- package/dist/src/router/o-node.router.d.ts +1 -0
- package/dist/src/router/o-node.router.d.ts.map +1 -1
- package/dist/src/router/o-node.router.js +62 -9
- package/dist/src/router/o-node.routing-policy.d.ts.map +1 -1
- package/dist/src/router/o-node.routing-policy.js +7 -2
- package/dist/src/router/resolvers/o-node.resolver.d.ts.map +1 -1
- package/dist/src/router/resolvers/o-node.resolver.js +5 -1
- package/dist/src/router/resolvers/o-node.search-resolver.d.ts.map +1 -1
- package/dist/src/router/resolvers/o-node.search-resolver.js +34 -9
- package/dist/src/utils/index.d.ts +3 -0
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js +2 -0
- package/dist/src/utils/stream.utils.d.ts +6 -0
- package/dist/src/utils/stream.utils.d.ts.map +1 -0
- package/dist/src/utils/stream.utils.js +31 -0
- package/dist/test/helpers/test-node.tool.d.ts +15 -0
- package/dist/test/helpers/test-node.tool.d.ts.map +1 -0
- package/dist/test/helpers/test-node.tool.js +27 -0
- package/package.json +6 -6
- package/dist/src/router/resolvers/o-node.child-resolver.d.ts +0 -11
- package/dist/src/router/resolvers/o-node.child-resolver.d.ts.map +0 -1
- package/dist/src/router/resolvers/o-node.child-resolver.js +0 -58
- package/dist/src/utils/leader-request-wrapper.d.ts +0 -45
- package/dist/src/utils/leader-request-wrapper.d.ts.map +0 -1
- package/dist/src/utils/leader-request-wrapper.js +0 -89
- package/dist/test/o-node.spec.d.ts +0 -2
- package/dist/test/o-node.spec.d.ts.map +0 -1
- package/dist/test/o-node.spec.js +0 -20
- package/dist/test/search-resolver.spec.d.ts +0 -2
- package/dist/test/search-resolver.spec.d.ts.map +0 -1
- package/dist/test/search-resolver.spec.js +0 -693
package/dist/src/o-node.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
116
|
-
this.logger.debug('Registering node with parent...', this.config.parent);
|
|
117
|
-
|
|
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
|
-
|
|
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(
|
|
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.
|
|
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
|
-
//
|
|
322
|
-
|
|
323
|
-
|
|
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(
|
|
350
|
-
|
|
351
|
-
|
|
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;
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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"}
|
package/dist/src/o-node.tool.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
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:
|
|
16
|
-
maxOutboundStreams:
|
|
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
|
-
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this.
|
|
34
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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"}
|