Spaces:
Runtime error
Runtime error
| /** | |
| * Custom error classes for categorizing CDPService launch failures | |
| * These allow for slightly more intelligent error handling and recovery strategies | |
| */ | |
| export enum LaunchErrorType { | |
| TIMEOUT = "TIMEOUT", | |
| CONFIGURATION = "CONFIGURATION", | |
| RESOURCE = "RESOURCE", | |
| SYSTEM = "SYSTEM", | |
| NETWORK = "NETWORK", | |
| FINGERPRINT = "FINGERPRINT", | |
| PLUGIN = "PLUGIN", | |
| CLEANUP = "CLEANUP", | |
| BROWSER_PROCESS = "BROWSER_PROCESS", | |
| SESSION_CONTEXT = "SESSION_CONTEXT", | |
| } | |
| export enum BrowserProcessState { | |
| PAGE_REFRESH = "page_refresh", | |
| LAUNCH_FAILED = "launch_failed", | |
| PAGE_ACCESS = "page_access", | |
| TARGET_SETUP = "target_setup", | |
| UNKNOWN = "unknown", | |
| } | |
| export enum PluginName { | |
| LAUNCH_MUTATOR = "launch_mutator", | |
| PLUGIN_MANAGER = "plugin_manager", | |
| UNKNOWN = "unknown", | |
| } | |
| export enum PluginOperation { | |
| PRE_LAUNCH_HOOK = "pre-launch hook", | |
| BROWSER_LAUNCH_NOTIFICATION = "browser launch notification", | |
| LAUNCH = "launch", | |
| } | |
| export enum CleanupType { | |
| PRE_LAUNCH_FILE_CLEANUP = "pre-launch file cleanup", | |
| GENERAL = "general", | |
| } | |
| export enum SessionContextType { | |
| CONTEXT_INJECTION = "context injection", | |
| } | |
| export enum FingerprintStage { | |
| GENERATION = "generation", | |
| INJECTION = "injection", | |
| } | |
| export enum ResourceType { | |
| EXTENSIONS = "extensions", | |
| FILE = "file", | |
| } | |
| export enum NetworkOperation { | |
| WEBSOCKET_SETUP = "websocket setup", | |
| PORT_BINDING = "port binding", | |
| NETWORK_SETUP = "network setup", | |
| } | |
| export enum SystemOperation { | |
| FILE_ACCESS = "file access", | |
| UNKNOWN_OPERATION = "unknown operation", | |
| } | |
| export enum ConfigurationField { | |
| DIMENSIONS = "dimensions", | |
| TIMEZONE = "timezone", | |
| PROXY_URL = "proxyUrl", | |
| } | |
| export enum ErrorCategories {} | |
| export abstract class BaseLaunchError extends Error { | |
| public readonly type: LaunchErrorType; | |
| public readonly isRetryable: boolean; | |
| public readonly context?: Record<string, any>; | |
| constructor( | |
| type: LaunchErrorType, | |
| message: string, | |
| isRetryable: boolean = false, | |
| context?: Record<string, any>, | |
| cause?: unknown, | |
| ) { | |
| super(message, { cause }); | |
| this.name = this.constructor.name; | |
| this.type = type; | |
| this.isRetryable = isRetryable; | |
| this.context = context; | |
| // Maintains proper stack trace for where error was thrown (only available on V8) | |
| if (Error.captureStackTrace) { | |
| Error.captureStackTrace(this, this.constructor); | |
| } | |
| } | |
| } | |
| /** | |
| * Thrown when browser launch times out after 30 seconds | |
| * This is typically retryable as it may be a temporary resource issue | |
| */ | |
| export class LaunchTimeoutError extends BaseLaunchError { | |
| constructor(timeoutMs: number = 30000, cause?: unknown) { | |
| super( | |
| LaunchErrorType.TIMEOUT, | |
| `Browser launch timeout after ${timeoutMs}ms`, | |
| true, | |
| { | |
| timeoutMs, | |
| }, | |
| cause, | |
| ); | |
| } | |
| } | |
| /** | |
| * Thrown when configuration parameters are invalid or incompatible | |
| * These are typically not retryable without fixing the configuration | |
| */ | |
| export class ConfigurationError extends BaseLaunchError { | |
| constructor( | |
| message: string, | |
| configField?: ConfigurationField, | |
| configValue?: any, | |
| cause?: unknown, | |
| ) { | |
| super( | |
| LaunchErrorType.CONFIGURATION, | |
| `Configuration error: ${message}`, | |
| false, | |
| { | |
| configField, | |
| configValue, | |
| }, | |
| cause, | |
| ); | |
| } | |
| } | |
| /** | |
| * Thrown when required system resources are unavailable | |
| * Some may be retryable (temporary disk space), others not (missing Chrome binary) | |
| */ | |
| export class ResourceError extends BaseLaunchError { | |
| constructor( | |
| message: string, | |
| resourceType: ResourceType, | |
| isRetryable: boolean = false, | |
| cause?: unknown, | |
| ) { | |
| super( | |
| LaunchErrorType.RESOURCE, | |
| `Resource error: ${message}`, | |
| isRetryable, | |
| { resourceType }, | |
| cause, | |
| ); | |
| } | |
| } | |
| /** | |
| * Thrown when system-level operations fail | |
| * Usually retryable as they may be temporary system issues | |
| */ | |
| export class SystemError extends BaseLaunchError { | |
| constructor(message: string, operation: SystemOperation, originalError?: Error) { | |
| super( | |
| LaunchErrorType.SYSTEM, | |
| `System error during ${operation}: ${message}`, | |
| true, | |
| { | |
| operation, | |
| originalError: originalError?.message, | |
| }, | |
| originalError, | |
| ); | |
| } | |
| } | |
| /** | |
| * Thrown when network-related operations fail (proxy, WebSocket setup) | |
| * Usually retryable as network issues are often temporary | |
| */ | |
| export class NetworkError extends BaseLaunchError { | |
| constructor(message: string, networkOperation: NetworkOperation, cause?: unknown) { | |
| super( | |
| LaunchErrorType.NETWORK, | |
| `Network error during ${networkOperation}: ${message}`, | |
| true, | |
| { | |
| networkOperation, | |
| }, | |
| cause, | |
| ); | |
| } | |
| } | |
| /** | |
| * Thrown when fingerprint generation or injection fails | |
| * Usually retryable, can fall back to no fingerprint | |
| */ | |
| export class FingerprintError extends BaseLaunchError { | |
| constructor(message: string, stage: FingerprintStage, cause?: unknown) { | |
| super( | |
| LaunchErrorType.FINGERPRINT, | |
| `Fingerprint error during ${stage}: ${message}`, | |
| true, | |
| { | |
| stage, | |
| }, | |
| cause, | |
| ); | |
| } | |
| } | |
| /** | |
| * Thrown when plugin operations fail during launch | |
| * May or may not be retryable depending on the plugin | |
| */ | |
| export class PluginError extends BaseLaunchError { | |
| constructor( | |
| message: string, | |
| pluginName: PluginName, | |
| operation: PluginOperation, | |
| isRetryable: boolean = true, | |
| cause?: unknown, | |
| ) { | |
| super( | |
| LaunchErrorType.PLUGIN, | |
| `Plugin error in ${pluginName} during ${operation}: ${message}`, | |
| isRetryable, | |
| { | |
| pluginName, | |
| operation, | |
| }, | |
| cause, | |
| ); | |
| } | |
| } | |
| /** | |
| * Thrown when file cleanup operations fail | |
| * Usually retryable and non-critical to browser launch | |
| */ | |
| export class CleanupError extends BaseLaunchError { | |
| constructor(message: string, cleanupType: CleanupType, cause?: unknown) { | |
| super( | |
| LaunchErrorType.CLEANUP, | |
| `Cleanup error during ${cleanupType}: ${message}`, | |
| true, | |
| { | |
| cleanupType, | |
| }, | |
| cause, | |
| ); | |
| } | |
| } | |
| /** | |
| * Thrown when the browser process fails to start or crashes immediately | |
| * Usually retryable as it may be a temporary issue | |
| */ | |
| export class BrowserProcessError extends BaseLaunchError { | |
| constructor( | |
| message: string, | |
| processState: BrowserProcessState, | |
| cause?: unknown, | |
| exitCode?: number, | |
| ) { | |
| super( | |
| LaunchErrorType.BROWSER_PROCESS, | |
| `Browser process error (${processState}): ${message}`, | |
| true, | |
| { | |
| processState, | |
| exitCode, | |
| }, | |
| cause, | |
| ); | |
| } | |
| } | |
| /** | |
| * Thrown when session context injection fails | |
| * Usually retryable, can fall back to launching without context | |
| */ | |
| export class SessionContextError extends BaseLaunchError { | |
| constructor(message: string, contextType: SessionContextType, cause?: unknown) { | |
| super( | |
| LaunchErrorType.SESSION_CONTEXT, | |
| `Session context error with ${contextType}: ${message}`, | |
| true, | |
| { | |
| contextType, | |
| }, | |
| cause, | |
| ); | |
| } | |
| } | |
| /** | |
| * Utility function to categorize unknown errors | |
| */ | |
| export function categorizeError(error: unknown, context?: string): BaseLaunchError { | |
| if (error instanceof BaseLaunchError) { | |
| return error; | |
| } | |
| const errorMessage = error instanceof Error ? error.message : String(error); | |
| const errorStack = error instanceof Error ? error.stack : undefined; | |
| // Analyze error message patterns to categorize | |
| const lowerMessage = errorMessage.toLowerCase(); | |
| if (lowerMessage.includes("timeout") || lowerMessage.includes("timed out")) { | |
| return new LaunchTimeoutError(); | |
| } | |
| if ( | |
| lowerMessage.includes("enoent") || | |
| lowerMessage.includes("not found") || | |
| lowerMessage.includes("no such file") | |
| ) { | |
| return new ResourceError(errorMessage, ResourceType.FILE, false); | |
| } | |
| if (lowerMessage.includes("eacces") || lowerMessage.includes("permission denied")) { | |
| return new SystemError( | |
| errorMessage, | |
| context ? SystemOperation.UNKNOWN_OPERATION : SystemOperation.FILE_ACCESS, | |
| ); | |
| } | |
| if (lowerMessage.includes("eaddrinuse") || lowerMessage.includes("address already in use")) { | |
| return new NetworkError(errorMessage, NetworkOperation.PORT_BINDING); | |
| } | |
| if (lowerMessage.includes("proxy") || lowerMessage.includes("websocket")) { | |
| return new NetworkError( | |
| errorMessage, | |
| context ? NetworkOperation.NETWORK_SETUP : NetworkOperation.NETWORK_SETUP, | |
| ); | |
| } | |
| if (lowerMessage.includes("fingerprint")) { | |
| return new FingerprintError(errorMessage, FingerprintStage.GENERATION); | |
| } | |
| if (lowerMessage.includes("plugin")) { | |
| return new PluginError( | |
| errorMessage, | |
| PluginName.UNKNOWN, | |
| context ? PluginOperation.LAUNCH : PluginOperation.LAUNCH, | |
| ); | |
| } | |
| if (lowerMessage.includes("cleanup") || lowerMessage.includes("clean")) { | |
| return new CleanupError(errorMessage, context ? CleanupType.GENERAL : CleanupType.GENERAL); | |
| } | |
| if ( | |
| lowerMessage.includes("chrome") || | |
| lowerMessage.includes("browser") || | |
| lowerMessage.includes("process") | |
| ) { | |
| return new BrowserProcessError(errorMessage, BrowserProcessState.UNKNOWN); | |
| } | |
| // Default to system error for unrecognized errors | |
| return new SystemError( | |
| errorMessage, | |
| context ? SystemOperation.UNKNOWN_OPERATION : SystemOperation.UNKNOWN_OPERATION, | |
| error instanceof Error ? error : undefined, | |
| ); | |
| } | |