@dexwox-labs/a2a-client
Version:
TypeScript client implementation for Google's Agent-to-Agent (A2A) protocol - includes HTTP/WebSocket communication, circuit breakers and error handling
154 lines • 4.84 kB
JavaScript
"use strict";
/**
* @module CircuitBreaker
* @description Circuit breaker implementation for improving reliability in network operations
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.CircuitBreaker = void 0;
const a2a_core_1 = require("@dexwox-labs/a2a-core");
/**
* Circuit breaker implementation for improving reliability
*
* The circuit breaker pattern prevents cascading failures by temporarily disabling
* operations that are likely to fail.
*
* @example
* ```typescript
* const breaker = new CircuitBreaker({
* failureThreshold: 3,
* successThreshold: 2,
* timeout: 10000
* });
*
* const result = await breaker.execute(async () => {
* return await fetch('https://api.example.com/data');
* });
* ```
*/
class CircuitBreaker {
/** @private Current state of the circuit breaker */
state = 'CLOSED';
/** @private Count of consecutive failures */
failureCount = 0;
/** @private Count of consecutive successes in HALF_OPEN state */
successCount = 0;
/** @private Timestamp of the last failure */
lastFailureTime = 0;
/** @private Configuration options */
options;
/**
* Creates a new circuit breaker instance
*
* @param options - Configuration options for the circuit breaker
*/
constructor(options) {
this.options = options;
}
/**
* Executes a function with circuit breaker protection
*
* This method wraps the provided function with circuit breaker logic.
* If the circuit is closed, the function executes normally. If the circuit
* is open, an error is thrown immediately without executing the function.
* If the circuit is half-open, the function is executed as a test to see
* if the underlying system has recovered.
*
* @param fn - The async function to execute
* @returns Promise resolving to the result of the function
* @throws {A2AError} If the circuit is open
* @throws Any error thrown by the executed function
*
* @example
* ```typescript
* try {
* const data = await breaker.execute(async () => {
* const response = await fetch('https://api.example.com/data');
* return response.json();
* });
* processData(data);
* } catch (error) {
* if (error.code === -32050) {
* console.error('Circuit is open, not attempting request');
* } else {
* console.error('Request failed:', error);
* }
* }
* ```
*/
async execute(fn) {
// If circuit is open, check if timeout has elapsed to transition to half-open
if (this.state === 'OPEN') {
const now = Date.now();
if (now - this.lastFailureTime > this.options.timeout) {
this.state = 'HALF_OPEN';
}
else {
throw new a2a_core_1.A2AError('Circuit breaker is open', -32050, { state: this.state });
}
}
try {
// Execute the function and track success
const result = await fn();
this.onSuccess();
return result;
}
catch (err) {
// Track failure and re-throw the error
this.onFailure();
throw err;
}
}
/**
* Handles successful operations
*
* Resets the failure count and, if in HALF_OPEN state, increments the success count.
* If enough consecutive successes occur in HALF_OPEN state, the circuit is closed.
*
* @private
*/
onSuccess() {
this.failureCount = 0;
if (this.state === 'HALF_OPEN') {
this.successCount++;
if (this.successCount >= this.options.successThreshold) {
this.state = 'CLOSED';
this.successCount = 0;
}
}
}
/**
* Handles failed operations
*
* Increments the failure count and records the time of failure.
* If enough consecutive failures occur, the circuit is opened.
*
* @private
*/
onFailure() {
this.failureCount++;
this.lastFailureTime = Date.now();
if (this.failureCount >= this.options.failureThreshold) {
this.state = 'OPEN';
}
}
/**
* Gets the current state of the circuit breaker
*
* @returns The current state: 'CLOSED', 'OPEN', or 'HALF_OPEN'
*
* @example
* ```typescript
* const state = breaker.getState();
* console.log(`Circuit breaker is currently ${state}`);
*
* if (state === 'OPEN') {
* console.log('Circuit is open, requests will be rejected');
* }
* ```
*/
getState() {
return this.state;
}
}
exports.CircuitBreaker = CircuitBreaker;
//# sourceMappingURL=circuit-breaker.js.map