"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TelemetryErrorAggregator = exports.TelemetryCircuitBreaker = exports.TelemetryError = exports.TelemetryErrorType = void 0;
const logger_1 = require("../utils/logger");
var telemetry_types_1 = require("./telemetry-types");
Object.defineProperty(exports, "TelemetryErrorType", { enumerable: true, get: function () { return telemetry_types_1.TelemetryErrorType; } });
class TelemetryError extends Error {
constructor(type, message, context, retryable = false) {
super(message);
this.name = 'TelemetryError';
this.type = type;
this.context = context;
this.timestamp = Date.now();
this.retryable = retryable;
Object.setPrototypeOf(this, TelemetryError.prototype);
}
toContext() {
return {
type: this.type,
message: this.message,
context: this.context,
timestamp: this.timestamp,
retryable: this.retryable
};
}
log() {
const logContext = {
type: this.type,
message: this.message,
...this.context
};
if (this.retryable) {
logger_1.logger.debug('Retryable telemetry error:', logContext);
}
else {
logger_1.logger.debug('Non-retryable telemetry error:', logContext);
}
}
}
exports.TelemetryError = TelemetryError;
class TelemetryCircuitBreaker {
constructor(failureThreshold = 5, resetTimeout = 60000, halfOpenRequests = 3) {
this.failureCount = 0;
this.lastFailureTime = 0;
this.state = 'closed';
this.halfOpenCount = 0;
this.failureThreshold = failureThreshold;
this.resetTimeout = resetTimeout;
this.halfOpenRequests = halfOpenRequests;
}
shouldAllow() {
const now = Date.now();
switch (this.state) {
case 'closed':
return true;
case 'open':
if (now - this.lastFailureTime > this.resetTimeout) {
this.state = 'half-open';
this.halfOpenCount = 0;
logger_1.logger.debug('Circuit breaker transitioning to half-open');
return true;
}
return false;
case 'half-open':
if (this.halfOpenCount < this.halfOpenRequests) {
this.halfOpenCount++;
return true;
}
return false;
default:
return false;
}
}
recordSuccess() {
if (this.state === 'half-open') {
if (this.halfOpenCount >= this.halfOpenRequests) {
this.state = 'closed';
this.failureCount = 0;
logger_1.logger.debug('Circuit breaker closed after successful recovery');
}
}
else if (this.state === 'closed') {
this.failureCount = 0;
}
}
recordFailure(error) {
this.failureCount++;
this.lastFailureTime = Date.now();
if (this.state === 'half-open') {
this.state = 'open';
logger_1.logger.debug('Circuit breaker opened from half-open state', { error: error?.message });
}
else if (this.state === 'closed' && this.failureCount >= this.failureThreshold) {
this.state = 'open';
logger_1.logger.debug(`Circuit breaker opened after ${this.failureCount} failures`, { error: error?.message });
}
}
getState() {
return {
state: this.state,
failureCount: this.failureCount,
canRetry: this.shouldAllow()
};
}
reset() {
this.state = 'closed';
this.failureCount = 0;
this.lastFailureTime = 0;
this.halfOpenCount = 0;
}
}
exports.TelemetryCircuitBreaker = TelemetryCircuitBreaker;
class TelemetryErrorAggregator {
constructor() {
this.errors = new Map();
this.errorDetails = [];
this.maxDetails = 100;
}
record(error) {
const count = this.errors.get(error.type) || 0;
this.errors.set(error.type, count + 1);
this.errorDetails.push(error.toContext());
if (this.errorDetails.length > this.maxDetails) {
this.errorDetails.shift();
}
}
getStats() {
const errorsByType = {};
let totalErrors = 0;
let mostCommonError;
let maxCount = 0;
for (const [type, count] of this.errors.entries()) {
errorsByType[type] = count;
totalErrors += count;
if (count > maxCount) {
maxCount = count;
mostCommonError = type;
}
}
return {
totalErrors,
errorsByType,
mostCommonError,
recentErrors: this.errorDetails.slice(-10)
};
}
reset() {
this.errors.clear();
this.errorDetails = [];
}
}
exports.TelemetryErrorAggregator = TelemetryErrorAggregator;
//# sourceMappingURL=telemetry-error.js.map