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