📄 classifier.ts  •  7363 bytes
/**
 * 容错系统 - 错误分类器
 * Phase 4: 智能错误分类
 */
import { 
  type ErrorClassification, 
  type ErrorCategory, 
  type ErrorSeverity,
  type ClassifierConfig 
} from './types'

/** 错误模式匹配规则 */
interface ErrorPattern {
  pattern: RegExp
  category: ErrorCategory
  severity: ErrorSeverity
  message: string
  recoverable: boolean
  action: string
}

/** 内置错误模式 */
const ERROR_PATTERNS: ErrorPattern[] = [
  // 网络错误
  { 
    pattern: /ECONNREFUSED|ENOTFOUND|ECONNRESET|ETIMEDOUT|ENETUNREACH|connection\s*refused/i,
    category: 'network',
    severity: 'medium',
    message: '网络连接失败',
    recoverable: true,
    action: '检查网络连接后重试',
  },
  { 
    pattern: /fetch.*failed|network.*error|net::ERR/i,
    category: 'network',
    severity: 'medium',
    message: '网络请求失败',
    recoverable: true,
    action: '稍后重试或检查网络',
  },
  
  // 超时错误
  { 
    pattern: /timeout|timed?\s*out|ETIMEDOUT/i,
    category: 'timeout',
    severity: 'medium',
    message: '操作超时',
    recoverable: true,
    action: '增加超时时间后重试',
  },
  { 
    pattern: /maxTurns|exceeded.*limit/i,
    category: 'timeout',
    severity: 'low',
    message: '达到循环上限',
    recoverable: false,
    action: '简化任务或增加限制',
  },
  
  // 语法错误
  { 
    pattern: /syntax.*error|unexpected.*token|parse.*error/i,
    category: 'syntax',
    severity: 'high',
    message: '代码语法错误',
    recoverable: false,
    action: '修复代码语法',
  },
  { 
    pattern: /unexpected end|unterminated|string/i,
    category: 'syntax',
    severity: 'high',
    message: '代码格式错误',
    recoverable: false,
    action: '检查代码完整性',
  },
  
  // 类型错误
  { 
    pattern: /type.*error|is not a|TypeError|Cannot read property/i,
    category: 'type',
    severity: 'high',
    message: '类型错误',
    recoverable: false,
    action: '检查类型定义和使用',
  },
  { 
    pattern: /undefined is not|is undefined|null.*is not|cannot.*undefined/i,
    category: 'type',
    severity: 'medium',
    message: '变量未定义或为空',
    recoverable: false,
    action: '添加空值检查',
  },
  
  // 运行时错误
  { 
    pattern: /runtime.*error|ReferenceError|EvalError|RangeError/i,
    category: 'runtime',
    severity: 'high',
    message: '运行时错误',
    recoverable: false,
    action: '检查代码逻辑',
  },
  
  // 资源不足
  { 
    pattern: /memory|heap|out of memory|OOM|allocation.*failed/i,
    category: 'resource',
    severity: 'critical',
    message: '内存不足',
    recoverable: true,
    action: '释放内存或增加资源',
  },
  { 
    pattern: /disk.*full|no.*space|ENOSPC/i,
    category: 'resource',
    severity: 'high',
    message: '磁盘空间不足',
    recoverable: false,
    action: '清理磁盘空间',
  },
  { 
    pattern: /max.*file.*size|file.*too.*large/i,
    category: 'resource',
    severity: 'medium',
    message: '文件过大',
    recoverable: false,
    action: '拆分文件或压缩',
  },
  
  // 权限错误
  { 
    pattern: /permission.*denied|EACCES|EPERM|unauthorized/i,
    category: 'permission',
    severity: 'high',
    message: '权限不足',
    recoverable: false,
    action: '检查文件权限',
  },
  
  // 未找到
  { 
    pattern: /not found|ENOENT|Cannot find|does not exist/i,
    category: 'not_found',
    severity: 'medium',
    message: '资源未找到',
    recoverable: false,
    action: '检查路径是否正确',
  },
  { 
    pattern: /module.*not.*found|require.*failed/i,
    category: 'not_found',
    severity: 'high',
    message: '模块未找到',
    recoverable: true,
    action: '安装依赖或检查路径',
  },
  
  // 验证失败
  { 
    pattern: /validation.*failed|invalid.*input|schema.*error/i,
    category: 'validation',
    severity: 'medium',
    message: '验证失败',
    recoverable: false,
    action: '检查输入数据',
  },
  { 
    pattern: /401|403|invalid.*token|authentication.*failed/i,
    category: 'validation',
    severity: 'high',
    message: '认证失败',
    recoverable: true,
    action: '重新登录或刷新令牌',
  },
  { 
    pattern: /404/i,
    category: 'not_found',
    severity: 'medium',
    message: '资源不存在',
    recoverable: false,
    action: '检查请求地址',
  },
  { 
    pattern: /429|rate.*limit|too.*many.*requests/i,
    category: 'resource',
    severity: 'medium',
    message: '请求过于频繁',
    recoverable: true,
    action: '等待后重试',
  },
  { 
    pattern: /500|503|server.*error|internal.*error/i,
    category: 'network',
    severity: 'high',
    message: '服务器错误',
    recoverable: true,
    action: '稍后重试',
  },
]

/** 分类缓存 */
const classificationCache = new Map<string, ErrorClassification>()

/**
 * 分类单个错误
 */
export function classifyError(error: Error | string, config?: Partial<ClassifierConfig>): ErrorClassification {
  const errorMessage = typeof error === 'string' ? error : error.message
  const errorCode = typeof error === 'string' ? undefined : (error as any).code
  
  // 检查缓存
  const cacheKey = errorMessage.slice(0, 100)
  if (config?.cacheClassifications !== false && classificationCache.has(cacheKey)) {
    return classificationCache.get(cacheKey)!
  }
  
  // 尝试匹配模式
  for (const pattern of ERROR_PATTERNS) {
    if (pattern.pattern.test(errorMessage) || (errorCode && pattern.pattern.test(errorCode))) {
      const classification: ErrorClassification = {
        category: pattern.category,
        severity: pattern.severity,
        message: pattern.message,
        code: errorCode,
        recoverable: pattern.recoverable,
        suggestedAction: pattern.action,
      }
      
      // 缓存结果
      if (config?.cacheClassifications !== false) {
        if (classificationCache.size > (config?.maxCacheSize || 100)) {
          const firstKey = classificationCache.keys().next().value
          if (firstKey) classificationCache.delete(firstKey)
        }
        classificationCache.set(cacheKey, classification)
      }
      
      return classification
    }
  }
  
  // 无法分类的错误
  const unknownClassification: ErrorClassification = {
    category: 'unknown',
    severity: 'medium',
    message: errorMessage.slice(0, 100),
    code: errorCode,
    recoverable: false,
    suggestedAction: '检查错误信息并手动处理',
  }
  
  return unknownClassification
}

/**
 * 批量分类错误
 */
export function classifyErrors(
  errors: (Error | string)[]
): ErrorClassification[] {
  return errors.map(e => classifyError(e))
}

/**
 * 检查是否可重试
 */
export function isRetryable(error: Error | string): boolean {
  const classification = classifyError(error)
  return classification.recoverable
}

/**
 * 获取错误严重程度
 */
export function getErrorSeverity(error: Error | string): ErrorSeverity {
  return classifyError(error).severity
}

/**
 * 清除分类缓存
 */
export function clearCache(): void {
  classificationCache.clear()
}

/**
 * 获取缓存统计
 */
export function getCacheStats(): { size: number; maxSize: number } {
  return {
    size: classificationCache.size,
    maxSize: 100,
  }
}