adpa-enterprise-framework-automation
Version:
Modular, standards-compliant Node.js/TypeScript automation framework for enterprise requirements, project, and data management. Provides CLI and API for BABOK v3, PMBOK 7th Edition, and DMBOK 2.0 (in progress). Production-ready Express.js API with TypeSpe
251 lines • 9.92 kB
JavaScript
/**
* Adobe Creative Suite Phase 2 - Enhanced Error Handling
*
* Provides comprehensive error handling, retry logic, and rate limiting
* for all Adobe Creative Suite API interactions.
*/
import { CreativeSuiteAuthenticator } from './authenticator.js';
export class EnhancedErrorHandler {
retryOptions;
rateLimitConfig;
requestQueue = [];
constructor(retryOptions, rateLimitConfig) {
this.retryOptions = {
maxRetries: 3,
baseDelay: 1000,
maxDelay: 30000,
backoffMultiplier: 2,
retryableErrors: [
'RATE_LIMITED',
'NETWORK_ERROR',
'TIMEOUT',
'SERVER_ERROR',
'AUTHENTICATION_EXPIRED'
],
...retryOptions
};
this.rateLimitConfig = {
requestsPerSecond: 10,
requestsPerMinute: 100,
burstLimit: 20,
...rateLimitConfig
};
}
/**
* Execute a function with comprehensive error handling and retry logic
*/
async executeWithRetry(operation, context, customRetryOptions) {
const options = { ...this.retryOptions, ...customRetryOptions };
let lastError;
for (let attempt = 0; attempt <= options.maxRetries; attempt++) {
try {
// Check rate limits before executing
await this.checkRateLimit(context);
// Execute the operation
const result = await operation();
// Record successful request
this.recordRequest(context);
return result;
}
catch (error) {
lastError = error;
const errorDetails = this.analyzeError(error, context);
// Don't retry if this is the last attempt or error is not retryable
if (attempt === options.maxRetries || !errorDetails.isRetryable) {
throw this.enhanceError(lastError, errorDetails, attempt);
}
// Calculate delay for next retry
const delay = this.calculateBackoffDelay(attempt, options);
console.warn(`🔄 Retry attempt ${attempt + 1}/${options.maxRetries} for ${context} after ${delay}ms delay. Error: ${errorDetails.message}`);
await this.sleep(delay);
}
}
// This should never be reached, but TypeScript requires it
throw this.enhanceError(lastError, this.analyzeError(lastError, context), options.maxRetries);
}
/**
* Check if the request is within rate limits
*/
async checkRateLimit(service) {
const now = new Date();
const oneSecondAgo = new Date(now.getTime() - 1000);
const oneMinuteAgo = new Date(now.getTime() - 60000);
// Clean old requests
this.requestQueue = this.requestQueue.filter(req => req.timestamp > oneMinuteAgo);
// Check rate limits
const recentRequests = this.requestQueue.filter(req => req.timestamp > oneSecondAgo);
const minuteRequests = this.requestQueue.length;
if (recentRequests.length >= this.rateLimitConfig.requestsPerSecond) {
const waitTime = 1000 - (now.getTime() - recentRequests[0].timestamp.getTime());
if (waitTime > 0) {
console.warn(`⏱️ Rate limit hit for ${service}. Waiting ${waitTime}ms...`);
await this.sleep(waitTime);
}
}
if (minuteRequests >= this.rateLimitConfig.requestsPerMinute) {
const waitTime = 60000 - (now.getTime() - this.requestQueue[0].timestamp.getTime());
if (waitTime > 0) {
console.warn(`⏱️ Minute rate limit hit for ${service}. Waiting ${waitTime}ms...`);
await this.sleep(waitTime);
}
}
}
/**
* Record a successful request for rate limiting
*/
recordRequest(service) {
this.requestQueue.push({
timestamp: new Date(),
service
});
// Keep only recent requests
const oneMinuteAgo = new Date(Date.now() - 60000);
this.requestQueue = this.requestQueue.filter(req => req.timestamp > oneMinuteAgo);
}
/**
* Analyze an error to determine if it's retryable and extract details
*/
analyzeError(error, context) {
const errorMessage = error.message.toLowerCase();
let code = 'UNKNOWN_ERROR';
let isRetryable = false;
// Determine error type and retryability
if (errorMessage.includes('rate limit') || errorMessage.includes('too many requests')) {
code = 'RATE_LIMITED';
isRetryable = true;
}
else if (errorMessage.includes('network') || errorMessage.includes('connection')) {
code = 'NETWORK_ERROR';
isRetryable = true;
}
else if (errorMessage.includes('timeout')) {
code = 'TIMEOUT';
isRetryable = true;
}
else if (errorMessage.includes('server error') || errorMessage.includes('internal error')) {
code = 'SERVER_ERROR';
isRetryable = true;
}
else if (errorMessage.includes('authentication') || errorMessage.includes('token')) {
code = 'AUTHENTICATION_ERROR';
isRetryable = errorMessage.includes('expired');
}
else if (errorMessage.includes('not found') || errorMessage.includes('invalid')) {
code = 'CLIENT_ERROR';
isRetryable = false;
}
return {
code,
message: error.message,
timestamp: new Date(),
context,
isRetryable: isRetryable && this.retryOptions.retryableErrors.includes(code)
};
}
/**
* Enhance an error with additional context and details
*/
enhanceError(originalError, errorDetails, attempts) {
const enhancedMessage = `${errorDetails.code}: ${originalError.message} (Context: ${errorDetails.context}, Attempts: ${attempts + 1})`;
const enhancedError = new Error(enhancedMessage);
enhancedError.name = errorDetails.code;
enhancedError.stack = originalError.stack;
// Add custom properties
enhancedError.originalError = originalError;
enhancedError.errorDetails = errorDetails;
enhancedError.attempts = attempts + 1;
return enhancedError;
}
/**
* Calculate exponential backoff delay
*/
calculateBackoffDelay(attempt, options) {
const baseDelay = options.baseDelay * Math.pow(options.backoffMultiplier, attempt);
const jitter = Math.random() * 0.1 * baseDelay; // Add up to 10% jitter
return Math.min(baseDelay + jitter, options.maxDelay);
}
/**
* Sleep for specified milliseconds
*/
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Get current rate limit status
*/
getRateLimitStatus() {
const now = new Date();
const oneSecondAgo = new Date(now.getTime() - 1000);
const oneMinuteAgo = new Date(now.getTime() - 60000);
const recentRequests = this.requestQueue.filter(req => req.timestamp > oneSecondAgo);
const minuteRequests = this.requestQueue.filter(req => req.timestamp > oneMinuteAgo);
return {
requestsInLastSecond: recentRequests.length,
requestsInLastMinute: minuteRequests.length,
remainingQuota: Math.max(0, this.rateLimitConfig.requestsPerMinute - minuteRequests.length)
};
}
}
/**
* Enhanced Creative Suite Authenticator with error handling
*/
export class RobustCreativeSuiteAuthenticator extends CreativeSuiteAuthenticator {
errorHandler;
constructor(errorHandlerOptions) {
super();
this.errorHandler = new EnhancedErrorHandler(errorHandlerOptions?.retryOptions, errorHandlerOptions?.rateLimitConfig);
}
/**
* Authenticate with comprehensive error handling and retry logic
*/
async authenticateWithRetry() {
return this.errorHandler.executeWithRetry(() => super.authenticate(), 'CreativeSuite Authentication', {
maxRetries: 3,
retryableErrors: ['AUTHENTICATION_EXPIRED', 'NETWORK_ERROR', 'RATE_LIMITED']
});
}
/**
* Get rate limit status for authentication requests
*/
getAuthRateLimitStatus() {
return this.errorHandler.getRateLimitStatus();
}
}
/**
* Enhanced API Client wrapper with error handling
*/
export class RobustAPIClient {
errorHandler;
authenticator;
constructor(authenticator, errorHandlerOptions) {
this.authenticator = authenticator || new RobustCreativeSuiteAuthenticator();
this.errorHandler = new EnhancedErrorHandler(errorHandlerOptions?.retryOptions, errorHandlerOptions?.rateLimitConfig);
}
/**
* Execute any API call with comprehensive error handling
*/
async executeAPICall(apiCall, context, customRetryOptions) {
return this.errorHandler.executeWithRetry(apiCall, context, customRetryOptions);
}
/**
* Get comprehensive API health status
*/
async getHealthStatus() {
try {
await this.authenticator.authenticateWithRetry();
return {
authentication: 'healthy',
rateLimits: this.errorHandler.getRateLimitStatus()
};
}
catch (error) {
const errorDetails = this.errorHandler.analyzeError(error, 'Health Check');
return {
authentication: errorDetails.isRetryable ? 'warning' : 'error',
rateLimits: this.errorHandler.getRateLimitStatus(),
lastError: errorDetails
};
}
}
}
//# sourceMappingURL=enhanced-error-handling.js.map