π File detail
utils/abortController.ts
π§© .tsπ 100 linesπΎ 3,364 bytesπ text
β Back to All Filesπ― Use case
This file lives under βutils/β, which covers cross-cutting helpers (shell, tempfiles, settings, messages, process input, β¦). On the API surface it exposes createAbortController and createChildAbortController β mainly functions, hooks, or classes. Dependencies touch Node events.
Generated from folder role, exports, dependency roots, and inline comments β not hand-reviewed for every path.
π§ Inline summary
import { setMaxListeners } from 'events' /** * Default max listeners for standard operations */
π€ Exports (heuristic)
createAbortControllercreateChildAbortController
π External import roots
Package roots from from "β¦" (relative paths omitted).
events
π₯οΈ Source preview
import { setMaxListeners } from 'events'
/**
* Default max listeners for standard operations
*/
const DEFAULT_MAX_LISTENERS = 50
/**
* Creates an AbortController with proper event listener limits set.
* This prevents MaxListenersExceededWarning when multiple listeners
* are attached to the abort signal.
*
* @param maxListeners - Maximum number of listeners (default: 50)
* @returns AbortController with configured listener limit
*/
export function createAbortController(
maxListeners: number = DEFAULT_MAX_LISTENERS,
): AbortController {
const controller = new AbortController()
setMaxListeners(maxListeners, controller.signal)
return controller
}
/**
* Propagates abort from a parent to a weakly-referenced child controller.
* Both parent and child are weakly held β neither direction creates a
* strong reference that could prevent GC.
* Module-scope function avoids per-call closure allocation.
*/
function propagateAbort(
this: WeakRef<AbortController>,
weakChild: WeakRef<AbortController>,
): void {
const parent = this.deref()
weakChild.deref()?.abort(parent?.signal.reason)
}
/**
* Removes an abort handler from a weakly-referenced parent signal.
* Both parent and handler are weakly held β if either has been GC'd
* or the parent already aborted ({once: true}), this is a no-op.
* Module-scope function avoids per-call closure allocation.
*/
function removeAbortHandler(
this: WeakRef<AbortController>,
weakHandler: WeakRef<(...args: unknown[]) => void>,
): void {
const parent = this.deref()
const handler = weakHandler.deref()
if (parent && handler) {
parent.signal.removeEventListener('abort', handler)
}
}
/**
* Creates a child AbortController that aborts when its parent aborts.
* Aborting the child does NOT affect the parent.
*
* Memory-safe: Uses WeakRef so the parent doesn't retain abandoned children.
* If the child is dropped without being aborted, it can still be GC'd.
* When the child IS aborted, the parent listener is removed to prevent
* accumulation of dead handlers.
*
* @param parent - The parent AbortController
* @param maxListeners - Maximum number of listeners (default: 50)
* @returns Child AbortController
*/
export function createChildAbortController(
parent: AbortController,
maxListeners?: number,
): AbortController {
const child = createAbortController(maxListeners)
// Fast path: parent already aborted, no listener setup needed
if (parent.signal.aborted) {
child.abort(parent.signal.reason)
return child
}
// WeakRef prevents the parent from keeping an abandoned child alive.
// If all strong references to child are dropped without aborting it,
// the child can still be GC'd β the parent only holds a dead WeakRef.
const weakChild = new WeakRef(child)
const weakParent = new WeakRef(parent)
const handler = propagateAbort.bind(weakParent, weakChild)
parent.signal.addEventListener('abort', handler, { once: true })
// Auto-cleanup: remove parent listener when child is aborted (from any source).
// Both parent and handler are weakly held β if either has been GC'd or the
// parent already aborted ({once: true}), the cleanup is a harmless no-op.
child.signal.addEventListener(
'abort',
removeAbortHandler.bind(weakParent, new WeakRef(handler)),
{ once: true },
)
return child
}