@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
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { oRequest, CoreUtils, oError, oErrorCodes, Logger, ResponseBuilder, } from '@olane/o-core';
|
|
2
|
+
/**
|
|
3
|
+
* StreamHandler centralizes all stream-related functionality including:
|
|
4
|
+
* - Message type detection (request vs response)
|
|
5
|
+
* - Stream lifecycle management (create, reuse, close)
|
|
6
|
+
* - Backpressure handling
|
|
7
|
+
* - Request/response handling
|
|
8
|
+
* - Stream routing for middleware nodes
|
|
9
|
+
*/
|
|
10
|
+
export class StreamHandler {
|
|
11
|
+
constructor(logger) {
|
|
12
|
+
this.logger = logger ?? new Logger('StreamHandler');
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Detects if a decoded message is a request
|
|
16
|
+
* Requests have a 'method' field and no 'result' field
|
|
17
|
+
*/
|
|
18
|
+
isRequest(message) {
|
|
19
|
+
return typeof message?.method === 'string' && message.result === undefined;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Detects if a decoded message is a response
|
|
23
|
+
* Responses have a 'result' field and no 'method' field
|
|
24
|
+
*/
|
|
25
|
+
isResponse(message) {
|
|
26
|
+
return message?.result !== undefined && message.method === undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Decodes a stream message event into a JSON object
|
|
30
|
+
*/
|
|
31
|
+
async decodeMessage(event) {
|
|
32
|
+
return CoreUtils.processStream(event);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Gets an existing open stream or creates a new one based on reuse policy
|
|
36
|
+
*
|
|
37
|
+
* @param connection - The libp2p connection
|
|
38
|
+
* @param protocol - The protocol to use for the stream
|
|
39
|
+
* @param config - Stream handler configuration
|
|
40
|
+
*/
|
|
41
|
+
async getOrCreateStream(connection, protocol, config = {}) {
|
|
42
|
+
if (connection.status !== 'open') {
|
|
43
|
+
throw new oError(oErrorCodes.INVALID_STATE, 'Connection not open');
|
|
44
|
+
}
|
|
45
|
+
const reusePolicy = config.reusePolicy ?? 'none';
|
|
46
|
+
this.logger.debug('Reuse policy:', reusePolicy);
|
|
47
|
+
// Check for existing stream if reuse is enabled
|
|
48
|
+
if (reusePolicy === 'reuse') {
|
|
49
|
+
this.logger.debug('Reusing existing stream if we can find one. Stream insights:', JSON.stringify({
|
|
50
|
+
streamCount: connection.streams.length,
|
|
51
|
+
protocol: protocol,
|
|
52
|
+
}));
|
|
53
|
+
connection.streams.forEach((stream) => {
|
|
54
|
+
this.logger.debug('Stream re-use option:', stream.protocol, stream.status, stream.direction, stream.remoteWriteStatus, stream.writeStatus, stream.remoteReadStatus, stream.readStatus);
|
|
55
|
+
});
|
|
56
|
+
const existingStream = connection.streams.find((stream) => stream.status === 'open' &&
|
|
57
|
+
stream.protocol === protocol &&
|
|
58
|
+
stream.writeStatus === 'writable' &&
|
|
59
|
+
stream.remoteReadStatus === 'readable');
|
|
60
|
+
if (existingStream) {
|
|
61
|
+
this.logger.debug('Reusing existing stream', existingStream.id, existingStream.direction);
|
|
62
|
+
return existingStream;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Create new stream
|
|
66
|
+
this.logger.debug('Creating new stream');
|
|
67
|
+
const stream = await connection.newStream(protocol, {
|
|
68
|
+
signal: config.signal,
|
|
69
|
+
maxOutboundStreams: config.maxOutboundStreams ?? 1000,
|
|
70
|
+
runOnLimitedConnection: config.runOnLimitedConnection ?? false,
|
|
71
|
+
});
|
|
72
|
+
return stream;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Sends data through a stream with backpressure handling
|
|
76
|
+
*
|
|
77
|
+
* @param stream - The stream to send data through
|
|
78
|
+
* @param data - The data to send
|
|
79
|
+
* @param config - Configuration for timeout and other options
|
|
80
|
+
*/
|
|
81
|
+
async send(stream, data, config = {}) {
|
|
82
|
+
// Send the data with backpressure handling (libp2p v3 best practice)
|
|
83
|
+
const sent = stream.send(data);
|
|
84
|
+
// If send() returns false, buffer is full - wait for drain
|
|
85
|
+
if (!sent) {
|
|
86
|
+
this.logger.debug('Stream buffer full, waiting for drain...');
|
|
87
|
+
const drainTimeout = config.drainTimeoutMs ?? 30000;
|
|
88
|
+
await stream.onDrain({
|
|
89
|
+
signal: AbortSignal.timeout(drainTimeout),
|
|
90
|
+
});
|
|
91
|
+
this.logger.debug('Stream drained successfully');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Closes a stream safely with error handling
|
|
96
|
+
*
|
|
97
|
+
* @param stream - The stream to close
|
|
98
|
+
* @param config - Configuration including reuse policy
|
|
99
|
+
*/
|
|
100
|
+
async close(stream, config = {}) {
|
|
101
|
+
// Don't close if reuse policy is enabled
|
|
102
|
+
if (config.reusePolicy === 'reuse') {
|
|
103
|
+
this.logger.debug('Stream reuse enabled, not closing stream');
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (stream.status === 'open') {
|
|
107
|
+
try {
|
|
108
|
+
// force the close for now until we can implement a proper close
|
|
109
|
+
await stream.abort(new Error('Stream closed'));
|
|
110
|
+
this.logger.debug('Stream closed successfully');
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
this.logger.debug('Error closing stream:', error.message);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Handles an incoming stream on the server side
|
|
119
|
+
* Attaches message listener immediately (libp2p v3 best practice)
|
|
120
|
+
* Routes requests or executes tools based on the message
|
|
121
|
+
*
|
|
122
|
+
* @param stream - The incoming stream
|
|
123
|
+
* @param connection - The connection the stream belongs to
|
|
124
|
+
* @param toolExecutor - Function to execute tools for requests
|
|
125
|
+
*/
|
|
126
|
+
async handleIncomingStream(stream, connection, toolExecutor) {
|
|
127
|
+
// CRITICAL: Attach message listener immediately to prevent buffer overflow (libp2p v3)
|
|
128
|
+
const messageHandler = async (event) => {
|
|
129
|
+
try {
|
|
130
|
+
// avoid processing non-olane messages
|
|
131
|
+
if (!event.data) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const messages = await this.decodeMessage(event);
|
|
135
|
+
await Promise.all(messages.map(async (message) => {
|
|
136
|
+
if (this.isRequest(message)) {
|
|
137
|
+
await this.handleRequestMessage(message, stream, toolExecutor);
|
|
138
|
+
}
|
|
139
|
+
else if (this.isResponse(message)) {
|
|
140
|
+
this.logger.warn('Received response message on server-side stream, ignoring', message);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
this.logger.warn('Received unknown message type', message);
|
|
144
|
+
}
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
this.logger.error('Error handling stream message:', error);
|
|
149
|
+
// Error already logged, stream will be closed by remote peer or timeout
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
const closeHandler = () => {
|
|
153
|
+
this.logger.debug('Stream closed by remote peer');
|
|
154
|
+
stream.removeEventListener('message', messageHandler);
|
|
155
|
+
stream.removeEventListener('close', closeHandler);
|
|
156
|
+
};
|
|
157
|
+
stream.addEventListener('message', messageHandler);
|
|
158
|
+
stream.addEventListener('close', closeHandler);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Handles a request message by executing the tool and sending response
|
|
162
|
+
*
|
|
163
|
+
* @param message - The decoded request message
|
|
164
|
+
* @param stream - The stream to send the response on
|
|
165
|
+
* @param toolExecutor - Function to execute the tool
|
|
166
|
+
*/
|
|
167
|
+
async handleRequestMessage(message, stream, toolExecutor) {
|
|
168
|
+
const request = new oRequest(message);
|
|
169
|
+
const responseBuilder = ResponseBuilder.create();
|
|
170
|
+
try {
|
|
171
|
+
this.logger.debug(`Processing request on stream: method=${request.method}, id=${request.id}`);
|
|
172
|
+
const result = await toolExecutor(request, stream);
|
|
173
|
+
const response = await responseBuilder.build(request, result, null);
|
|
174
|
+
await CoreUtils.sendResponse(response, stream);
|
|
175
|
+
this.logger.debug(`Successfully processed request: method=${request.method}, id=${request.id}`);
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
this.logger.error(`Error processing request: method=${request.method}, id=${request.id}`, error);
|
|
179
|
+
const errorResponse = await responseBuilder.buildError(request, error);
|
|
180
|
+
await CoreUtils.sendResponse(errorResponse, stream);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Handles an outgoing stream on the client side
|
|
185
|
+
* Listens for response messages and emits them via the event emitter
|
|
186
|
+
* If requestHandler is provided, also processes incoming router requests
|
|
187
|
+
*
|
|
188
|
+
* @param stream - The outgoing stream
|
|
189
|
+
* @param emitter - Event emitter for chunk events
|
|
190
|
+
* @param config - Configuration including abort signal
|
|
191
|
+
* @param requestHandler - Optional handler for processing router requests received on this stream
|
|
192
|
+
* @returns Promise that resolves with the final response
|
|
193
|
+
*/
|
|
194
|
+
async handleOutgoingStream(stream, emitter, config = {}, requestHandler) {
|
|
195
|
+
return new Promise((resolve, reject) => {
|
|
196
|
+
let lastResponse;
|
|
197
|
+
const messageHandler = async (event) => {
|
|
198
|
+
// avoid processing non-olane messages
|
|
199
|
+
if (!event.data) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
const messages = await this.decodeMessage(event);
|
|
204
|
+
await Promise.all(messages.map(async (message) => {
|
|
205
|
+
if (this.isResponse(message)) {
|
|
206
|
+
// Emit chunk for streaming responses
|
|
207
|
+
emitter.emit('chunk', message);
|
|
208
|
+
// Check if this is the last chunk
|
|
209
|
+
if (message.result._last || !message.result._isStreaming) {
|
|
210
|
+
lastResponse = message;
|
|
211
|
+
cleanup();
|
|
212
|
+
resolve(message);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else if (this.isRequest(message)) {
|
|
216
|
+
// Process incoming router requests if handler is provided
|
|
217
|
+
if (requestHandler) {
|
|
218
|
+
this.logger.debug('Received router request on client-side stream, processing...', message);
|
|
219
|
+
await this.handleRequestMessage(message, stream, requestHandler);
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
this.logger.warn('Received request message on client-side stream, ignoring (no handler)', message);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
this.logger.warn('Received unknown message type', message);
|
|
227
|
+
}
|
|
228
|
+
}));
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
this.logger.error('Error handling response message:', error);
|
|
232
|
+
cleanup();
|
|
233
|
+
reject(error);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
const closeHandler = () => {
|
|
237
|
+
this.logger.debug('Stream closed by remote peer');
|
|
238
|
+
cleanup();
|
|
239
|
+
if (lastResponse) {
|
|
240
|
+
resolve(lastResponse);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
reject(new oError(oErrorCodes.TIMEOUT, 'Stream closed before response received'));
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
const abortHandler = () => {
|
|
247
|
+
this.logger.debug('Request aborted');
|
|
248
|
+
cleanup();
|
|
249
|
+
try {
|
|
250
|
+
stream.abort(new Error('Request aborted'));
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
this.logger.debug('Error aborting stream:', error.message);
|
|
254
|
+
}
|
|
255
|
+
reject(new oError(oErrorCodes.TIMEOUT, 'Request aborted'));
|
|
256
|
+
};
|
|
257
|
+
const cleanup = () => {
|
|
258
|
+
stream.removeEventListener('message', messageHandler);
|
|
259
|
+
stream.removeEventListener('close', closeHandler);
|
|
260
|
+
if (config.signal) {
|
|
261
|
+
config.signal.removeEventListener('abort', abortHandler);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
stream.addEventListener('message', messageHandler);
|
|
265
|
+
stream.addEventListener('close', closeHandler);
|
|
266
|
+
if (config.signal) {
|
|
267
|
+
config.signal.addEventListener('abort', abortHandler);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Forwards a request to the next hop and relays response chunks back
|
|
273
|
+
* This implements the middleware/proxy pattern for intermediate nodes
|
|
274
|
+
*
|
|
275
|
+
* @param request - The router request to forward
|
|
276
|
+
* @param incomingStream - The stream to send responses back on
|
|
277
|
+
* @param dialFn - Function to dial the next hop connection
|
|
278
|
+
*/
|
|
279
|
+
async forwardRequest(request, incomingStream, dialFn) {
|
|
280
|
+
try {
|
|
281
|
+
// Connect to next hop
|
|
282
|
+
const nextHopConnection = await dialFn(request.params.address);
|
|
283
|
+
// Set up chunk relay - forward responses from next hop back to incoming stream
|
|
284
|
+
nextHopConnection.onChunk(async (response) => {
|
|
285
|
+
try {
|
|
286
|
+
await CoreUtils.sendStreamResponse(response, incomingStream);
|
|
287
|
+
}
|
|
288
|
+
catch (error) {
|
|
289
|
+
this.logger.error('Error forwarding chunk:', error);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
// Transmit the request to next hop
|
|
293
|
+
await nextHopConnection.transmit(request);
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
this.logger.error('Error forwarding request:', error);
|
|
297
|
+
// Send error response back on incoming stream using ResponseBuilder
|
|
298
|
+
const responseBuilder = ResponseBuilder.create();
|
|
299
|
+
const errorResponse = await responseBuilder.buildError(request, error);
|
|
300
|
+
await CoreUtils.sendResponse(errorResponse, incomingStream);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from './o-node.js';
|
|
2
|
-
export * from './utils/
|
|
2
|
+
export * from './utils/index.js';
|
|
3
3
|
export * from './o-node.hierarchy-manager.js';
|
|
4
4
|
export * from './interfaces/o-node.config.js';
|
|
5
5
|
export * from './connection/index.js';
|
|
@@ -7,4 +7,5 @@ export * from './o-node.tool.js';
|
|
|
7
7
|
export * from './nodes/index.js';
|
|
8
8
|
export * from './interfaces/o-node.tool-config.js';
|
|
9
9
|
export * from './router/index.js';
|
|
10
|
+
export * from './connection/o-stream.request.js';
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oCAAoC,CAAC;AACnD,cAAc,mBAAmB,CAAC;AAClC,cAAc,kCAAkC,CAAC"}
|
package/dist/src/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from './o-node.js';
|
|
2
|
-
export * from './utils/
|
|
2
|
+
export * from './utils/index.js';
|
|
3
3
|
export * from './o-node.hierarchy-manager.js';
|
|
4
4
|
export * from './interfaces/o-node.config.js';
|
|
5
5
|
export * from './connection/index.js';
|
|
@@ -7,3 +7,4 @@ export * from './o-node.tool.js';
|
|
|
7
7
|
export * from './nodes/index.js';
|
|
8
8
|
export * from './interfaces/o-node.tool-config.js';
|
|
9
9
|
export * from './router/index.js';
|
|
10
|
+
export * from './connection/o-stream.request.js';
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { oNotificationManager } from '@olane/o-core';
|
|
2
|
+
import { Libp2p } from '@olane/o-config';
|
|
3
|
+
import { oNodeAddress } from '../router/o-node.address.js';
|
|
4
|
+
/**
|
|
5
|
+
* Interface for nodes that support connection heartbeat monitoring.
|
|
6
|
+
* This interface defines the contract that oConnectionHeartbeatManager needs
|
|
7
|
+
* to access live hierarchy state without holding stale references.
|
|
8
|
+
*/
|
|
9
|
+
export interface IHeartbeatableNode {
|
|
10
|
+
/**
|
|
11
|
+
* The node's current address
|
|
12
|
+
*/
|
|
13
|
+
address: oNodeAddress;
|
|
14
|
+
/**
|
|
15
|
+
* The notification manager for emitting heartbeat events
|
|
16
|
+
*/
|
|
17
|
+
notificationManager: oNotificationManager;
|
|
18
|
+
/**
|
|
19
|
+
* The underlying libp2p node for ping operations
|
|
20
|
+
*/
|
|
21
|
+
p2pNode: Libp2p;
|
|
22
|
+
/**
|
|
23
|
+
* The current parent address (with live transport updates)
|
|
24
|
+
* @returns Parent address or null if no parent
|
|
25
|
+
*/
|
|
26
|
+
parent: oNodeAddress | null;
|
|
27
|
+
/**
|
|
28
|
+
* Get the current list of leader addresses
|
|
29
|
+
* @returns Array of leader addresses (empty if this node is the leader)
|
|
30
|
+
*/
|
|
31
|
+
getLeaders(): oNodeAddress[];
|
|
32
|
+
/**
|
|
33
|
+
* Get the current list of parent addresses
|
|
34
|
+
* @returns Array of parent addresses
|
|
35
|
+
*/
|
|
36
|
+
getParents(): oNodeAddress[];
|
|
37
|
+
/**
|
|
38
|
+
* Get the current list of child addresses
|
|
39
|
+
* @returns Array of child addresses
|
|
40
|
+
*/
|
|
41
|
+
getChildren(): oNodeAddress[];
|
|
42
|
+
/**
|
|
43
|
+
* Remove a child from the hierarchy
|
|
44
|
+
* @param childAddress The address of the child to remove
|
|
45
|
+
*/
|
|
46
|
+
removeChild(childAddress: oNodeAddress): void;
|
|
47
|
+
use(param1: any, param2: any): Promise<any>;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=i-heartbeatable-node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i-heartbeatable-node.d.ts","sourceRoot":"","sources":["../../../src/interfaces/i-heartbeatable-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,OAAO,EAAE,YAAY,CAAC;IAEtB;;OAEG;IACH,mBAAmB,EAAE,oBAAoB,CAAC;IAE1C;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAE5B;;;OAGG;IACH,UAAU,IAAI,YAAY,EAAE,CAAC;IAE7B;;;OAGG;IACH,UAAU,IAAI,YAAY,EAAE,CAAC;IAE7B;;;OAGG;IACH,WAAW,IAAI,YAAY,EAAE,CAAC;IAE9B;;;OAGG;IACH,WAAW,CAAC,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAE9C,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;CAC7C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -27,6 +27,11 @@ export interface IReconnectableNode {
|
|
|
27
27
|
* Register with the parent node
|
|
28
28
|
*/
|
|
29
29
|
registerParent(): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Register with the leader's global registry
|
|
32
|
+
*/
|
|
33
|
+
register(): Promise<void>;
|
|
34
|
+
useSelf(request?: any): Promise<any>;
|
|
30
35
|
/**
|
|
31
36
|
* Execute a method on another node
|
|
32
37
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i-reconnectable-node.d.ts","sourceRoot":"","sources":["../../../src/interfaces/i-reconnectable-node.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"i-reconnectable-node.d.ts","sourceRoot":"","sources":["../../../src/interfaces/i-reconnectable-node.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,SAAS,EACT,oBAAoB,EAErB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,MAAM,EAAE,WAAW,CAAC;IAEpB;;OAEG;IACH,OAAO,EAAE,YAAY,CAAC;IAEtB;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IAEjB;;OAEG;IACH,mBAAmB,EAAE,oBAAoB,CAAC;IAE1C;;OAEG;IACH,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhC;;OAEG;IACH,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1B,OAAO,CAAC,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAErC;;OAEG;IACH,GAAG,CACD,OAAO,EAAE,QAAQ,EACjB,IAAI,CAAC,EAAE;QACL,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;SAAE,CAAC;QAChC,EAAE,CAAC,EAAE,MAAM,CAAC;KACb,GACA,OAAO,CAAC,GAAG,CAAC,CAAC;CACjB"}
|
|
@@ -26,17 +26,25 @@ export interface oNodeConfig extends oCoreConfig {
|
|
|
26
26
|
baseDelayMs?: number;
|
|
27
27
|
maxDelayMs?: number;
|
|
28
28
|
useLeaderFallback?: boolean;
|
|
29
|
+
parentDiscoveryIntervalMs?: number;
|
|
30
|
+
parentDiscoveryMaxDelayMs?: number;
|
|
29
31
|
};
|
|
30
32
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
+
* Connection timeout configuration
|
|
34
|
+
* Controls timeouts for stream read and drain operations in connections
|
|
33
35
|
*/
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
connectionTimeouts?: {
|
|
37
|
+
/**
|
|
38
|
+
* Timeout in milliseconds for reading response data from a stream
|
|
39
|
+
* Default: 120000 (2 minutes)
|
|
40
|
+
*/
|
|
41
|
+
readTimeoutMs?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Timeout in milliseconds for waiting for stream buffer to drain when backpressure occurs
|
|
44
|
+
* Default: 30000 (30 seconds)
|
|
45
|
+
*/
|
|
46
|
+
drainTimeoutMs?: number;
|
|
40
47
|
};
|
|
48
|
+
runOnLimitedConnection?: boolean;
|
|
41
49
|
}
|
|
42
50
|
//# 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,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,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,YAAY,CAAC,EAAE;QACb,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,iBAAiB,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,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,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,YAAY,CAAC,EAAE;QACb,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,yBAAyB,CAAC,EAAE,MAAM,CAAC;QACnC,yBAAyB,CAAC,EAAE,MAAM,CAAC;KACpC,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;CAClC"}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { Libp2p } from '@olane/o-config';
|
|
2
1
|
import { oObject } from '@olane/o-core';
|
|
3
2
|
import { oNodeAddress } from '../router/o-node.address.js';
|
|
4
|
-
import {
|
|
5
|
-
import { oNotificationManager } from '@olane/o-core';
|
|
3
|
+
import { IHeartbeatableNode } from '../interfaces/i-heartbeatable-node.js';
|
|
6
4
|
export interface HeartbeatConfig {
|
|
7
5
|
enabled: boolean;
|
|
8
6
|
intervalMs: number;
|
|
@@ -35,22 +33,20 @@ export interface ConnectionHealth {
|
|
|
35
33
|
* - Emits ParentDisconnectedEvent when parent dies (triggers reconnection)
|
|
36
34
|
*/
|
|
37
35
|
export declare class oConnectionHeartbeatManager extends oObject {
|
|
38
|
-
private
|
|
39
|
-
private hierarchyManager;
|
|
40
|
-
private notificationManager;
|
|
41
|
-
private address;
|
|
36
|
+
private node;
|
|
42
37
|
private config;
|
|
43
38
|
private heartbeatInterval?;
|
|
44
39
|
private healthMap;
|
|
45
|
-
|
|
40
|
+
private isRunning;
|
|
41
|
+
constructor(node: IHeartbeatableNode, config: HeartbeatConfig);
|
|
46
42
|
start(): Promise<void>;
|
|
47
43
|
stop(): Promise<void>;
|
|
48
44
|
private performHeartbeatCycle;
|
|
45
|
+
private doPing;
|
|
49
46
|
private pingTarget;
|
|
50
47
|
private handleConnectionDead;
|
|
51
48
|
private emitConnectionDegradedEvent;
|
|
52
49
|
private emitConnectionRecoveredEvent;
|
|
53
|
-
private extractPeerIdFromAddress;
|
|
54
50
|
/**
|
|
55
51
|
* Get current health status of all connections
|
|
56
52
|
*/
|
|
@@ -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,
|
|
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,SAAS,EAAE,MAAM,CAAC;IAClB,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;;;;;;;;;;;;;GAaG;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,qBAAqB;IAgDnC,OAAO,CAAC,MAAM;YAcA,UAAU;IAuFxB,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"}
|