source dump of claude code
at main 99 lines 3.4 kB view raw
1import { setMaxListeners } from 'events' 2 3/** 4 * Default max listeners for standard operations 5 */ 6const DEFAULT_MAX_LISTENERS = 50 7 8/** 9 * Creates an AbortController with proper event listener limits set. 10 * This prevents MaxListenersExceededWarning when multiple listeners 11 * are attached to the abort signal. 12 * 13 * @param maxListeners - Maximum number of listeners (default: 50) 14 * @returns AbortController with configured listener limit 15 */ 16export function createAbortController( 17 maxListeners: number = DEFAULT_MAX_LISTENERS, 18): AbortController { 19 const controller = new AbortController() 20 setMaxListeners(maxListeners, controller.signal) 21 return controller 22} 23 24/** 25 * Propagates abort from a parent to a weakly-referenced child controller. 26 * Both parent and child are weakly held — neither direction creates a 27 * strong reference that could prevent GC. 28 * Module-scope function avoids per-call closure allocation. 29 */ 30function propagateAbort( 31 this: WeakRef<AbortController>, 32 weakChild: WeakRef<AbortController>, 33): void { 34 const parent = this.deref() 35 weakChild.deref()?.abort(parent?.signal.reason) 36} 37 38/** 39 * Removes an abort handler from a weakly-referenced parent signal. 40 * Both parent and handler are weakly held — if either has been GC'd 41 * or the parent already aborted ({once: true}), this is a no-op. 42 * Module-scope function avoids per-call closure allocation. 43 */ 44function removeAbortHandler( 45 this: WeakRef<AbortController>, 46 weakHandler: WeakRef<(...args: unknown[]) => void>, 47): void { 48 const parent = this.deref() 49 const handler = weakHandler.deref() 50 if (parent && handler) { 51 parent.signal.removeEventListener('abort', handler) 52 } 53} 54 55/** 56 * Creates a child AbortController that aborts when its parent aborts. 57 * Aborting the child does NOT affect the parent. 58 * 59 * Memory-safe: Uses WeakRef so the parent doesn't retain abandoned children. 60 * If the child is dropped without being aborted, it can still be GC'd. 61 * When the child IS aborted, the parent listener is removed to prevent 62 * accumulation of dead handlers. 63 * 64 * @param parent - The parent AbortController 65 * @param maxListeners - Maximum number of listeners (default: 50) 66 * @returns Child AbortController 67 */ 68export function createChildAbortController( 69 parent: AbortController, 70 maxListeners?: number, 71): AbortController { 72 const child = createAbortController(maxListeners) 73 74 // Fast path: parent already aborted, no listener setup needed 75 if (parent.signal.aborted) { 76 child.abort(parent.signal.reason) 77 return child 78 } 79 80 // WeakRef prevents the parent from keeping an abandoned child alive. 81 // If all strong references to child are dropped without aborting it, 82 // the child can still be GC'd — the parent only holds a dead WeakRef. 83 const weakChild = new WeakRef(child) 84 const weakParent = new WeakRef(parent) 85 const handler = propagateAbort.bind(weakParent, weakChild) 86 87 parent.signal.addEventListener('abort', handler, { once: true }) 88 89 // Auto-cleanup: remove parent listener when child is aborted (from any source). 90 // Both parent and handler are weakly held — if either has been GC'd or the 91 // parent already aborted ({once: true}), the cleanup is a harmless no-op. 92 child.signal.addEventListener( 93 'abort', 94 removeAbortHandler.bind(weakParent, new WeakRef(handler)), 95 { once: true }, 96 ) 97 98 return child 99}