[whatwg/streams] Proposal: `ReadableStream.withSafeResolvers()` (Issue #1359)

juner created an issue (whatwg/streams#1359)

### What problem are you trying to solve?

There is no ergonomic, standardized way to externally control a `ReadableStream` (i.e., enqueue, close, or error) with a safe interface.

Today:
- The only way is to keep a reference to `ReadableStreamDefaultController`.
- Calling `controller.enqueue()`, `controller.close()`, or `controller.error()` after the stream is closed/errored/canceled may throw.
- Developers sometimes implement ad-hoc helpers ("withResolvers"-style wrappers) to build "pushable streams", but this pattern is not standardized.

This makes event-based or imperative stream production awkward and error-prone.

### What solutions exist today?

1. **Manually handling `ReadableStreamDefaultController`**
   - Requires writing boilerplate.
   - Unsafe: calling methods after close/error/cancel throws.
   - Requires custom guarding logic.

2. **Ad-hoc helper utilities**
   - Non-standard.
   - Inconsistent behavior (especially around cancellation and error propagation).

3. **Async generator wrappers**
   - Cannot expose a truly push-based API.
   - Cannot support backpressure-compatible push-style enqueuing.


### How would you solve it?

Introduce a resolver-style API, similar to `Promise.withResolvers()`:

```ts
interface ReadableStream {
  static withSafeResolvers<T = unknown>(): {
    stream: ReadableStream<T>;
    enqueue(chunk: T): void;
    close(): void;
    error(reason: unknown): void;
  };
}
```

### Key behavior

- `enqueue(chunk)` — enqueues a chunk; ignored if closed/errored/canceled.
- `close()` — closes the stream safely; ignored after finalization.
- `error(reason)` — errors the stream; ignored after finalization.
- All operations after the stream is finalized (closed, errored, or canceled) are silently ignored.

This provides an ergonomic, safe way to create externally controlled *pushable streams*.

### Anything else?

A reference implementation exists:

- **NPM**: https://www.npmjs.com/package/readable-stream-with-safe-resolvers
- TypeScript implementation
- Fully safe for multiple calls to enqueue/close/error even after finalization
- Already used in real-world patterns such as event streams and imperatively controlled data sources

This proposal maintains compatibility with all existing stream semantics (including backpressure). The API surface is minimal and follows existing web-standard precedents such as `Promise.withResolvers()`.

-- 
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/streams/issues/1359
You are receiving this because you are subscribed to this thread.

Message ID: <whatwg/streams/issues/1359@github.com>

Received on Saturday, 15 November 2025 07:33:57 UTC