@olane/o-node 0.7.12-alpha.9 → 0.7.13-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/src/connection/interfaces/o-node-connection-manager.config.d.ts +1 -0
  2. package/dist/src/connection/interfaces/o-node-connection-manager.config.d.ts.map +1 -1
  3. package/dist/src/connection/interfaces/o-node-connection.config.d.ts +1 -0
  4. package/dist/src/connection/interfaces/o-node-connection.config.d.ts.map +1 -1
  5. package/dist/src/connection/o-node-connection.d.ts +7 -2
  6. package/dist/src/connection/o-node-connection.d.ts.map +1 -1
  7. package/dist/src/connection/o-node-connection.js +56 -32
  8. package/dist/src/connection/o-node-connection.manager.d.ts +20 -5
  9. package/dist/src/connection/o-node-connection.manager.d.ts.map +1 -1
  10. package/dist/src/connection/o-node-connection.manager.js +95 -65
  11. package/dist/src/connection/o-stream.request.d.ts +11 -0
  12. package/dist/src/connection/o-stream.request.d.ts.map +1 -0
  13. package/dist/src/connection/o-stream.request.js +7 -0
  14. package/dist/src/connection/stream-handler.config.d.ts +46 -0
  15. package/dist/src/connection/stream-handler.config.d.ts.map +1 -0
  16. package/dist/src/connection/stream-handler.config.js +1 -0
  17. package/dist/src/connection/stream-handler.d.ts +98 -0
  18. package/dist/src/connection/stream-handler.d.ts.map +1 -0
  19. package/dist/src/connection/stream-handler.js +310 -0
  20. package/dist/src/index.d.ts +2 -1
  21. package/dist/src/index.d.ts.map +1 -1
  22. package/dist/src/index.js +2 -1
  23. package/dist/src/interfaces/i-heartbeatable-node.d.ts +49 -0
  24. package/dist/src/interfaces/i-heartbeatable-node.d.ts.map +1 -0
  25. package/dist/src/interfaces/i-heartbeatable-node.js +1 -0
  26. package/dist/src/interfaces/i-reconnectable-node.d.ts +5 -0
  27. package/dist/src/interfaces/i-reconnectable-node.d.ts.map +1 -1
  28. package/dist/src/interfaces/o-node.config.d.ts +16 -22
  29. package/dist/src/interfaces/o-node.config.d.ts.map +1 -1
  30. package/dist/src/managers/o-connection-heartbeat.manager.d.ts +17 -17
  31. package/dist/src/managers/o-connection-heartbeat.manager.d.ts.map +1 -1
  32. package/dist/src/managers/o-connection-heartbeat.manager.js +83 -78
  33. package/dist/src/managers/o-reconnection.manager.d.ts +12 -0
  34. package/dist/src/managers/o-reconnection.manager.d.ts.map +1 -1
  35. package/dist/src/managers/o-reconnection.manager.js +138 -22
  36. package/dist/src/o-node.d.ts +23 -12
  37. package/dist/src/o-node.d.ts.map +1 -1
  38. package/dist/src/o-node.js +227 -72
  39. package/dist/src/o-node.notification-manager.d.ts.map +1 -1
  40. package/dist/src/o-node.notification-manager.js +17 -12
  41. package/dist/src/o-node.tool.d.ts +1 -0
  42. package/dist/src/o-node.tool.d.ts.map +1 -1
  43. package/dist/src/o-node.tool.js +18 -34
  44. package/dist/src/router/o-node.router.d.ts +1 -0
  45. package/dist/src/router/o-node.router.d.ts.map +1 -1
  46. package/dist/src/router/o-node.router.js +62 -9
  47. package/dist/src/router/o-node.routing-policy.d.ts.map +1 -1
  48. package/dist/src/router/o-node.routing-policy.js +7 -2
  49. package/dist/src/router/resolvers/o-node.resolver.d.ts.map +1 -1
  50. package/dist/src/router/resolvers/o-node.resolver.js +5 -1
  51. package/dist/src/router/resolvers/o-node.search-resolver.d.ts.map +1 -1
  52. package/dist/src/router/resolvers/o-node.search-resolver.js +34 -9
  53. package/dist/src/utils/index.d.ts +3 -0
  54. package/dist/src/utils/index.d.ts.map +1 -0
  55. package/dist/src/utils/index.js +2 -0
  56. package/dist/src/utils/stream.utils.d.ts +6 -0
  57. package/dist/src/utils/stream.utils.d.ts.map +1 -0
  58. package/dist/src/utils/stream.utils.js +31 -0
  59. package/dist/test/helpers/test-node.tool.d.ts +15 -0
  60. package/dist/test/helpers/test-node.tool.d.ts.map +1 -0
  61. package/dist/test/helpers/test-node.tool.js +27 -0
  62. package/package.json +6 -6
  63. package/dist/src/router/resolvers/o-node.child-resolver.d.ts +0 -11
  64. package/dist/src/router/resolvers/o-node.child-resolver.d.ts.map +0 -1
  65. package/dist/src/router/resolvers/o-node.child-resolver.js +0 -58
  66. package/dist/src/utils/leader-request-wrapper.d.ts +0 -45
  67. package/dist/src/utils/leader-request-wrapper.d.ts.map +0 -1
  68. package/dist/src/utils/leader-request-wrapper.js +0 -89
  69. package/dist/test/o-node.spec.d.ts +0 -2
  70. package/dist/test/o-node.spec.d.ts.map +0 -1
  71. package/dist/test/o-node.spec.js +0 -20
  72. package/dist/test/search-resolver.spec.d.ts +0 -2
  73. package/dist/test/search-resolver.spec.d.ts.map +0 -1
  74. package/dist/test/search-resolver.spec.js +0 -693
@@ -0,0 +1,310 @@
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
+ const existingStream = connection.streams.find((stream) => stream.status === 'open' &&
50
+ stream.protocol === protocol &&
51
+ stream.writeStatus === 'writable' &&
52
+ stream.remoteReadStatus === 'readable');
53
+ if (existingStream) {
54
+ this.logger.debug('Reusing existing stream', existingStream.id, existingStream.direction);
55
+ return existingStream;
56
+ }
57
+ }
58
+ // Create new stream
59
+ this.logger.debug('Creating new stream');
60
+ const stream = await connection.newStream(protocol, {
61
+ signal: config.signal,
62
+ maxOutboundStreams: config.maxOutboundStreams ?? 1000,
63
+ runOnLimitedConnection: config.runOnLimitedConnection ?? false,
64
+ });
65
+ return stream;
66
+ }
67
+ /**
68
+ * Sends data through a stream with backpressure handling
69
+ *
70
+ * @param stream - The stream to send data through
71
+ * @param data - The data to send
72
+ * @param config - Configuration for timeout and other options
73
+ */
74
+ async send(stream, data, config = {}) {
75
+ // Send the data with backpressure handling (libp2p v3 best practice)
76
+ const sent = stream.send(data);
77
+ // If send() returns false, buffer is full - wait for drain
78
+ if (!sent) {
79
+ this.logger.debug('Stream buffer full, waiting for drain...');
80
+ const drainTimeout = config.drainTimeoutMs ?? 30000;
81
+ await stream.onDrain({
82
+ signal: AbortSignal.timeout(drainTimeout),
83
+ });
84
+ this.logger.debug('Stream drained successfully');
85
+ }
86
+ }
87
+ /**
88
+ * Closes a stream safely with error handling
89
+ *
90
+ * @param stream - The stream to close
91
+ * @param config - Configuration including reuse policy
92
+ */
93
+ async close(stream, config = {}) {
94
+ // Don't close if reuse policy is enabled
95
+ if (config.reusePolicy === 'reuse') {
96
+ this.logger.debug('Stream reuse enabled, not closing stream');
97
+ return;
98
+ }
99
+ if (stream.status === 'open') {
100
+ try {
101
+ // force the close for now until we can implement a proper close
102
+ await stream.abort(new Error('Stream closed'));
103
+ this.logger.debug('Stream closed successfully');
104
+ }
105
+ catch (error) {
106
+ this.logger.debug('Error closing stream:', error.message);
107
+ }
108
+ }
109
+ }
110
+ /**
111
+ * Handles an incoming stream on the server side
112
+ * Attaches message listener immediately (libp2p v3 best practice)
113
+ * Routes requests or executes tools based on the message
114
+ *
115
+ * @param stream - The incoming stream
116
+ * @param connection - The connection the stream belongs to
117
+ * @param toolExecutor - Function to execute tools for requests
118
+ */
119
+ async handleIncomingStream(stream, connection, toolExecutor) {
120
+ // CRITICAL: Attach message listener immediately to prevent buffer overflow (libp2p v3)
121
+ const messageHandler = async (event) => {
122
+ try {
123
+ // avoid processing non-olane messages
124
+ if (!event.data) {
125
+ return;
126
+ }
127
+ const message = await this.decodeMessage(event);
128
+ if (typeof message === 'string') {
129
+ return;
130
+ }
131
+ if (this.isRequest(message)) {
132
+ await this.handleRequestMessage(message, stream, toolExecutor);
133
+ }
134
+ else if (this.isResponse(message)) {
135
+ this.logger.warn('Received response message on server-side stream, ignoring', message);
136
+ }
137
+ else {
138
+ this.logger.warn('Received unknown message type', message);
139
+ }
140
+ }
141
+ catch (error) {
142
+ this.logger.error('Error handling stream message:', error);
143
+ // Error already logged, stream will be closed by remote peer or timeout
144
+ }
145
+ };
146
+ const closeHandler = () => {
147
+ this.logger.debug('Stream closed by remote peer');
148
+ stream.removeEventListener('message', messageHandler);
149
+ stream.removeEventListener('close', closeHandler);
150
+ };
151
+ stream.addEventListener('message', messageHandler);
152
+ stream.addEventListener('close', closeHandler);
153
+ }
154
+ /**
155
+ * Handles a request message by executing the tool and sending response
156
+ *
157
+ * @param message - The decoded request message
158
+ * @param stream - The stream to send the response on
159
+ * @param toolExecutor - Function to execute the tool
160
+ */
161
+ async handleRequestMessage(message, stream, toolExecutor) {
162
+ const request = new oRequest(message);
163
+ const responseBuilder = ResponseBuilder.create();
164
+ try {
165
+ this.logger.debug(`Processing request on stream: method=${request.method}, id=${request.id}`);
166
+ const result = await toolExecutor(request, stream);
167
+ const response = await responseBuilder.build(request, result, null);
168
+ await CoreUtils.sendResponse(response, stream);
169
+ this.logger.debug(`Successfully processed request: method=${request.method}, id=${request.id}`);
170
+ }
171
+ catch (error) {
172
+ this.logger.error(`Error processing request: method=${request.method}, id=${request.id}`, error);
173
+ const errorResponse = await responseBuilder.buildError(request, error);
174
+ await CoreUtils.sendResponse(errorResponse, stream);
175
+ }
176
+ }
177
+ /**
178
+ * Handles an outgoing stream on the client side
179
+ * Listens for response messages and emits them via the event emitter
180
+ * If requestHandler is provided, also processes incoming router requests
181
+ *
182
+ * @param stream - The outgoing stream
183
+ * @param emitter - Event emitter for chunk events
184
+ * @param config - Configuration including abort signal
185
+ * @param requestHandler - Optional handler for processing router requests received on this stream
186
+ * @param requestId - Optional request ID to filter responses (for stream reuse scenarios)
187
+ * @returns Promise that resolves with the final response
188
+ */
189
+ async handleOutgoingStream(stream, emitter, config = {}, requestHandler, requestId) {
190
+ return new Promise((resolve, reject) => {
191
+ let lastResponse;
192
+ const messageHandler = async (event) => {
193
+ // avoid processing non-olane messages
194
+ if (!event.data) {
195
+ return;
196
+ }
197
+ try {
198
+ const message = await this.decodeMessage(event);
199
+ if (typeof message === 'string') {
200
+ // this.logger.warn(
201
+ // 'Received string message on server-side stream, ignoring',
202
+ // message,
203
+ // );
204
+ return;
205
+ }
206
+ if (this.isResponse(message)) {
207
+ const response = await CoreUtils.processStreamResponse(event);
208
+ // If requestId is provided, filter responses to only process those matching our request
209
+ // This prevents premature termination when multiple requests share the same stream
210
+ if (requestId !== undefined && response.id !== requestId) {
211
+ this.logger.debug(`Ignoring response for different request (expected: ${requestId}, received: ${response.id})`);
212
+ return;
213
+ }
214
+ // Emit chunk for streaming responses
215
+ emitter.emit('chunk', response);
216
+ // Check if this is the last chunk for THIS request
217
+ if (response.result._last || !response.result._isStreaming) {
218
+ lastResponse = response;
219
+ cleanup();
220
+ resolve(response);
221
+ }
222
+ }
223
+ else if (this.isRequest(message)) {
224
+ // Process incoming router requests if handler is provided
225
+ if (requestHandler) {
226
+ this.logger.debug('Received router request on client-side stream, processing...', message);
227
+ await this.handleRequestMessage(message, stream, requestHandler);
228
+ }
229
+ else {
230
+ this.logger.warn('Received request message on client-side stream, ignoring (no handler)', message);
231
+ }
232
+ }
233
+ else {
234
+ this.logger.warn('Received unknown message type', message);
235
+ }
236
+ }
237
+ catch (error) {
238
+ this.logger.error('Error handling response message:', error);
239
+ cleanup();
240
+ reject(error);
241
+ }
242
+ };
243
+ const closeHandler = () => {
244
+ this.logger.debug('Stream closed by remote peer');
245
+ cleanup();
246
+ if (lastResponse) {
247
+ resolve(lastResponse);
248
+ }
249
+ else {
250
+ reject(new oError(oErrorCodes.TIMEOUT, 'Stream closed before response received'));
251
+ }
252
+ };
253
+ const abortHandler = () => {
254
+ this.logger.debug('Request aborted');
255
+ cleanup();
256
+ try {
257
+ stream.abort(new Error('Request aborted'));
258
+ }
259
+ catch (error) {
260
+ this.logger.debug('Error aborting stream:', error.message);
261
+ }
262
+ reject(new oError(oErrorCodes.TIMEOUT, 'Request aborted'));
263
+ };
264
+ const cleanup = () => {
265
+ stream.removeEventListener('message', messageHandler);
266
+ stream.removeEventListener('close', closeHandler);
267
+ if (config.signal) {
268
+ config.signal.removeEventListener('abort', abortHandler);
269
+ }
270
+ };
271
+ stream.addEventListener('message', messageHandler);
272
+ stream.addEventListener('close', closeHandler);
273
+ if (config.signal) {
274
+ config.signal.addEventListener('abort', abortHandler);
275
+ }
276
+ });
277
+ }
278
+ /**
279
+ * Forwards a request to the next hop and relays response chunks back
280
+ * This implements the middleware/proxy pattern for intermediate nodes
281
+ *
282
+ * @param request - The router request to forward
283
+ * @param incomingStream - The stream to send responses back on
284
+ * @param dialFn - Function to dial the next hop connection
285
+ */
286
+ async forwardRequest(request, incomingStream, dialFn) {
287
+ try {
288
+ // Connect to next hop
289
+ const nextHopConnection = await dialFn(request.params.address);
290
+ // Set up chunk relay - forward responses from next hop back to incoming stream
291
+ nextHopConnection.onChunk(async (response) => {
292
+ try {
293
+ await CoreUtils.sendStreamResponse(response, incomingStream);
294
+ }
295
+ catch (error) {
296
+ this.logger.error('Error forwarding chunk:', error);
297
+ }
298
+ });
299
+ // Transmit the request to next hop
300
+ await nextHopConnection.transmit(request);
301
+ }
302
+ catch (error) {
303
+ this.logger.error('Error forwarding request:', error);
304
+ // Send error response back on incoming stream using ResponseBuilder
305
+ const responseBuilder = ResponseBuilder.create();
306
+ const errorResponse = await responseBuilder.buildError(request, error);
307
+ await CoreUtils.sendResponse(errorResponse, incomingStream);
308
+ }
309
+ }
310
+ }
@@ -1,5 +1,5 @@
1
1
  export * from './o-node.js';
2
- export * from './utils/network.utils.js';
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,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"}
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/network.utils.js';
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,EAAE,QAAQ,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC1E,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,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"}
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"}
@@ -4,39 +4,33 @@ export interface oNodeConfig extends oCoreConfig {
4
4
  leader: oNodeAddress | null;
5
5
  parent: oNodeAddress | null;
6
6
  /**
7
- * Connection heartbeat configuration (libp2p-native pings)
8
- * Detects dead connections via periodic pings using libp2p's ping service
7
+ * Connection health monitoring configuration
8
+ * Detects dead connections by checking libp2p connection state
9
9
  */
10
10
  connectionHeartbeat?: {
11
11
  enabled?: boolean;
12
12
  intervalMs?: number;
13
- timeoutMs?: number;
14
13
  failureThreshold?: number;
15
14
  checkChildren?: boolean;
16
15
  checkParent?: boolean;
17
16
  checkLeader?: boolean;
18
17
  };
19
18
  /**
20
- * Automatic reconnection configuration
21
- * Handles parent connection failures and attempts to reconnect
19
+ * Connection timeout configuration
20
+ * Controls timeouts for stream read and drain operations in connections
22
21
  */
23
- reconnection?: {
24
- enabled?: boolean;
25
- maxAttempts?: number;
26
- baseDelayMs?: number;
27
- maxDelayMs?: number;
28
- useLeaderFallback?: boolean;
29
- };
30
- /**
31
- * Leader request retry configuration
32
- * Handles temporary leader unavailability (healing, maintenance)
33
- */
34
- leaderRetry?: {
35
- enabled?: boolean;
36
- maxAttempts?: number;
37
- baseDelayMs?: number;
38
- maxDelayMs?: number;
39
- timeoutMs?: number;
22
+ connectionTimeouts?: {
23
+ /**
24
+ * Timeout in milliseconds for reading response data from a stream
25
+ * Default: 120000 (2 minutes)
26
+ */
27
+ readTimeoutMs?: number;
28
+ /**
29
+ * Timeout in milliseconds for waiting for stream buffer to drain when backpressure occurs
30
+ * Default: 30000 (30 seconds)
31
+ */
32
+ drainTimeoutMs?: number;
40
33
  };
34
+ runOnLimitedConnection?: boolean;
41
35
  }
42
36
  //# 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;KAC7B,CAAC;IAEF;;;OAGG;IACH,WAAW,CAAC,EAAE;QACZ,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH"}
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;CAClC"}
@@ -1,12 +1,9 @@
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 { oNodeHierarchyManager } from '../o-node.hierarchy-manager.js';
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;
9
- timeoutMs: number;
10
7
  failureThreshold: number;
11
8
  checkChildren: boolean;
12
9
  checkParent: boolean;
@@ -21,36 +18,39 @@ export interface ConnectionHealth {
21
18
  status: 'healthy' | 'degraded' | 'dead';
22
19
  }
23
20
  /**
24
- * Connection Heartbeat Manager
21
+ * Connection Health Monitor
25
22
  *
26
- * Uses libp2p's native ping service to detect dead connections early.
27
- * Continuously pings parent and children to ensure they're alive.
23
+ * Monitors connection health by checking libp2p's connection state directly.
24
+ * Continuously checks parent and children connections to detect failures early.
28
25
  *
29
26
  * How it works:
30
- * - Every `intervalMs`, pings all tracked connections
31
- * - If ping fails, increments failure counter
27
+ * - Every `intervalMs`, checks connection status of all tracked connections
28
+ * - Uses libp2p's connection state (no network overhead from pings)
29
+ * - If connection is not open, increments failure counter
32
30
  * - After `failureThreshold` failures, marks connection as dead
33
31
  * - Emits events for degraded/recovered/dead connections
34
32
  * - Automatically removes dead children from hierarchy
35
33
  * - Emits ParentDisconnectedEvent when parent dies (triggers reconnection)
36
34
  */
37
35
  export declare class oConnectionHeartbeatManager extends oObject {
38
- private p2pNode;
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
- constructor(p2pNode: Libp2p, hierarchyManager: oNodeHierarchyManager, notificationManager: oNotificationManager, address: oNodeAddress, config: HeartbeatConfig);
40
+ private isRunning;
41
+ constructor(node: IHeartbeatableNode, config: HeartbeatConfig);
46
42
  start(): Promise<void>;
47
43
  stop(): Promise<void>;
48
- private performHeartbeatCycle;
49
- private pingTarget;
44
+ private performHealthCheckCycle;
45
+ /**
46
+ * Check if a connection to the given address is open by examining libp2p's connection state
47
+ * @returns true if an open connection exists, false otherwise
48
+ */
49
+ private checkConnectionStatus;
50
+ private checkTarget;
50
51
  private handleConnectionDead;
51
52
  private emitConnectionDegradedEvent;
52
53
  private emitConnectionRecoveredEvent;
53
- private extractPeerIdFromAddress;
54
54
  /**
55
55
  * Get current health status of all connections
56
56
  */
@@ -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,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EACL,OAAO,EAMR,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAErD,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;IAKpD,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,gBAAgB;IACxB,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,MAAM;IARhB,OAAO,CAAC,iBAAiB,CAAC,CAAiB;IAC3C,OAAO,CAAC,SAAS,CAAuC;gBAG9C,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,qBAAqB,EACvC,mBAAmB,EAAE,oBAAoB,EACzC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe;IAK3B,KAAK;IAqBL,IAAI;YAQI,qBAAqB;YAuCrB,UAAU;IAyFxB,OAAO,CAAC,oBAAoB;IAyD5B,OAAO,CAAC,2BAA2B;IAmBnC,OAAO,CAAC,4BAA4B;IAiBpC,OAAO,CAAC,wBAAwB;IAahC;;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
+ {"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;YA8Bf,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"}