Skip to content

Commit fdbfc17

Browse files
committed
stream: improve handling of falsy errors in stream/iter
Signed-off-by: James M Snell <jasnell@gmail.com> Assisted-by: Opencode
1 parent 10720f7 commit fdbfc17

20 files changed

Lines changed: 1230 additions & 382 deletions

doc/api/stream_iter.md

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -463,14 +463,15 @@ if (result < 0) {
463463
}
464464
```
465465

466-
#### `writer.fail(reason)`
466+
#### `writer.fail([reason])`
467467

468468
* `reason` {any}
469469

470470
Put the writer into a terminal error state. If the writer is already closed
471471
or errored, this is a no-op. Unlike `write()` and `end()`, `fail()` is
472472
unconditionally synchronous because failing a writer is a pure state
473-
transition with no async work to perform.
473+
transition with no async work to perform. The reason is stored and propagated
474+
without modification. If omitted, the reason is `undefined`.
474475

475476
#### `writer[Symbol.asyncDispose]()`
476477

@@ -1337,9 +1338,10 @@ run().catch(console.error);
13371338

13381339
#### `broadcast.cancel([reason])`
13391340

1340-
* `reason` {Error}
1341+
* `reason` {any}
13411342

1342-
Cancel the broadcast. All consumers receive an error.
1343+
Cancel the broadcast. If `reason` is provided, all consumers reject with that
1344+
exact reason. If it is omitted, consumers complete normally.
13431345

13441346
#### `broadcast.consumerCount`
13451347

@@ -1447,9 +1449,10 @@ Create a {Share} from an existing source.
14471449

14481450
#### `share.cancel([reason])`
14491451

1450-
* `reason` {Error}
1452+
* `reason` {any}
14511453

1452-
Cancel the share. All consumers receive an error.
1454+
Cancel the share. If `reason` is provided, all consumers reject with that exact
1455+
reason. If it is omitted, consumers complete normally.
14531456

14541457
#### `share.consumerCount`
14551458

@@ -1521,9 +1524,10 @@ The number of chunks currently buffered.
15211524

15221525
#### `share.cancel([reason])`
15231526

1524-
* `reason` {Error}
1527+
* `reason` {any}
15251528

1526-
Cancel the share. All consumers receive an error.
1529+
Cancel the share. If `reason` is provided, all consumers throw that exact
1530+
reason. If it is omitted, consumers complete normally.
15271531

15281532
#### `share.consumerCount`
15291533

@@ -1647,6 +1651,11 @@ the synchronous Writer methods (`writeSync`, `writevSync`, `endSync`) always
16471651
return `false` or `-1`, deferring to the async path. The per-write
16481652
`options.signal` parameter from the Writer interface is also ignored.
16491653

1654+
If `writer.fail(reason)` receives a non-Error reason, the classic Writable is
1655+
destroyed with an `ERR_FALSY_VALUE_REJECTION` or `ERR_OPERATION_FAILED` error.
1656+
Its `reason` property contains the original value, which remains the Writer's
1657+
stored failure reason.
1658+
16501659
The result is cached per instance and backpressure policy -- calling
16511660
`fromWritable()` twice with the same stream and `backpressure` option returns
16521661
the same Writer.
@@ -1705,6 +1714,11 @@ Creates a byte-mode [`stream.Readable`][] from the `source`
17051714
(the native batch format used by the stream/iter API). Each `Uint8Array` in a
17061715
yielded batch is pushed as a separate chunk into the Readable.
17071716

1717+
Classic streams cannot represent arbitrary values as emitted errors. A
1718+
non-Error reason is wrapped in an `ERR_FALSY_VALUE_REJECTION` or
1719+
`ERR_OPERATION_FAILED` error whose `reason` property contains the original
1720+
value.
1721+
17081722
```mjs
17091723
import { createWriteStream } from 'node:fs';
17101724
import { from, pull, toReadable } from 'node:stream/iter';
@@ -1789,6 +1803,11 @@ sync path returns `false`. Similarly, `_final()` tries `endSync()`
17891803
before `end()`. When the sync path succeeds, the callback is deferred via
17901804
`queueMicrotask` to preserve the async resolution contract.
17911805

1806+
Classic stream callbacks cannot represent arbitrary values as errors. A
1807+
non-Error reason is wrapped in an `ERR_FALSY_VALUE_REJECTION` or
1808+
`ERR_OPERATION_FAILED` error before it is passed to the callback. The error's
1809+
`reason` property contains the original value.
1810+
17921811
The Writable's `highWaterMark` is set to `Number.MAX_SAFE_INTEGER` to
17931812
effectively disable its internal buffering, allowing the underlying Writer
17941813
to manage backpressure directly.

lib/internal/abort_controller.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,7 @@ module.exports = {
639639
AbortController,
640640
AbortSignal,
641641
ClonedAbortSignal,
642+
abortSignal,
642643
aborted,
643644
transferableAbortSignal,
644645
transferableAbortController,

0 commit comments

Comments
 (0)