@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.
Files changed (28) hide show
  1. package/dist/src/connection/o-node-connection.d.ts.map +1 -1
  2. package/dist/src/connection/o-node-connection.js +0 -2
  3. package/dist/src/connection/o-node-connection.manager.d.ts +25 -34
  4. package/dist/src/connection/o-node-connection.manager.d.ts.map +1 -1
  5. package/dist/src/connection/o-node-connection.manager.js +90 -241
  6. package/dist/src/interfaces/i-reconnectable-node.d.ts +9 -0
  7. package/dist/src/interfaces/i-reconnectable-node.d.ts.map +1 -1
  8. package/dist/src/interfaces/i-registrable-node.d.ts +68 -0
  9. package/dist/src/interfaces/i-registrable-node.d.ts.map +1 -0
  10. package/dist/src/interfaces/i-registrable-node.js +1 -0
  11. package/dist/src/managers/o-reconnection.manager.d.ts.map +1 -1
  12. package/dist/src/managers/o-reconnection.manager.js +42 -15
  13. package/dist/src/managers/o-registration.manager.d.ts +79 -0
  14. package/dist/src/managers/o-registration.manager.d.ts.map +1 -0
  15. package/dist/src/managers/o-registration.manager.js +216 -0
  16. package/dist/src/o-node.d.ts +3 -1
  17. package/dist/src/o-node.d.ts.map +1 -1
  18. package/dist/src/o-node.js +14 -59
  19. package/dist/src/o-node.tool.d.ts +1 -0
  20. package/dist/src/o-node.tool.d.ts.map +1 -1
  21. package/dist/src/o-node.tool.js +7 -3
  22. package/dist/src/utils/connection.utils.d.ts +5 -0
  23. package/dist/src/utils/connection.utils.d.ts.map +1 -1
  24. package/dist/src/utils/connection.utils.js +31 -10
  25. package/package.json +7 -7
  26. package/dist/test/astream-reuse.spec.d.ts +0 -2
  27. package/dist/test/astream-reuse.spec.d.ts.map +0 -1
  28. package/dist/test/astream-reuse.spec.js +0 -107
@@ -47,15 +47,19 @@ export class oNodeTool extends oTool(oServerNode) {
47
47
  });
48
48
  await this.handleProtocolReuse(address);
49
49
  }
50
- async initialize() {
51
- await super.initialize();
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;WACtB,qBAAqB,CAAC,OAAO,EAAE;QACjD,WAAW,EAAE,GAAG,CAAC;QACjB,UAAU,EAAE,UAAU,CAAC;KACxB;CA+DF"}
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
- static async addressFromConnection(options) {
6
- try {
7
- const { currentNode, connection } = options;
8
- const p2pNode = currentNode.p2pNode;
9
- // Extract the actual olane address from the peer store
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() === connection.remotePeer.toString();
16
+ return peer.id.toString() === remotePeerId.toString();
13
17
  });
14
- if (!remotePeer) {
15
- console.log('Failed to find peer:', remotePeer);
16
- throw new Error(`Failed to extract remote address, peer ${connection.remotePeer.toString()} not found in peer store.`);
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.47",
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.47",
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.47",
64
- "@olane/o-core": "0.7.47",
65
- "@olane/o-protocol": "0.7.47",
66
- "@olane/o-tool": "0.7.47",
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": "2730d1ccdfb58549ebd253ded9479156086cc066"
71
+ "gitHead": "710399363610686d2c245af0ac6773fb794add25"
72
72
  }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=astream-reuse.spec.d.ts.map
@@ -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
- });