UNPKG

@agentdao/core

Version:

Core functionality, skills, and ready-made UI components for AgentDAO - Web3 subscriptions, content generation, social media, help support, live chat, RSS fetching, web search, and agent pricing integration

242 lines (241 loc) 7.58 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RobustUtils = void 0; class RobustUtils { /** * Execute function with retry logic */ static async withRetry(fn, config = {}) { const retryConfig = { ...this.DEFAULT_RETRY_CONFIG, ...config }; let lastError; for (let attempt = 0; attempt <= retryConfig.maxRetries; attempt++) { try { return await fn(); } catch (error) { lastError = error; if (attempt === retryConfig.maxRetries) { throw lastError; } // Don't retry on certain errors if (this.isNonRetryableError(error)) { throw lastError; } const delay = Math.min(retryConfig.baseDelay * Math.pow(retryConfig.backoffMultiplier, attempt), retryConfig.maxDelay); await this.sleep(delay); } } throw lastError; } /** * Execute function with rate limiting */ static async withRateLimit(key, fn, config = {}) { const rateLimitConfig = { ...this.DEFAULT_RATE_LIMIT_CONFIG, ...config }; const now = Date.now(); const requestData = this.requestCounts.get(key); if (requestData && now < requestData.resetTime) { if (requestData.count >= rateLimitConfig.maxRequests) { const waitTime = requestData.resetTime - now; throw new Error(`Rate limit exceeded. Try again in ${Math.ceil(waitTime / 1000)} seconds.`); } requestData.count++; } else { this.requestCounts.set(key, { count: 1, resetTime: now + rateLimitConfig.timeWindow }); } return await fn(); } /** * Execute function with both retry and rate limiting */ static async withRetryAndRateLimit(key, fn, retryConfig, rateLimitConfig) { return await this.withRetry(() => this.withRateLimit(key, fn, rateLimitConfig), retryConfig); } /** * Validate API response */ static validateApiResponse(response, expectedFields) { if (!response) { throw new Error('Empty API response'); } for (const field of expectedFields) { if (!(field in response)) { throw new Error(`Missing required field: ${field}`); } } } /** * Sanitize user input */ static sanitizeInput(input) { return input .trim() .replace(/[<>]/g, '') // Remove potential HTML tags .substring(0, 1000); // Limit length } /** * Validate search query */ static validateSearchQuery(query) { const errors = []; if (!query || query.trim().length === 0) { errors.push('Query cannot be empty'); } if (query.length < 2) { errors.push('Query must be at least 2 characters long'); } if (query.length > 500) { errors.push('Query cannot exceed 500 characters'); } // Check for potentially harmful content const harmfulPatterns = [ /script/i, /javascript:/i, /data:/i, /vbscript:/i, /onload/i, /onerror/i ]; for (const pattern of harmfulPatterns) { if (pattern.test(query)) { errors.push('Query contains potentially harmful content'); break; } } return { isValid: errors.length === 0, errors }; } /** * Validate content generation parameters */ static validateContentParams(params) { const errors = []; if (!params.topic || params.topic.trim().length === 0) { errors.push('Topic is required'); } if (params.topic && params.topic.length > 200) { errors.push('Topic cannot exceed 200 characters'); } if (params.keywords && !Array.isArray(params.keywords)) { errors.push('Keywords must be an array'); } if (params.keywords && params.keywords.length > 20) { errors.push('Cannot exceed 20 keywords'); } return { isValid: errors.length === 0, errors }; } /** * Create a circuit breaker */ static createCircuitBreaker(failureThreshold = 5, resetTimeout = 60000) { let failureCount = 0; let lastFailureTime = 0; let state = 'CLOSED'; return { async execute(fn) { if (state === 'OPEN') { if (Date.now() - lastFailureTime > resetTimeout) { state = 'HALF_OPEN'; } else { throw new Error('Circuit breaker is OPEN'); } } try { const result = await fn(); if (state === 'HALF_OPEN') { state = 'CLOSED'; failureCount = 0; } return result; } catch (error) { failureCount++; lastFailureTime = Date.now(); if (failureCount >= failureThreshold) { state = 'OPEN'; } throw error; } }, getState() { return state; }, getFailureCount() { return failureCount; } }; } /** * Cache with TTL */ static createCache(ttl = 300000) { const cache = new Map(); return { get(key) { const item = cache.get(key); if (!item) return null; if (Date.now() > item.expiry) { cache.delete(key); return null; } return item.value; }, set(key, value) { cache.set(key, { value, expiry: Date.now() + ttl }); }, clear() { cache.clear(); }, size() { return cache.size; } }; } // Private helper methods static isNonRetryableError(error) { const nonRetryableStatuses = [400, 401, 403, 404, 422]; const nonRetryableMessages = [ 'invalid api key', 'authentication failed', 'not found', 'bad request' ]; if (error.status && nonRetryableStatuses.includes(error.status)) { return true; } if (error.message) { const message = error.message.toLowerCase(); return nonRetryableMessages.some(msg => message.includes(msg)); } return false; } static sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } exports.RobustUtils = RobustUtils; RobustUtils.DEFAULT_RETRY_CONFIG = { maxRetries: 3, baseDelay: 1000, maxDelay: 10000, backoffMultiplier: 2 }; RobustUtils.DEFAULT_RATE_LIMIT_CONFIG = { maxRequests: 10, timeWindow: 60000 // 1 minute }; RobustUtils.requestCounts = new Map();