@sailboat-computer/data-storage
Version:
Shared data storage library for sailboat computer v3
202 lines • 6.33 kB
JavaScript
;
/**
* Error handling and resilience utilities
*
* This file contains error classes and utility functions for error handling
* and resilience patterns like circuit breakers, retries, and timeouts.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.withBulkhead = exports.withTimeout = exports.withRetry = exports.CircuitBreaker = exports.StorageError = exports.StorageErrorCode = void 0;
const types_1 = require("../types");
// Re-export StorageErrorCode for backward compatibility
var types_2 = require("../types");
Object.defineProperty(exports, "StorageErrorCode", { enumerable: true, get: function () { return types_2.StorageErrorCode; } });
/**
* Storage error class
*/
class StorageError extends Error {
/**
* Create a new storage error
*
* @param code - Error code
* @param message - Error message
* @param details - Error details
*/
constructor(code, message, details = {}) {
super(message);
this.name = 'StorageError';
this.code = code;
this.details = details;
// Capture stack trace
if (Error.captureStackTrace) {
Error.captureStackTrace(this, StorageError);
}
}
/**
* Convert error to string
*
* @returns String representation of error
*/
toString() {
return `${this.name}[${this.code}]: ${this.message}`;
}
/**
* Convert error to JSON
*
* @returns JSON representation of error
*/
toJSON() {
return {
name: this.name,
code: this.code,
message: this.message,
details: this.details,
stack: this.stack
};
}
}
exports.StorageError = StorageError;
/**
* Circuit breaker implementation
*
* Implements the circuit breaker pattern to prevent cascading failures
* by failing fast when a service is unavailable.
*/
class CircuitBreaker {
/**
* Create a new circuit breaker
*
* @param failureThreshold - Number of failures before opening circuit
* @param resetTimeoutMs - Reset timeout in milliseconds
*/
constructor(failureThreshold = 3, resetTimeoutMs = 30000) {
this.state = types_1.CircuitBreakerState.CLOSED;
this.failureCount = 0;
this.lastFailureTime = 0;
this.failureThreshold = failureThreshold;
this.resetTimeoutMs = resetTimeoutMs;
}
/**
* Execute a function with circuit breaker protection
*
* @param fn - Function to execute
* @returns Function result
* @throws Error if circuit is open
*/
async execute(fn) {
if (this.state === types_1.CircuitBreakerState.OPEN) {
// Check if reset timeout has elapsed
if (Date.now() - this.lastFailureTime >= this.resetTimeoutMs) {
this.state = types_1.CircuitBreakerState.HALF_OPEN;
}
else {
throw new Error('Circuit is open');
}
}
try {
// Execute function
const result = await fn();
// Reset failure count on success
if (this.state === types_1.CircuitBreakerState.HALF_OPEN) {
this.state = types_1.CircuitBreakerState.CLOSED;
}
this.failureCount = 0;
return result;
}
catch (error) {
// Increment failure count
this.failureCount++;
this.lastFailureTime = Date.now();
// Open circuit if failure threshold is reached
if (this.failureCount >= this.failureThreshold) {
this.state = types_1.CircuitBreakerState.OPEN;
}
throw error;
}
}
/**
* Get circuit breaker state
*
* @returns Circuit breaker state
*/
getState() {
return this.state;
}
/**
* Reset circuit breaker
*/
reset() {
this.state = types_1.CircuitBreakerState.CLOSED;
this.failureCount = 0;
this.lastFailureTime = 0;
}
}
exports.CircuitBreaker = CircuitBreaker;
/**
* Execute a function with retry logic
*
* @param fn - Function to execute
* @param maxRetries - Maximum number of retries
* @param baseDelayMs - Base delay in milliseconds
* @returns Function result
* @throws Error if all retries fail
*/
async function withRetry(fn, maxRetries = 3, baseDelayMs = 1000) {
let lastError;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
}
catch (error) {
lastError = error instanceof Error ? error : new Error(String(error));
if (attempt < maxRetries) {
// Exponential backoff with jitter
const jitter = Math.random() * 0.3 + 0.85; // 0.85-1.15
const delay = baseDelayMs * Math.pow(2, attempt) * jitter;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw lastError;
}
exports.withRetry = withRetry;
/**
* Execute a function with timeout
*
* @param promise - Promise to execute
* @param timeoutMs - Timeout in milliseconds
* @returns Promise result
* @throws Error if timeout is reached
*/
async function withTimeout(promise, timeoutMs = 5000) {
let timeoutId;
const timeoutPromise = new Promise((_, reject) => {
timeoutId = setTimeout(() => {
reject(new Error(`Operation timed out after ${timeoutMs}ms`));
}, timeoutMs);
});
try {
return await Promise.race([promise, timeoutPromise]);
}
finally {
clearTimeout(timeoutId);
}
}
exports.withTimeout = withTimeout;
/**
* Execute a function with bulkhead protection
*
* @param fn - Function to execute
* @param maxConcurrent - Maximum number of concurrent executions
* @param maxQueue - Maximum queue size
* @returns Function result
* @throws Error if queue is full
*/
async function withBulkhead(fn, maxConcurrent = 10, maxQueue = 100) {
// Implementation of bulkhead pattern
// This is a simplified version that doesn't actually limit concurrency
// A real implementation would use a semaphore or similar mechanism
return await fn();
}
exports.withBulkhead = withBulkhead;
//# sourceMappingURL=errors.js.map