Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
9.1 kB · 286 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287import { feature } from 'bun:bundle'import type { WriteFileOptions } from 'fs'import { closeSync, writeFileSync as fsWriteFileSync, fsyncSync, openSync,} from 'fs'// biome-ignore lint: This file IS the cloneDeep wrapper - it must import the originalimport lodashCloneDeep from 'lodash-es/cloneDeep.js'import { addSlowOperation } from '../bootstrap/state.js'import { logForDebugging } from './debug.js'
// Extended WriteFileOptions to include 'flush' which is available in Node.js 20.1.0+// but not yet in @types/nodetype WriteFileOptionsWithFlush = | WriteFileOptions | (WriteFileOptions & { flush?: boolean })
// --- Slow operation logging infrastructure ---
/** * Threshold in milliseconds for logging slow JSON/clone operations. * Operations taking longer than this will be logged for debugging. * - Override: set CLAUDE_CODE_SLOW_OPERATION_THRESHOLD_MS to a number * - Dev builds: 20ms (lower threshold for development) * - Ants: 300ms (enabled for all internal users) */const SLOW_OPERATION_THRESHOLD_MS = (() => { const envValue = process.env.CLAUDE_CODE_SLOW_OPERATION_THRESHOLD_MS if (envValue !== undefined) { const parsed = Number(envValue) if (!Number.isNaN(parsed) && parsed >= 0) { return parsed } } if (process.env.NODE_ENV === 'development') { return 20 } if (process.env.USER_TYPE === 'ant') { return 300 } return Infinity})()
// Re-export for callers that still need the threshold value directlyexport { SLOW_OPERATION_THRESHOLD_MS }
// Module-level re-entrancy guard. logForDebugging writes to a debug file via// appendFileSync, which goes through slowLogging again. Without this guard,// a slow appendFileSync → dispose → logForDebugging → appendFileSync → dispose → ...let isLogging = false
/** * Extract the first stack frame outside this file, so the DevBar warning * points at the actual caller instead of a useless `Object{N keys}`. * Only called when an operation was actually slow — never on the fast path. */export function callerFrame(stack: string | undefined): string { if (!stack) return '' for (const line of stack.split('\n')) { if (line.includes('slowOperations')) continue const m = line.match(/([^/\\]+?):(\d+):\d+\)?$/) if (m) return ` @ ${m[1]}:${m[2]}` } return ''}
/** * Builds a human-readable description from tagged template arguments. * Only called when an operation was actually slow — never on the fast path. * * args[0] = TemplateStringsArray, args[1..n] = interpolated values */function buildDescription(args: IArguments): string { const strings = args[0] as TemplateStringsArray let result = '' for (let i = 0; i < strings.length; i++) { result += strings[i] if (i + 1 < args.length) { const v = args[i + 1] if (Array.isArray(v)) { result += `Array[${(v as unknown[]).length}]` } else if (v !== null && typeof v === 'object') { result += `Object{${Object.keys(v as Record<string, unknown>).length} keys}` } else if (typeof v === 'string') { result += v.length > 80 ? `${v.slice(0, 80)}…` : v } else { result += String(v) } } } return result}
class AntSlowLogger { startTime: number args: IArguments err: Error
constructor(args: IArguments) { this.startTime = performance.now() this.args = args // V8/JSC capture the stack at construction but defer the expensive string // formatting until .stack is read — so this stays off the fast path. this.err = new Error() }
[Symbol.dispose](): void { const duration = performance.now() - this.startTime if (duration > SLOW_OPERATION_THRESHOLD_MS && !isLogging) { isLogging = true try { const description = buildDescription(this.args) + callerFrame(this.err.stack) logForDebugging( `[SLOW OPERATION DETECTED] ${description} (${duration.toFixed(1)}ms)`, ) addSlowOperation(description, duration) } finally { isLogging = false } } }}
const NOOP_LOGGER: Disposable = { [Symbol.dispose]() {} }
// Must be regular functions (not arrows) to access `arguments`function slowLoggingAnt( _strings: TemplateStringsArray, ..._values: unknown[]): AntSlowLogger { // eslint-disable-next-line prefer-rest-params return new AntSlowLogger(arguments)}
function slowLoggingExternal(): Disposable { return NOOP_LOGGER}
/** * Tagged template for slow operation logging. * * In ANT builds: creates an AntSlowLogger that times the operation and logs * if it exceeds the threshold. Description is built lazily only when slow. * * In external builds: returns a singleton no-op disposable. Zero allocations, * zero timing. AntSlowLogger and buildDescription are dead-code-eliminated. * * @example * using _ = slowLogging`structuredClone(${value})` * const result = structuredClone(value) */export const slowLogging: { (strings: TemplateStringsArray, ...values: unknown[]): Disposable} = feature('SLOW_OPERATION_LOGGING') ? slowLoggingAnt : slowLoggingExternal
// --- Wrapped operations ---
/** * Wrapped JSON.stringify with slow operation logging. * Use this instead of JSON.stringify directly to detect performance issues. * * @example * import { jsonStringify } from './slowOperations.js' * const json = jsonStringify(data) * const prettyJson = jsonStringify(data, null, 2) */export function jsonStringify( value: unknown, replacer?: (this: unknown, key: string, value: unknown) => unknown, space?: string | number,): stringexport function jsonStringify( value: unknown, replacer?: (number | string)[] | null, space?: string | number,): stringexport function jsonStringify( value: unknown, replacer?: | ((this: unknown, key: string, value: unknown) => unknown) | (number | string)[] | null, space?: string | number,): string { using _ = slowLogging`JSON.stringify(${value})` return JSON.stringify( value, replacer as Parameters<typeof JSON.stringify>[1], space, )}
/** * Wrapped JSON.parse with slow operation logging. * Use this instead of JSON.parse directly to detect performance issues. * * @example * import { jsonParse } from './slowOperations.js' * const data = jsonParse(jsonString) */export const jsonParse: typeof JSON.parse = (text, reviver) => { using _ = slowLogging`JSON.parse(${text})` // V8 de-opts JSON.parse when a second argument is passed, even if undefined. // Branch explicitly so the common (no-reviver) path stays on the fast path. return typeof reviver === 'undefined' ? JSON.parse(text) : JSON.parse(text, reviver)}
/** * Wrapped structuredClone with slow operation logging. * Use this instead of structuredClone directly to detect performance issues. * * @example * import { clone } from './slowOperations.js' * const copy = clone(originalObject) */export function clone<T>(value: T, options?: StructuredSerializeOptions): T { using _ = slowLogging`structuredClone(${value})` return structuredClone(value, options)}
/** * Wrapped cloneDeep with slow operation logging. * Use this instead of lodash cloneDeep directly to detect performance issues. * * @example * import { cloneDeep } from './slowOperations.js' * const copy = cloneDeep(originalObject) */export function cloneDeep<T>(value: T): T { using _ = slowLogging`cloneDeep(${value})` return lodashCloneDeep(value)}
/** * Wrapper around fs.writeFileSync with slow operation logging. * Supports flush option to ensure data is written to disk before returning. * @param filePath The path to the file to write to * @param data The data to write (string or Buffer) * @param options Optional write options (encoding, mode, flag, flush) * @deprecated Use `fs.promises.writeFile` instead for non-blocking writes. * Sync file writes block the event loop and cause performance issues. */export function writeFileSync_DEPRECATED( filePath: string, data: string | NodeJS.ArrayBufferView, options?: WriteFileOptionsWithFlush,): void { using _ = slowLogging`fs.writeFileSync(${filePath}, ${data})`
// Check if flush is requested (for object-style options) const needsFlush = options !== null && typeof options === 'object' && 'flush' in options && options.flush === true
if (needsFlush) { // Manual flush: open file, write, fsync, close const encoding = typeof options === 'object' && 'encoding' in options ? options.encoding : undefined const mode = typeof options === 'object' && 'mode' in options ? options.mode : undefined let fd: number | undefined try { fd = openSync(filePath, 'w', mode) fsWriteFileSync(fd, data, { encoding: encoding ?? undefined }) fsyncSync(fd) } finally { if (fd !== undefined) { closeSync(fd) } } } else { // No flush needed, use standard writeFileSync fsWriteFileSync(filePath, data, options as WriteFileOptions) }}