@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.
- package/dist/o-core/src/connection/o-response.d.ts +26 -0
- package/dist/o-core/src/connection/o-response.d.ts.map +1 -0
- package/dist/o-core/src/connection/o-response.js +45 -0
- package/dist/o-core/src/error/enums/codes.error.d.ts +20 -0
- package/dist/o-core/src/error/enums/codes.error.d.ts.map +1 -0
- package/dist/o-core/src/error/enums/codes.error.js +20 -0
- package/dist/o-core/src/error/interfaces/o-error.interface.d.ts +6 -0
- package/dist/o-core/src/error/interfaces/o-error.interface.d.ts.map +1 -0
- package/dist/o-core/src/error/interfaces/o-error.interface.js +1 -0
- package/dist/o-core/src/error/o-error.d.ts +15 -0
- package/dist/o-core/src/error/o-error.d.ts.map +1 -0
- package/dist/o-core/src/error/o-error.js +27 -0
- package/dist/o-core/src/streaming/index.d.ts +11 -0
- package/dist/o-core/src/streaming/index.d.ts.map +1 -0
- package/dist/o-core/src/streaming/index.js +14 -0
- package/dist/o-core/src/streaming/protocol-builder.d.ts +62 -0
- package/dist/o-core/src/streaming/protocol-builder.d.ts.map +1 -0
- package/dist/o-core/src/streaming/protocol-builder.js +93 -0
- package/dist/o-core/src/streaming/stream-config.d.ts +36 -0
- package/dist/o-core/src/streaming/stream-config.d.ts.map +1 -0
- package/dist/o-core/src/streaming/stream-config.js +19 -0
- package/dist/o-core/src/streaming/stream-handler.base.d.ts +85 -0
- package/dist/o-core/src/streaming/stream-handler.base.d.ts.map +1 -0
- package/dist/o-core/src/streaming/stream-handler.base.js +112 -0
- package/dist/o-core/src/streaming/stream-transport.interface.d.ts +61 -0
- package/dist/o-core/src/streaming/stream-transport.interface.d.ts.map +1 -0
- package/dist/o-core/src/streaming/stream-transport.interface.js +10 -0
- package/dist/o-core/src/utils/streaming.utils.d.ts +37 -0
- package/dist/o-core/src/utils/streaming.utils.d.ts.map +1 -0
- package/dist/o-core/src/utils/streaming.utils.js +71 -0
- package/dist/o-node/src/connection/index.d.ts +5 -0
- package/dist/o-node/src/connection/index.d.ts.map +1 -0
- package/dist/o-node/src/connection/index.js +4 -0
- package/dist/o-node/src/connection/interfaces/o-node-connection-manager.config.d.ts +6 -0
- package/dist/o-node/src/connection/interfaces/o-node-connection-manager.config.d.ts.map +1 -0
- package/dist/o-node/src/connection/interfaces/o-node-connection-manager.config.js +1 -0
- package/dist/o-node/src/connection/interfaces/o-node-connection.config.d.ts +6 -0
- package/dist/o-node/src/connection/interfaces/o-node-connection.config.d.ts.map +1 -0
- package/dist/o-node/src/connection/interfaces/o-node-connection.config.js +1 -0
- package/dist/o-node/src/connection/o-node-connection.d.ts +20 -0
- package/dist/o-node/src/connection/o-node-connection.d.ts.map +1 -0
- package/dist/o-node/src/connection/o-node-connection.js +146 -0
- package/dist/o-node/src/connection/o-node-connection.manager.d.ts +19 -0
- package/dist/o-node/src/connection/o-node-connection.manager.d.ts.map +1 -0
- package/dist/o-node/src/connection/o-node-connection.manager.js +92 -0
- package/dist/o-node/src/index.d.ts +10 -0
- package/dist/o-node/src/index.d.ts.map +1 -0
- package/dist/o-node/src/index.js +9 -0
- package/dist/o-node/src/interfaces/i-heartbeatable-node.d.ts +49 -0
- package/dist/o-node/src/interfaces/i-heartbeatable-node.d.ts.map +1 -0
- package/dist/o-node/src/interfaces/i-heartbeatable-node.js +1 -0
- package/dist/o-node/src/interfaces/i-reconnectable-node.d.ts +46 -0
- package/dist/o-node/src/interfaces/i-reconnectable-node.d.ts.map +1 -0
- package/dist/o-node/src/interfaces/i-reconnectable-node.js +1 -0
- package/dist/o-node/src/interfaces/o-node.config.d.ts +66 -0
- package/dist/o-node/src/interfaces/o-node.config.d.ts.map +1 -0
- package/dist/o-node/src/interfaces/o-node.config.js +1 -0
- package/dist/o-node/src/interfaces/o-node.tool-config.d.ts +4 -0
- package/dist/o-node/src/interfaces/o-node.tool-config.d.ts.map +1 -0
- package/dist/o-node/src/interfaces/o-node.tool-config.js +1 -0
- package/dist/o-node/src/lib/network-activity.lib.d.ts +1 -0
- package/dist/o-node/src/lib/network-activity.lib.d.ts.map +1 -0
- package/dist/o-node/src/lib/network-activity.lib.js +34 -0
- package/dist/o-node/src/managers/o-connection-heartbeat.manager.d.ts +62 -0
- package/dist/o-node/src/managers/o-connection-heartbeat.manager.d.ts.map +1 -0
- package/dist/o-node/src/managers/o-connection-heartbeat.manager.js +213 -0
- package/dist/o-node/src/managers/o-reconnection.manager.d.ts +51 -0
- package/dist/o-node/src/managers/o-reconnection.manager.d.ts.map +1 -0
- package/dist/o-node/src/managers/o-reconnection.manager.js +266 -0
- package/dist/o-node/src/nodes/client.node.d.ts +7 -0
- package/dist/o-node/src/nodes/client.node.d.ts.map +1 -0
- package/dist/o-node/src/nodes/client.node.js +16 -0
- package/dist/o-node/src/nodes/index.d.ts +4 -0
- package/dist/o-node/src/nodes/index.d.ts.map +1 -0
- package/dist/o-node/src/nodes/index.js +3 -0
- package/dist/o-node/src/nodes/server.node.d.ts +7 -0
- package/dist/o-node/src/nodes/server.node.d.ts.map +1 -0
- package/dist/o-node/src/nodes/server.node.js +20 -0
- package/dist/o-node/src/nodes/websocket.node.d.ts +7 -0
- package/dist/o-node/src/nodes/websocket.node.d.ts.map +1 -0
- package/dist/o-node/src/nodes/websocket.node.js +18 -0
- package/dist/o-node/src/o-node.d.ts +72 -0
- package/dist/o-node/src/o-node.d.ts.map +1 -0
- package/dist/o-node/src/o-node.hierarchy-manager.d.ts +15 -0
- package/dist/o-node/src/o-node.hierarchy-manager.d.ts.map +1 -0
- package/dist/o-node/src/o-node.hierarchy-manager.js +15 -0
- package/dist/o-node/src/o-node.js +420 -0
- package/dist/o-node/src/o-node.notification-manager.d.ts +52 -0
- package/dist/o-node/src/o-node.notification-manager.d.ts.map +1 -0
- package/dist/o-node/src/o-node.notification-manager.js +185 -0
- package/dist/o-node/src/o-node.tool.d.ts +18 -0
- package/dist/o-node/src/o-node.tool.d.ts.map +1 -0
- package/dist/o-node/src/o-node.tool.js +116 -0
- package/dist/o-node/src/router/index.d.ts +6 -0
- package/dist/o-node/src/router/index.d.ts.map +1 -0
- package/dist/o-node/src/router/index.js +5 -0
- package/dist/o-node/src/router/interfaces/o-node-router.config.d.ts +3 -0
- package/dist/o-node/src/router/interfaces/o-node-router.config.d.ts.map +1 -0
- package/dist/o-node/src/router/interfaces/o-node-router.config.js +1 -0
- package/dist/o-node/src/router/interfaces/o-node-router.response.d.ts +8 -0
- package/dist/o-node/src/router/interfaces/o-node-router.response.d.ts.map +1 -0
- package/dist/o-node/src/router/interfaces/o-node-router.response.js +1 -0
- package/dist/o-node/src/router/o-node.address.d.ts +18 -0
- package/dist/o-node/src/router/o-node.address.d.ts.map +1 -0
- package/dist/o-node/src/router/o-node.address.js +29 -0
- package/dist/o-node/src/router/o-node.router.d.ts +45 -0
- package/dist/o-node/src/router/o-node.router.d.ts.map +1 -0
- package/dist/o-node/src/router/o-node.router.js +125 -0
- package/dist/o-node/src/router/o-node.routing-policy.d.ts +30 -0
- package/dist/o-node/src/router/o-node.routing-policy.d.ts.map +1 -0
- package/dist/o-node/src/router/o-node.routing-policy.js +57 -0
- package/dist/o-node/src/router/o-node.transport.d.ts +11 -0
- package/dist/o-node/src/router/o-node.transport.d.ts.map +1 -0
- package/dist/o-node/src/router/o-node.transport.js +18 -0
- package/dist/o-node/src/router/resolvers/index.d.ts +4 -0
- package/dist/o-node/src/router/resolvers/index.d.ts.map +1 -0
- package/dist/o-node/src/router/resolvers/index.js +3 -0
- package/dist/o-node/src/router/resolvers/o-node.leader-resolver-fallback.d.ts +8 -0
- package/dist/o-node/src/router/resolvers/o-node.leader-resolver-fallback.d.ts.map +1 -0
- package/dist/o-node/src/router/resolvers/o-node.leader-resolver-fallback.js +35 -0
- package/dist/o-node/src/router/resolvers/o-node.resolver.d.ts +11 -0
- package/dist/o-node/src/router/resolvers/o-node.resolver.d.ts.map +1 -0
- package/dist/o-node/src/router/resolvers/o-node.resolver.js +41 -0
- package/dist/o-node/src/router/resolvers/o-node.search-resolver.d.ts +170 -0
- package/dist/o-node/src/router/resolvers/o-node.search-resolver.d.ts.map +1 -0
- package/dist/o-node/src/router/resolvers/o-node.search-resolver.js +285 -0
- package/dist/o-node/src/router/route.request.d.ts +14 -0
- package/dist/o-node/src/router/route.request.d.ts.map +1 -0
- package/dist/o-node/src/router/route.request.js +1 -0
- package/dist/o-node/src/streaming/index.d.ts +10 -0
- package/dist/o-node/src/streaming/index.d.ts.map +1 -0
- package/dist/o-node/src/streaming/index.js +12 -0
- package/dist/o-node/src/streaming/libp2p-stream-transport.d.ts +50 -0
- package/dist/o-node/src/streaming/libp2p-stream-transport.d.ts.map +1 -0
- package/dist/o-node/src/streaming/libp2p-stream-transport.js +137 -0
- package/dist/o-node/src/streaming/node-stream-handler.d.ts +65 -0
- package/dist/o-node/src/streaming/node-stream-handler.d.ts.map +1 -0
- package/dist/o-node/src/streaming/node-stream-handler.js +101 -0
- package/dist/o-node/src/utils/circuit-breaker.d.ts +107 -0
- package/dist/o-node/src/utils/circuit-breaker.d.ts.map +1 -0
- package/dist/o-node/src/utils/circuit-breaker.js +175 -0
- package/dist/o-node/src/utils/circuit-breaker.test.d.ts +2 -0
- package/dist/o-node/src/utils/circuit-breaker.test.d.ts.map +1 -0
- package/dist/o-node/src/utils/circuit-breaker.test.js +262 -0
- package/dist/o-node/src/utils/leader-request-wrapper.d.ts +66 -0
- package/dist/o-node/src/utils/leader-request-wrapper.d.ts.map +1 -0
- package/dist/o-node/src/utils/leader-request-wrapper.js +160 -0
- package/dist/o-node/src/utils/leader-request-wrapper.test.d.ts +1 -0
- package/dist/o-node/src/utils/leader-request-wrapper.test.d.ts.map +1 -0
- package/dist/o-node/src/utils/leader-request-wrapper.test.js +246 -0
- package/dist/o-node/src/utils/network.utils.d.ts +20 -0
- package/dist/o-node/src/utils/network.utils.d.ts.map +1 -0
- package/dist/o-node/src/utils/network.utils.js +74 -0
- package/dist/o-node/test/o-node.spec.d.ts +2 -0
- package/dist/o-node/test/o-node.spec.d.ts.map +1 -0
- package/dist/o-node/test/o-node.spec.js +20 -0
- package/dist/o-node/test/search-resolver.spec.d.ts +2 -0
- package/dist/o-node/test/search-resolver.spec.d.ts.map +1 -0
- package/dist/o-node/test/search-resolver.spec.js +693 -0
- package/dist/src/connection/o-node-connection.d.ts +7 -0
- package/dist/src/connection/o-node-connection.d.ts.map +1 -1
- package/dist/src/connection/o-node-connection.js +89 -2
- package/dist/src/connection/o-node-connection.manager.d.ts +2 -0
- package/dist/src/connection/o-node-connection.manager.d.ts.map +1 -1
- package/dist/src/connection/o-node-connection.manager.js +5 -1
- package/dist/src/interfaces/o-node.config.d.ts +16 -0
- package/dist/src/interfaces/o-node.config.d.ts.map +1 -1
- package/dist/src/o-node.d.ts +1 -1
- package/dist/src/o-node.d.ts.map +1 -1
- package/dist/src/o-node.js +5 -1
- package/dist/src/o-node.tool.d.ts.map +1 -1
- package/dist/src/o-node.tool.js +37 -7
- package/dist/src/router/resolvers/o-node.search-resolver.d.ts.map +1 -1
- package/dist/src/router/resolvers/o-node.search-resolver.js +10 -3
- package/package.json +6 -6
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// import { expect } from 'chai';
|
|
3
|
+
// import { LeaderRequestWrapper } from './leader-request-wrapper.js';
|
|
4
|
+
// import { oAddress } from '@olane/o-core';
|
|
5
|
+
// import { CircuitState } from './circuit-breaker.js';
|
|
6
|
+
// describe('LeaderRequestWrapper with Circuit Breaker', () => {
|
|
7
|
+
// let wrapper: LeaderRequestWrapper;
|
|
8
|
+
// const leaderAddress = new oAddress('o://leader');
|
|
9
|
+
// const registryAddress = new oAddress('o://registry');
|
|
10
|
+
// const regularAddress = new oAddress('o://some-service');
|
|
11
|
+
// beforeEach(() => {
|
|
12
|
+
// wrapper = new LeaderRequestWrapper({
|
|
13
|
+
// enabled: true,
|
|
14
|
+
// maxAttempts: 5,
|
|
15
|
+
// baseDelayMs: 10,
|
|
16
|
+
// maxDelayMs: 100,
|
|
17
|
+
// timeoutMs: 1000,
|
|
18
|
+
// circuitBreaker: {
|
|
19
|
+
// enabled: true,
|
|
20
|
+
// failureThreshold: 3,
|
|
21
|
+
// openTimeoutMs: 500,
|
|
22
|
+
// halfOpenMaxAttempts: 1,
|
|
23
|
+
// },
|
|
24
|
+
// });
|
|
25
|
+
// });
|
|
26
|
+
// describe('Circuit Breaker Integration', () => {
|
|
27
|
+
// it('should execute request successfully and record success', async () => {
|
|
28
|
+
// const mockRequest = vi.fn().mockResolvedValue('success');
|
|
29
|
+
// const result = await wrapper.execute(mockRequest, leaderAddress);
|
|
30
|
+
// expect(result).toBe('success');
|
|
31
|
+
// expect(mockRequest).toHaveBeenCalledTimes(1);
|
|
32
|
+
// const stats = wrapper.getCircuitBreakerStats();
|
|
33
|
+
// expect(stats.leader.totalSuccesses).toBe(1);
|
|
34
|
+
// expect(stats.leader.state).toBe(CircuitState.CLOSED);
|
|
35
|
+
// });
|
|
36
|
+
// it('should record failures in circuit breaker', async () => {
|
|
37
|
+
// const mockRequest = vi.fn().mockRejectedValue(new Error('Service down'));
|
|
38
|
+
// await expect(
|
|
39
|
+
// wrapper.execute(mockRequest, leaderAddress),
|
|
40
|
+
// ).rejects.toThrow();
|
|
41
|
+
// const stats = wrapper.getCircuitBreakerStats();
|
|
42
|
+
// expect(stats.leader.totalFailures).toBeGreaterThan(0);
|
|
43
|
+
// expect(stats.leader.consecutiveFailures).toBeGreaterThan(0);
|
|
44
|
+
// });
|
|
45
|
+
// it('should open circuit after threshold failures', async () => {
|
|
46
|
+
// const mockRequest = vi.fn().mockRejectedValue(new Error('Service down'));
|
|
47
|
+
// // Attempt request multiple times to trigger circuit breaker
|
|
48
|
+
// await expect(
|
|
49
|
+
// wrapper.execute(mockRequest, leaderAddress),
|
|
50
|
+
// ).rejects.toThrow();
|
|
51
|
+
// const stats = wrapper.getCircuitBreakerStats();
|
|
52
|
+
// expect(stats.leader.state).toBe(CircuitState.OPEN);
|
|
53
|
+
// });
|
|
54
|
+
// it('should fast-fail when circuit is open', async () => {
|
|
55
|
+
// const mockRequest = vi.fn().mockRejectedValue(new Error('Service down'));
|
|
56
|
+
// // First request to open circuit
|
|
57
|
+
// await expect(
|
|
58
|
+
// wrapper.execute(mockRequest, leaderAddress),
|
|
59
|
+
// ).rejects.toThrow();
|
|
60
|
+
// // Reset mock to verify it's not called
|
|
61
|
+
// mockRequest.mockClear();
|
|
62
|
+
// // Second request should fast-fail
|
|
63
|
+
// await expect(wrapper.execute(mockRequest, leaderAddress)).rejects.toThrow(
|
|
64
|
+
// /Circuit breaker is OPEN/,
|
|
65
|
+
// );
|
|
66
|
+
// // Request function should not have been called
|
|
67
|
+
// expect(mockRequest).not.toHaveBeenCalled();
|
|
68
|
+
// });
|
|
69
|
+
// it('should stop retrying when circuit opens mid-retry', async () => {
|
|
70
|
+
// let callCount = 0;
|
|
71
|
+
// const mockRequest = vi.fn().mockImplementation(() => {
|
|
72
|
+
// callCount++;
|
|
73
|
+
// throw new Error('Service down');
|
|
74
|
+
// });
|
|
75
|
+
// await expect(
|
|
76
|
+
// wrapper.execute(mockRequest, leaderAddress),
|
|
77
|
+
// ).rejects.toThrow();
|
|
78
|
+
// // Should have stopped retrying after circuit opened
|
|
79
|
+
// expect(callCount).toBeLessThan(5); // Less than maxAttempts
|
|
80
|
+
// expect(callCount).toBeGreaterThanOrEqual(3); // At least threshold attempts
|
|
81
|
+
// });
|
|
82
|
+
// it('should track separate circuits for leader and registry', async () => {
|
|
83
|
+
// const failingRequest = vi
|
|
84
|
+
// .fn()
|
|
85
|
+
// .mockRejectedValue(new Error('Service down'));
|
|
86
|
+
// // Fail leader requests
|
|
87
|
+
// await expect(
|
|
88
|
+
// wrapper.execute(failingRequest, leaderAddress),
|
|
89
|
+
// ).rejects.toThrow();
|
|
90
|
+
// const stats = wrapper.getCircuitBreakerStats();
|
|
91
|
+
// expect(stats.leader.state).toBe(CircuitState.OPEN);
|
|
92
|
+
// expect(stats.registry.state).toBe(CircuitState.CLOSED);
|
|
93
|
+
// });
|
|
94
|
+
// it('should allow recovery after timeout in HALF_OPEN state', async () => {
|
|
95
|
+
// const mockRequest = vi
|
|
96
|
+
// .fn()
|
|
97
|
+
// .mockRejectedValueOnce(new Error('Service down'))
|
|
98
|
+
// .mockResolvedValue('recovered');
|
|
99
|
+
// // Open circuit
|
|
100
|
+
// await expect(
|
|
101
|
+
// wrapper.execute(mockRequest, leaderAddress),
|
|
102
|
+
// ).rejects.toThrow();
|
|
103
|
+
// expect(wrapper.getCircuitBreakerStats().leader.state).toBe(
|
|
104
|
+
// CircuitState.OPEN,
|
|
105
|
+
// );
|
|
106
|
+
// // Wait for circuit to attempt recovery
|
|
107
|
+
// await new Promise((resolve) => setTimeout(resolve, 600));
|
|
108
|
+
// // Should succeed and close circuit
|
|
109
|
+
// const result = await wrapper.execute(mockRequest, leaderAddress);
|
|
110
|
+
// expect(result).toBe('recovered');
|
|
111
|
+
// expect(wrapper.getCircuitBreakerStats().leader.state).toBe(
|
|
112
|
+
// CircuitState.CLOSED,
|
|
113
|
+
// );
|
|
114
|
+
// });
|
|
115
|
+
// });
|
|
116
|
+
// describe('Non-Leader Addresses', () => {
|
|
117
|
+
// it('should not use circuit breaker for non-leader addresses', async () => {
|
|
118
|
+
// const mockRequest = vi.fn().mockRejectedValue(new Error('Service down'));
|
|
119
|
+
// await expect(
|
|
120
|
+
// wrapper.execute(mockRequest, regularAddress),
|
|
121
|
+
// ).rejects.toThrow('Service down');
|
|
122
|
+
// // Should execute directly without retry
|
|
123
|
+
// expect(mockRequest).toHaveBeenCalledTimes(1);
|
|
124
|
+
// // Should not affect circuit breaker stats
|
|
125
|
+
// const stats = wrapper.getCircuitBreakerStats();
|
|
126
|
+
// expect(stats.leader.totalFailures).toBe(0);
|
|
127
|
+
// expect(stats.registry.totalFailures).toBe(0);
|
|
128
|
+
// });
|
|
129
|
+
// it('should execute successful requests directly', async () => {
|
|
130
|
+
// const mockRequest = vi.fn().mockResolvedValue('success');
|
|
131
|
+
// const result = await wrapper.execute(mockRequest, regularAddress);
|
|
132
|
+
// expect(result).toBe('success');
|
|
133
|
+
// expect(mockRequest).toHaveBeenCalledTimes(1);
|
|
134
|
+
// });
|
|
135
|
+
// });
|
|
136
|
+
// describe('Timeout Handling', () => {
|
|
137
|
+
// it('should timeout long-running requests', async () => {
|
|
138
|
+
// const longRequest = vi.fn().mockImplementation(
|
|
139
|
+
// () =>
|
|
140
|
+
// new Promise((resolve) => {
|
|
141
|
+
// setTimeout(() => resolve('too-late'), 2000);
|
|
142
|
+
// }),
|
|
143
|
+
// );
|
|
144
|
+
// await expect(wrapper.execute(longRequest, leaderAddress)).rejects.toThrow(
|
|
145
|
+
// /timeout/,
|
|
146
|
+
// );
|
|
147
|
+
// });
|
|
148
|
+
// it('should record timeout as failure in circuit breaker', async () => {
|
|
149
|
+
// const longRequest = vi.fn().mockImplementation(
|
|
150
|
+
// () =>
|
|
151
|
+
// new Promise((resolve) => {
|
|
152
|
+
// setTimeout(() => resolve('too-late'), 2000);
|
|
153
|
+
// }),
|
|
154
|
+
// );
|
|
155
|
+
// await expect(
|
|
156
|
+
// wrapper.execute(longRequest, leaderAddress),
|
|
157
|
+
// ).rejects.toThrow();
|
|
158
|
+
// const stats = wrapper.getCircuitBreakerStats();
|
|
159
|
+
// expect(stats.leader.totalFailures).toBeGreaterThan(0);
|
|
160
|
+
// });
|
|
161
|
+
// });
|
|
162
|
+
// describe('Configuration', () => {
|
|
163
|
+
// it('should bypass retry when disabled', async () => {
|
|
164
|
+
// const disabledWrapper = new LeaderRequestWrapper({
|
|
165
|
+
// enabled: false,
|
|
166
|
+
// maxAttempts: 5,
|
|
167
|
+
// baseDelayMs: 10,
|
|
168
|
+
// maxDelayMs: 100,
|
|
169
|
+
// timeoutMs: 1000,
|
|
170
|
+
// });
|
|
171
|
+
// const mockRequest = vi.fn().mockRejectedValue(new Error('Fail'));
|
|
172
|
+
// await expect(
|
|
173
|
+
// disabledWrapper.execute(mockRequest, leaderAddress),
|
|
174
|
+
// ).rejects.toThrow('Fail');
|
|
175
|
+
// // Should only call once (no retry)
|
|
176
|
+
// expect(mockRequest).toHaveBeenCalledTimes(1);
|
|
177
|
+
// });
|
|
178
|
+
// it('should allow circuit breaker to be disabled', async () => {
|
|
179
|
+
// const noBreakerWrapper = new LeaderRequestWrapper({
|
|
180
|
+
// enabled: true,
|
|
181
|
+
// maxAttempts: 3,
|
|
182
|
+
// baseDelayMs: 10,
|
|
183
|
+
// maxDelayMs: 100,
|
|
184
|
+
// timeoutMs: 1000,
|
|
185
|
+
// circuitBreaker: {
|
|
186
|
+
// enabled: false,
|
|
187
|
+
// failureThreshold: 3,
|
|
188
|
+
// openTimeoutMs: 500,
|
|
189
|
+
// halfOpenMaxAttempts: 1,
|
|
190
|
+
// },
|
|
191
|
+
// });
|
|
192
|
+
// let callCount = 0;
|
|
193
|
+
// const mockRequest = vi.fn().mockImplementation(() => {
|
|
194
|
+
// callCount++;
|
|
195
|
+
// throw new Error('Fail');
|
|
196
|
+
// });
|
|
197
|
+
// await expect(
|
|
198
|
+
// noBreakerWrapper.execute(mockRequest, leaderAddress),
|
|
199
|
+
// ).rejects.toThrow();
|
|
200
|
+
// // Should retry all attempts even with failures
|
|
201
|
+
// expect(callCount).toBe(3);
|
|
202
|
+
// });
|
|
203
|
+
// });
|
|
204
|
+
// describe('Manual Reset', () => {
|
|
205
|
+
// it('should reset circuit breakers manually', async () => {
|
|
206
|
+
// const mockRequest = vi.fn().mockRejectedValue(new Error('Service down'));
|
|
207
|
+
// // Open circuit
|
|
208
|
+
// await expect(
|
|
209
|
+
// wrapper.execute(mockRequest, leaderAddress),
|
|
210
|
+
// ).rejects.toThrow();
|
|
211
|
+
// expect(wrapper.getCircuitBreakerStats().leader.state).toBe(
|
|
212
|
+
// CircuitState.OPEN,
|
|
213
|
+
// );
|
|
214
|
+
// // Reset
|
|
215
|
+
// wrapper.resetCircuitBreakers();
|
|
216
|
+
// expect(wrapper.getCircuitBreakerStats().leader.state).toBe(
|
|
217
|
+
// CircuitState.CLOSED,
|
|
218
|
+
// );
|
|
219
|
+
// expect(wrapper.getCircuitBreakerStats().registry.state).toBe(
|
|
220
|
+
// CircuitState.CLOSED,
|
|
221
|
+
// );
|
|
222
|
+
// });
|
|
223
|
+
// });
|
|
224
|
+
// describe('Registry Address', () => {
|
|
225
|
+
// it('should use separate circuit breaker for registry', async () => {
|
|
226
|
+
// const mockRequest = vi.fn().mockRejectedValue(new Error('Registry down'));
|
|
227
|
+
// await expect(
|
|
228
|
+
// wrapper.execute(mockRequest, registryAddress),
|
|
229
|
+
// ).rejects.toThrow();
|
|
230
|
+
// const stats = wrapper.getCircuitBreakerStats();
|
|
231
|
+
// expect(stats.registry.totalFailures).toBeGreaterThan(0);
|
|
232
|
+
// expect(stats.registry.state).toBe(CircuitState.OPEN);
|
|
233
|
+
// expect(stats.leader.state).toBe(CircuitState.CLOSED);
|
|
234
|
+
// });
|
|
235
|
+
// });
|
|
236
|
+
// describe('Statistics', () => {
|
|
237
|
+
// it('should provide circuit breaker statistics', () => {
|
|
238
|
+
// const stats = wrapper.getCircuitBreakerStats();
|
|
239
|
+
// expect(stats).toHaveProperty('leader');
|
|
240
|
+
// expect(stats).toHaveProperty('registry');
|
|
241
|
+
// expect(stats.leader).toHaveProperty('state');
|
|
242
|
+
// expect(stats.leader).toHaveProperty('consecutiveFailures');
|
|
243
|
+
// expect(stats.registry).toHaveProperty('state');
|
|
244
|
+
// });
|
|
245
|
+
// });
|
|
246
|
+
// });
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Libp2p, Multiaddr } from '@olane/o-config';
|
|
2
|
+
import { oObject } from '@olane/o-core';
|
|
3
|
+
import { CID } from 'multiformats';
|
|
4
|
+
import { oNodeAddress } from '../router/o-node.address.js';
|
|
5
|
+
export declare class NetworkUtils extends oObject {
|
|
6
|
+
static findProviders(p2pNode: Libp2p, cid: CID): Promise<{
|
|
7
|
+
transports: Multiaddr[];
|
|
8
|
+
staticAddress: string;
|
|
9
|
+
absoluteAddress: string;
|
|
10
|
+
}>;
|
|
11
|
+
static findNode(p2pNode: Libp2p, address: oNodeAddress): Promise<{
|
|
12
|
+
transports: Multiaddr[];
|
|
13
|
+
staticAddress: string;
|
|
14
|
+
absoluteAddress: string;
|
|
15
|
+
}>;
|
|
16
|
+
static advertiseValueToNetwork(value: CID, p2pNode: Libp2p): Promise<void>;
|
|
17
|
+
static dhtProvide(value: CID, p2pNode: Libp2p): Promise<void>;
|
|
18
|
+
static advertiseToNetwork(address: oNodeAddress, staticAddress: oNodeAddress, p2pNode: Libp2p): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=network.utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.utils.d.ts","sourceRoot":"","sources":["../../../../src/utils/network.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,qBAAa,YAAa,SAAQ,OAAO;WACnB,aAAa,CAC/B,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,GAAG,GACP,OAAO,CAAC;QACT,UAAU,EAAE,SAAS,EAAE,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;WA2BkB,QAAQ,CAC1B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC;QACT,UAAU,EAAE,SAAS,EAAE,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;WAiBkB,uBAAuB,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM;WAgBnD,UAAU,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM;WAYtC,kBAAkB,CACpC,OAAO,EAAE,YAAY,EACrB,aAAa,EAAE,YAAY,EAC3B,OAAO,EAAE,MAAM;CAoClB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { oObject } from '@olane/o-core';
|
|
2
|
+
import { oNodeAddress } from '../router/o-node.address.js';
|
|
3
|
+
export class NetworkUtils extends oObject {
|
|
4
|
+
static async findProviders(p2pNode, cid) {
|
|
5
|
+
let peer = null;
|
|
6
|
+
let multiaddrs = [];
|
|
7
|
+
for await (const event of p2pNode.services.dht.findProviders(cid)) {
|
|
8
|
+
// Look for events that contain actual provider information
|
|
9
|
+
if (event.name === 'PEER_RESPONSE') {
|
|
10
|
+
if (event.providers?.length === 0) {
|
|
11
|
+
NetworkUtils.log('No providers found');
|
|
12
|
+
break;
|
|
13
|
+
}
|
|
14
|
+
peer = event.providers[0].id;
|
|
15
|
+
multiaddrs = event.providers[0].multiaddrs;
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
if (event.name === 'PATH_ENDED' || event.name === 'QUERY_ERROR') {
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
transports: multiaddrs,
|
|
24
|
+
staticAddress: '',
|
|
25
|
+
absoluteAddress: '',
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
static async findNode(p2pNode, address) {
|
|
29
|
+
const cid = await address.toCID();
|
|
30
|
+
return await Promise.race([
|
|
31
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Content routing provide timeout')), 5000)),
|
|
32
|
+
this.findProviders(p2pNode, cid),
|
|
33
|
+
]);
|
|
34
|
+
}
|
|
35
|
+
static async advertiseValueToNetwork(value, p2pNode) {
|
|
36
|
+
NetworkUtils.log('Advertising value to network: ', value.toString());
|
|
37
|
+
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Advertise Content routing provide timeout')), 5000));
|
|
38
|
+
await Promise.race([
|
|
39
|
+
NetworkUtils.dhtProvide(value, p2pNode),
|
|
40
|
+
timeoutPromise,
|
|
41
|
+
]);
|
|
42
|
+
NetworkUtils.log('Advertise complete!');
|
|
43
|
+
}
|
|
44
|
+
static async dhtProvide(value, p2pNode) {
|
|
45
|
+
for await (const event of p2pNode.services.dht.provide(value)) {
|
|
46
|
+
if (event.name === 'PATH_ENDED' ||
|
|
47
|
+
event.name === 'QUERY_ERROR' ||
|
|
48
|
+
event.name === 'PEER_RESPONSE') {
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
static async advertiseToNetwork(address, staticAddress, p2pNode) {
|
|
54
|
+
NetworkUtils.log('Advertising to network our static and absolute addresses...');
|
|
55
|
+
// advertise the absolute address to the network with timeout
|
|
56
|
+
const add = new oNodeAddress(address.toString());
|
|
57
|
+
const absoluteAddressCid = await add.toCID();
|
|
58
|
+
// Add timeout to prevent hanging
|
|
59
|
+
NetworkUtils.advertiseValueToNetwork(absoluteAddressCid, p2pNode)
|
|
60
|
+
.then((d) => {
|
|
61
|
+
NetworkUtils.log(`${address.toString()} - Successfully advertised absolute address`);
|
|
62
|
+
})
|
|
63
|
+
.catch((error) => {
|
|
64
|
+
NetworkUtils.log(`${address.toString()} - Failed to advertise absolute address (this is normal for isolated nodes):`, error.message);
|
|
65
|
+
});
|
|
66
|
+
// advertise the static address to the network with timeout
|
|
67
|
+
const staticAdd = new oNodeAddress(staticAddress.toString());
|
|
68
|
+
const staticAddressCid = await staticAdd.toCID();
|
|
69
|
+
// Add timeout to prevent hanging
|
|
70
|
+
NetworkUtils.advertiseValueToNetwork(staticAddressCid, p2pNode).catch((error) => {
|
|
71
|
+
NetworkUtils.log('Failed to advertise absolute address (this is normal for isolated nodes):', error.message);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"o-node.spec.d.ts","sourceRoot":"","sources":["../../../test/o-node.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { NodeState } from '@olane/o-core';
|
|
2
|
+
import { expect } from 'chai';
|
|
3
|
+
import { oNode } from '../src/index.js';
|
|
4
|
+
import { oNodeAddress } from '../src/router/o-node.address.js';
|
|
5
|
+
describe('in-process @memory', () => {
|
|
6
|
+
it('should be able to start a single node with no leader', async () => {
|
|
7
|
+
const node = new oNode({
|
|
8
|
+
address: new oNodeAddress('o://test'),
|
|
9
|
+
leader: null,
|
|
10
|
+
parent: null,
|
|
11
|
+
});
|
|
12
|
+
await node.start();
|
|
13
|
+
expect(node.state).to.equal(NodeState.RUNNING);
|
|
14
|
+
const transports = node.transports;
|
|
15
|
+
// expect(transports.length).to.equal(1);
|
|
16
|
+
// expect(transports[0].toString()).to.contain('/memory');
|
|
17
|
+
await node.stop();
|
|
18
|
+
expect(node.state).to.equal(NodeState.STOPPED);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-resolver.spec.d.ts","sourceRoot":"","sources":["../../../test/search-resolver.spec.ts"],"names":[],"mappings":""}
|