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.reason
abortSignal.onabort
abortSignal.throwIfAborted()
Docs:
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.rs defines 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 handle onabort or 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), or abortSignal.throwIfAborted().
crates/perry-codegen/src/expr/static_method.rs special-cases only AbortSignal.timeout(...).
crates/perry-codegen/src/lower_call/options/abort.rs handles only controller.abort(reason?) and controller.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.ts already expects AbortSignal.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 throwIfAborted
AbortSignal timeout abort reason
AbortController 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) includes abort, timeout, and any.
Object.getOwnPropertyNames(AbortSignal.prototype) includes aborted, onabort, reason, and throwIfAborted.
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() throws abortSignal.reason when aborted.
- Abort listeners and
onabort are invoked once per signal with a Node-compatible abort event shape.
Summary
Node's global
AbortController/AbortSignalsurface 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:
Perry
origin/mainimplements enough fornew AbortController(),controller.abort(reason), simplecontroller.signal.addEventListener('abort', cb), and anAbortSignal.timeout()shape stub, but it does not complete the Node lifecycle/static helper behavior.Node behavior
Local Node probe with
node v25.9.0:Probe script:
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.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 AbortSignalAbortSignal.any OR throwIfAborted OR AbortSignal.abortNo existing AbortSignal lifecycle/static-helper tracker matched. This is separate from #2575, which tracks global DOM
Event/EventTargetconstructors anddispatchEvent; 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.onabortare invoked once per signal with a Node-compatible abort event shape.