Tracked by: #134 and #246 Related infrastructure: #330
Problem
The current global setTimeout is a test-shell compatibility shim that sleeps the executing agent, then queues a Promise reaction. It has no timer handle, clearTimeout, interval semantics, ref/unref ownership, or real event-loop scheduling. #330 needs an unrefed internal timer lane for AbortSignal.timeout, but silently treating that focused internal record as a complete host timer API would leave the public globals misleading.
Scope
- Replace the blocking shim with context-owned monotonic event-loop timer queues.
- Implement stable timeout/interval handles,
clearTimeout/clearInterval, ordering, cancellation, reentrancy, and overflow rules. - Define refed keepalive versus unrefed work explicitly for shell, C API, worker, and shared-realm execution.
- Run callbacks only on the owning JS event loop; background wakeups may publish readiness but must not execute JS.
- Integrate timer roots/finalizers with precise GC and realm teardown.
- Add fake-clock or bounded deterministic tests instead of timing-sensitive sleeps where practical.
Acceptance
- Calling
setTimeoutnever blocks the executing JavaScript agent. - Cancellation and intervals have web/Node-compatible observable ordering and exact-once ownership.
- Timer callbacks participate in normal task/microtask checkpoints and keepalive semantics.
- Focused tests and a descendant full CI matrix are green.
#330 may land its deliberately unrefed AbortSignal timer record first; this issue owns the complete public host timer API rather than expanding that ABI slice implicitly.