source dump of claude code
at main 121 lines 4.5 kB view raw
1import { posix, win32 } from 'path' 2import { 3 type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, 4 logEvent, 5} from 'src/services/analytics/index.js' 6import { logForDebugging } from './debug.js' 7import { isEnvTruthy } from './envUtils.js' 8import { getPlatform } from './platform.js' 9 10// Track warnings to avoid spam — bounded to prevent unbounded memory growth 11export const MAX_WARNING_KEYS = 1000 12const warningCounts = new Map<string, number>() 13 14// Check if running from a build directory (development mode) 15// This is a sync version of the logic in getCurrentInstallationType() 16function isRunningFromBuildDirectory(): boolean { 17 let invokedPath = process.argv[1] || '' 18 let execPath = process.execPath || process.argv[0] || '' 19 20 // On Windows, convert backslashes to forward slashes for consistent path matching 21 if (getPlatform() === 'windows') { 22 invokedPath = invokedPath.split(win32.sep).join(posix.sep) 23 execPath = execPath.split(win32.sep).join(posix.sep) 24 } 25 26 const pathsToCheck = [invokedPath, execPath] 27 const buildDirs = [ 28 '/build-ant/', 29 '/build-external/', 30 '/build-external-native/', 31 '/build-ant-native/', 32 ] 33 34 return pathsToCheck.some(path => buildDirs.some(dir => path.includes(dir))) 35} 36 37// Warnings we know about and want to suppress from users 38const INTERNAL_WARNINGS = [ 39 /MaxListenersExceededWarning.*AbortSignal/, 40 /MaxListenersExceededWarning.*EventTarget/, 41] 42 43function isInternalWarning(warning: Error): boolean { 44 const warningStr = `${warning.name}: ${warning.message}` 45 return INTERNAL_WARNINGS.some(pattern => pattern.test(warningStr)) 46} 47 48// Store reference to our warning handler so we can detect if it's already installed 49let warningHandler: ((warning: Error) => void) | null = null 50 51// For testing only - allows resetting the warning handler state 52export function resetWarningHandler(): void { 53 if (warningHandler) { 54 process.removeListener('warning', warningHandler) 55 } 56 warningHandler = null 57 warningCounts.clear() 58} 59 60export function initializeWarningHandler(): void { 61 // Only set up handler once - check if our handler is already installed 62 const currentListeners = process.listeners('warning') 63 if (warningHandler && currentListeners.includes(warningHandler)) { 64 return 65 } 66 67 // For external users, remove default Node.js handler to suppress stderr output 68 // For internal users, only keep default warnings for development builds 69 // Check development mode directly to avoid async call in init 70 // This preserves the same logic as getCurrentInstallationType() without async 71 const isDevelopment = 72 process.env.NODE_ENV === 'development' || isRunningFromBuildDirectory() 73 if (!isDevelopment) { 74 process.removeAllListeners('warning') 75 } 76 77 // Create and store our warning handler 78 warningHandler = (warning: Error) => { 79 try { 80 const warningKey = `${warning.name}: ${warning.message.slice(0, 50)}` 81 const count = warningCounts.get(warningKey) || 0 82 83 // Bound the map to prevent unbounded memory growth from unique warning keys. 84 // Once the cap is reached, new unique keys are not tracked — their 85 // occurrence_count will always be reported as 1 in analytics. 86 if ( 87 warningCounts.has(warningKey) || 88 warningCounts.size < MAX_WARNING_KEYS 89 ) { 90 warningCounts.set(warningKey, count + 1) 91 } 92 93 const isInternal = isInternalWarning(warning) 94 95 // Always log to Statsig for monitoring 96 // Include full details for ant users only, since they may contain code or filepaths 97 logEvent('tengu_node_warning', { 98 is_internal: isInternal ? 1 : 0, 99 occurrence_count: count + 1, 100 classname: 101 warning.name as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, 102 ...(process.env.USER_TYPE === 'ant' && { 103 message: 104 warning.message as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, 105 }), 106 }) 107 108 // In debug mode, show all warnings with context 109 if (isEnvTruthy(process.env.CLAUDE_DEBUG)) { 110 const prefix = isInternal ? '[Internal Warning]' : '[Warning]' 111 logForDebugging(`${prefix} ${warning.toString()}`, { level: 'warn' }) 112 } 113 // Hide all warnings from users - they are only logged to Statsig for monitoring 114 } catch { 115 // Fail silently - we don't want the warning handler to cause issues 116 } 117 } 118 119 // Install the warning handler 120 process.on('warning', warningHandler) 121}