Three findings, all real:
- The deadline stopped at the response headers. `fetch` resolves once
headers arrive, so a server that sent headers — or half a JSON
document — and then stalled left the caller's `response.json()`
hanging with nothing watching it. The body is now buffered under the
same race and the response rebuilt around it, preserving `url`,
`redirected` and `type`, which have no constructor option, and
passing `null` for a null-body status.
- The caller's signal was only forwarded, not raced. On the browser's
`createRemoteFetch` path the signal is dropped in flight, so an abort
left the promise pending for the whole budget rather than the outer
cancellation winning as documented. It is raced now, with the
caller's own `reason` preserved, and the listener removed in
`finally` since one caller signal outlives several requests.
- A `Request` carries its signal on the input, not in `init`, so always
passing our own `signal` in `init` silently overrode it. The caller
signal is now derived from the `Request` when `init` has no `signal`
key — and an explicit `null` there still wins, per the fetch spec.
Eight new tests cover each: the stalled body, caller cancellation
against a signal-ignoring fetch, an already-aborted signal, the
Request-embedded signal, the explicit-null override, listener removal,
the rebuilt response's metadata and a null-body status.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YFnM6RqB4nXqNysgUYmjmq
Signed-off-by: cliffhall <cliff@futurescale.com>