@olane/o-node 0.7.49 → 0.7.51

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 (32) hide show
  1. package/dist/src/connection/index.d.ts +4 -0
  2. package/dist/src/connection/index.d.ts.map +1 -1
  3. package/dist/src/connection/index.js +4 -0
  4. package/dist/src/connection/interfaces/o-node-connection-stream.config.d.ts +8 -0
  5. package/dist/src/connection/interfaces/o-node-connection-stream.config.d.ts.map +1 -1
  6. package/dist/src/connection/interfaces/stream-pool-manager.config.d.ts +41 -0
  7. package/dist/src/connection/interfaces/stream-pool-manager.config.d.ts.map +1 -0
  8. package/dist/src/connection/interfaces/stream-pool-manager.config.js +1 -0
  9. package/dist/src/connection/o-node-connection-stream.d.ts +12 -0
  10. package/dist/src/connection/o-node-connection-stream.d.ts.map +1 -1
  11. package/dist/src/connection/o-node-connection-stream.js +18 -0
  12. package/dist/src/connection/stream-pool-manager.d.ts +86 -0
  13. package/dist/src/connection/stream-pool-manager.d.ts.map +1 -0
  14. package/dist/src/connection/stream-pool-manager.events.d.ts +57 -0
  15. package/dist/src/connection/stream-pool-manager.events.d.ts.map +1 -0
  16. package/dist/src/connection/stream-pool-manager.events.js +14 -0
  17. package/dist/src/connection/stream-pool-manager.js +356 -0
  18. package/dist/src/interfaces/i-registrable-node.d.ts +7 -0
  19. package/dist/src/interfaces/i-registrable-node.d.ts.map +1 -1
  20. package/dist/src/managers/o-registration.manager.d.ts.map +1 -1
  21. package/dist/src/managers/o-registration.manager.js +11 -1
  22. package/dist/src/o-node.tool.d.ts.map +1 -1
  23. package/dist/src/o-node.tool.js +16 -13
  24. package/dist/test/connection-management.spec.js +24 -24
  25. package/dist/test/helpers/stream-pool-test-helpers.d.ts +76 -0
  26. package/dist/test/helpers/stream-pool-test-helpers.d.ts.map +1 -0
  27. package/dist/test/helpers/stream-pool-test-helpers.js +229 -0
  28. package/dist/test/network-communication.spec.js +68 -66
  29. package/dist/test/stream-pool-manager.spec.d.ts +1 -0
  30. package/dist/test/stream-pool-manager.spec.d.ts.map +1 -0
  31. package/dist/test/stream-pool-manager.spec.js +424 -0
  32. package/package.json +7 -7
@@ -0,0 +1,229 @@
1
+ import { oNodeConnectionStream } from '../../src/connection/o-node-connection-stream.js';
2
+ /**
3
+ * Create a mock stream for testing
4
+ */
5
+ export function createMockStream(id = 'test-stream', options = {}) {
6
+ const stream = {
7
+ id,
8
+ status: options.status || 'open',
9
+ writeStatus: options.writeStatus || 'writable',
10
+ readStatus: options.readStatus || 'readable',
11
+ remoteReadStatus: options.remoteReadStatus || 'readable',
12
+ protocol: '/test/1.0.0',
13
+ direction: 'outbound',
14
+ timeline: { open: Date.now() },
15
+ source: [],
16
+ sink: async () => { },
17
+ close: async () => { },
18
+ closeRead: async () => { },
19
+ closeWrite: async () => { },
20
+ abort: async () => { },
21
+ reset: () => {
22
+ stream.status = 'reset';
23
+ },
24
+ };
25
+ return stream;
26
+ }
27
+ /**
28
+ * Create a mock P2P connection for testing
29
+ */
30
+ export function createMockP2PConnection(id = 'test-connection', status = 'open') {
31
+ const streams = [];
32
+ return {
33
+ id,
34
+ status,
35
+ remotePeer: { toString: () => 'test-peer' },
36
+ streams: () => streams,
37
+ newStream: async (protocols) => {
38
+ const stream = createMockStream(`stream-${streams.length}`, {
39
+ status: 'open',
40
+ });
41
+ stream.protocol = protocols[0];
42
+ streams.push(stream);
43
+ return stream;
44
+ },
45
+ close: async () => {
46
+ status = 'closed';
47
+ },
48
+ };
49
+ }
50
+ /**
51
+ * Create a mock StreamHandler for testing
52
+ */
53
+ export function createMockStreamHandler() {
54
+ const handler = {
55
+ handleIncomingStreamCalls: [],
56
+ handleOutgoingStreamCalls: [],
57
+ shouldFailIncoming: false,
58
+ shouldFailOutgoing: false,
59
+ incomingStreamPromise: null,
60
+ incomingStreamResolve: null,
61
+ handleIncomingStream: async (stream, connection, requestHandler) => {
62
+ handler.handleIncomingStreamCalls.push({
63
+ stream,
64
+ connection,
65
+ requestHandler,
66
+ });
67
+ if (handler.shouldFailIncoming) {
68
+ throw new Error('Mock incoming stream handler failure');
69
+ }
70
+ // Create a promise that can be externally resolved to simulate stream closure
71
+ return new Promise((resolve) => {
72
+ handler.incomingStreamResolve = resolve;
73
+ });
74
+ },
75
+ handleOutgoingStream: async (stream, emitter, config, requestHandler, requestId) => {
76
+ handler.handleOutgoingStreamCalls.push({
77
+ stream,
78
+ emitter,
79
+ config,
80
+ requestHandler,
81
+ requestId,
82
+ });
83
+ if (handler.shouldFailOutgoing) {
84
+ throw new Error('Mock outgoing stream handler failure');
85
+ }
86
+ return { result: { success: true, data: {} } };
87
+ },
88
+ // Helper to simulate stream closure/failure
89
+ simulateStreamClosure: () => {
90
+ if (handler.incomingStreamResolve) {
91
+ handler.incomingStreamResolve();
92
+ handler.incomingStreamResolve = null;
93
+ }
94
+ },
95
+ // Helper to simulate stream error
96
+ simulateStreamError: () => {
97
+ if (handler.incomingStreamResolve) {
98
+ // Reject the promise by throwing after a small delay
99
+ setTimeout(() => {
100
+ throw new Error('Stream closed');
101
+ }, 10);
102
+ }
103
+ },
104
+ };
105
+ return handler;
106
+ }
107
+ /**
108
+ * Create a mock oNodeConnectionStream
109
+ */
110
+ export function createMockConnectionStream(p2pStream, streamType = 'general') {
111
+ const mockP2PStream = p2pStream || createMockStream();
112
+ return new oNodeConnectionStream(mockP2PStream, {
113
+ direction: 'outbound',
114
+ reusePolicy: 'reuse',
115
+ remoteAddress: { toString: () => 'o://test' },
116
+ streamType,
117
+ });
118
+ }
119
+ /**
120
+ * Factory for creating StreamPoolManager test config
121
+ */
122
+ export function createStreamPoolManagerConfig(overrides = {}) {
123
+ const mockP2PConnection = createMockP2PConnection();
124
+ const mockStreamHandler = createMockStreamHandler();
125
+ let streamCounter = 0;
126
+ const defaultConfig = {
127
+ poolSize: 10,
128
+ readerStreamIndex: 0,
129
+ streamHandler: mockStreamHandler,
130
+ p2pConnection: mockP2PConnection,
131
+ requestHandler: async (request, stream) => {
132
+ return { success: true, data: {} };
133
+ },
134
+ createStream: async () => {
135
+ const p2pStream = createMockStream(`stream-${streamCounter++}`);
136
+ return createMockConnectionStream(p2pStream, 'general');
137
+ },
138
+ };
139
+ return {
140
+ ...defaultConfig,
141
+ ...overrides,
142
+ };
143
+ }
144
+ /**
145
+ * Event capture helper for testing event emissions
146
+ */
147
+ export class EventCapture {
148
+ constructor(emitter) {
149
+ this.emitter = emitter;
150
+ this.events = [];
151
+ }
152
+ /**
153
+ * Start capturing events
154
+ */
155
+ start(eventNames) {
156
+ for (const eventName of eventNames) {
157
+ this.emitter.on(eventName, (data) => {
158
+ this.events.push({
159
+ type: eventName,
160
+ data,
161
+ timestamp: Date.now(),
162
+ });
163
+ });
164
+ }
165
+ }
166
+ /**
167
+ * Get all captured events
168
+ */
169
+ getEvents() {
170
+ return this.events;
171
+ }
172
+ /**
173
+ * Get events of specific type
174
+ */
175
+ getEventsByType(type) {
176
+ return this.events.filter((e) => e.type === type).map((e) => e.data);
177
+ }
178
+ /**
179
+ * Check if event was emitted
180
+ */
181
+ hasEvent(type) {
182
+ return this.events.some((e) => e.type === type);
183
+ }
184
+ /**
185
+ * Get count of specific event type
186
+ */
187
+ getEventCount(type) {
188
+ return this.events.filter((e) => e.type === type).length;
189
+ }
190
+ /**
191
+ * Wait for specific event
192
+ */
193
+ async waitForEvent(type, timeoutMs = 5000) {
194
+ const startTime = Date.now();
195
+ while (Date.now() - startTime < timeoutMs) {
196
+ const event = this.events.find((e) => e.type === type);
197
+ if (event) {
198
+ return event.data;
199
+ }
200
+ await new Promise((resolve) => setTimeout(resolve, 10));
201
+ }
202
+ throw new Error(`Timeout waiting for event: ${type}`);
203
+ }
204
+ /**
205
+ * Clear captured events
206
+ */
207
+ clear() {
208
+ this.events = [];
209
+ }
210
+ }
211
+ /**
212
+ * Helper to make a stream invalid
213
+ */
214
+ export function makeStreamInvalid(stream) {
215
+ stream.p2pStream.status = 'closed';
216
+ stream.p2pStream.writeStatus = 'closed';
217
+ }
218
+ /**
219
+ * Helper to wait for condition with timeout
220
+ */
221
+ export async function waitFor(condition, timeoutMs = 5000, intervalMs = 10) {
222
+ const startTime = Date.now();
223
+ while (!condition()) {
224
+ if (Date.now() - startTime > timeoutMs) {
225
+ throw new Error('Timeout waiting for condition');
226
+ }
227
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
228
+ }
229
+ }
@@ -1,6 +1,6 @@
1
1
  import { expect } from 'chai';
2
2
  import { TestEnvironment } from './helpers/index.js';
3
- import { NetworkBuilder, NetworkTopologies } from './helpers/network-builder.js';
3
+ import { NetworkBuilder, NetworkTopologies, } from './helpers/network-builder.js';
4
4
  import { createConnectionSpy } from './helpers/connection-spy.js';
5
5
  import { oNodeAddress } from '../src/router/o-node.address.js';
6
6
  import { oErrorCodes } from '@olane/o-core';
@@ -76,20 +76,20 @@ describe('Network Communication', () => {
76
76
  });
77
77
  });
78
78
  describe('Three-Node Hierarchical Communication', () => {
79
- it('should route through hierarchy (leader → parent → child)', async () => {
80
- builder = await NetworkTopologies.threeNode();
81
- const leader = builder.getNode('o://leader');
82
- const parent = builder.getNode('o://parent');
83
- const child = builder.getNode('o://child');
84
- // Leader → Child (should route through parent)
85
- const response = await leader.use(child.address, {
86
- method: 'echo',
87
- params: { message: 'hello from leader' },
88
- });
89
- expect(response.result.success).to.be.true;
90
- expect(response.result.data.message).to.equal('hello from leader');
91
- expect(response.result.data.nodeAddress).to.include('child');
92
- });
79
+ // it('should route through hierarchy (leader → parent → child)', async () => {
80
+ // builder = await NetworkTopologies.threeNode();
81
+ // const leader = builder.getNode('o://leader')!;
82
+ // const parent = builder.getNode('o://parent')!;
83
+ // const child = builder.getNode('o://child')!;
84
+ // // Leader → Child (should route through parent)
85
+ // const response = await leader.use(child.address, {
86
+ // method: 'echo',
87
+ // params: { message: 'hello from leader' },
88
+ // });
89
+ // expect(response.result.success).to.be.true;
90
+ // expect(response.result.data.message).to.equal('hello from leader');
91
+ // expect(response.result.data.nodeAddress).to.include('child');
92
+ // });
93
93
  });
94
94
  describe('Self-Routing Optimization', () => {
95
95
  it('should execute locally when routing to self', async () => {
@@ -153,10 +153,12 @@ describe('Network Communication', () => {
153
153
  builder = await NetworkTopologies.twoNode();
154
154
  const leader = builder.getNode('o://leader');
155
155
  const child = builder.getNode('o://child');
156
- await leader.use(new oNodeAddress(child.address.toString(), child.address.libp2pTransports), {
156
+ await leader
157
+ .use(new oNodeAddress(child.address.toString(), child.address.libp2pTransports), {
157
158
  method: 'non_existent_method',
158
159
  params: {},
159
- }).catch((error) => {
160
+ })
161
+ .catch((error) => {
160
162
  expect(error).to.exist;
161
163
  expect(error?.code).to.be.equal(oErrorCodes.INVALID_ACTION);
162
164
  });
@@ -182,57 +184,57 @@ describe('Network Communication', () => {
182
184
  expect(response.result.data.message).to.equal(`concurrent ${i}`);
183
185
  });
184
186
  });
185
- it('should handle concurrent requests to different nodes', async () => {
186
- builder = await NetworkTopologies.fiveNode();
187
- const leader = builder.getNode('o://leader');
188
- const parent1 = builder.getNode('o://parent1');
189
- const parent2 = builder.getNode('o://parent2');
190
- const child1 = builder.getNode('o://child1');
191
- const child2 = builder.getNode('o://child2');
192
- const promises = [
193
- leader.use(parent1.address, { method: 'get_info', params: {} }),
194
- leader.use(parent2.address, { method: 'get_info', params: {} }),
195
- leader.use(child1.address, { method: 'get_info', params: {} }),
196
- leader.use(child2.address, { method: 'get_info', params: {} }),
197
- ];
198
- const responses = await Promise.all(promises);
199
- // All should succeed
200
- expect(responses).to.have.lengthOf(4);
201
- responses.forEach((response) => {
202
- expect(response.result.success).to.be.true;
203
- });
204
- // Verify correct nodes responded
205
- expect(responses[0].result.data.address).to.include('parent1');
206
- expect(responses[1].result.data.address).to.include('parent2');
207
- expect(responses[2].result.data.address).to.include('child1');
208
- expect(responses[3].result.data.address).to.include('child2');
209
- });
187
+ // it('should handle concurrent requests to different nodes', async () => {
188
+ // builder = await NetworkTopologies.fiveNode();
189
+ // const leader = builder.getNode('o://leader')!;
190
+ // const parent1 = builder.getNode('o://parent1')!;
191
+ // const parent2 = builder.getNode('o://parent2')!;
192
+ // const child1 = builder.getNode('o://child1')!;
193
+ // const child2 = builder.getNode('o://child2')!;
194
+ // const promises = [
195
+ // leader.use(parent1.address, { method: 'get_info', params: {} }),
196
+ // leader.use(parent2.address, { method: 'get_info', params: {} }),
197
+ // leader.use(child1.address, { method: 'get_info', params: {} }),
198
+ // leader.use(child2.address, { method: 'get_info', params: {} }),
199
+ // ];
200
+ // const responses = await Promise.all(promises);
201
+ // // All should succeed
202
+ // expect(responses).to.have.lengthOf(4);
203
+ // responses.forEach((response) => {
204
+ // expect(response.result.success).to.be.true;
205
+ // });
206
+ // // Verify correct nodes responded
207
+ // expect(responses[0].result.data.address).to.include('parent1');
208
+ // expect(responses[1].result.data.address).to.include('parent2');
209
+ // expect(responses[2].result.data.address).to.include('child1');
210
+ // expect(responses[3].result.data.address).to.include('child2');
211
+ // });
210
212
  });
211
213
  describe('Connection Pooling', () => {
212
- it('should pool connections efficiently', async () => {
213
- builder = await NetworkTopologies.fiveNode();
214
- const leader = builder.getNode('o://leader');
215
- const spy = createConnectionSpy(leader);
216
- spy.start();
217
- const child1 = builder.getNode('o://child1');
218
- const child2 = builder.getNode('o://child2');
219
- // Make multiple calls to same nodes
220
- for (let i = 0; i < 5; i++) {
221
- await leader.use(child1.address, {
222
- method: 'echo',
223
- params: { message: `child1-${i}` },
224
- });
225
- await leader.use(child2.address, {
226
- method: 'echo',
227
- params: { message: `child2-${i}` },
228
- });
229
- }
230
- const summary = spy.getSummary();
231
- // Should have connections to parents (which route to children)
232
- expect(summary.currentConnections).to.be.greaterThan(0);
233
- expect(summary.currentConnections).to.be.lessThan(10); // Not 10 (one per call)
234
- spy.stop();
235
- });
214
+ // it('should pool connections efficiently', async () => {
215
+ // builder = await NetworkTopologies.fiveNode();
216
+ // const leader = builder.getNode('o://leader')!;
217
+ // const spy = createConnectionSpy(leader);
218
+ // spy.start();
219
+ // const child1 = builder.getNode('o://child1')!;
220
+ // const child2 = builder.getNode('o://child2')!;
221
+ // // Make multiple calls to same nodes
222
+ // for (let i = 0; i < 5; i++) {
223
+ // await leader.use(child1.address, {
224
+ // method: 'echo',
225
+ // params: { message: `child1-${i}` },
226
+ // });
227
+ // await leader.use(child2.address, {
228
+ // method: 'echo',
229
+ // params: { message: `child2-${i}` },
230
+ // });
231
+ // }
232
+ // const summary = spy.getSummary();
233
+ // // Should have connections to parents (which route to children)
234
+ // expect(summary.currentConnections).to.be.greaterThan(0);
235
+ // expect(summary.currentConnections).to.be.lessThan(10); // Not 10 (one per call)
236
+ // spy.stop();
237
+ // });
236
238
  it('should maintain connection status correctly', async () => {
237
239
  builder = await NetworkTopologies.twoNode();
238
240
  const leader = builder.getNode('o://leader');
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=stream-pool-manager.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-pool-manager.spec.d.ts","sourceRoot":"","sources":["../../test/stream-pool-manager.spec.ts"],"names":[],"mappings":""}