@olane/o-node 0.7.29 → 0.7.33
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.config.d.ts +5 -0
- package/dist/src/connection/interfaces/o-node-connection.config.d.ts.map +1 -1
- package/dist/src/connection/o-node-connection.d.ts.map +1 -1
- package/dist/src/connection/o-node-connection.js +10 -1
- package/dist/src/connection/stream-handler.config.d.ts +13 -0
- package/dist/src/connection/stream-handler.config.d.ts.map +1 -1
- package/dist/src/connection/stream-handler.d.ts +35 -1
- package/dist/src/connection/stream-handler.d.ts.map +1 -1
- package/dist/src/connection/stream-handler.js +143 -5
- package/dist/src/interfaces/o-node.config.d.ts +7 -0
- package/dist/src/interfaces/o-node.config.d.ts.map +1 -1
- package/dist/src/managers/o-connection-heartbeat.manager.d.ts.map +1 -1
- package/dist/src/managers/o-connection-heartbeat.manager.js +6 -3
- package/dist/src/managers/o-reconnection.manager.d.ts.map +1 -1
- package/dist/src/managers/o-reconnection.manager.js +34 -21
- package/dist/src/o-node.d.ts +6 -2
- package/dist/src/o-node.d.ts.map +1 -1
- package/dist/src/o-node.hierarchy-manager.d.ts +41 -0
- package/dist/src/o-node.hierarchy-manager.d.ts.map +1 -1
- package/dist/src/o-node.hierarchy-manager.js +170 -1
- package/dist/src/o-node.js +71 -29
- package/dist/src/o-node.notification-manager.d.ts +6 -17
- package/dist/src/o-node.notification-manager.d.ts.map +1 -1
- package/dist/src/o-node.notification-manager.js +38 -119
- package/dist/src/o-node.tool.d.ts.map +1 -1
- package/dist/src/o-node.tool.js +8 -1
- package/dist/test/connection-management.spec.js +54 -50
- package/package.json +7 -7
|
@@ -3,5 +3,10 @@ import { oConnectionConfig } from '@olane/o-core';
|
|
|
3
3
|
export interface oNodeConnectionConfig extends oConnectionConfig {
|
|
4
4
|
p2pConnection: Connection;
|
|
5
5
|
runOnLimitedConnection?: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Enable length-prefixed streaming (libp2p v3 best practice)
|
|
8
|
+
* @default false (for backward compatibility)
|
|
9
|
+
*/
|
|
10
|
+
useLengthPrefixing?: boolean;
|
|
6
11
|
}
|
|
7
12
|
//# sourceMappingURL=o-node-connection.config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"o-node-connection.config.d.ts","sourceRoot":"","sources":["../../../../src/connection/interfaces/o-node-connection.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC9D,aAAa,EAAE,UAAU,CAAC;IAC1B,sBAAsB,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"o-node-connection.config.d.ts","sourceRoot":"","sources":["../../../../src/connection/interfaces/o-node-connection.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC9D,aAAa,EAAE,UAAU,CAAC;IAC1B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"o-node-connection.d.ts","sourceRoot":"","sources":["../../../src/connection/o-node-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAEL,WAAW,EAGX,QAAQ,EACR,SAAS,EACV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD,qBAAa,eAAgB,SAAQ,WAAW;IAIlC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,qBAAqB;IAHrD,aAAa,EAAE,UAAU,CAAC;IACjC,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;gBAER,MAAM,EAAE,qBAAqB;IAM5D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM;IAmBlB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"o-node-connection.d.ts","sourceRoot":"","sources":["../../../src/connection/o-node-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAEL,WAAW,EAGX,QAAQ,EACR,SAAS,EACV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD,qBAAa,eAAgB,SAAQ,WAAW;IAIlC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,qBAAqB;IAHrD,aAAa,EAAE,UAAU,CAAC;IACjC,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;gBAER,MAAM,EAAE,qBAAqB;IAM5D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM;IAmBlB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IAmBpC,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAkD/C,YAAY,CAAC,MAAM,EAAE,MAAM;IAQ3B,KAAK,CAAC,KAAK,EAAE,KAAK;IAMlB,KAAK;CAKZ"}
|
|
@@ -28,6 +28,7 @@ export class oNodeConnection extends oConnection {
|
|
|
28
28
|
runOnLimitedConnection: this.config.runOnLimitedConnection ?? false,
|
|
29
29
|
reusePolicy: 'none', // Default policy, can be overridden in subclasses
|
|
30
30
|
drainTimeoutMs: this.config.drainTimeoutMs,
|
|
31
|
+
useLengthPrefixing: true,
|
|
31
32
|
};
|
|
32
33
|
return this.streamHandler.getOrCreateStream(this.p2pConnection, this.nextHopAddress.protocol, streamConfig);
|
|
33
34
|
}
|
|
@@ -42,10 +43,18 @@ export class oNodeConnection extends oConnection {
|
|
|
42
43
|
signal: this.abortSignal,
|
|
43
44
|
drainTimeoutMs: this.config.drainTimeoutMs,
|
|
44
45
|
reusePolicy: 'none', // Default policy
|
|
46
|
+
useLengthPrefixing: this.config.useLengthPrefixing ?? true,
|
|
45
47
|
};
|
|
46
48
|
// Send the request with backpressure handling
|
|
47
49
|
const data = new TextEncoder().encode(request.toString());
|
|
48
|
-
|
|
50
|
+
// Use length-prefixed encoding if enabled
|
|
51
|
+
if (streamConfig.useLengthPrefixing) {
|
|
52
|
+
this.logger.info('Length prefix enabled...');
|
|
53
|
+
await this.streamHandler.sendLengthPrefixed(stream, data, streamConfig);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
await this.streamHandler.send(stream, data, streamConfig);
|
|
57
|
+
}
|
|
49
58
|
// Handle response using StreamHandler
|
|
50
59
|
// Pass request handler if configured to enable bidirectional stream processing
|
|
51
60
|
// Pass request ID to enable proper response correlation on shared streams
|
|
@@ -34,6 +34,19 @@ export interface StreamHandlerConfig {
|
|
|
34
34
|
* AbortSignal for cancellation
|
|
35
35
|
*/
|
|
36
36
|
signal?: AbortSignal;
|
|
37
|
+
/**
|
|
38
|
+
* Enable length-prefixed streaming (libp2p v3 best practice)
|
|
39
|
+
* When enabled, all messages are prefixed with a varint indicating message length
|
|
40
|
+
* This provides proper message boundaries and eliminates concatenation issues
|
|
41
|
+
* @default false (for backward compatibility)
|
|
42
|
+
*/
|
|
43
|
+
useLengthPrefixing?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Auto-detect protocol (length-prefixed vs raw JSON)
|
|
46
|
+
* When enabled, automatically detects the protocol by examining first byte
|
|
47
|
+
* @default false
|
|
48
|
+
*/
|
|
49
|
+
autoDetectProtocol?: boolean;
|
|
37
50
|
}
|
|
38
51
|
/**
|
|
39
52
|
* Context for stream lifecycle operations
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream-handler.config.d.ts","sourceRoot":"","sources":["../../../src/connection/stream-handler.config.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAEhC;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"stream-handler.config.d.ts","sourceRoot":"","sources":["../../../src/connection/stream-handler.config.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAEhC;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IAErB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,mBAAmB,CAAC;CAC7B"}
|
|
@@ -47,6 +47,16 @@ export declare class StreamHandler {
|
|
|
47
47
|
* @param config - Configuration for timeout and other options
|
|
48
48
|
*/
|
|
49
49
|
send(stream: Stream, data: Uint8Array, config?: StreamHandlerConfig): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Sends data through a stream using length-prefixed encoding (libp2p v3 best practice)
|
|
52
|
+
* Each message is automatically prefixed with a varint indicating the message length
|
|
53
|
+
* This ensures proper message boundaries and eliminates concatenation issues
|
|
54
|
+
*
|
|
55
|
+
* @param stream - The stream to send data through
|
|
56
|
+
* @param data - The data to send
|
|
57
|
+
* @param config - Configuration for timeout and other options
|
|
58
|
+
*/
|
|
59
|
+
sendLengthPrefixed(stream: Stream, data: Uint8Array, config?: StreamHandlerConfig): Promise<void>;
|
|
50
60
|
/**
|
|
51
61
|
* Closes a stream safely with error handling
|
|
52
62
|
*
|
|
@@ -54,6 +64,16 @@ export declare class StreamHandler {
|
|
|
54
64
|
* @param config - Configuration including reuse policy
|
|
55
65
|
*/
|
|
56
66
|
close(stream: Stream, config?: StreamHandlerConfig): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Handles an incoming stream on the server side using length-prefixed protocol
|
|
69
|
+
* Uses async read loops instead of event listeners (libp2p v3 best practice)
|
|
70
|
+
* Processes complete messages with proper boundaries
|
|
71
|
+
*
|
|
72
|
+
* @param stream - The incoming stream
|
|
73
|
+
* @param connection - The connection the stream belongs to
|
|
74
|
+
* @param toolExecutor - Function to execute tools for requests
|
|
75
|
+
*/
|
|
76
|
+
handleIncomingStreamLP(stream: Stream, connection: Connection, toolExecutor: (request: oRequest, stream: Stream) => Promise<RunResult>): Promise<void>;
|
|
57
77
|
/**
|
|
58
78
|
* Handles an incoming stream on the server side
|
|
59
79
|
* Attaches message listener immediately (libp2p v3 best practice)
|
|
@@ -62,16 +82,30 @@ export declare class StreamHandler {
|
|
|
62
82
|
* @param stream - The incoming stream
|
|
63
83
|
* @param connection - The connection the stream belongs to
|
|
64
84
|
* @param toolExecutor - Function to execute tools for requests
|
|
85
|
+
* @param config - Configuration to determine protocol handling
|
|
65
86
|
*/
|
|
66
|
-
handleIncomingStream(stream: Stream, connection: Connection, toolExecutor: (request: oRequest, stream: Stream) => Promise<RunResult
|
|
87
|
+
handleIncomingStream(stream: Stream, connection: Connection, toolExecutor: (request: oRequest, stream: Stream) => Promise<RunResult>, config?: StreamHandlerConfig): Promise<void>;
|
|
67
88
|
/**
|
|
68
89
|
* Handles a request message by executing the tool and sending response
|
|
69
90
|
*
|
|
70
91
|
* @param message - The decoded request message
|
|
71
92
|
* @param stream - The stream to send the response on
|
|
72
93
|
* @param toolExecutor - Function to execute the tool
|
|
94
|
+
* @param useLengthPrefixing - Whether to use length-prefixed response encoding
|
|
73
95
|
*/
|
|
74
96
|
private handleRequestMessage;
|
|
97
|
+
/**
|
|
98
|
+
* Handles an outgoing stream on the client side using length-prefixed protocol
|
|
99
|
+
* Uses async read loops to process responses with proper message boundaries
|
|
100
|
+
*
|
|
101
|
+
* @param stream - The outgoing stream
|
|
102
|
+
* @param emitter - Event emitter for chunk events
|
|
103
|
+
* @param config - Configuration including abort signal
|
|
104
|
+
* @param requestHandler - Optional handler for processing router requests received on this stream
|
|
105
|
+
* @param requestId - Optional request ID to filter responses (for stream reuse scenarios)
|
|
106
|
+
* @returns Promise that resolves with the final response
|
|
107
|
+
*/
|
|
108
|
+
handleOutgoingStreamLP(stream: Stream, emitter: EventEmitter, config?: StreamHandlerConfig, requestHandler?: (request: oRequest, stream: Stream) => Promise<RunResult>, requestId?: string | number): Promise<oResponse>;
|
|
75
109
|
/**
|
|
76
110
|
* Handles an outgoing stream on the client side
|
|
77
111
|
* Listens for response messages and emits them via the event emitter
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream-handler.d.ts","sourceRoot":"","sources":["../../../src/connection/stream-handler.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EACL,QAAQ,EACR,SAAS,EAIT,MAAM,EAEP,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAGtE;;;;;;;GAOG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,CAAC,EAAE,MAAM;IAI3B;;;OAGG;IACH,SAAS,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO;IAIhC;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO;IAIjC;;OAEG;IACG,aAAa,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAI7C;;;;;;OAMG;IACG,iBAAiB,CACrB,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,mBAAwB,GAC/B,OAAO,CAAC,MAAM,CAAC;IAoClB;;;;;;OAMG;IACG,IAAI,CACR,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,EAChB,MAAM,GAAE,mBAAwB,GAC/B,OAAO,CAAC,IAAI,CAAC;IAiBhB;;;;;OAKG;IACG,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB5E;;;;;;;;OAQG;IACG,oBAAoB,CACxB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"stream-handler.d.ts","sourceRoot":"","sources":["../../../src/connection/stream-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { oRequest, CoreUtils, oError, oErrorCodes, Logger, ResponseBuilder, } from '@olane/o-core';
|
|
1
|
+
import { oRequest, oResponse, CoreUtils, oError, oErrorCodes, Logger, ResponseBuilder, } from '@olane/o-core';
|
|
2
|
+
import { lpStream } from '@olane/o-config';
|
|
2
3
|
/**
|
|
3
4
|
* StreamHandler centralizes all stream-related functionality including:
|
|
4
5
|
* - Message type detection (request vs response)
|
|
@@ -82,6 +83,19 @@ export class StreamHandler {
|
|
|
82
83
|
this.logger.debug('Stream drained successfully');
|
|
83
84
|
}
|
|
84
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Sends data through a stream using length-prefixed encoding (libp2p v3 best practice)
|
|
88
|
+
* Each message is automatically prefixed with a varint indicating the message length
|
|
89
|
+
* This ensures proper message boundaries and eliminates concatenation issues
|
|
90
|
+
*
|
|
91
|
+
* @param stream - The stream to send data through
|
|
92
|
+
* @param data - The data to send
|
|
93
|
+
* @param config - Configuration for timeout and other options
|
|
94
|
+
*/
|
|
95
|
+
async sendLengthPrefixed(stream, data, config = {}) {
|
|
96
|
+
const lp = lpStream(stream);
|
|
97
|
+
await lp.write(data, { signal: config.signal });
|
|
98
|
+
}
|
|
85
99
|
/**
|
|
86
100
|
* Closes a stream safely with error handling
|
|
87
101
|
*
|
|
@@ -104,6 +118,45 @@ export class StreamHandler {
|
|
|
104
118
|
}
|
|
105
119
|
}
|
|
106
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Handles an incoming stream on the server side using length-prefixed protocol
|
|
123
|
+
* Uses async read loops instead of event listeners (libp2p v3 best practice)
|
|
124
|
+
* Processes complete messages with proper boundaries
|
|
125
|
+
*
|
|
126
|
+
* @param stream - The incoming stream
|
|
127
|
+
* @param connection - The connection the stream belongs to
|
|
128
|
+
* @param toolExecutor - Function to execute tools for requests
|
|
129
|
+
*/
|
|
130
|
+
async handleIncomingStreamLP(stream, connection, toolExecutor) {
|
|
131
|
+
const lp = lpStream(stream);
|
|
132
|
+
try {
|
|
133
|
+
while (stream.status === 'open') {
|
|
134
|
+
// Read complete length-prefixed message
|
|
135
|
+
const messageBytes = await lp.read();
|
|
136
|
+
const decoded = new TextDecoder().decode(messageBytes.subarray());
|
|
137
|
+
// Ignore non-JSON messages
|
|
138
|
+
if (!decoded.startsWith('{')) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const message = JSON.parse(decoded);
|
|
142
|
+
if (this.isRequest(message)) {
|
|
143
|
+
await this.handleRequestMessage(message, stream, toolExecutor, true);
|
|
144
|
+
}
|
|
145
|
+
else if (this.isResponse(message)) {
|
|
146
|
+
this.logger.warn('Received response message on server-side stream, ignoring', message);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
this.logger.warn('Received unknown message type', message);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
// Stream closed or error occurred
|
|
155
|
+
if (stream.status === 'open') {
|
|
156
|
+
this.logger.error('Error in length-prefixed stream handler:', error);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
107
160
|
/**
|
|
108
161
|
* Handles an incoming stream on the server side
|
|
109
162
|
* Attaches message listener immediately (libp2p v3 best practice)
|
|
@@ -112,8 +165,13 @@ export class StreamHandler {
|
|
|
112
165
|
* @param stream - The incoming stream
|
|
113
166
|
* @param connection - The connection the stream belongs to
|
|
114
167
|
* @param toolExecutor - Function to execute tools for requests
|
|
168
|
+
* @param config - Configuration to determine protocol handling
|
|
115
169
|
*/
|
|
116
|
-
async handleIncomingStream(stream, connection, toolExecutor) {
|
|
170
|
+
async handleIncomingStream(stream, connection, toolExecutor, config = {}) {
|
|
171
|
+
// Route to length-prefixed handler if enabled
|
|
172
|
+
if (config.useLengthPrefixing) {
|
|
173
|
+
return this.handleIncomingStreamLP(stream, connection, toolExecutor);
|
|
174
|
+
}
|
|
117
175
|
// CRITICAL: Attach message listener immediately to prevent buffer overflow (libp2p v3)
|
|
118
176
|
const messageHandler = async (event) => {
|
|
119
177
|
try {
|
|
@@ -153,8 +211,9 @@ export class StreamHandler {
|
|
|
153
211
|
* @param message - The decoded request message
|
|
154
212
|
* @param stream - The stream to send the response on
|
|
155
213
|
* @param toolExecutor - Function to execute the tool
|
|
214
|
+
* @param useLengthPrefixing - Whether to use length-prefixed response encoding
|
|
156
215
|
*/
|
|
157
|
-
async handleRequestMessage(message, stream, toolExecutor) {
|
|
216
|
+
async handleRequestMessage(message, stream, toolExecutor, useLengthPrefixing = false) {
|
|
158
217
|
const request = new oRequest(message);
|
|
159
218
|
const responseBuilder = ResponseBuilder.create();
|
|
160
219
|
try {
|
|
@@ -163,13 +222,88 @@ export class StreamHandler {
|
|
|
163
222
|
// );
|
|
164
223
|
const result = await toolExecutor(request, stream);
|
|
165
224
|
const response = await responseBuilder.build(request, result, null);
|
|
166
|
-
|
|
225
|
+
// Use length-prefixed encoding if enabled
|
|
226
|
+
if (useLengthPrefixing) {
|
|
227
|
+
await CoreUtils.sendResponseLP(response, stream);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
await CoreUtils.sendResponse(response, stream);
|
|
231
|
+
}
|
|
167
232
|
this.logger.debug(`Successfully processed request: method=${request.method}, id=${request.id}`);
|
|
168
233
|
}
|
|
169
234
|
catch (error) {
|
|
170
235
|
this.logger.error(`Error processing request: method=${request.method}, id=${request.id}`, error);
|
|
171
236
|
const errorResponse = await responseBuilder.buildError(request, error);
|
|
172
|
-
|
|
237
|
+
// Use length-prefixed encoding if enabled
|
|
238
|
+
if (useLengthPrefixing) {
|
|
239
|
+
await CoreUtils.sendResponseLP(errorResponse, stream);
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
await CoreUtils.sendResponse(errorResponse, stream);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Handles an outgoing stream on the client side using length-prefixed protocol
|
|
248
|
+
* Uses async read loops to process responses with proper message boundaries
|
|
249
|
+
*
|
|
250
|
+
* @param stream - The outgoing stream
|
|
251
|
+
* @param emitter - Event emitter for chunk events
|
|
252
|
+
* @param config - Configuration including abort signal
|
|
253
|
+
* @param requestHandler - Optional handler for processing router requests received on this stream
|
|
254
|
+
* @param requestId - Optional request ID to filter responses (for stream reuse scenarios)
|
|
255
|
+
* @returns Promise that resolves with the final response
|
|
256
|
+
*/
|
|
257
|
+
async handleOutgoingStreamLP(stream, emitter, config = {}, requestHandler, requestId) {
|
|
258
|
+
const lp = lpStream(stream);
|
|
259
|
+
try {
|
|
260
|
+
while (stream.status === 'open') {
|
|
261
|
+
// Read complete length-prefixed message
|
|
262
|
+
const messageBytes = await lp.read({ signal: config.signal });
|
|
263
|
+
const decoded = new TextDecoder().decode(messageBytes.subarray());
|
|
264
|
+
// Ignore non-JSON messages
|
|
265
|
+
if (!decoded.startsWith('{')) {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
const message = JSON.parse(decoded);
|
|
269
|
+
if (this.isResponse(message)) {
|
|
270
|
+
const response = new oResponse({
|
|
271
|
+
...message.result,
|
|
272
|
+
id: message.id,
|
|
273
|
+
});
|
|
274
|
+
// Filter by request ID if provided
|
|
275
|
+
if (requestId !== undefined && response.id !== requestId) {
|
|
276
|
+
this.logger.debug(`Ignoring response for different request (expected: ${requestId}, received: ${response.id})`);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
// Emit chunk for streaming responses
|
|
280
|
+
emitter.emit('chunk', response);
|
|
281
|
+
// Check if this is the last chunk
|
|
282
|
+
if (response.result._last || !response.result._isStreaming) {
|
|
283
|
+
return response;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
else if (this.isRequest(message)) {
|
|
287
|
+
// Process incoming router requests if handler is provided
|
|
288
|
+
if (requestHandler) {
|
|
289
|
+
this.logger.debug('Received router request on client-side stream, processing...', message);
|
|
290
|
+
await this.handleRequestMessage(message, stream, requestHandler, true);
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
this.logger.warn('Received request message on client-side stream, ignoring (no handler)', message);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
this.logger.warn('Received unknown message type', message);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
throw new oError(oErrorCodes.TIMEOUT, 'Stream closed before response received');
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
if (config.signal?.aborted) {
|
|
304
|
+
throw new oError(oErrorCodes.TIMEOUT, 'Request aborted');
|
|
305
|
+
}
|
|
306
|
+
throw error;
|
|
173
307
|
}
|
|
174
308
|
}
|
|
175
309
|
/**
|
|
@@ -185,6 +319,10 @@ export class StreamHandler {
|
|
|
185
319
|
* @returns Promise that resolves with the final response
|
|
186
320
|
*/
|
|
187
321
|
async handleOutgoingStream(stream, emitter, config = {}, requestHandler, requestId) {
|
|
322
|
+
// Route to length-prefixed handler if enabled
|
|
323
|
+
if (config.useLengthPrefixing) {
|
|
324
|
+
return this.handleOutgoingStreamLP(stream, emitter, config, requestHandler, requestId);
|
|
325
|
+
}
|
|
188
326
|
return new Promise((resolve, reject) => {
|
|
189
327
|
let lastResponse;
|
|
190
328
|
const messageHandler = async (event) => {
|
|
@@ -32,5 +32,12 @@ export interface oNodeConfig extends oCoreConfig {
|
|
|
32
32
|
drainTimeoutMs?: number;
|
|
33
33
|
};
|
|
34
34
|
runOnLimitedConnection?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Enable length-prefixed streaming (libp2p v3 best practice)
|
|
37
|
+
* When enabled, all messages are prefixed with a varint indicating message length
|
|
38
|
+
* This provides proper message boundaries and eliminates concatenation issues
|
|
39
|
+
* @default false (for backward compatibility)
|
|
40
|
+
*/
|
|
41
|
+
useLengthPrefixing?: boolean;
|
|
35
42
|
}
|
|
36
43
|
//# sourceMappingURL=o-node.config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"o-node.config.d.ts","sourceRoot":"","sources":["../../../src/interfaces/o-node.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC9C,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAE5B;;;OAGG;IACH,mBAAmB,CAAC,EAAE;QACpB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB,CAAC;IAEF;;;OAGG;IACH,kBAAkB,CAAC,EAAE;QACnB;;;WAGG;QACH,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB;;;WAGG;QACH,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IAEF,sBAAsB,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"o-node.config.d.ts","sourceRoot":"","sources":["../../../src/interfaces/o-node.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC9C,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAE5B;;;OAGG;IACH,mBAAmB,CAAC,EAAE;QACpB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB,CAAC;IAEF;;;OAGG;IACH,kBAAkB,CAAC,EAAE;QACnB;;;WAGG;QACH,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB;;;WAGG;QACH,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IAEF,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"o-connection-heartbeat.manager.d.ts","sourceRoot":"","sources":["../../../src/managers/o-connection-heartbeat.manager.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EAOR,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAE3E,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;CACzC;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,2BAA4B,SAAQ,OAAO;IAMpD,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,MAAM;IANhB,OAAO,CAAC,iBAAiB,CAAC,CAAiB;IAC3C,OAAO,CAAC,SAAS,CAAuC;IACxD,OAAO,CAAC,SAAS,CAAS;gBAGhB,IAAI,EAAE,kBAAkB,EACxB,MAAM,EAAE,eAAe;IAK3B,KAAK;IAqBL,IAAI;YAQI,uBAAuB;IAgDrC;;;OAGG;IACH,OAAO,CAAC,qBAAqB;
|
|
1
|
+
{"version":3,"file":"o-connection-heartbeat.manager.d.ts","sourceRoot":"","sources":["../../../src/managers/o-connection-heartbeat.manager.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EAOR,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAE3E,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;CACzC;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,2BAA4B,SAAQ,OAAO;IAMpD,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,MAAM;IANhB,OAAO,CAAC,iBAAiB,CAAC,CAAiB;IAC3C,OAAO,CAAC,SAAS,CAAuC;IACxD,OAAO,CAAC,SAAS,CAAS;gBAGhB,IAAI,EAAE,kBAAkB,EACxB,MAAM,EAAE,eAAe;IAK3B,KAAK;IAqBL,IAAI;YAQI,uBAAuB;IAgDrC;;;OAGG;IACH,OAAO,CAAC,qBAAqB;YAgDf,WAAW;IAoEzB,OAAO,CAAC,oBAAoB;IAyD5B,OAAO,CAAC,2BAA2B;IAmBnC,OAAO,CAAC,4BAA4B;IAiBpC;;OAEG;IACH,eAAe,IAAI,gBAAgB,EAAE;IAIrC;;OAEG;IACH,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,gBAAgB,GAAG,SAAS;IAIxE;;OAEG;IACH,SAAS,IAAI,eAAe;CAG7B"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { oObject, ChildLeftEvent, ParentDisconnectedEvent, LeaderDisconnectedEvent, ConnectionDegradedEvent, ConnectionRecoveredEvent, oAddress, } from '@olane/o-core';
|
|
2
|
+
import { oNodeAddress } from '../router/o-node.address.js';
|
|
2
3
|
/**
|
|
3
4
|
* Connection Health Monitor
|
|
4
5
|
*
|
|
@@ -100,9 +101,11 @@ export class oConnectionHeartbeatManager extends oObject {
|
|
|
100
101
|
return true;
|
|
101
102
|
}
|
|
102
103
|
// check via ping
|
|
103
|
-
const pingResponse = this.node
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
const pingResponse = this.node
|
|
105
|
+
.use(new oNodeAddress(address?.value), {
|
|
106
|
+
method: 'ping',
|
|
107
|
+
})
|
|
108
|
+
.catch((err) => {
|
|
106
109
|
if (err.message === 'Can not dial self') {
|
|
107
110
|
return true;
|
|
108
111
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"o-reconnection.manager.d.ts","sourceRoot":"","sources":["../../../src/managers/o-reconnection.manager.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EASR,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAI3E,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,yBAAyB,EAAE,MAAM,CAAC;IAClC,yBAAyB,EAAE,MAAM,CAAC;CACnC;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,oBAAqB,SAAQ,OAAO;IAI7C,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,MAAM;IAJhB,OAAO,CAAC,YAAY,CAAS;gBAGnB,IAAI,EAAE,kBAAkB,EACxB,MAAM,EAAE,kBAAkB;IAMpC,OAAO,CAAC,mBAAmB;IAyBrB,mBAAmB,CAAC,KAAK,EAAE,GAAG;
|
|
1
|
+
{"version":3,"file":"o-reconnection.manager.d.ts","sourceRoot":"","sources":["../../../src/managers/o-reconnection.manager.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EASR,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAI3E,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,yBAAyB,EAAE,MAAM,CAAC;IAClC,yBAAyB,EAAE,MAAM,CAAC;CACnC;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,oBAAqB,SAAQ,OAAO;IAI7C,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,MAAM;IAJhB,OAAO,CAAC,YAAY,CAAS;gBAGnB,IAAI,EAAE,kBAAkB,EACxB,MAAM,EAAE,kBAAkB;IAMpC,OAAO,CAAC,mBAAmB;IAyBrB,mBAAmB,CAAC,KAAK,EAAE,GAAG;YAoBtB,wBAAwB;YAaxB,wBAAwB;YAexB,wBAAwB;IAehC,mBAAmB;YAgDX,2BAA2B;YAiB3B,iBAAiB;IAkB/B;;;OAGG;YACW,yBAAyB;IAiFvC;;OAEG;IACG,yBAAyB;IA4G/B,OAAO,CAAC,yBAAyB;IAajC,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,KAAK;CAGd"}
|
|
@@ -32,6 +32,7 @@ export class oReconnectionManager extends oObject {
|
|
|
32
32
|
}
|
|
33
33
|
async handleNodeConnected(event) {
|
|
34
34
|
const connectedEvent = event;
|
|
35
|
+
// if leader is back online re-register
|
|
35
36
|
if (connectedEvent.nodeAddress.toString() === oAddress.leader().toString()) {
|
|
36
37
|
// the leader is back online, let's re-register & tell sub-graphs to re-register
|
|
37
38
|
await this.node.useSelf({
|
|
@@ -39,6 +40,10 @@ export class oReconnectionManager extends oObject {
|
|
|
39
40
|
params: {},
|
|
40
41
|
});
|
|
41
42
|
}
|
|
43
|
+
if (connectedEvent.nodeAddress.toString() === this.node.config.parent?.value) {
|
|
44
|
+
// connect to the parent and register
|
|
45
|
+
await this.node.registerParent();
|
|
46
|
+
}
|
|
42
47
|
}
|
|
43
48
|
async handleConnectionDegraded(event) {
|
|
44
49
|
const degradedEvent = event;
|
|
@@ -73,26 +78,32 @@ export class oReconnectionManager extends oObject {
|
|
|
73
78
|
}
|
|
74
79
|
this.reconnecting = true;
|
|
75
80
|
let attempt = 0;
|
|
76
|
-
while (attempt < this.config.maxAttempts) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
81
|
+
// while (attempt < this.config.maxAttempts) {
|
|
82
|
+
// attempt++;
|
|
83
|
+
// this.logger.info(
|
|
84
|
+
// `Reconnection attempt ${attempt}/${this.config.maxAttempts} to parent: ${this.node.config.parent}`,
|
|
85
|
+
// );
|
|
86
|
+
// try {
|
|
87
|
+
// // Strategy 1: Try direct parent reconnection
|
|
88
|
+
// await this.tryDirectParentReconnection();
|
|
89
|
+
// // Success!
|
|
90
|
+
// this.reconnecting = false;
|
|
91
|
+
// this.logger.info(
|
|
92
|
+
// `Successfully reconnected to parent after ${attempt} attempts`,
|
|
93
|
+
// );
|
|
94
|
+
// return;
|
|
95
|
+
// } catch (error) {
|
|
96
|
+
// this.logger.warn(
|
|
97
|
+
// `Reconnection attempt ${attempt} failed:`,
|
|
98
|
+
// error instanceof Error ? error.message : error,
|
|
99
|
+
// );
|
|
100
|
+
// if (attempt < this.config.maxAttempts) {
|
|
101
|
+
// const delay = this.calculateBackoffDelay(attempt);
|
|
102
|
+
// this.logger.debug(`Waiting ${delay}ms before next attempt...`);
|
|
103
|
+
// await this.sleep(delay);
|
|
104
|
+
// }
|
|
105
|
+
// }
|
|
106
|
+
// }
|
|
96
107
|
// All direct attempts failed - try leader fallback
|
|
97
108
|
if (this.config.useLeaderFallback) {
|
|
98
109
|
await this.tryLeaderFallback();
|
|
@@ -186,6 +197,7 @@ export class oReconnectionManager extends oObject {
|
|
|
186
197
|
* Wait for non-leader parent to appear in registry and reconnect
|
|
187
198
|
*/
|
|
188
199
|
async waitForParentAndReconnect() {
|
|
200
|
+
this.logger.debug('waitForParentAndReconnect...');
|
|
189
201
|
const startTime = Date.now();
|
|
190
202
|
let attempt = 0;
|
|
191
203
|
let currentDelay = this.config.parentDiscoveryIntervalMs;
|
|
@@ -202,7 +214,8 @@ export class oReconnectionManager extends oObject {
|
|
|
202
214
|
if (!this.node.config.parent) {
|
|
203
215
|
throw new Error('Invalid parent definition');
|
|
204
216
|
}
|
|
205
|
-
|
|
217
|
+
this.logger.debug('Calling parent identify');
|
|
218
|
+
const response = await this.node.use(new oNodeAddress(this.node.config.parent.value), {
|
|
206
219
|
method: 'identify',
|
|
207
220
|
params: {},
|
|
208
221
|
});
|
package/dist/src/o-node.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ export declare class oNode extends oToolBase {
|
|
|
21
21
|
connectionHeartbeatManager?: oConnectionHeartbeatManager;
|
|
22
22
|
protected reconnectionManager?: oReconnectionManager;
|
|
23
23
|
protected didRegister: boolean;
|
|
24
|
+
protected hooksStartFinished: any[];
|
|
25
|
+
protected hooksInitFinished: any[];
|
|
24
26
|
constructor(config: oNodeConfig);
|
|
25
27
|
get leader(): oNodeAddress | null;
|
|
26
28
|
get networkConfig(): Libp2pConfig;
|
|
@@ -47,8 +49,10 @@ export declare class oNode extends oToolBase {
|
|
|
47
49
|
protected createNode(): Promise<Libp2p>;
|
|
48
50
|
connect(config: oNodeConnectionConfig): Promise<oNodeConnection>;
|
|
49
51
|
initConnectionManager(): Promise<void>;
|
|
50
|
-
hookInitializeFinished(): Promise<void>;
|
|
51
|
-
|
|
52
|
+
protected hookInitializeFinished(): Promise<void>;
|
|
53
|
+
onInitFinished(cb: Function): void;
|
|
54
|
+
onStartFinished(cb: Function): void;
|
|
55
|
+
protected hookStartFinished(): Promise<void>;
|
|
52
56
|
/**
|
|
53
57
|
* Validates that if a leader address is defined, it has associated transports.
|
|
54
58
|
* This is critical for non-leader nodes to be able to connect to their leader.
|
package/dist/src/o-node.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"o-node.d.ts","sourceRoot":"","sources":["../../src/o-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,MAAM,EACN,YAAY,EAEb,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAKL,QAAQ,EAER,oBAAoB,EAGrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AAEnF,OAAO,EAAmB,SAAS,EAAE,MAAM,eAAe,CAAC;AAG3D,OAAO,EAAE,2BAA2B,EAAE,MAAM,8CAA8C,CAAC;AAC3F,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAE5E,qBAAa,KAAM,SAAQ,SAAS;IAC3B,MAAM,EAAG,MAAM,CAAC;IAChB,OAAO,EAAG,MAAM,CAAC;IACjB,OAAO,EAAG,YAAY,CAAC;IACvB,MAAM,EAAE,WAAW,CAAC;IACpB,iBAAiB,EAAG,sBAAsB,CAAC;IAC3C,gBAAgB,EAAG,qBAAqB,CAAC;IACzC,0BAA0B,CAAC,EAAE,2BAA2B,CAAC;IAChE,SAAS,CAAC,mBAAmB,CAAC,EAAE,oBAAoB,CAAC;IACrD,SAAS,CAAC,WAAW,EAAE,OAAO,CAAS;
|
|
1
|
+
{"version":3,"file":"o-node.d.ts","sourceRoot":"","sources":["../../src/o-node.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,15 +1,56 @@
|
|
|
1
1
|
import { oHierarchyManager, oHierarchyManagerConfig } from '@olane/o-core';
|
|
2
2
|
import { oNodeAddress } from './router/o-node.address.js';
|
|
3
|
+
import { oNodeNotificationManager } from './o-node.notification-manager.js';
|
|
3
4
|
export interface oNodeHierarchyManagerConfig extends oHierarchyManagerConfig {
|
|
4
5
|
leaders: oNodeAddress[];
|
|
5
6
|
children: oNodeAddress[];
|
|
6
7
|
parents: oNodeAddress[];
|
|
8
|
+
notificationManager?: oNodeNotificationManager;
|
|
9
|
+
address: oNodeAddress;
|
|
7
10
|
}
|
|
8
11
|
export declare class oNodeHierarchyManager extends oHierarchyManager {
|
|
9
12
|
leaders: oNodeAddress[];
|
|
10
13
|
children: oNodeAddress[];
|
|
11
14
|
parents: oNodeAddress[];
|
|
15
|
+
private notificationManager?;
|
|
16
|
+
private address;
|
|
12
17
|
constructor(config: oNodeHierarchyManagerConfig);
|
|
18
|
+
/**
|
|
19
|
+
* Set up event listeners to react to notification manager's peer events
|
|
20
|
+
*/
|
|
21
|
+
setupEventListeners(notificationManager: oNodeNotificationManager): void;
|
|
13
22
|
get leader(): oNodeAddress | null;
|
|
23
|
+
/**
|
|
24
|
+
* Handle peer connected event from notification manager
|
|
25
|
+
* Resolves peer ID to address and emits hierarchy-aware events
|
|
26
|
+
*/
|
|
27
|
+
private handlePeerConnected;
|
|
28
|
+
/**
|
|
29
|
+
* Handle peer disconnected event from notification manager
|
|
30
|
+
* Resolves peer ID to address and emits hierarchy-aware events
|
|
31
|
+
*/
|
|
32
|
+
private handlePeerDisconnected;
|
|
33
|
+
/**
|
|
34
|
+
* Handle peer discovered event from notification manager
|
|
35
|
+
* Resolves peer ID to address and emits node discovered event
|
|
36
|
+
*/
|
|
37
|
+
private handlePeerDiscovered;
|
|
38
|
+
/**
|
|
39
|
+
* Resolve a libp2p peer ID to an Olane address
|
|
40
|
+
* Checks children, parents, and leaders for matching transports
|
|
41
|
+
*/
|
|
42
|
+
peerIdToAddress(peerId: string): oNodeAddress | null;
|
|
43
|
+
/**
|
|
44
|
+
* Check if an address is a direct child
|
|
45
|
+
*/
|
|
46
|
+
isChild(address: oNodeAddress): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Check if an address is a parent
|
|
49
|
+
*/
|
|
50
|
+
isParent(address: oNodeAddress): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Check if an address is a leader
|
|
53
|
+
*/
|
|
54
|
+
isLeader(address: oNodeAddress): boolean;
|
|
14
55
|
}
|
|
15
56
|
//# sourceMappingURL=o-node.hierarchy-manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"o-node.hierarchy-manager.d.ts","sourceRoot":"","sources":["../../src/o-node.hierarchy-manager.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"o-node.hierarchy-manager.d.ts","sourceRoot":"","sources":["../../src/o-node.hierarchy-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EAaxB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAE5E,MAAM,WAAW,2BAA4B,SAAQ,uBAAuB;IAC1E,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,mBAAmB,CAAC,EAAE,wBAAwB,CAAC;IAC/C,OAAO,EAAE,YAAY,CAAC;CACvB;AAED,qBAAa,qBAAsB,SAAQ,iBAAiB;IACnD,OAAO,EAAE,YAAY,EAAE,CAAM;IAC7B,QAAQ,EAAE,YAAY,EAAE,CAAM;IAC9B,OAAO,EAAE,YAAY,EAAE,CAAM;IACpC,OAAO,CAAC,mBAAmB,CAAC,CAA2B;IACvD,OAAO,CAAC,OAAO,CAAe;gBAElB,MAAM,EAAE,2BAA2B;IAW/C;;OAEG;IACH,mBAAmB,CAAC,mBAAmB,EAAE,wBAAwB,GAAG,IAAI;IAqBxE,IAAI,MAAM,IAAI,YAAY,GAAG,IAAI,CAEhC;IAED;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IA8C3B;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IAuD9B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAmB5B;;;OAGG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAqCpD;;OAEG;IACH,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;IAMvC;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;IAMxC;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;CAKzC"}
|