Repository navigation
globals: complete AbortSignal static helpers and abort lifecycle #2582
Description
Summary
Node's global AbortController / AbortSignal surface is broader than Perry's current partial implementation. Current Node docs cover:
abortController.abort([reason])AbortSignal.abort([reason])AbortSignal.timeout(delay)AbortSignal.any(signals)abortSignal.reasonabortSignal.onabortabortSignal.throwIfAborted()
Docs:
- https://nodejs.org/api/globals.html#class-abortcontroller
- https://nodejs.org/api/globals.html#class-abortsignal
- https://nodejs.org/api/globals.html#static-method-abortsignalabortreason
- https://nodejs.org/api/globals.html#static-method-abortsignalanysignals
- https://nodejs.org/api/globals.html#abortsignalthrowifaborted
Perry origin/main implements enough for new AbortController(), controller.abort(reason), simple controller.signal.addEventListener('abort', cb), and an AbortSignal.timeout() shape stub, but it does not complete the Node lifecycle/static helper behavior.
Node behavior
Local Node probe with node v25.9.0:
AbortSignal static
abort,any,length,name,prototype,timeout
AbortSignal proto
aborted,constructor,onabort,reason,throwIfAborted
AbortController proto
abort,constructor,signal
initial false undefined
event reason boom
after true boom 1
throwIfAborted true boom
abort static true x
timeout initial false undefined
any initial false
any after true y
Probe script:
console.log('AbortSignal static');
console.log(Object.getOwnPropertyNames(AbortSignal).sort().join(','));
console.log('AbortSignal proto');
console.log(Object.getOwnPropertyNames(AbortSignal.prototype).sort().join(','));
console.log('AbortController proto');
console.log(Object.getOwnPropertyNames(AbortController.prototype).sort().join(','));
const ac = new AbortController();
console.log('initial', ac.signal.aborted, ac.signal.reason);
let seen = 0;
ac.signal.addEventListener('abort', () => {
seen++;
console.log('event reason', ac.signal.reason?.message || ac.signal.reason);
});
ac.abort(new Error('boom'));
console.log('after', ac.signal.aborted, ac.signal.reason?.message, seen);
try {
ac.signal.throwIfAborted();
} catch (err) {
console.log('throwIfAborted', err === ac.signal.reason, err.message);
}
const s1 = AbortSignal.abort('x');
console.log('abort static', s1.aborted, s1.reason);
const t = AbortSignal.timeout(5);
console.log('timeout initial', t.aborted, typeof t.reason);
const a = new AbortController();
const any = AbortSignal.any([a.signal]);
console.log('any initial', any.aborted);
a.abort('y');
console.log('any after', any.aborted, any.reason);Perry evidence
Source review against origin/main:
crates/perry-runtime/src/url/abort.rsdefines only three AbortSignal fields:aborted,reason, and_listeners.js_abort_controller_abort_reason()stores the reason and fires listeners, but listener firing calls callbacks with no Event object and does not handleonabortor listener options such as{ once: true }.js_abort_signal_timeout(_ms)explicitly documents the current limitation: it returns a signal that is initially not aborted and does not start a real timer.- There is no runtime implementation for
AbortSignal.abort(reason),AbortSignal.any(signals), orabortSignal.throwIfAborted(). crates/perry-codegen/src/expr/static_method.rsspecial-cases onlyAbortSignal.timeout(...).crates/perry-codegen/src/lower_call/options/abort.rshandles onlycontroller.abort(reason?)andcontroller.signal.addEventListener('abort', cb). Its static-timeout path is commented as skipped in the call-lowering path.test-parity/node-suite/stream/abort/signal-timeout.tsalready expectsAbortSignal.timeout(ms)to abort after the delay, but the runtime helper currently returns a never-aborted signal.
Duplicate check
Searched existing issues and PRs for:
AbortSignal.any AbortSignal.abort throwIfAbortedAbortSignal timeout abort reasonAbortController AbortSignal- PR search for
AbortSignal.any OR throwIfAborted OR AbortSignal.abort
No existing AbortSignal lifecycle/static-helper tracker matched. This is separate from #2575, which tracks global DOM Event / EventTarget constructors and dispatchEvent; this issue is specifically the AbortSignal lifecycle surface and static helper behavior.
Expected compatibility
Object.getOwnPropertyNames(AbortSignal)includesabort,timeout, andany.Object.getOwnPropertyNames(AbortSignal.prototype)includesaborted,onabort,reason, andthrowIfAborted.AbortSignal.abort(reason)returns an already-aborted signal with that reason.AbortSignal.timeout(delay)aborts after the delay with a Node-compatible timeout abort reason.AbortSignal.any(signals)aborts when the first input signal aborts and adopts that signal's reason.abortSignal.throwIfAborted()throwsabortSignal.reasonwhen aborted.- Abort listeners and
onabortare invoked once per signal with a Node-compatible abort event shape.
Activity
fix(globals): AbortSignal static helpers/lifecycle + navigator object (…