@private.me/xbind 3.0.0 → 3.0.2
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/README.md +55 -7
- package/dist-standalone/_deps/mldsa-wasm/dist/mldsa.js +1920 -1
- package/dist-standalone/_deps/shared/cjs/errors.js +729 -1
- package/dist-standalone/_deps/shared/cjs/index.js +463 -1
- package/dist-standalone/_deps/shared/cjs/types.js +315 -1
- package/dist-standalone/_deps/shared/errors.js +244 -1
- package/dist-standalone/_deps/shared/index.js +72 -1
- package/dist-standalone/_deps/shared/types.js +86 -1
- package/dist-standalone/_deps/ux-helpers/cjs/errors.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/index.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/pagination.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/progress.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/search.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/types.js +1 -1
- package/dist-standalone/_deps/ux-helpers/errors.js +1 -1
- package/dist-standalone/_deps/ux-helpers/index.js +1 -1
- package/dist-standalone/_deps/ux-helpers/pagination.js +1 -1
- package/dist-standalone/_deps/ux-helpers/progress.js +1 -1
- package/dist-standalone/_deps/ux-helpers/search.js +1 -1
- package/dist-standalone/_deps/xchange/auto-accept.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/auto-accept.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/errors.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/index.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/invite-client.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/lazy-init.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/trust-integration.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/xchange.js +1 -1
- package/dist-standalone/_deps/xchange/errors.js +1 -1
- package/dist-standalone/_deps/xchange/index.js +1 -1
- package/dist-standalone/_deps/xchange/invite-client.js +1 -1
- package/dist-standalone/_deps/xchange/lazy-init.js +1 -1
- package/dist-standalone/_deps/xchange/trust-integration.js +1 -1
- package/dist-standalone/_deps/xchange/xchange.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/discovery.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/errors.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/index.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/registry.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/schema.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/types.js +1 -1
- package/dist-standalone/_deps/xregistry/discovery.js +1 -1
- package/dist-standalone/_deps/xregistry/errors.js +1 -1
- package/dist-standalone/_deps/xregistry/index.js +1 -1
- package/dist-standalone/_deps/xregistry/registry.js +1 -1
- package/dist-standalone/_deps/xregistry/schema.js +1 -1
- package/dist-standalone/_deps/xregistry/types.js +1 -1
- package/dist-standalone/agent-call.js +659 -1
- package/dist-standalone/agent-sdk.js +328 -1
- package/dist-standalone/agent.js +1800 -1
- package/dist-standalone/approval.js +193 -1
- package/dist-standalone/async-iterators.js +382 -1
- package/dist-standalone/auth.js +219 -1
- package/dist-standalone/auto-accept.js +229 -1
- package/dist-standalone/backup-config.js +201 -1
- package/dist-standalone/backup.js +326 -1
- package/dist-standalone/batch-operations.js +388 -1
- package/dist-standalone/cancellation.js +477 -1
- package/dist-standalone/checkpoint.js +186 -1
- package/dist-standalone/circuit-breaker.js +468 -1
- package/dist-standalone/cjs/agent-call.js +701 -1
- package/dist-standalone/cjs/agent-sdk.js +332 -1
- package/dist-standalone/cjs/agent.js +1837 -1
- package/dist-standalone/cjs/approval.js +199 -1
- package/dist-standalone/cjs/async-iterators.js +392 -1
- package/dist-standalone/cjs/auth.js +225 -1
- package/dist-standalone/cjs/auto-accept.js +233 -1
- package/dist-standalone/cjs/backup-config.js +207 -1
- package/dist-standalone/cjs/backup.js +330 -1
- package/dist-standalone/cjs/batch-operations.js +397 -1
- package/dist-standalone/cjs/cancellation.js +490 -1
- package/dist-standalone/cjs/checkpoint.js +193 -1
- package/dist-standalone/cjs/circuit-breaker.js +476 -1
- package/dist-standalone/cjs/cli/init.js +492 -1
- package/dist-standalone/cjs/config-validation.js +522 -1
- package/dist-standalone/cjs/connect.js +312 -1
- package/dist-standalone/cjs/connection-pool.js +506 -1
- package/dist-standalone/cjs/correlation-id.js +339 -1
- package/dist-standalone/cjs/crypto-utils.js +176 -1
- package/dist-standalone/cjs/debug-mode.js +534 -1
- package/dist-standalone/cjs/did-document.js +101 -1
- package/dist-standalone/cjs/did-privateme.js +130 -1
- package/dist-standalone/cjs/did-web.js +201 -1
- package/dist-standalone/cjs/discovery.js +462 -1
- package/dist-standalone/cjs/dual-mode.js +251 -1
- package/dist-standalone/cjs/email-templates.js +313 -1
- package/dist-standalone/cjs/email-transport.js +239 -1
- package/dist-standalone/cjs/envelope.js +538 -1
- package/dist-standalone/cjs/errors.js +913 -1
- package/dist-standalone/cjs/event-emitter.js +461 -1
- package/dist-standalone/cjs/gateway-state.js +55 -1
- package/dist-standalone/cjs/gateway-transport.js +120 -1
- package/dist-standalone/cjs/graceful-degradation.js +403 -1
- package/dist-standalone/cjs/guardrails.js +223 -1
- package/dist-standalone/cjs/health-check.js +336 -1
- package/dist-standalone/cjs/http-compat.js +272 -1
- package/dist-standalone/cjs/http-status-map.js +571 -1
- package/dist-standalone/cjs/identity.js +645 -1
- package/dist-standalone/cjs/index.js +406 -1
- package/dist-standalone/cjs/invitation.js +421 -1
- package/dist-standalone/cjs/invite.js +328 -1
- package/dist-standalone/cjs/key-agreement.js +335 -1
- package/dist-standalone/cjs/lazy-init.js +300 -1
- package/dist-standalone/cjs/logger.js +291 -1
- package/dist-standalone/cjs/mdns-discovery.js +202 -1
- package/dist-standalone/cjs/nonce-store.js +80 -1
- package/dist-standalone/cjs/pairing-manager.js +223 -1
- package/dist-standalone/cjs/plugin-system.js +264 -1
- package/dist-standalone/cjs/plugins/logging.js +168 -1
- package/dist-standalone/cjs/plugins/metrics.js +181 -1
- package/dist-standalone/cjs/plugins/validation.js +302 -1
- package/dist-standalone/cjs/policy.js +320 -1
- package/dist-standalone/cjs/progress-callbacks.js +583 -1
- package/dist-standalone/cjs/redis-nonce-store.js +76 -1
- package/dist-standalone/cjs/registry-middleware.js +50 -1
- package/dist-standalone/cjs/retry-strategies.js +544 -1
- package/dist-standalone/cjs/retry-transport.js +102 -1
- package/dist-standalone/cjs/runtime/browser.js +533 -1
- package/dist-standalone/cjs/runtime/edge.js +526 -1
- package/dist-standalone/cjs/runtime/react-native.js +394 -1
- package/dist-standalone/cjs/security-policy.js +245 -1
- package/dist-standalone/cjs/serialization.js +1040 -1
- package/dist-standalone/cjs/split-channel.js +225 -1
- package/dist-standalone/cjs/subscription-proof.js +230 -1
- package/dist-standalone/cjs/succession.js +148 -1
- package/dist-standalone/cjs/timeouts.js +412 -1
- package/dist-standalone/cjs/trace-context.js +424 -1
- package/dist-standalone/cjs/trace-spans.js +495 -1
- package/dist-standalone/cjs/transport.js +63 -1
- package/dist-standalone/cjs/trust-registry.js +991 -1
- package/dist-standalone/cjs/types/error-response.js +56 -1
- package/dist-standalone/cjs/vault-auth.js +178 -1
- package/dist-standalone/cjs/vault-store-loader.js +194 -1
- package/dist-standalone/cjs/verify.js +25 -1
- package/dist-standalone/cjs/version-info.js +543 -1
- package/dist-standalone/cjs/xfetch.js +340 -1
- package/dist-standalone/cli/init.js +455 -1
- package/dist-standalone/cli/setup.js +514 -1
- package/dist-standalone/cli/types.js +27 -1
- package/dist-standalone/cli/xbind.js +148 -1
- package/dist-standalone/config-validation.js +513 -1
- package/dist-standalone/connect.js +274 -1
- package/dist-standalone/connection-pool.js +500 -1
- package/dist-standalone/correlation-id.js +326 -1
- package/dist-standalone/crypto-utils.js +157 -1
- package/dist-standalone/debug-mode.js +510 -1
- package/dist-standalone/did-document.js +96 -1
- package/dist-standalone/did-privateme.js +121 -1
- package/dist-standalone/did-web.js +196 -1
- package/dist-standalone/discovery.js +458 -1
- package/dist-standalone/dual-mode.js +247 -1
- package/dist-standalone/email-templates.js +309 -1
- package/dist-standalone/email-transport.js +232 -1
- package/dist-standalone/envelope.js +525 -1
- package/dist-standalone/errors.js +896 -1
- package/dist-standalone/event-emitter.js +456 -1
- package/dist-standalone/gateway-state.js +51 -1
- package/dist-standalone/gateway-transport.js +116 -1
- package/dist-standalone/graceful-degradation.js +396 -1
- package/dist-standalone/guardrails.js +216 -1
- package/dist-standalone/health-check.js +332 -1
- package/dist-standalone/http-compat.js +267 -1
- package/dist-standalone/http-status-map.js +561 -1
- package/dist-standalone/identity.js +619 -1
- package/dist-standalone/index.js +78 -1
- package/dist-standalone/invitation.js +415 -1
- package/dist-standalone/invite.js +324 -1
- package/dist-standalone/key-agreement.js +325 -1
- package/dist-standalone/lazy-init.js +295 -1
- package/dist-standalone/logger.js +285 -1
- package/dist-standalone/mdns-discovery.js +195 -1
- package/dist-standalone/nonce-store.js +76 -1
- package/dist-standalone/pairing-manager.js +219 -1
- package/dist-standalone/plugin-system.js +257 -1
- package/dist-standalone/plugins/logging.d.ts +84 -0
- package/dist-standalone/plugins/logging.js +163 -0
- package/dist-standalone/plugins/metrics.d.ts +111 -0
- package/dist-standalone/plugins/metrics.js +176 -0
- package/dist-standalone/plugins/validation.d.ts +104 -0
- package/dist-standalone/plugins/validation.js +297 -0
- package/dist-standalone/policy.js +315 -1
- package/dist-standalone/progress-callbacks.js +576 -1
- package/dist-standalone/redis-nonce-store.js +72 -1
- package/dist-standalone/registry-middleware.js +47 -1
- package/dist-standalone/retry-strategies.js +534 -1
- package/dist-standalone/retry-transport.js +98 -1
- package/dist-standalone/runtime/browser.d.ts +311 -0
- package/dist-standalone/runtime/browser.js +516 -0
- package/dist-standalone/runtime/edge.d.ts +282 -0
- package/dist-standalone/runtime/edge.js +511 -0
- package/dist-standalone/runtime/react-native.d.ts +157 -0
- package/dist-standalone/runtime/react-native.js +383 -0
- package/dist-standalone/security-policy.js +239 -1
- package/dist-standalone/serialization.js +1031 -1
- package/dist-standalone/split-channel.js +219 -1
- package/dist-standalone/subscription-proof.js +224 -1
- package/dist-standalone/succession.js +142 -1
- package/dist-standalone/timeouts.js +398 -1
- package/dist-standalone/trace-context.js +414 -1
- package/dist-standalone/trace-spans.js +488 -1
- package/dist-standalone/transport.js +59 -1
- package/dist-standalone/trust-registry.js +950 -1
- package/dist-standalone/types/error-response.d.ts +209 -0
- package/dist-standalone/types/error-response.js +52 -0
- package/dist-standalone/vault-auth.js +174 -1
- package/dist-standalone/vault-store-loader.js +187 -1
- package/dist-standalone/verify.js +16 -1
- package/dist-standalone/version-info.js +530 -1
- package/dist-standalone/xfetch.js +335 -1
- package/package.json +4 -10
- package/share1.dat +0 -0
- package/dist-standalone/_deps/mldsa-wasm/LICENSE +0 -24
- package/dist-standalone/_deps/mldsa-wasm/package.json +0 -46
- package/dist-standalone/_deps/shared/cjs/package.json +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/package.json +0 -1
- package/dist-standalone/_deps/xchange/cjs/package.json +0 -1
- package/dist-standalone/_deps/xregistry/cjs/package.json +0 -1
- package/dist-standalone/cjs/package.json +0 -3
- package/dist-standalone/package.json +0 -10
|
@@ -1 +1,476 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module circuit-breaker
|
|
4
|
+
* Circuit breaker pattern for enhanced fault tolerance
|
|
5
|
+
*
|
|
6
|
+
* Protects external service calls (registry, gateway, S3) from cascading failures
|
|
7
|
+
* by automatically opening circuits after repeated failures and closing them after
|
|
8
|
+
* recovery periods.
|
|
9
|
+
*
|
|
10
|
+
* Architecture:
|
|
11
|
+
* - CLOSED: Normal operation, all requests pass through
|
|
12
|
+
* - OPEN: Circuit tripped, fast-fail all requests without calling service
|
|
13
|
+
* - HALF_OPEN: Testing recovery, allow limited requests to probe service health
|
|
14
|
+
*
|
|
15
|
+
* Features:
|
|
16
|
+
* - Automatic state transitions based on failure thresholds
|
|
17
|
+
* - Exponential backoff for recovery attempts
|
|
18
|
+
* - Per-service circuit isolation
|
|
19
|
+
* - Metrics integration for monitoring
|
|
20
|
+
* - Type-safe error handling
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const breaker = new CircuitBreaker({
|
|
25
|
+
* failureThreshold: 5,
|
|
26
|
+
* recoveryTimeout: 60000,
|
|
27
|
+
* halfOpenMaxCalls: 3,
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* const result = await breaker.execute(async () => {
|
|
31
|
+
* return await registryClient.lookup(did);
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.CircuitBreakerManager = exports.CircuitBreaker = void 0;
|
|
37
|
+
exports.createRegistryCircuitBreaker = createRegistryCircuitBreaker;
|
|
38
|
+
exports.createGatewayCircuitBreaker = createGatewayCircuitBreaker;
|
|
39
|
+
exports.createS3CircuitBreaker = createS3CircuitBreaker;
|
|
40
|
+
const shared_1 = require("../_deps/shared/index.js");
|
|
41
|
+
/* ── Implementation ── */
|
|
42
|
+
/**
|
|
43
|
+
* Circuit breaker for protecting external service calls.
|
|
44
|
+
*
|
|
45
|
+
* Implements the circuit breaker pattern to prevent cascading failures
|
|
46
|
+
* when external services (registry, gateway, S3) become unavailable.
|
|
47
|
+
*
|
|
48
|
+
* State transitions:
|
|
49
|
+
* - CLOSED → OPEN: After failureThreshold consecutive failures
|
|
50
|
+
* - OPEN → HALF_OPEN: After recoveryTimeout milliseconds
|
|
51
|
+
* - HALF_OPEN → CLOSED: After successThreshold successful calls
|
|
52
|
+
* - HALF_OPEN → OPEN: On any failure
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* // Create circuit breaker for registry calls
|
|
57
|
+
* const registryBreaker = new CircuitBreaker({
|
|
58
|
+
* name: 'registry',
|
|
59
|
+
* failureThreshold: 5,
|
|
60
|
+
* recoveryTimeout: 60000,
|
|
61
|
+
* });
|
|
62
|
+
*
|
|
63
|
+
* // Execute protected call
|
|
64
|
+
* const result = await registryBreaker.execute(async () => {
|
|
65
|
+
* return await registry.lookup(did);
|
|
66
|
+
* });
|
|
67
|
+
*
|
|
68
|
+
* if (!result.ok) {
|
|
69
|
+
* console.error('Circuit breaker error:', result.error);
|
|
70
|
+
* }
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
class CircuitBreaker {
|
|
74
|
+
state = 'CLOSED';
|
|
75
|
+
consecutiveFailures = 0;
|
|
76
|
+
consecutiveSuccesses = 0;
|
|
77
|
+
successCount = 0;
|
|
78
|
+
failureCount = 0;
|
|
79
|
+
rejectedCount = 0;
|
|
80
|
+
lastOpenedAt;
|
|
81
|
+
lastClosedAt;
|
|
82
|
+
recoveryTimer;
|
|
83
|
+
failureThreshold;
|
|
84
|
+
recoveryTimeout;
|
|
85
|
+
halfOpenMaxCalls;
|
|
86
|
+
successThreshold;
|
|
87
|
+
name;
|
|
88
|
+
onStateChange;
|
|
89
|
+
onOpen;
|
|
90
|
+
onClose;
|
|
91
|
+
onHalfOpen;
|
|
92
|
+
/**
|
|
93
|
+
* Create a new circuit breaker.
|
|
94
|
+
*
|
|
95
|
+
* @param options - Circuit breaker configuration
|
|
96
|
+
*/
|
|
97
|
+
constructor(options = {}) {
|
|
98
|
+
this.failureThreshold = options.failureThreshold ?? 5;
|
|
99
|
+
this.recoveryTimeout = options.recoveryTimeout ?? 60000;
|
|
100
|
+
this.halfOpenMaxCalls = options.halfOpenMaxCalls ?? 3;
|
|
101
|
+
this.successThreshold = options.successThreshold ?? 2;
|
|
102
|
+
this.name = options.name ?? 'default';
|
|
103
|
+
this.onStateChange = options.onStateChange;
|
|
104
|
+
this.onOpen = options.onOpen;
|
|
105
|
+
this.onClose = options.onClose;
|
|
106
|
+
this.onHalfOpen = options.onHalfOpen;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Execute a function with circuit breaker protection.
|
|
110
|
+
*
|
|
111
|
+
* If the circuit is OPEN, immediately returns an error without calling the function.
|
|
112
|
+
* If the circuit is HALF_OPEN, limits the number of concurrent calls.
|
|
113
|
+
* If the circuit is CLOSED, executes the function normally.
|
|
114
|
+
*
|
|
115
|
+
* @param fn - Async function to execute
|
|
116
|
+
* @returns Result of function execution or circuit breaker error
|
|
117
|
+
*/
|
|
118
|
+
async execute(fn) {
|
|
119
|
+
// Check if circuit is open
|
|
120
|
+
if (this.state === 'OPEN') {
|
|
121
|
+
this.rejectedCount++;
|
|
122
|
+
return (0, shared_1.err)('CIRCUIT_OPEN');
|
|
123
|
+
}
|
|
124
|
+
// Check if we've exceeded half-open call limit
|
|
125
|
+
// Use total calls attempted (success + failure) not just counter
|
|
126
|
+
if (this.state === 'HALF_OPEN') {
|
|
127
|
+
const totalAttempts = this.consecutiveSuccesses + (this.consecutiveFailures > 0 ? 1 : 0);
|
|
128
|
+
if (totalAttempts >= this.halfOpenMaxCalls) {
|
|
129
|
+
this.rejectedCount++;
|
|
130
|
+
return (0, shared_1.err)('HALF_OPEN_LIMIT_EXCEEDED');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const result = await fn();
|
|
135
|
+
this.onSuccess();
|
|
136
|
+
return (0, shared_1.ok)(result);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
this.onFailure(error);
|
|
140
|
+
return (0, shared_1.err)('EXECUTION_FAILED');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Get current circuit breaker metrics.
|
|
145
|
+
*
|
|
146
|
+
* @returns Current metrics snapshot
|
|
147
|
+
*/
|
|
148
|
+
getMetrics() {
|
|
149
|
+
return {
|
|
150
|
+
state: this.state,
|
|
151
|
+
successCount: this.successCount,
|
|
152
|
+
failureCount: this.failureCount,
|
|
153
|
+
rejectedCount: this.rejectedCount,
|
|
154
|
+
consecutiveFailures: this.consecutiveFailures,
|
|
155
|
+
consecutiveSuccesses: this.consecutiveSuccesses,
|
|
156
|
+
lastOpenedAt: this.lastOpenedAt,
|
|
157
|
+
lastClosedAt: this.lastClosedAt,
|
|
158
|
+
name: this.name,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get current circuit state.
|
|
163
|
+
*
|
|
164
|
+
* @returns Current state (CLOSED, OPEN, or HALF_OPEN)
|
|
165
|
+
*/
|
|
166
|
+
getState() {
|
|
167
|
+
return this.state;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Manually reset the circuit breaker to CLOSED state.
|
|
171
|
+
*
|
|
172
|
+
* Useful for testing or manual recovery scenarios.
|
|
173
|
+
* Clears all counters and timers.
|
|
174
|
+
*/
|
|
175
|
+
reset() {
|
|
176
|
+
this.clearRecoveryTimer();
|
|
177
|
+
this.transitionTo('CLOSED', 'manual reset');
|
|
178
|
+
this.consecutiveFailures = 0;
|
|
179
|
+
this.consecutiveSuccesses = 0;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Manually open the circuit breaker.
|
|
183
|
+
*
|
|
184
|
+
* Useful for maintenance windows or manual intervention.
|
|
185
|
+
*
|
|
186
|
+
* @param reason - Optional reason for opening circuit
|
|
187
|
+
*/
|
|
188
|
+
forceOpen(reason = 'manual intervention') {
|
|
189
|
+
this.transitionTo('OPEN', reason);
|
|
190
|
+
this.scheduleRecovery();
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Dispose of the circuit breaker and clean up resources.
|
|
194
|
+
*
|
|
195
|
+
* Clears any pending recovery timers.
|
|
196
|
+
*/
|
|
197
|
+
dispose() {
|
|
198
|
+
this.clearRecoveryTimer();
|
|
199
|
+
}
|
|
200
|
+
/* ── Private Methods ── */
|
|
201
|
+
/**
|
|
202
|
+
* Handle successful execution.
|
|
203
|
+
*/
|
|
204
|
+
onSuccess() {
|
|
205
|
+
this.successCount++;
|
|
206
|
+
this.consecutiveFailures = 0;
|
|
207
|
+
if (this.state === 'HALF_OPEN') {
|
|
208
|
+
this.consecutiveSuccesses++;
|
|
209
|
+
// Close circuit if we've reached success threshold
|
|
210
|
+
if (this.consecutiveSuccesses >= this.successThreshold) {
|
|
211
|
+
this.transitionTo('CLOSED', `${this.consecutiveSuccesses} consecutive successes`);
|
|
212
|
+
this.consecutiveSuccesses = 0;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Handle failed execution.
|
|
218
|
+
*
|
|
219
|
+
* @param _error - Error that occurred (unused but kept for potential future logging)
|
|
220
|
+
*/
|
|
221
|
+
onFailure(_error) {
|
|
222
|
+
this.failureCount++;
|
|
223
|
+
this.consecutiveFailures++;
|
|
224
|
+
this.consecutiveSuccesses = 0;
|
|
225
|
+
// Open circuit if we've exceeded failure threshold in CLOSED state
|
|
226
|
+
if (this.state === 'CLOSED' && this.consecutiveFailures >= this.failureThreshold) {
|
|
227
|
+
const reason = `${this.consecutiveFailures} consecutive failures`;
|
|
228
|
+
this.transitionTo('OPEN', reason);
|
|
229
|
+
this.scheduleRecovery();
|
|
230
|
+
}
|
|
231
|
+
// Reopen circuit on any failure in HALF_OPEN state
|
|
232
|
+
if (this.state === 'HALF_OPEN') {
|
|
233
|
+
this.transitionTo('OPEN', 'failure in HALF_OPEN state');
|
|
234
|
+
this.scheduleRecovery();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Transition to a new state.
|
|
239
|
+
*
|
|
240
|
+
* @param newState - Target state
|
|
241
|
+
* @param reason - Reason for transition
|
|
242
|
+
*/
|
|
243
|
+
transitionTo(newState, reason) {
|
|
244
|
+
const oldState = this.state;
|
|
245
|
+
if (oldState === newState) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
this.state = newState;
|
|
249
|
+
// Update timestamps
|
|
250
|
+
if (newState === 'OPEN') {
|
|
251
|
+
this.lastOpenedAt = Date.now();
|
|
252
|
+
this.onOpen?.(reason);
|
|
253
|
+
}
|
|
254
|
+
else if (newState === 'CLOSED') {
|
|
255
|
+
this.lastClosedAt = Date.now();
|
|
256
|
+
this.onClose?.();
|
|
257
|
+
}
|
|
258
|
+
else if (newState === 'HALF_OPEN') {
|
|
259
|
+
this.onHalfOpen?.();
|
|
260
|
+
}
|
|
261
|
+
// Invoke state change callback
|
|
262
|
+
this.onStateChange?.(oldState, newState, reason);
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Schedule automatic recovery from OPEN to HALF_OPEN state.
|
|
266
|
+
*/
|
|
267
|
+
scheduleRecovery() {
|
|
268
|
+
this.clearRecoveryTimer();
|
|
269
|
+
this.recoveryTimer = setTimeout(() => {
|
|
270
|
+
if (this.state === 'OPEN') {
|
|
271
|
+
this.transitionTo('HALF_OPEN', 'recovery timeout elapsed');
|
|
272
|
+
}
|
|
273
|
+
}, this.recoveryTimeout);
|
|
274
|
+
// Don't prevent Node.js from exiting
|
|
275
|
+
if (this.recoveryTimer.unref) {
|
|
276
|
+
this.recoveryTimer.unref();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Clear any pending recovery timer.
|
|
281
|
+
*/
|
|
282
|
+
clearRecoveryTimer() {
|
|
283
|
+
if (this.recoveryTimer) {
|
|
284
|
+
clearTimeout(this.recoveryTimer);
|
|
285
|
+
this.recoveryTimer = undefined;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
exports.CircuitBreaker = CircuitBreaker;
|
|
290
|
+
/* ── Service-Specific Circuit Breakers ── */
|
|
291
|
+
/**
|
|
292
|
+
* Create a circuit breaker configured for registry operations.
|
|
293
|
+
*
|
|
294
|
+
* Registry operations have:
|
|
295
|
+
* - Higher failure threshold (10) - registry is critical, tolerate transient failures
|
|
296
|
+
* - Longer recovery timeout (2 minutes) - DNS/network issues take time
|
|
297
|
+
* - More half-open probes (5) - verify stability before full recovery
|
|
298
|
+
*
|
|
299
|
+
* @param options - Optional overrides
|
|
300
|
+
* @returns Configured circuit breaker for registry calls
|
|
301
|
+
*/
|
|
302
|
+
function createRegistryCircuitBreaker(options = {}) {
|
|
303
|
+
return new CircuitBreaker({
|
|
304
|
+
name: 'registry',
|
|
305
|
+
failureThreshold: 10,
|
|
306
|
+
recoveryTimeout: 120000, // 2 minutes
|
|
307
|
+
halfOpenMaxCalls: 5,
|
|
308
|
+
successThreshold: 3,
|
|
309
|
+
...options,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Create a circuit breaker configured for gateway operations.
|
|
314
|
+
*
|
|
315
|
+
* Gateway operations have:
|
|
316
|
+
* - Medium failure threshold (5) - gateway should be reliable
|
|
317
|
+
* - Standard recovery timeout (1 minute)
|
|
318
|
+
* - Standard half-open probes (3)
|
|
319
|
+
*
|
|
320
|
+
* @param options - Optional overrides
|
|
321
|
+
* @returns Configured circuit breaker for gateway calls
|
|
322
|
+
*/
|
|
323
|
+
function createGatewayCircuitBreaker(options = {}) {
|
|
324
|
+
return new CircuitBreaker({
|
|
325
|
+
name: 'gateway',
|
|
326
|
+
failureThreshold: 5,
|
|
327
|
+
recoveryTimeout: 60000, // 1 minute
|
|
328
|
+
halfOpenMaxCalls: 3,
|
|
329
|
+
successThreshold: 2,
|
|
330
|
+
...options,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Create a circuit breaker configured for S3 storage operations.
|
|
335
|
+
*
|
|
336
|
+
* S3 operations have:
|
|
337
|
+
* - Lower failure threshold (3) - storage failures are serious
|
|
338
|
+
* - Shorter recovery timeout (30 seconds) - AWS S3 recovers quickly
|
|
339
|
+
* - Fewer half-open probes (2) - S3 is usually reliable
|
|
340
|
+
*
|
|
341
|
+
* @param options - Optional overrides
|
|
342
|
+
* @returns Configured circuit breaker for S3 calls
|
|
343
|
+
*/
|
|
344
|
+
function createS3CircuitBreaker(options = {}) {
|
|
345
|
+
return new CircuitBreaker({
|
|
346
|
+
name: 's3',
|
|
347
|
+
failureThreshold: 3,
|
|
348
|
+
recoveryTimeout: 30000, // 30 seconds
|
|
349
|
+
halfOpenMaxCalls: 2,
|
|
350
|
+
successThreshold: 2,
|
|
351
|
+
...options,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
/* ── Multi-Service Circuit Breaker Manager ── */
|
|
355
|
+
/**
|
|
356
|
+
* Manages multiple circuit breakers for different services.
|
|
357
|
+
*
|
|
358
|
+
* Provides centralized access to service-specific circuit breakers
|
|
359
|
+
* and aggregated metrics across all circuits.
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```typescript
|
|
363
|
+
* const manager = new CircuitBreakerManager();
|
|
364
|
+
*
|
|
365
|
+
* // Execute registry call with protection
|
|
366
|
+
* const result = await manager.executeRegistry(async () => {
|
|
367
|
+
* return await registry.lookup(did);
|
|
368
|
+
* });
|
|
369
|
+
*
|
|
370
|
+
* // Get all metrics
|
|
371
|
+
* const metrics = manager.getAllMetrics();
|
|
372
|
+
* console.log('Registry state:', metrics.registry.state);
|
|
373
|
+
* ```
|
|
374
|
+
*/
|
|
375
|
+
class CircuitBreakerManager {
|
|
376
|
+
breakers = new Map();
|
|
377
|
+
constructor() {
|
|
378
|
+
// Initialize default breakers
|
|
379
|
+
this.breakers.set('registry', createRegistryCircuitBreaker());
|
|
380
|
+
this.breakers.set('gateway', createGatewayCircuitBreaker());
|
|
381
|
+
this.breakers.set('s3', createS3CircuitBreaker());
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Execute a function with registry circuit breaker protection.
|
|
385
|
+
*
|
|
386
|
+
* @param fn - Async function to execute
|
|
387
|
+
* @returns Result of function execution or circuit breaker error
|
|
388
|
+
*/
|
|
389
|
+
async executeRegistry(fn) {
|
|
390
|
+
const breaker = this.breakers.get('registry');
|
|
391
|
+
if (!breaker) {
|
|
392
|
+
return (0, shared_1.err)('Circuit breaker not found: registry');
|
|
393
|
+
}
|
|
394
|
+
return breaker.execute(fn);
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Execute a function with gateway circuit breaker protection.
|
|
398
|
+
*
|
|
399
|
+
* @param fn - Async function to execute
|
|
400
|
+
* @returns Result of function execution or circuit breaker error
|
|
401
|
+
*/
|
|
402
|
+
async executeGateway(fn) {
|
|
403
|
+
const breaker = this.breakers.get('gateway');
|
|
404
|
+
if (!breaker) {
|
|
405
|
+
return (0, shared_1.err)('Circuit breaker not found: gateway');
|
|
406
|
+
}
|
|
407
|
+
return breaker.execute(fn);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Execute a function with S3 circuit breaker protection.
|
|
411
|
+
*
|
|
412
|
+
* @param fn - Async function to execute
|
|
413
|
+
* @returns Result of function execution or circuit breaker error
|
|
414
|
+
*/
|
|
415
|
+
async executeS3(fn) {
|
|
416
|
+
const breaker = this.breakers.get('s3');
|
|
417
|
+
if (!breaker) {
|
|
418
|
+
return (0, shared_1.err)('Circuit breaker not found: s3');
|
|
419
|
+
}
|
|
420
|
+
return breaker.execute(fn);
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Get metrics for a specific circuit breaker.
|
|
424
|
+
*
|
|
425
|
+
* @param name - Circuit breaker name
|
|
426
|
+
* @returns Metrics or undefined if breaker not found
|
|
427
|
+
*/
|
|
428
|
+
getMetrics(name) {
|
|
429
|
+
return this.breakers.get(name)?.getMetrics();
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Get metrics for all circuit breakers.
|
|
433
|
+
*
|
|
434
|
+
* @returns Map of circuit name to metrics
|
|
435
|
+
*/
|
|
436
|
+
getAllMetrics() {
|
|
437
|
+
const metrics = {};
|
|
438
|
+
for (const [name, breaker] of this.breakers.entries()) {
|
|
439
|
+
metrics[name] = breaker.getMetrics();
|
|
440
|
+
}
|
|
441
|
+
return metrics;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Get or create a custom circuit breaker.
|
|
445
|
+
*
|
|
446
|
+
* @param name - Circuit breaker name
|
|
447
|
+
* @param options - Optional configuration (used only on first access)
|
|
448
|
+
* @returns Circuit breaker instance
|
|
449
|
+
*/
|
|
450
|
+
getOrCreate(name, options) {
|
|
451
|
+
let breaker = this.breakers.get(name);
|
|
452
|
+
if (!breaker) {
|
|
453
|
+
breaker = new CircuitBreaker({ ...options, name });
|
|
454
|
+
this.breakers.set(name, breaker);
|
|
455
|
+
}
|
|
456
|
+
return breaker;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Reset all circuit breakers to CLOSED state.
|
|
460
|
+
*/
|
|
461
|
+
resetAll() {
|
|
462
|
+
for (const breaker of this.breakers.values()) {
|
|
463
|
+
breaker.reset();
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Dispose of all circuit breakers and clean up resources.
|
|
468
|
+
*/
|
|
469
|
+
dispose() {
|
|
470
|
+
for (const breaker of this.breakers.values()) {
|
|
471
|
+
breaker.dispose();
|
|
472
|
+
}
|
|
473
|
+
this.breakers.clear();
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
exports.CircuitBreakerManager = CircuitBreakerManager;
|