mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-01 07:33:12 +08:00
`writable.write(chunk, cb)` with `cb` passed as a function VALUE lost the completion's error: `lowerWriteCb` checked only the callback's syntax and lowered every other callback through `lowerExprExpecting(cbArg, funcOf([], VOID))`, which adapts a one-parameter function down to zero parameters. The native binary therefore printed `undefined` where Node prints `null`, or the write's own error (`ERR_STREAM_WRITE_AFTER_END` / `ERR_STREAM_DESTROYED` for late and destroyed writes). Lower such callbacks through a new `writable.writeEcb` libCall `(recv, mode, chunk, encoding, cb)`: a zero-parameter callback keeps the existing synchronous path, and a callback that declares the error (a dyn value, or `null | Error`) is wrapped by `scr_stream_ecb_wrap` with the compiler-built adapter that delivers the error the write completion carries. The stream unit publishes that error only in its `SCR_STREAM_WRITE_ERRORS` build, selected by the new `streamWriteErrors` runtime feature (rule: any `writable.writeEcb`). Every other build compiles `SCR_WCB_CALL` to the same plain call, so their objects are unchanged. The destruction path also matched Node: `afterWrite` calls `cb(null)` even for a stream destroyed after the write was queued (Node 24 `internal/streams/writable`), so the destroyed `SCR_ST_AFTER_WRITE` tick passes NULL rather than the stream's stored error. Corpus: `tests/corpus/writable-error-callbacks.ts` covers success, an erroring write, a write after end, and a write on a destroyed stream. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>