@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
JavaScript
;
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();