@olane/o-node 0.7.12-alpha.21 → 0.7.12-alpha.23

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 (175) hide show
  1. package/dist/o-core/src/connection/o-response.d.ts +26 -0
  2. package/dist/o-core/src/connection/o-response.d.ts.map +1 -0
  3. package/dist/o-core/src/connection/o-response.js +45 -0
  4. package/dist/o-core/src/error/enums/codes.error.d.ts +20 -0
  5. package/dist/o-core/src/error/enums/codes.error.d.ts.map +1 -0
  6. package/dist/o-core/src/error/enums/codes.error.js +20 -0
  7. package/dist/o-core/src/error/interfaces/o-error.interface.d.ts +6 -0
  8. package/dist/o-core/src/error/interfaces/o-error.interface.d.ts.map +1 -0
  9. package/dist/o-core/src/error/interfaces/o-error.interface.js +1 -0
  10. package/dist/o-core/src/error/o-error.d.ts +15 -0
  11. package/dist/o-core/src/error/o-error.d.ts.map +1 -0
  12. package/dist/o-core/src/error/o-error.js +27 -0
  13. package/dist/o-core/src/streaming/index.d.ts +11 -0
  14. package/dist/o-core/src/streaming/index.d.ts.map +1 -0
  15. package/dist/o-core/src/streaming/index.js +14 -0
  16. package/dist/o-core/src/streaming/protocol-builder.d.ts +62 -0
  17. package/dist/o-core/src/streaming/protocol-builder.d.ts.map +1 -0
  18. package/dist/o-core/src/streaming/protocol-builder.js +93 -0
  19. package/dist/o-core/src/streaming/stream-config.d.ts +36 -0
  20. package/dist/o-core/src/streaming/stream-config.d.ts.map +1 -0
  21. package/dist/o-core/src/streaming/stream-config.js +19 -0
  22. package/dist/o-core/src/streaming/stream-handler.base.d.ts +85 -0
  23. package/dist/o-core/src/streaming/stream-handler.base.d.ts.map +1 -0
  24. package/dist/o-core/src/streaming/stream-handler.base.js +112 -0
  25. package/dist/o-core/src/streaming/stream-transport.interface.d.ts +61 -0
  26. package/dist/o-core/src/streaming/stream-transport.interface.d.ts.map +1 -0
  27. package/dist/o-core/src/streaming/stream-transport.interface.js +10 -0
  28. package/dist/o-core/src/utils/streaming.utils.d.ts +37 -0
  29. package/dist/o-core/src/utils/streaming.utils.d.ts.map +1 -0
  30. package/dist/o-core/src/utils/streaming.utils.js +71 -0
  31. package/dist/o-node/src/connection/index.d.ts +5 -0
  32. package/dist/o-node/src/connection/index.d.ts.map +1 -0
  33. package/dist/o-node/src/connection/index.js +4 -0
  34. package/dist/o-node/src/connection/interfaces/o-node-connection-manager.config.d.ts +6 -0
  35. package/dist/o-node/src/connection/interfaces/o-node-connection-manager.config.d.ts.map +1 -0
  36. package/dist/o-node/src/connection/interfaces/o-node-connection-manager.config.js +1 -0
  37. package/dist/o-node/src/connection/interfaces/o-node-connection.config.d.ts +6 -0
  38. package/dist/o-node/src/connection/interfaces/o-node-connection.config.d.ts.map +1 -0
  39. package/dist/o-node/src/connection/interfaces/o-node-connection.config.js +1 -0
  40. package/dist/o-node/src/connection/o-node-connection.d.ts +20 -0
  41. package/dist/o-node/src/connection/o-node-connection.d.ts.map +1 -0
  42. package/dist/o-node/src/connection/o-node-connection.js +146 -0
  43. package/dist/o-node/src/connection/o-node-connection.manager.d.ts +19 -0
  44. package/dist/o-node/src/connection/o-node-connection.manager.d.ts.map +1 -0
  45. package/dist/o-node/src/connection/o-node-connection.manager.js +92 -0
  46. package/dist/o-node/src/index.d.ts +10 -0
  47. package/dist/o-node/src/index.d.ts.map +1 -0
  48. package/dist/o-node/src/index.js +9 -0
  49. package/dist/o-node/src/interfaces/i-heartbeatable-node.d.ts +49 -0
  50. package/dist/o-node/src/interfaces/i-heartbeatable-node.d.ts.map +1 -0
  51. package/dist/o-node/src/interfaces/i-heartbeatable-node.js +1 -0
  52. package/dist/o-node/src/interfaces/i-reconnectable-node.d.ts +46 -0
  53. package/dist/o-node/src/interfaces/i-reconnectable-node.d.ts.map +1 -0
  54. package/dist/o-node/src/interfaces/i-reconnectable-node.js +1 -0
  55. package/dist/o-node/src/interfaces/o-node.config.d.ts +66 -0
  56. package/dist/o-node/src/interfaces/o-node.config.d.ts.map +1 -0
  57. package/dist/o-node/src/interfaces/o-node.config.js +1 -0
  58. package/dist/o-node/src/interfaces/o-node.tool-config.d.ts +4 -0
  59. package/dist/o-node/src/interfaces/o-node.tool-config.d.ts.map +1 -0
  60. package/dist/o-node/src/interfaces/o-node.tool-config.js +1 -0
  61. package/dist/o-node/src/lib/network-activity.lib.d.ts +1 -0
  62. package/dist/o-node/src/lib/network-activity.lib.d.ts.map +1 -0
  63. package/dist/o-node/src/lib/network-activity.lib.js +34 -0
  64. package/dist/o-node/src/managers/o-connection-heartbeat.manager.d.ts +62 -0
  65. package/dist/o-node/src/managers/o-connection-heartbeat.manager.d.ts.map +1 -0
  66. package/dist/o-node/src/managers/o-connection-heartbeat.manager.js +213 -0
  67. package/dist/o-node/src/managers/o-reconnection.manager.d.ts +51 -0
  68. package/dist/o-node/src/managers/o-reconnection.manager.d.ts.map +1 -0
  69. package/dist/o-node/src/managers/o-reconnection.manager.js +266 -0
  70. package/dist/o-node/src/nodes/client.node.d.ts +7 -0
  71. package/dist/o-node/src/nodes/client.node.d.ts.map +1 -0
  72. package/dist/o-node/src/nodes/client.node.js +16 -0
  73. package/dist/o-node/src/nodes/index.d.ts +4 -0
  74. package/dist/o-node/src/nodes/index.d.ts.map +1 -0
  75. package/dist/o-node/src/nodes/index.js +3 -0
  76. package/dist/o-node/src/nodes/server.node.d.ts +7 -0
  77. package/dist/o-node/src/nodes/server.node.d.ts.map +1 -0
  78. package/dist/o-node/src/nodes/server.node.js +20 -0
  79. package/dist/o-node/src/nodes/websocket.node.d.ts +7 -0
  80. package/dist/o-node/src/nodes/websocket.node.d.ts.map +1 -0
  81. package/dist/o-node/src/nodes/websocket.node.js +18 -0
  82. package/dist/o-node/src/o-node.d.ts +72 -0
  83. package/dist/o-node/src/o-node.d.ts.map +1 -0
  84. package/dist/o-node/src/o-node.hierarchy-manager.d.ts +15 -0
  85. package/dist/o-node/src/o-node.hierarchy-manager.d.ts.map +1 -0
  86. package/dist/o-node/src/o-node.hierarchy-manager.js +15 -0
  87. package/dist/o-node/src/o-node.js +420 -0
  88. package/dist/o-node/src/o-node.notification-manager.d.ts +52 -0
  89. package/dist/o-node/src/o-node.notification-manager.d.ts.map +1 -0
  90. package/dist/o-node/src/o-node.notification-manager.js +185 -0
  91. package/dist/o-node/src/o-node.tool.d.ts +18 -0
  92. package/dist/o-node/src/o-node.tool.d.ts.map +1 -0
  93. package/dist/o-node/src/o-node.tool.js +116 -0
  94. package/dist/o-node/src/router/index.d.ts +6 -0
  95. package/dist/o-node/src/router/index.d.ts.map +1 -0
  96. package/dist/o-node/src/router/index.js +5 -0
  97. package/dist/o-node/src/router/interfaces/o-node-router.config.d.ts +3 -0
  98. package/dist/o-node/src/router/interfaces/o-node-router.config.d.ts.map +1 -0
  99. package/dist/o-node/src/router/interfaces/o-node-router.config.js +1 -0
  100. package/dist/o-node/src/router/interfaces/o-node-router.response.d.ts +8 -0
  101. package/dist/o-node/src/router/interfaces/o-node-router.response.d.ts.map +1 -0
  102. package/dist/o-node/src/router/interfaces/o-node-router.response.js +1 -0
  103. package/dist/o-node/src/router/o-node.address.d.ts +18 -0
  104. package/dist/o-node/src/router/o-node.address.d.ts.map +1 -0
  105. package/dist/o-node/src/router/o-node.address.js +29 -0
  106. package/dist/o-node/src/router/o-node.router.d.ts +45 -0
  107. package/dist/o-node/src/router/o-node.router.d.ts.map +1 -0
  108. package/dist/o-node/src/router/o-node.router.js +125 -0
  109. package/dist/o-node/src/router/o-node.routing-policy.d.ts +30 -0
  110. package/dist/o-node/src/router/o-node.routing-policy.d.ts.map +1 -0
  111. package/dist/o-node/src/router/o-node.routing-policy.js +57 -0
  112. package/dist/o-node/src/router/o-node.transport.d.ts +11 -0
  113. package/dist/o-node/src/router/o-node.transport.d.ts.map +1 -0
  114. package/dist/o-node/src/router/o-node.transport.js +18 -0
  115. package/dist/o-node/src/router/resolvers/index.d.ts +4 -0
  116. package/dist/o-node/src/router/resolvers/index.d.ts.map +1 -0
  117. package/dist/o-node/src/router/resolvers/index.js +3 -0
  118. package/dist/o-node/src/router/resolvers/o-node.leader-resolver-fallback.d.ts +8 -0
  119. package/dist/o-node/src/router/resolvers/o-node.leader-resolver-fallback.d.ts.map +1 -0
  120. package/dist/o-node/src/router/resolvers/o-node.leader-resolver-fallback.js +35 -0
  121. package/dist/o-node/src/router/resolvers/o-node.resolver.d.ts +11 -0
  122. package/dist/o-node/src/router/resolvers/o-node.resolver.d.ts.map +1 -0
  123. package/dist/o-node/src/router/resolvers/o-node.resolver.js +41 -0
  124. package/dist/o-node/src/router/resolvers/o-node.search-resolver.d.ts +170 -0
  125. package/dist/o-node/src/router/resolvers/o-node.search-resolver.d.ts.map +1 -0
  126. package/dist/o-node/src/router/resolvers/o-node.search-resolver.js +285 -0
  127. package/dist/o-node/src/router/route.request.d.ts +14 -0
  128. package/dist/o-node/src/router/route.request.d.ts.map +1 -0
  129. package/dist/o-node/src/router/route.request.js +1 -0
  130. package/dist/o-node/src/streaming/index.d.ts +10 -0
  131. package/dist/o-node/src/streaming/index.d.ts.map +1 -0
  132. package/dist/o-node/src/streaming/index.js +12 -0
  133. package/dist/o-node/src/streaming/libp2p-stream-transport.d.ts +50 -0
  134. package/dist/o-node/src/streaming/libp2p-stream-transport.d.ts.map +1 -0
  135. package/dist/o-node/src/streaming/libp2p-stream-transport.js +137 -0
  136. package/dist/o-node/src/streaming/node-stream-handler.d.ts +65 -0
  137. package/dist/o-node/src/streaming/node-stream-handler.d.ts.map +1 -0
  138. package/dist/o-node/src/streaming/node-stream-handler.js +101 -0
  139. package/dist/o-node/src/utils/circuit-breaker.d.ts +107 -0
  140. package/dist/o-node/src/utils/circuit-breaker.d.ts.map +1 -0
  141. package/dist/o-node/src/utils/circuit-breaker.js +175 -0
  142. package/dist/o-node/src/utils/circuit-breaker.test.d.ts +2 -0
  143. package/dist/o-node/src/utils/circuit-breaker.test.d.ts.map +1 -0
  144. package/dist/o-node/src/utils/circuit-breaker.test.js +262 -0
  145. package/dist/o-node/src/utils/leader-request-wrapper.d.ts +66 -0
  146. package/dist/o-node/src/utils/leader-request-wrapper.d.ts.map +1 -0
  147. package/dist/o-node/src/utils/leader-request-wrapper.js +160 -0
  148. package/dist/o-node/src/utils/leader-request-wrapper.test.d.ts +1 -0
  149. package/dist/o-node/src/utils/leader-request-wrapper.test.d.ts.map +1 -0
  150. package/dist/o-node/src/utils/leader-request-wrapper.test.js +246 -0
  151. package/dist/o-node/src/utils/network.utils.d.ts +20 -0
  152. package/dist/o-node/src/utils/network.utils.d.ts.map +1 -0
  153. package/dist/o-node/src/utils/network.utils.js +74 -0
  154. package/dist/o-node/test/o-node.spec.d.ts +2 -0
  155. package/dist/o-node/test/o-node.spec.d.ts.map +1 -0
  156. package/dist/o-node/test/o-node.spec.js +20 -0
  157. package/dist/o-node/test/search-resolver.spec.d.ts +2 -0
  158. package/dist/o-node/test/search-resolver.spec.d.ts.map +1 -0
  159. package/dist/o-node/test/search-resolver.spec.js +693 -0
  160. package/dist/src/connection/o-node-connection.d.ts +7 -0
  161. package/dist/src/connection/o-node-connection.d.ts.map +1 -1
  162. package/dist/src/connection/o-node-connection.js +89 -2
  163. package/dist/src/connection/o-node-connection.manager.d.ts +2 -0
  164. package/dist/src/connection/o-node-connection.manager.d.ts.map +1 -1
  165. package/dist/src/connection/o-node-connection.manager.js +5 -1
  166. package/dist/src/interfaces/o-node.config.d.ts +16 -0
  167. package/dist/src/interfaces/o-node.config.d.ts.map +1 -1
  168. package/dist/src/o-node.d.ts +1 -1
  169. package/dist/src/o-node.d.ts.map +1 -1
  170. package/dist/src/o-node.js +5 -1
  171. package/dist/src/o-node.tool.d.ts.map +1 -1
  172. package/dist/src/o-node.tool.js +37 -7
  173. package/dist/src/router/resolvers/o-node.search-resolver.d.ts.map +1 -1
  174. package/dist/src/router/resolvers/o-node.search-resolver.js +10 -3
  175. package/package.json +6 -6
@@ -0,0 +1,420 @@
1
+ import { createNode, defaultLibp2pConfig, } from '@olane/o-config';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ import { oNodeRouter } from './router/o-node.router.js';
4
+ import { oNodeHierarchyManager } from './o-node.hierarchy-manager.js';
5
+ import { oNodeTransport } from './router/o-node.transport.js';
6
+ import { CoreUtils, NodeState, NodeType, oAddress, RestrictedAddresses, } from '@olane/o-core';
7
+ import { oNodeAddress } from './router/o-node.address.js';
8
+ import { oNodeConnectionManager } from './connection/o-node-connection.manager.js';
9
+ import { oNodeResolver } from './router/resolvers/o-node.resolver.js';
10
+ import { oMethodResolver, oToolBase } from '@olane/o-tool';
11
+ import { oLeaderResolverFallback } from './router/index.js';
12
+ import { oNodeNotificationManager } from './o-node.notification-manager.js';
13
+ import { oConnectionHeartbeatManager } from './managers/o-connection-heartbeat.manager.js';
14
+ import { oReconnectionManager } from './managers/o-reconnection.manager.js';
15
+ import { LeaderRequestWrapper } from './utils/leader-request-wrapper.js';
16
+ export class oNode extends oToolBase {
17
+ constructor(config) {
18
+ super(config);
19
+ this.didRegister = false;
20
+ this.config = config;
21
+ }
22
+ get leader() {
23
+ return this.isLeader ? this.address : this.config?.leader || null;
24
+ }
25
+ get networkConfig() {
26
+ return {
27
+ ...defaultLibp2pConfig,
28
+ ...(this.config.network || {}),
29
+ };
30
+ }
31
+ get parentPeerId() {
32
+ if (!this.parent || this.parent?.transports?.length === 0) {
33
+ return null;
34
+ }
35
+ const transport = this.parent?.transports[0];
36
+ const peerId = transport.toPeerId();
37
+ return peerId;
38
+ }
39
+ configureTransports() {
40
+ return [...(defaultLibp2pConfig.transports || [])];
41
+ }
42
+ async initializeRouter() {
43
+ this.hierarchyManager = new oNodeHierarchyManager({
44
+ leaders: this.config.leader ? [this.config.leader] : [],
45
+ parents: this.config.parent ? [this.config.parent] : [],
46
+ children: [],
47
+ });
48
+ this.router = new oNodeRouter();
49
+ }
50
+ createNotificationManager() {
51
+ return new oNodeNotificationManager(this.p2pNode, this.hierarchyManager, this.address);
52
+ }
53
+ get staticAddress() {
54
+ return this.config.address;
55
+ }
56
+ get parentTransports() {
57
+ return this.config.parent?.transports || [];
58
+ }
59
+ get transports() {
60
+ return this.p2pNode
61
+ .getMultiaddrs()
62
+ .map((multiaddr) => new oNodeTransport(multiaddr.toString()));
63
+ }
64
+ async unregister() {
65
+ this.logger.debug('Unregistering node...');
66
+ if (this.type === NodeType.LEADER) {
67
+ this.logger.debug('Skipping unregistration, node is leader');
68
+ return;
69
+ }
70
+ // Notify parent we're stopping (best-effort, 2s timeout)
71
+ if (this.config.parent) {
72
+ try {
73
+ await Promise.race([
74
+ this.use(this.config.parent, {
75
+ method: 'notify',
76
+ params: {
77
+ eventType: 'node:stopping',
78
+ eventData: {
79
+ address: this.address.toString(),
80
+ reason: 'graceful_shutdown',
81
+ expectedDowntime: null,
82
+ },
83
+ source: this.address.toString(),
84
+ },
85
+ }),
86
+ new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 2000)),
87
+ ]);
88
+ this.logger.debug('Notified parent of shutdown');
89
+ }
90
+ catch (error) {
91
+ this.logger.warn('Failed to notify parent (will be detected by heartbeat):', error instanceof Error ? error.message : error);
92
+ }
93
+ }
94
+ if (!this.config.leader) {
95
+ this.logger.debug('No leader found, skipping unregistration');
96
+ return;
97
+ }
98
+ const address = new oNodeAddress(RestrictedAddresses.REGISTRY);
99
+ // attempt to unregister from the network
100
+ const params = {
101
+ method: 'remove',
102
+ params: {
103
+ peerId: this.peerId.toString(),
104
+ },
105
+ };
106
+ await this.use(address, params);
107
+ }
108
+ async registerParent() {
109
+ if (this.type === NodeType.LEADER) {
110
+ this.logger.debug('Skipping parent registration, node is leader');
111
+ return;
112
+ }
113
+ if (!this.parent?.libp2pTransports?.length) {
114
+ this.logger.debug('Parent has no transports, waiting for reconnection & leader ack');
115
+ if (this.parent?.toString() === oAddress.leader().toString()) {
116
+ this.parent.setTransports(this.leader?.libp2pTransports || []);
117
+ }
118
+ else {
119
+ this.logger.debug('Waiting for parent and reconnecting...');
120
+ await this.reconnectionManager?.waitForParentAndReconnect();
121
+ }
122
+ }
123
+ // if no parent transports, register with the parent to get them
124
+ // TODO: should we remove the transports check to make this more consistent?
125
+ if (this.config.parent) {
126
+ this.logger.debug('Registering node with parent...', this.config.parent);
127
+ await this.use(this.config.parent, {
128
+ method: 'child_register',
129
+ params: {
130
+ address: this.address.toString(),
131
+ transports: this.transports.map((t) => t.toString()),
132
+ peerId: this.peerId.toString(),
133
+ _token: this.config.joinToken,
134
+ },
135
+ });
136
+ }
137
+ }
138
+ async registerLeader() {
139
+ const address = oAddress.registry();
140
+ const params = {
141
+ method: 'commit',
142
+ params: {
143
+ peerId: this.peerId.toString(),
144
+ address: this.address.toString(),
145
+ protocols: this.p2pNode.getProtocols(),
146
+ transports: this.transports,
147
+ staticAddress: this.staticAddress.toString(),
148
+ },
149
+ };
150
+ await this.use(address, params);
151
+ }
152
+ async register() {
153
+ if (this.type === NodeType.LEADER) {
154
+ this.logger.debug('Skipping registration, node is leader');
155
+ return;
156
+ }
157
+ if (this.didRegister) {
158
+ this.logger.debug('Node already registered, skipping registration');
159
+ return;
160
+ }
161
+ this.didRegister = true;
162
+ this.logger.debug('Registering node...');
163
+ // register with the leader global registry
164
+ if (!this.config.leader) {
165
+ this.logger.warn('No leaders found, skipping registration');
166
+ return;
167
+ }
168
+ else {
169
+ this.logger.debug('Registering node with leader...');
170
+ }
171
+ await this.registerParent();
172
+ await this.registerLeader();
173
+ this.logger.debug('Registration successful');
174
+ }
175
+ extractMethod(address) {
176
+ return address.protocol.split('/').pop() || '';
177
+ }
178
+ async start() {
179
+ await super.start();
180
+ // Start heartbeat after node is running
181
+ if (this.connectionHeartbeatManager) {
182
+ await this.connectionHeartbeatManager.start();
183
+ }
184
+ }
185
+ async validateJoinRequest(request) {
186
+ return true;
187
+ }
188
+ /**
189
+ * Configure the libp2p node
190
+ * @returns The libp2p config
191
+ */
192
+ async configure() {
193
+ const params = {
194
+ ...defaultLibp2pConfig,
195
+ ...this.networkConfig,
196
+ transports: this.configureTransports(),
197
+ listeners: (this.config.network?.listeners ||
198
+ defaultLibp2pConfig.listeners ||
199
+ []).concat(`/memory/${uuidv4()}`), // ensure we allow for local in-memory communication
200
+ };
201
+ // if the seed is provided, use it to generate the private key
202
+ if (this.config.seed) {
203
+ this.logger.debug('Seed provided, generating private key...');
204
+ const privateKey = await CoreUtils.generatePrivateKey(this.config.seed);
205
+ params.privateKey = privateKey;
206
+ }
207
+ else {
208
+ this.logger.debug('No seed provided, generating private key...');
209
+ this.logger.debug('Without providing a seed, this node peer id will be destroyed after the node shuts down.');
210
+ // TODO: add a link to documentation about how to setup a seed
211
+ }
212
+ // this is a child node of the network, so communication is heavily restricted
213
+ if (this.parentTransports.length > 0) {
214
+ // peer discovery is only allowed through the parent transports
215
+ const transports = this.parentTransports.map((t) => t.toMultiaddr().toString()) || [];
216
+ // this.logger.debug('Parent transports: ', transports);
217
+ // this.logger.debug(
218
+ // 'Bootstrap transports: ',
219
+ // transports.concat(
220
+ // this.leader?.libp2pTransports.map((t) => t.toString()) || [],
221
+ // ),
222
+ // );
223
+ // params.peerDiscovery = [
224
+ // bootstrap({
225
+ // list: transports.concat(
226
+ // this.leader?.libp2pTransports.map((t) => t.toString()) || [],
227
+ // ),
228
+ // }),
229
+ // ...(defaultLibp2pConfig.peerDiscovery || []),
230
+ // ];
231
+ // // let's make sure we only allow communication through the parent transports
232
+ params.connectionGater = {
233
+ denyDialPeer: (peerId) => {
234
+ // we can call the leader
235
+ if (this.config.leader?.libp2pTransports.some((t) => t.toPeerId() === peerId.toString())) {
236
+ return false;
237
+ }
238
+ // we can call our parent
239
+ if (this.parentPeerId === peerId.toString()) {
240
+ return false;
241
+ }
242
+ // we can call our children
243
+ if (this.hierarchyManager.children.some((c) => c.libp2pTransports.some((t) => t.toPeerId() === peerId.toString()))) {
244
+ return false;
245
+ }
246
+ return true;
247
+ },
248
+ // who can call us?
249
+ denyInboundEncryptedConnection: (peerId, maConn) => {
250
+ // deny all inbound connections unless they are from a parent transport
251
+ if (this.parentPeerId === peerId.toString()) {
252
+ return false;
253
+ }
254
+ // allow connections from children (for ping)
255
+ if (this.hierarchyManager.children.some((c) => c.libp2pTransports.some((t) => t.toPeerId() === peerId.toString()))) {
256
+ return false;
257
+ }
258
+ // allow leader inbounds
259
+ if (this.config.type === NodeType.LEADER) {
260
+ return false;
261
+ }
262
+ // deny everything else
263
+ return true;
264
+ },
265
+ // allow the user to override the default connection gater
266
+ ...(this.config.network?.connectionGater || {}),
267
+ };
268
+ }
269
+ // handle the address encapsulation
270
+ if (this.config.leader &&
271
+ !this.address.protocol.includes(this.config.leader.protocol)) {
272
+ const parentAddress = this.config.parent || this.config.leader;
273
+ this.address = CoreUtils.childAddress(parentAddress, this.address);
274
+ }
275
+ return params;
276
+ }
277
+ async createNode() {
278
+ const params = await this.configure();
279
+ this.p2pNode = await createNode(params);
280
+ return this.p2pNode;
281
+ }
282
+ async connect(nextHopAddress, targetAddress, readTimeoutMs, drainTimeoutMs) {
283
+ if (!this.connectionManager) {
284
+ this.logger.error('Connection manager not initialized');
285
+ throw new Error('Node is not ready to connect to other nodes');
286
+ }
287
+ const connection = await this.connectionManager
288
+ .connect({
289
+ address: targetAddress,
290
+ nextHopAddress,
291
+ callerAddress: this.address,
292
+ readTimeoutMs,
293
+ drainTimeoutMs,
294
+ })
295
+ .catch((error) => {
296
+ // TODO: we need to handle this better and document
297
+ if (error.message === 'Can not dial self') {
298
+ this.logger.error('Make sure you are entering the network not directly through the leader node.');
299
+ }
300
+ throw error;
301
+ });
302
+ if (!connection) {
303
+ throw new Error('Connection failed');
304
+ }
305
+ return connection;
306
+ }
307
+ async initConnectionManager() {
308
+ this.connectionManager = new oNodeConnectionManager({
309
+ p2pNode: this.p2pNode,
310
+ defaultReadTimeoutMs: this.config.connectionTimeouts?.readTimeoutMs,
311
+ defaultDrainTimeoutMs: this.config.connectionTimeouts?.drainTimeoutMs,
312
+ });
313
+ }
314
+ async initReconnectionManager() {
315
+ // Initialize reconnection manager
316
+ if (this.config.reconnection?.enabled !== false) {
317
+ this.reconnectionManager = new oReconnectionManager(this, {
318
+ enabled: true,
319
+ maxAttempts: this.config.reconnection?.maxAttempts ?? 10,
320
+ baseDelayMs: this.config.reconnection?.baseDelayMs ?? 5000,
321
+ maxDelayMs: this.config.reconnection?.maxDelayMs ?? 60000,
322
+ useLeaderFallback: this.config.reconnection?.useLeaderFallback ?? true,
323
+ parentDiscoveryIntervalMs: this.config.reconnection?.parentDiscoveryIntervalMs ?? 10000,
324
+ parentDiscoveryMaxDelayMs: this.config.reconnection?.parentDiscoveryMaxDelayMs ?? 60000,
325
+ });
326
+ }
327
+ }
328
+ async hookInitializeFinished() { }
329
+ async hookStartFinished() {
330
+ // Initialize connection heartbeat manager
331
+ this.connectionHeartbeatManager = new oConnectionHeartbeatManager(this, {
332
+ enabled: this.config.connectionHeartbeat?.enabled ?? true,
333
+ intervalMs: this.config.connectionHeartbeat?.intervalMs ?? 15000,
334
+ timeoutMs: this.config.connectionHeartbeat?.timeoutMs ?? 15000,
335
+ failureThreshold: this.config.connectionHeartbeat?.failureThreshold ?? 3,
336
+ checkChildren: this.config.connectionHeartbeat?.checkChildren ?? false,
337
+ checkParent: this.config.connectionHeartbeat?.checkParent ?? true,
338
+ checkLeader: true,
339
+ });
340
+ this.logger.info(`Connection heartbeat config: leader=${this.connectionHeartbeatManager.getConfig().checkLeader}, ` +
341
+ `parent=${this.connectionHeartbeatManager.getConfig().checkParent}`);
342
+ }
343
+ async initialize() {
344
+ this.logger.debug('Initializing node...');
345
+ if (this.p2pNode && this.state !== NodeState.STOPPED) {
346
+ throw new Error('Node is not in a valid state to be initialized');
347
+ }
348
+ if (!this.address.validate()) {
349
+ throw new Error('Invalid address');
350
+ }
351
+ await this.createNode();
352
+ await this.initializeRouter();
353
+ // need to wait until our libpp2 node is initialized before calling super.initialize
354
+ await super.initialize();
355
+ this.logger.debug('Node initializedddd!', this.transports.map((t) => t.toString()));
356
+ this.address.setTransports(this.transports);
357
+ this.peerId = this.p2pNode.peerId;
358
+ // initialize connection manager
359
+ await this.initConnectionManager();
360
+ // Initialize leader request wrapper with circuit breaker
361
+ this.leaderRequestWrapper = new LeaderRequestWrapper({
362
+ enabled: this.config.leaderRetry?.enabled ?? true,
363
+ maxAttempts: this.config.leaderRetry?.maxAttempts ?? 20,
364
+ baseDelayMs: this.config.leaderRetry?.baseDelayMs ?? 2000,
365
+ maxDelayMs: this.config.leaderRetry?.maxDelayMs ?? 30000,
366
+ timeoutMs: this.config.leaderRetry?.timeoutMs ?? 120000,
367
+ circuitBreaker: {
368
+ enabled: this.config.leaderRetry?.circuitBreaker?.enabled ?? true,
369
+ failureThreshold: this.config.leaderRetry?.circuitBreaker?.failureThreshold ?? 3,
370
+ openTimeoutMs: this.config.leaderRetry?.circuitBreaker?.openTimeoutMs ?? 30000,
371
+ halfOpenMaxAttempts: this.config.leaderRetry?.circuitBreaker?.halfOpenMaxAttempts ?? 1,
372
+ },
373
+ });
374
+ this.logger.info(`Leader retry config: enabled=${this.leaderRequestWrapper.getConfig().enabled}, ` +
375
+ `maxAttempts=${this.leaderRequestWrapper.getConfig().maxAttempts}, ` +
376
+ `circuitBreaker.enabled=${this.leaderRequestWrapper.getConfig().circuitBreaker?.enabled}`);
377
+ // initialize address resolution
378
+ this.router.addResolver(new oMethodResolver(this.address));
379
+ this.router.addResolver(new oNodeResolver(this.address));
380
+ // setup a fallback resolver for non-leader nodes
381
+ if (this.isLeader === false) {
382
+ this.logger.debug('Adding leader resolver fallback...');
383
+ this.router.addResolver(new oLeaderResolverFallback(this.address));
384
+ }
385
+ // initialize reconnection manager
386
+ await this.initReconnectionManager();
387
+ await this.hookInitializeFinished();
388
+ }
389
+ /**
390
+ * Override use() to wrap leader/registry requests with retry logic
391
+ */
392
+ async use(address, data, options) {
393
+ // Wrap leader/registry requests with retry logic
394
+ return this.leaderRequestWrapper.execute(() => super.use(address, data, { noRouting: options?.noRouting ?? false }), address, data?.method);
395
+ }
396
+ async teardown() {
397
+ // Stop heartbeat before parent teardown
398
+ if (this.connectionHeartbeatManager) {
399
+ await this.connectionHeartbeatManager.stop();
400
+ }
401
+ await this.unregister();
402
+ await super.teardown();
403
+ if (this.p2pNode) {
404
+ await this.p2pNode.stop();
405
+ }
406
+ }
407
+ // IHeartbeatableNode interface methods
408
+ getLeaders() {
409
+ return [this.leader];
410
+ }
411
+ getParents() {
412
+ return this.hierarchyManager.getParents();
413
+ }
414
+ getChildren() {
415
+ return this.hierarchyManager.getChildren();
416
+ }
417
+ removeChild(childAddress) {
418
+ this.hierarchyManager.removeChild(childAddress);
419
+ }
420
+ }
@@ -0,0 +1,52 @@
1
+ import { Libp2p } from '@olane/o-config';
2
+ import { oNotificationManager } from '@olane/o-core';
3
+ import { oNodeAddress } from './router/o-node.address.js';
4
+ import { oNodeHierarchyManager } from './o-node.hierarchy-manager.js';
5
+ /**
6
+ * libp2p-specific implementation of oNotificationManager
7
+ * Wraps libp2p events and enriches them with Olane context
8
+ */
9
+ export declare class oNodeNotificationManager extends oNotificationManager {
10
+ private p2pNode;
11
+ private hierarchyManager;
12
+ private address;
13
+ constructor(p2pNode: Libp2p, hierarchyManager: oNodeHierarchyManager, address: oNodeAddress);
14
+ /**
15
+ * Wire up libp2p event listeners
16
+ */
17
+ protected setupListeners(): void;
18
+ /**
19
+ * Handle peer connect event from libp2p
20
+ */
21
+ private handlePeerConnect;
22
+ /**
23
+ * Handle peer disconnect event from libp2p
24
+ */
25
+ private handlePeerDisconnect;
26
+ /**
27
+ * Handle peer discovery event from libp2p
28
+ */
29
+ private handlePeerDiscovery;
30
+ /**
31
+ * Handle connection open event from libp2p
32
+ */
33
+ private handleConnectionOpen;
34
+ /**
35
+ * Handle connection close event from libp2p
36
+ */
37
+ private handleConnectionClose;
38
+ /**
39
+ * Try to resolve a libp2p peer ID to an Olane address
40
+ * Checks hierarchy manager for known peers
41
+ */
42
+ private peerIdToAddress;
43
+ /**
44
+ * Check if an address is a direct child
45
+ */
46
+ private isChild;
47
+ /**
48
+ * Check if an address is a parent
49
+ */
50
+ private isParent;
51
+ }
52
+ //# sourceMappingURL=o-node.notification-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"o-node.notification-manager.d.ts","sourceRoot":"","sources":["../../../src/o-node.notification-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EACL,oBAAoB,EAQrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAEtE;;;GAGG;AACH,qBAAa,wBAAyB,SAAQ,oBAAoB;IAE9D,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,gBAAgB;IACxB,OAAO,CAAC,OAAO;gBAFP,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,qBAAqB,EACvC,OAAO,EAAE,YAAY;IAK/B;;OAEG;IACH,SAAS,CAAC,cAAc,IAAI,IAAI;IAgChC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAkDzB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAoD5B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAmB3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAI5B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAI7B;;;OAGG;IACH,OAAO,CAAC,eAAe;IAkCvB;;OAEG;IACH,OAAO,CAAC,OAAO;IAMf;;OAEG;IACH,OAAO,CAAC,QAAQ;CAKjB"}
@@ -0,0 +1,185 @@
1
+ import { oNotificationManager, NodeConnectedEvent, NodeDisconnectedEvent, NodeDiscoveredEvent, ChildJoinedEvent, ChildLeftEvent, ParentConnectedEvent, ParentDisconnectedEvent, } from '@olane/o-core';
2
+ /**
3
+ * libp2p-specific implementation of oNotificationManager
4
+ * Wraps libp2p events and enriches them with Olane context
5
+ */
6
+ export class oNodeNotificationManager extends oNotificationManager {
7
+ constructor(p2pNode, hierarchyManager, address) {
8
+ super();
9
+ this.p2pNode = p2pNode;
10
+ this.hierarchyManager = hierarchyManager;
11
+ this.address = address;
12
+ }
13
+ /**
14
+ * Wire up libp2p event listeners
15
+ */
16
+ setupListeners() {
17
+ this.logger.debug('Setting up libp2p event listeners...');
18
+ // Peer connection events
19
+ this.p2pNode.addEventListener('peer:connect', this.handlePeerConnect.bind(this));
20
+ this.p2pNode.addEventListener('peer:disconnect', this.handlePeerDisconnect.bind(this));
21
+ // Peer discovery events
22
+ this.p2pNode.addEventListener('peer:discovery', this.handlePeerDiscovery.bind(this));
23
+ // Connection events
24
+ this.p2pNode.addEventListener('connection:open', this.handleConnectionOpen.bind(this));
25
+ this.p2pNode.addEventListener('connection:close', this.handleConnectionClose.bind(this));
26
+ this.logger.debug('libp2p event listeners configured');
27
+ }
28
+ /**
29
+ * Handle peer connect event from libp2p
30
+ */
31
+ handlePeerConnect(evt) {
32
+ const peerId = evt.detail;
33
+ // this.logger.debug(`Peer connected: ${peerId.toString()}`);
34
+ // Try to resolve peer ID to Olane address
35
+ const nodeAddress = this.peerIdToAddress(peerId.toString());
36
+ if (!nodeAddress) {
37
+ // this.logger.debug(
38
+ // `Could not resolve peer ID ${peerId.toString()} to address`,
39
+ // );
40
+ return;
41
+ }
42
+ // Emit generic node connected event
43
+ this.emit(new NodeConnectedEvent({
44
+ source: this.address,
45
+ nodeAddress,
46
+ connectionMetadata: {
47
+ peerId: peerId.toString(),
48
+ transport: 'libp2p',
49
+ },
50
+ }));
51
+ // Check if this is a child node
52
+ if (this.isChild(nodeAddress)) {
53
+ // this.logger.debug(`Child node connected: ${nodeAddress.toString()}`);
54
+ this.emit(new ChildJoinedEvent({
55
+ source: this.address,
56
+ childAddress: nodeAddress,
57
+ parentAddress: this.address,
58
+ }));
59
+ }
60
+ // Check if this is a parent node
61
+ if (this.isParent(nodeAddress)) {
62
+ // this.logger.debug(`Parent node connected: ${nodeAddress.toString()}`);
63
+ this.emit(new ParentConnectedEvent({
64
+ source: this.address,
65
+ parentAddress: nodeAddress,
66
+ }));
67
+ }
68
+ }
69
+ /**
70
+ * Handle peer disconnect event from libp2p
71
+ */
72
+ handlePeerDisconnect(evt) {
73
+ const peerId = evt.detail;
74
+ // this.logger.debug(`Peer disconnected: ${peerId.toString()}`);
75
+ // Try to resolve peer ID to Olane address
76
+ const nodeAddress = this.peerIdToAddress(peerId.toString());
77
+ if (!nodeAddress) {
78
+ // this.logger.debug(
79
+ // `Could not resolve peer ID ${peerId.toString()} to address`,
80
+ // );
81
+ return;
82
+ }
83
+ // Emit generic node disconnected event
84
+ this.emit(new NodeDisconnectedEvent({
85
+ source: this.address,
86
+ nodeAddress,
87
+ reason: 'peer_disconnected',
88
+ }));
89
+ // Check if this is a child node
90
+ if (this.isChild(nodeAddress)) {
91
+ this.logger.debug(`Child node disconnected: ${nodeAddress.toString()}`);
92
+ this.emit(new ChildLeftEvent({
93
+ source: this.address,
94
+ childAddress: nodeAddress,
95
+ parentAddress: this.address,
96
+ reason: 'peer_disconnected',
97
+ }));
98
+ // Optionally remove from hierarchy (auto-cleanup)
99
+ // this.hierarchyManager.removeChild(nodeAddress);
100
+ }
101
+ // Check if this is a parent node
102
+ if (this.isParent(nodeAddress)) {
103
+ this.logger.debug(`Parent node disconnected: ${nodeAddress.toString()}`);
104
+ this.emit(new ParentDisconnectedEvent({
105
+ source: this.address,
106
+ parentAddress: nodeAddress,
107
+ reason: 'peer_disconnected',
108
+ }));
109
+ }
110
+ }
111
+ /**
112
+ * Handle peer discovery event from libp2p
113
+ */
114
+ handlePeerDiscovery(evt) {
115
+ const peerInfo = evt.detail;
116
+ // this.logger.debug(`Peer discovered: ${peerInfo.id.toString()}`);
117
+ // Try to resolve peer ID to Olane address
118
+ const nodeAddress = this.peerIdToAddress(peerInfo.id.toString());
119
+ if (!nodeAddress) {
120
+ return;
121
+ }
122
+ this.emit(new NodeDiscoveredEvent({
123
+ source: this.address,
124
+ nodeAddress,
125
+ }));
126
+ }
127
+ /**
128
+ * Handle connection open event from libp2p
129
+ */
130
+ handleConnectionOpen(evt) {
131
+ // do nothing for now
132
+ }
133
+ /**
134
+ * Handle connection close event from libp2p
135
+ */
136
+ handleConnectionClose(evt) {
137
+ // do nothing for now
138
+ }
139
+ /**
140
+ * Try to resolve a libp2p peer ID to an Olane address
141
+ * Checks hierarchy manager for known peers
142
+ */
143
+ peerIdToAddress(peerId) {
144
+ // Check children
145
+ for (const child of this.hierarchyManager.children) {
146
+ const childTransports = child.transports;
147
+ for (const transport of childTransports) {
148
+ if (transport.toString().includes(peerId)) {
149
+ return child;
150
+ }
151
+ }
152
+ }
153
+ // Check parents
154
+ for (const parent of this.hierarchyManager.parents) {
155
+ const parentTransports = parent.transports;
156
+ for (const transport of parentTransports) {
157
+ if (transport.toString().includes(peerId)) {
158
+ return parent;
159
+ }
160
+ }
161
+ }
162
+ // Check leaders
163
+ for (const leader of this.hierarchyManager.leaders) {
164
+ const leaderTransports = leader.transports;
165
+ for (const transport of leaderTransports) {
166
+ if (transport.toString().includes(peerId)) {
167
+ return leader;
168
+ }
169
+ }
170
+ }
171
+ return null;
172
+ }
173
+ /**
174
+ * Check if an address is a direct child
175
+ */
176
+ isChild(address) {
177
+ return this.hierarchyManager.children.some((child) => child.toString() === address.toString());
178
+ }
179
+ /**
180
+ * Check if an address is a parent
181
+ */
182
+ isParent(address) {
183
+ return this.hierarchyManager.parents.some((parent) => parent.toString() === address.toString());
184
+ }
185
+ }
@@ -0,0 +1,18 @@
1
+ import { oAddress, oRequest } from '@olane/o-core';
2
+ import { oServerNode } from './nodes/server.node.js';
3
+ import { Connection, Stream } from '@olane/o-config';
4
+ declare const oNodeTool_base: typeof oServerNode;
5
+ /**
6
+ * oTool is a mixin that extends the base class and implements the oTool interface
7
+ * @param Base - The base class to extend
8
+ * @returns A new class that extends the base class and implements the oTool interface
9
+ */
10
+ export declare class oNodeTool extends oNodeTool_base {
11
+ handleProtocol(address: oAddress): Promise<void>;
12
+ initialize(): Promise<void>;
13
+ handleStream(stream: Stream, connection: Connection): Promise<void>;
14
+ _tool_identify(): Promise<any>;
15
+ _tool_child_register(request: oRequest): Promise<any>;
16
+ }
17
+ export {};
18
+ //# sourceMappingURL=o-node.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"o-node.tool.d.ts","sourceRoot":"","sources":["../../../src/o-node.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,QAAQ,EAGR,QAAQ,EAGT,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;;AAMrD;;;;GAIG;AACH,qBAAa,SAAU,SAAQ,cAAkB;IACzC,cAAc,CAAC,OAAO,EAAE,QAAQ;IAQhC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAW3B,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA2EnE,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAQ9B,oBAAoB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;CA2B5D"}