@olane/o-node 0.7.47 → 0.7.49
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/o-node-connection.d.ts.map +1 -1
- package/dist/src/connection/o-node-connection.js +0 -2
- package/dist/src/connection/o-node-connection.manager.d.ts +25 -34
- package/dist/src/connection/o-node-connection.manager.d.ts.map +1 -1
- package/dist/src/connection/o-node-connection.manager.js +90 -241
- package/dist/src/interfaces/i-reconnectable-node.d.ts +9 -0
- package/dist/src/interfaces/i-reconnectable-node.d.ts.map +1 -1
- package/dist/src/interfaces/i-registrable-node.d.ts +68 -0
- package/dist/src/interfaces/i-registrable-node.d.ts.map +1 -0
- package/dist/src/interfaces/i-registrable-node.js +1 -0
- package/dist/src/managers/o-reconnection.manager.d.ts.map +1 -1
- package/dist/src/managers/o-reconnection.manager.js +42 -15
- package/dist/src/managers/o-registration.manager.d.ts +79 -0
- package/dist/src/managers/o-registration.manager.d.ts.map +1 -0
- package/dist/src/managers/o-registration.manager.js +216 -0
- package/dist/src/o-node.d.ts +3 -1
- package/dist/src/o-node.d.ts.map +1 -1
- package/dist/src/o-node.js +14 -59
- 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 +7 -3
- package/dist/src/utils/connection.utils.d.ts +5 -0
- package/dist/src/utils/connection.utils.d.ts.map +1 -1
- package/dist/src/utils/connection.utils.js +31 -10
- package/package.json +7 -7
- package/dist/test/astream-reuse.spec.d.ts +0 -2
- package/dist/test/astream-reuse.spec.d.ts.map +0 -1
- package/dist/test/astream-reuse.spec.js +0 -107
package/dist/src/o-node.tool.js
CHANGED
|
@@ -47,15 +47,19 @@ export class oNodeTool extends oTool(oServerNode) {
|
|
|
47
47
|
});
|
|
48
48
|
await this.handleProtocolReuse(address);
|
|
49
49
|
}
|
|
50
|
-
async
|
|
51
|
-
|
|
52
|
-
this.streamHandler = new StreamHandler(this.logger);
|
|
50
|
+
async initializeProtocols() {
|
|
51
|
+
this.logger.info('Initializing custom protocols for node...');
|
|
53
52
|
await this.handleProtocol(this.address);
|
|
54
53
|
if (this.staticAddress &&
|
|
55
54
|
this.staticAddress?.toString() !== this.address.toString()) {
|
|
56
55
|
await this.handleProtocol(this.staticAddress);
|
|
57
56
|
}
|
|
58
57
|
}
|
|
58
|
+
async initialize() {
|
|
59
|
+
await super.initialize();
|
|
60
|
+
this.streamHandler = new StreamHandler(this.logger);
|
|
61
|
+
await this.initializeProtocols();
|
|
62
|
+
}
|
|
59
63
|
async handleStreamReuse(stream, connection) {
|
|
60
64
|
return this.handleStream(stream, connection, true);
|
|
61
65
|
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { Connection } from '@olane/o-config';
|
|
2
2
|
import { oObject } from '@olane/o-core';
|
|
3
3
|
export declare class ConnectionUtils extends oObject {
|
|
4
|
+
/**
|
|
5
|
+
* Waits for a peer to appear in the peer store with sufficient protocol information.
|
|
6
|
+
* Implements retry logic to handle race conditions where peer store is not immediately populated.
|
|
7
|
+
*/
|
|
8
|
+
private static waitForPeerInStore;
|
|
4
9
|
static addressFromConnection(options: {
|
|
5
10
|
currentNode: any;
|
|
6
11
|
connection: Connection;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.utils.d.ts","sourceRoot":"","sources":["../../../src/utils/connection.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA2B,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAuB,OAAO,EAAE,MAAM,eAAe,CAAC;AAI7D,qBAAa,eAAgB,SAAQ,OAAO;
|
|
1
|
+
{"version":3,"file":"connection.utils.d.ts","sourceRoot":"","sources":["../../../src/utils/connection.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA2B,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAuB,OAAO,EAAE,MAAM,eAAe,CAAC;AAI7D,qBAAa,eAAgB,SAAQ,OAAO;IAC1C;;;OAGG;mBACkB,kBAAkB;WAgCnB,qBAAqB,CAAC,OAAO,EAAE;QACjD,WAAW,EAAE,GAAG,CAAC;QACjB,UAAU,EAAE,UAAU,CAAC;KACxB;CA0DF"}
|
|
@@ -2,25 +2,46 @@ import { oError, oErrorCodes, oObject } from '@olane/o-core';
|
|
|
2
2
|
import { oNodeAddress } from '../router/o-node.address.js';
|
|
3
3
|
import { oNodeTransport } from '../router/o-node.transport.js';
|
|
4
4
|
export class ConnectionUtils extends oObject {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Waits for a peer to appear in the peer store with sufficient protocol information.
|
|
7
|
+
* Implements retry logic to handle race conditions where peer store is not immediately populated.
|
|
8
|
+
*/
|
|
9
|
+
static async waitForPeerInStore(p2pNode, remotePeerId) {
|
|
10
|
+
const MAX_RETRIES = 100; // 5 seconds total (100 * 50ms)
|
|
11
|
+
const RETRY_DELAY_MS = 50;
|
|
12
|
+
const MIN_PROTOCOLS = 2;
|
|
13
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
10
14
|
const peers = await p2pNode.peerStore.all();
|
|
11
15
|
const remotePeer = peers.find((peer) => {
|
|
12
|
-
return peer.id.toString() ===
|
|
16
|
+
return peer.id.toString() === remotePeerId.toString();
|
|
13
17
|
});
|
|
14
|
-
if
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
// Check if peer exists and has sufficient protocol information
|
|
19
|
+
if (remotePeer && remotePeer.protocols.length > MIN_PROTOCOLS) {
|
|
20
|
+
return remotePeer;
|
|
21
|
+
}
|
|
22
|
+
// Wait before next retry
|
|
23
|
+
if (attempt < MAX_RETRIES - 1) {
|
|
24
|
+
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS));
|
|
17
25
|
}
|
|
26
|
+
}
|
|
27
|
+
// Timeout exceeded
|
|
28
|
+
throw new Error(`Timeout waiting for peer ${remotePeerId.toString()} to appear in peer store with sufficient protocols (waited ${MAX_RETRIES * RETRY_DELAY_MS}ms)`);
|
|
29
|
+
}
|
|
30
|
+
// TODO: improve this logic (poor implementation for now)
|
|
31
|
+
static async addressFromConnection(options) {
|
|
32
|
+
try {
|
|
33
|
+
const { currentNode, connection } = options;
|
|
34
|
+
const p2pNode = currentNode.p2pNode;
|
|
35
|
+
// Wait for peer to appear in peer store with sufficient protocol information
|
|
36
|
+
// This handles race conditions where peer store is not immediately populated
|
|
37
|
+
const remotePeer = await this.waitForPeerInStore(p2pNode, connection.remotePeer);
|
|
18
38
|
// Get origin address for comparison
|
|
19
39
|
const originAddress = currentNode.address?.value;
|
|
20
40
|
if (!originAddress) {
|
|
21
41
|
throw new Error('Origin address is not configured');
|
|
22
42
|
}
|
|
23
|
-
const oProtocol = remotePeer.protocols.find((p) => p.startsWith('/o/')
|
|
43
|
+
const oProtocol = remotePeer.protocols.find((p) => p.startsWith('/o/') &&
|
|
44
|
+
p.includes(currentNode?.address?.protocol) === false);
|
|
24
45
|
if (!oProtocol) {
|
|
25
46
|
throw new Error('Failed to extract remote address, could not find o-protocol in peer protocols.');
|
|
26
47
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@olane/o-node",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.49",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"types": "dist/src/index.d.ts",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@eslint/eslintrc": "^3.3.1",
|
|
42
42
|
"@eslint/js": "^9.29.0",
|
|
43
|
-
"@olane/o-test": "0.7.
|
|
43
|
+
"@olane/o-test": "0.7.49",
|
|
44
44
|
"@tsconfig/node20": "^20.1.6",
|
|
45
45
|
"@types/jest": "^30.0.0",
|
|
46
46
|
"@types/json5": "^2.2.0",
|
|
@@ -60,13 +60,13 @@
|
|
|
60
60
|
"typescript": "5.4.5"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@olane/o-config": "0.7.
|
|
64
|
-
"@olane/o-core": "0.7.
|
|
65
|
-
"@olane/o-protocol": "0.7.
|
|
66
|
-
"@olane/o-tool": "0.7.
|
|
63
|
+
"@olane/o-config": "0.7.49",
|
|
64
|
+
"@olane/o-core": "0.7.49",
|
|
65
|
+
"@olane/o-protocol": "0.7.49",
|
|
66
|
+
"@olane/o-tool": "0.7.49",
|
|
67
67
|
"debug": "^4.4.1",
|
|
68
68
|
"dotenv": "^16.5.0",
|
|
69
69
|
"json5": "^2.2.3"
|
|
70
70
|
},
|
|
71
|
-
"gitHead": "
|
|
71
|
+
"gitHead": "710399363610686d2c245af0ac6773fb794add25"
|
|
72
72
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"astream-reuse.spec.d.ts","sourceRoot":"","sources":["../../test/astream-reuse.spec.ts"],"names":[],"mappings":""}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { expect } from 'chai';
|
|
2
|
-
import { TestEnvironment } from './helpers/index.js';
|
|
3
|
-
import { NetworkTopologies, } from './helpers/network-builder.js';
|
|
4
|
-
import { oNodeAddress } from '../src/router/o-node.address.js';
|
|
5
|
-
describe('Stream Reuse', () => {
|
|
6
|
-
const env = new TestEnvironment();
|
|
7
|
-
let builder;
|
|
8
|
-
afterEach(async () => {
|
|
9
|
-
if (builder) {
|
|
10
|
-
await builder.cleanup();
|
|
11
|
-
}
|
|
12
|
-
await env.cleanup();
|
|
13
|
-
});
|
|
14
|
-
describe('Stream Reuse with reusePolicy="reuse"', () => {
|
|
15
|
-
it('should reuse the same stream across multiple requests', async () => {
|
|
16
|
-
builder = await NetworkTopologies.twoNode();
|
|
17
|
-
const leader = builder.getNode('o://leader');
|
|
18
|
-
const child = builder.getNode('o://child');
|
|
19
|
-
// Track stream IDs
|
|
20
|
-
const streamIds = [];
|
|
21
|
-
// Make first request
|
|
22
|
-
const response1 = await leader.use(new oNodeAddress(child.address.toString(), child.address.libp2pTransports), {
|
|
23
|
-
method: 'echo',
|
|
24
|
-
params: { message: 'first' },
|
|
25
|
-
});
|
|
26
|
-
expect(response1.result.success).to.be.true;
|
|
27
|
-
// Get active connection to inspect streams
|
|
28
|
-
const connectionManager = leader.connectionManager;
|
|
29
|
-
const connection = connectionManager.getCachedLibp2pConnection(child.address);
|
|
30
|
-
connection.reusePolicy;
|
|
31
|
-
if (connection) {
|
|
32
|
-
// Store initial stream count and first stream ID
|
|
33
|
-
const initialStreamCount = connection.streams.length;
|
|
34
|
-
if (connection.streams.length > 0) {
|
|
35
|
-
streamIds.push(connection.streams[0].id);
|
|
36
|
-
}
|
|
37
|
-
// Make second request
|
|
38
|
-
const response2 = await leader.use(new oNodeAddress(child.address.toString(), child.address.libp2pTransports), {
|
|
39
|
-
method: 'echo',
|
|
40
|
-
params: { message: 'second' },
|
|
41
|
-
});
|
|
42
|
-
expect(response2.result.success).to.be.true;
|
|
43
|
-
// Get stream count after second request
|
|
44
|
-
const finalStreamCount = connection.streams.length;
|
|
45
|
-
if (connection.streams.length > 0) {
|
|
46
|
-
streamIds.push(connection.streams[0].id);
|
|
47
|
-
}
|
|
48
|
-
// With default behavior (reusePolicy='none'), new streams are created
|
|
49
|
-
// This test verifies current behavior
|
|
50
|
-
expect(streamIds.length).to.be.greaterThan(0);
|
|
51
|
-
expect(finalStreamCount).to.be.greaterThan(0);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
describe('Stream Creation with reusePolicy="none"', () => {
|
|
56
|
-
it('should create new streams for each request', async () => {
|
|
57
|
-
builder = await NetworkTopologies.twoNode();
|
|
58
|
-
const leader = builder.getNode('o://leader');
|
|
59
|
-
const child = builder.getNode('o://child');
|
|
60
|
-
// Make multiple requests
|
|
61
|
-
for (let i = 0; i < 3; i++) {
|
|
62
|
-
const response = await leader.use(new oNodeAddress(child.address.toString(), child.address.libp2pTransports), {
|
|
63
|
-
method: 'echo',
|
|
64
|
-
params: { message: `request-${i}` },
|
|
65
|
-
});
|
|
66
|
-
expect(response.result.success).to.be.true;
|
|
67
|
-
}
|
|
68
|
-
// Get connection to verify stream behavior
|
|
69
|
-
const connectionManager = leader.connectionManager;
|
|
70
|
-
const connection = connectionManager.getCachedLibp2pConnection(child.address);
|
|
71
|
-
// With default reusePolicy='none', streams are closed after each request
|
|
72
|
-
// So we expect minimal open streams
|
|
73
|
-
expect(connection).to.exist;
|
|
74
|
-
if (connection) {
|
|
75
|
-
// The number of open streams should be limited since old ones are closed
|
|
76
|
-
expect(connection.streams.length).to.be.lessThan(10);
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
describe('Stream Persistence After Transmission', () => {
|
|
81
|
-
it('should close streams after transmission with reusePolicy="none"', async () => {
|
|
82
|
-
builder = await NetworkTopologies.twoNode();
|
|
83
|
-
const leader = builder.getNode('o://leader');
|
|
84
|
-
const child = builder.getNode('o://child');
|
|
85
|
-
// Make a request
|
|
86
|
-
const response = await leader.use(new oNodeAddress(child.address.toString(), child.address.libp2pTransports), {
|
|
87
|
-
method: 'echo',
|
|
88
|
-
params: { message: 'test' },
|
|
89
|
-
});
|
|
90
|
-
expect(response.result.success).to.be.true;
|
|
91
|
-
// Get connection
|
|
92
|
-
const connectionManager = leader.connectionManager;
|
|
93
|
-
const connection = connectionManager.getCachedLibp2pConnection(child.address);
|
|
94
|
-
expect(connection).to.exist;
|
|
95
|
-
if (connection) {
|
|
96
|
-
// Make another request to verify new stream can be created
|
|
97
|
-
const response2 = await leader.use(new oNodeAddress(child.address.toString(), child.address.libp2pTransports), {
|
|
98
|
-
method: 'echo',
|
|
99
|
-
params: { message: 'test2' },
|
|
100
|
-
});
|
|
101
|
-
expect(response2.result.success).to.be.true;
|
|
102
|
-
// Verify connection is still functional
|
|
103
|
-
expect(connection.status).to.equal('open');
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
});
|