@olane/o-node 0.7.12-alpha.9 → 0.7.12
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 +56 -32
- package/dist/src/connection/o-node-connection.manager.d.ts +20 -5
- package/dist/src/connection/o-node-connection.manager.d.ts.map +1 -1
- package/dist/src/connection/o-node-connection.manager.js +95 -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 +98 -0
- package/dist/src/connection/stream-handler.d.ts.map +1 -0
- package/dist/src/connection/stream-handler.js +310 -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 -22
- package/dist/src/interfaces/o-node.config.d.ts.map +1 -1
- package/dist/src/managers/o-connection-heartbeat.manager.d.ts +17 -17
- package/dist/src/managers/o-connection-heartbeat.manager.d.ts.map +1 -1
- package/dist/src/managers/o-connection-heartbeat.manager.js +83 -78
- 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 +23 -12
- package/dist/src/o-node.d.ts.map +1 -1
- package/dist/src/o-node.js +227 -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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createNode, defaultLibp2pConfig, } from '@olane/o-config';
|
|
1
|
+
import { createNode, defaultLibp2pConfig, KEEP_ALIVE, } from '@olane/o-config';
|
|
2
2
|
import { v4 as uuidv4 } from 'uuid';
|
|
3
3
|
import { oNodeRouter } from './router/o-node.router.js';
|
|
4
4
|
import { oNodeHierarchyManager } from './o-node.hierarchy-manager.js';
|
|
@@ -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,54 @@ 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
|
+
});
|
|
108
|
+
}
|
|
109
|
+
async setKeepAliveTag(address) {
|
|
110
|
+
if (!address || !address.libp2pTransports?.length) {
|
|
111
|
+
this.logger.warn('Address has no transports, skipping keep alive tag!', address);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const peers = await this.p2pNode.peerStore.all();
|
|
116
|
+
// find the peer that is already indexed rather than building the PeerId from the string value to avoid browser issues
|
|
117
|
+
const peer = peers.find((p) => p.id.toString() === address.libp2pTransports[0].toPeerId().toString());
|
|
118
|
+
if (!peer) {
|
|
119
|
+
this.logger.warn('Peer not found, skipping keep alive tag!', address);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
await this.p2pNode.peerStore.merge(peer.id, {
|
|
123
|
+
tags: {
|
|
124
|
+
[KEEP_ALIVE]: { value: 100 },
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
this.logger.debug('Set keep alive tag for peer:', peer.id.toString(), ' with address:', address.toString());
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
this.logger.error('Failed to set keep alive tag:', error);
|
|
131
|
+
}
|
|
107
132
|
}
|
|
108
133
|
async registerParent() {
|
|
109
134
|
if (this.type === NodeType.LEADER) {
|
|
110
135
|
this.logger.debug('Skipping parent registration, node is leader');
|
|
111
136
|
return;
|
|
112
137
|
}
|
|
138
|
+
if (!this.parent?.libp2pTransports?.length) {
|
|
139
|
+
this.logger.debug('Parent has no transports, waiting for reconnection & leader ack');
|
|
140
|
+
if (this.parent?.toString() === oAddress.leader().toString()) {
|
|
141
|
+
this.parent.setTransports(this.leader?.libp2pTransports || []);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
this.logger.debug('Waiting for parent and reconnecting...');
|
|
145
|
+
await this.reconnectionManager?.waitForParentAndReconnect();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
113
148
|
// if no parent transports, register with the parent to get them
|
|
114
149
|
// 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
|
-
|
|
150
|
+
if (this.config.parent) {
|
|
151
|
+
this.logger.debug('Registering node with parent...', this.config.parent?.toString());
|
|
152
|
+
await this.use(this.config.parent, {
|
|
118
153
|
method: 'child_register',
|
|
119
154
|
params: {
|
|
120
155
|
address: this.address.toString(),
|
|
@@ -123,11 +158,24 @@ export class oNode extends oToolBase {
|
|
|
123
158
|
_token: this.config.joinToken,
|
|
124
159
|
},
|
|
125
160
|
});
|
|
126
|
-
|
|
127
|
-
// update the parent transports
|
|
128
|
-
this.config.parent.setTransports(parentTransports.map((t) => new oNodeTransport(t)));
|
|
161
|
+
this.setKeepAliveTag(this.parent);
|
|
129
162
|
}
|
|
130
163
|
}
|
|
164
|
+
async registerLeader() {
|
|
165
|
+
const address = oAddress.registry();
|
|
166
|
+
const params = {
|
|
167
|
+
method: 'commit',
|
|
168
|
+
params: {
|
|
169
|
+
peerId: this.peerId.toString(),
|
|
170
|
+
address: this.address.toString(),
|
|
171
|
+
protocols: this.p2pNode.getProtocols(),
|
|
172
|
+
transports: this.transports,
|
|
173
|
+
staticAddress: this.staticAddress.toString(),
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
await this.use(address, params);
|
|
177
|
+
this.setKeepAliveTag(this.leader);
|
|
178
|
+
}
|
|
131
179
|
async register() {
|
|
132
180
|
if (this.type === NodeType.LEADER) {
|
|
133
181
|
this.logger.debug('Skipping registration, node is leader');
|
|
@@ -148,18 +196,7 @@ export class oNode extends oToolBase {
|
|
|
148
196
|
this.logger.debug('Registering node with leader...');
|
|
149
197
|
}
|
|
150
198
|
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);
|
|
199
|
+
await this.registerLeader();
|
|
163
200
|
this.logger.debug('Registration successful');
|
|
164
201
|
}
|
|
165
202
|
extractMethod(address) {
|
|
@@ -171,11 +208,6 @@ export class oNode extends oToolBase {
|
|
|
171
208
|
if (this.connectionHeartbeatManager) {
|
|
172
209
|
await this.connectionHeartbeatManager.start();
|
|
173
210
|
}
|
|
174
|
-
// await NetworkUtils.advertiseToNetwork(
|
|
175
|
-
// this.address,
|
|
176
|
-
// this.staticAddress,
|
|
177
|
-
// this.p2pNode,
|
|
178
|
-
// );
|
|
179
211
|
}
|
|
180
212
|
async validateJoinRequest(request) {
|
|
181
213
|
return true;
|
|
@@ -225,12 +257,32 @@ export class oNode extends oToolBase {
|
|
|
225
257
|
// ];
|
|
226
258
|
// // let's make sure we only allow communication through the parent transports
|
|
227
259
|
params.connectionGater = {
|
|
260
|
+
denyDialPeer: (peerId) => {
|
|
261
|
+
// we can call the leader
|
|
262
|
+
if (this.config.leader?.libp2pTransports.some((t) => t.toPeerId() === peerId.toString())) {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
// we can call our parent
|
|
266
|
+
if (this.parentPeerId === peerId.toString()) {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
// we can call our children
|
|
270
|
+
if (this.hierarchyManager.children.some((c) => c.libp2pTransports.some((t) => t.toPeerId() === peerId.toString()))) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
// check for standalone node
|
|
274
|
+
if (!this.config.parent && !this.config.leader) {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
return true;
|
|
278
|
+
},
|
|
228
279
|
// who can call us?
|
|
229
280
|
denyInboundEncryptedConnection: (peerId, maConn) => {
|
|
230
281
|
// deny all inbound connections unless they are from a parent transport
|
|
231
282
|
if (this.parentPeerId === peerId.toString()) {
|
|
232
283
|
return false;
|
|
233
284
|
}
|
|
285
|
+
// allow connections from children (for ping)
|
|
234
286
|
if (this.hierarchyManager.children.some((c) => c.libp2pTransports.some((t) => t.toPeerId() === peerId.toString()))) {
|
|
235
287
|
return false;
|
|
236
288
|
}
|
|
@@ -245,6 +297,13 @@ export class oNode extends oToolBase {
|
|
|
245
297
|
...(this.config.network?.connectionGater || {}),
|
|
246
298
|
};
|
|
247
299
|
}
|
|
300
|
+
params.connectionManager = {
|
|
301
|
+
...(params.connectionManager || {}),
|
|
302
|
+
reconnectRetries: 20,
|
|
303
|
+
reconnectRetryInterval: 2000,
|
|
304
|
+
reconnectBackoffFactor: 1.2,
|
|
305
|
+
maxParallelReconnects: 10,
|
|
306
|
+
};
|
|
248
307
|
// handle the address encapsulation
|
|
249
308
|
if (this.config.leader &&
|
|
250
309
|
!this.address.protocol.includes(this.config.leader.protocol)) {
|
|
@@ -258,17 +317,13 @@ export class oNode extends oToolBase {
|
|
|
258
317
|
this.p2pNode = await createNode(params);
|
|
259
318
|
return this.p2pNode;
|
|
260
319
|
}
|
|
261
|
-
async connect(
|
|
320
|
+
async connect(config) {
|
|
262
321
|
if (!this.connectionManager) {
|
|
263
322
|
this.logger.error('Connection manager not initialized');
|
|
264
323
|
throw new Error('Node is not ready to connect to other nodes');
|
|
265
324
|
}
|
|
266
325
|
const connection = await this.connectionManager
|
|
267
|
-
.connect(
|
|
268
|
-
address: targetAddress,
|
|
269
|
-
nextHopAddress,
|
|
270
|
-
callerAddress: this.address,
|
|
271
|
-
})
|
|
326
|
+
.connect(config)
|
|
272
327
|
.catch((error) => {
|
|
273
328
|
// TODO: we need to handle this better and document
|
|
274
329
|
if (error.message === 'Can not dial self') {
|
|
@@ -281,6 +336,28 @@ export class oNode extends oToolBase {
|
|
|
281
336
|
}
|
|
282
337
|
return connection;
|
|
283
338
|
}
|
|
339
|
+
async initConnectionManager() {
|
|
340
|
+
this.connectionManager = new oNodeConnectionManager({
|
|
341
|
+
p2pNode: this.p2pNode,
|
|
342
|
+
defaultReadTimeoutMs: this.config.connectionTimeouts?.readTimeoutMs,
|
|
343
|
+
defaultDrainTimeoutMs: this.config.connectionTimeouts?.drainTimeoutMs,
|
|
344
|
+
runOnLimitedConnection: this.config.runOnLimitedConnection ?? false,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
async hookInitializeFinished() { }
|
|
348
|
+
async hookStartFinished() {
|
|
349
|
+
// Initialize connection health monitor
|
|
350
|
+
this.connectionHeartbeatManager = new oConnectionHeartbeatManager(this, {
|
|
351
|
+
enabled: this.config.connectionHeartbeat?.enabled ?? true,
|
|
352
|
+
intervalMs: this.config.connectionHeartbeat?.intervalMs ?? 15000,
|
|
353
|
+
failureThreshold: this.config.connectionHeartbeat?.failureThreshold ?? 3,
|
|
354
|
+
checkChildren: this.config.connectionHeartbeat?.checkChildren ?? false,
|
|
355
|
+
checkParent: this.config.connectionHeartbeat?.checkParent ?? true,
|
|
356
|
+
checkLeader: true,
|
|
357
|
+
});
|
|
358
|
+
this.logger.info(`Connection heartbeat config: leader=${this.connectionHeartbeatManager.getConfig().checkLeader}, ` +
|
|
359
|
+
`parent=${this.connectionHeartbeatManager.getConfig().checkParent}`);
|
|
360
|
+
}
|
|
284
361
|
async initialize() {
|
|
285
362
|
this.logger.debug('Initializing node...');
|
|
286
363
|
if (this.p2pNode && this.state !== NodeState.STOPPED) {
|
|
@@ -297,19 +374,7 @@ export class oNode extends oToolBase {
|
|
|
297
374
|
this.address.setTransports(this.transports);
|
|
298
375
|
this.peerId = this.p2pNode.peerId;
|
|
299
376
|
// 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}`);
|
|
377
|
+
await this.initConnectionManager();
|
|
313
378
|
// initialize address resolution
|
|
314
379
|
this.router.addResolver(new oMethodResolver(this.address));
|
|
315
380
|
this.router.addResolver(new oNodeResolver(this.address));
|
|
@@ -318,38 +383,34 @@ export class oNode extends oToolBase {
|
|
|
318
383
|
this.logger.debug('Adding leader resolver fallback...');
|
|
319
384
|
this.router.addResolver(new oLeaderResolverFallback(this.address));
|
|
320
385
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
checkChildren: this.config.connectionHeartbeat?.checkChildren ?? true,
|
|
330
|
-
checkParent: this.config.connectionHeartbeat?.checkParent ?? true,
|
|
331
|
-
checkLeader: this.config.connectionHeartbeat?.checkLeader ?? enableLeaderHeartbeat,
|
|
386
|
+
this.reconnectionManager = new oReconnectionManager(this, {
|
|
387
|
+
enabled: true,
|
|
388
|
+
maxAttempts: 10,
|
|
389
|
+
baseDelayMs: 5000,
|
|
390
|
+
maxDelayMs: 20000,
|
|
391
|
+
useLeaderFallback: true,
|
|
392
|
+
parentDiscoveryIntervalMs: 5000,
|
|
393
|
+
parentDiscoveryMaxDelayMs: 20000,
|
|
332
394
|
});
|
|
333
|
-
|
|
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
|
-
}
|
|
395
|
+
await this.hookInitializeFinished();
|
|
345
396
|
}
|
|
346
397
|
/**
|
|
347
398
|
* Override use() to wrap leader/registry requests with retry logic
|
|
348
399
|
*/
|
|
349
|
-
async use(
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
400
|
+
// async use(
|
|
401
|
+
// address: oAddress,
|
|
402
|
+
// data?: {
|
|
403
|
+
// method?: string;
|
|
404
|
+
// params?: { [key: string]: any };
|
|
405
|
+
// id?: string;
|
|
406
|
+
// },
|
|
407
|
+
// options?: UseOptions,
|
|
408
|
+
// ): Promise<any> {
|
|
409
|
+
// // Wrap leader/registry requests with retry logic
|
|
410
|
+
// return super.use(address, data, options),
|
|
411
|
+
// address,
|
|
412
|
+
// data?.method,
|
|
413
|
+
// }
|
|
353
414
|
async teardown() {
|
|
354
415
|
// Stop heartbeat before parent teardown
|
|
355
416
|
if (this.connectionHeartbeatManager) {
|
|
@@ -361,4 +422,98 @@ export class oNode extends oToolBase {
|
|
|
361
422
|
await this.p2pNode.stop();
|
|
362
423
|
}
|
|
363
424
|
}
|
|
425
|
+
// IHeartbeatableNode interface methods
|
|
426
|
+
getLeaders() {
|
|
427
|
+
return [this.leader];
|
|
428
|
+
}
|
|
429
|
+
getParents() {
|
|
430
|
+
return this.hierarchyManager.getParents();
|
|
431
|
+
}
|
|
432
|
+
getChildren() {
|
|
433
|
+
return this.hierarchyManager.getChildren();
|
|
434
|
+
}
|
|
435
|
+
removeChild(childAddress) {
|
|
436
|
+
this.hierarchyManager.removeChild(childAddress);
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Get the total number of active streams across all connections
|
|
440
|
+
* @returns Total count of active streams
|
|
441
|
+
*/
|
|
442
|
+
getStreamCount() {
|
|
443
|
+
if (!this.p2pNode) {
|
|
444
|
+
return 0;
|
|
445
|
+
}
|
|
446
|
+
const connections = this.p2pNode.getConnections();
|
|
447
|
+
return connections.reduce((count, conn) => {
|
|
448
|
+
return count + (conn.streams?.length || 0);
|
|
449
|
+
}, 0);
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Get libp2p metrics for this node
|
|
453
|
+
* Tool method that can be called remotely by monitoring systems
|
|
454
|
+
*/
|
|
455
|
+
async _tool_get_libp2p_metrics(request) {
|
|
456
|
+
if (!this.p2pNode) {
|
|
457
|
+
return {
|
|
458
|
+
error: 'libp2p node not available',
|
|
459
|
+
available: false,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
try {
|
|
463
|
+
// Get basic connection stats
|
|
464
|
+
const connections = this.p2pNode.getConnections();
|
|
465
|
+
const peers = await this.p2pNode.peerStore.all();
|
|
466
|
+
const inbound = connections.filter((c) => c.direction === 'inbound').length;
|
|
467
|
+
const outbound = connections.filter((c) => c.direction === 'outbound').length;
|
|
468
|
+
// Get DHT info if available
|
|
469
|
+
const services = this.p2pNode.services;
|
|
470
|
+
const dht = services?.dht;
|
|
471
|
+
const routingTable = dht?.routingTable;
|
|
472
|
+
const kBuckets = routingTable?.kb || null;
|
|
473
|
+
let routingTableSize = 0;
|
|
474
|
+
if (kBuckets) {
|
|
475
|
+
// Handle both array and object-like structures
|
|
476
|
+
if (Array.isArray(kBuckets)) {
|
|
477
|
+
for (const bucket of kBuckets) {
|
|
478
|
+
routingTableSize += bucket.peers?.length || 0;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
else if (typeof kBuckets === 'object') {
|
|
482
|
+
// If it's an object, iterate over its values
|
|
483
|
+
for (const bucket of Object.values(kBuckets)) {
|
|
484
|
+
routingTableSize += bucket?.peers?.length || 0;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
// Calculate total stream count across all connections
|
|
489
|
+
const streamCount = connections.reduce((count, conn) => {
|
|
490
|
+
return count + (conn.streams?.length || 0);
|
|
491
|
+
}, 0);
|
|
492
|
+
return {
|
|
493
|
+
available: true,
|
|
494
|
+
timestamp: Date.now(),
|
|
495
|
+
nodeAddress: this.address.toString(),
|
|
496
|
+
peerCount: peers.length,
|
|
497
|
+
connectionCount: connections.length,
|
|
498
|
+
inboundConnections: inbound,
|
|
499
|
+
outboundConnections: outbound,
|
|
500
|
+
streamCount,
|
|
501
|
+
dhtEnabled: !!dht,
|
|
502
|
+
dhtMode: dht?.clientMode ? 'client' : 'server',
|
|
503
|
+
dhtRoutingTableSize: routingTableSize,
|
|
504
|
+
protocols: Array.from(this.p2pNode.getProtocols()),
|
|
505
|
+
selfPeerId: this.p2pNode.peerId.toString(),
|
|
506
|
+
multiaddrs: this.p2pNode
|
|
507
|
+
.getMultiaddrs()
|
|
508
|
+
.map((ma) => ma.toString()),
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
return {
|
|
513
|
+
error: `Failed to collect libp2p metrics: ${error.message}`,
|
|
514
|
+
available: false,
|
|
515
|
+
nodeAddress: this.address.toString(),
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
}
|
|
364
519
|
}
|
|
@@ -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"}
|