vite-compatible-readable-stream review
vite-compatible-readable-stream 3.6.1 is a fork of readable-stream 3.6 whose circular imports were rearranged for Rollup and Vite production builds. It carries the Node-style Readable, Writable, Duplex, Transform, PassThrough, `pipeline`, and `finished` APIs, and its 2-sentence README names `vite-plugin-shim-react-pdf` as the expected consumer. Our browser-target check still failed, so this is a narrow dependency workaround that must be tested in the exact Vite graph, not a general browser-stream recommendation.
vite-compatible-readable-stream 3.6.1 installed in 0.8 seconds and left 5 packages using 1 MB in our sandbox, but our browser bundle failed despite the fork's Vite-focused purpose. Install it only for a reproduced dependency-specific workaround that passes your complete production build; use native Web Streams or `node:stream` for new code.
We installed it
| Install | ✓ · 0.8s | 5 packages on disk · 1 MB |
| Import | ✓ | ESM import works · require() works · CommonJS package |
| Browser | n/a | could not be bundled for the browser (Node-only code, most likely) |
| Types | — | no TypeScript types found |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does vite-compatible-readable-stream install cleanly?
Yes. In a fresh container with an empty cache, npm install vite-compatible-readable-stream finished in 0.8s, leaving 5 packages and 1 MB on disk. npm audit reported no known vulnerabilities.
Can vite-compatible-readable-stream run in a browser?
Not directly: esbuild could not bundle it for the browser in our run, which normally means it depends on Node built-ins. Use it on the server, or find a browser-targeted alternative.
Does vite-compatible-readable-stream work with both ESM and CommonJS?
Yes. Both import 'vite-compatible-readable-stream' and require('vite-compatible-readable-stream') worked in Node 22 in our run. The package is published as CommonJS.
Does vite-compatible-readable-stream include TypeScript types?
No type declarations were found in our install, so TypeScript users need their own declarations.
vite-compatible-readable-stream or readable-stream: which should you use?
readable-stream: Use the maintained userland Node stream mirror when your bundler accepts its module graph. vite-compatible-readable-stream 3.6.1 installed in 0.8 seconds and left 5 packages using 1 MB in our sandbox, but our browser bundle failed despite the fork's Vite-focused purpose.
When should you not use vite-compatible-readable-stream?
You are writing new browser stream code. Native ReadableStream and WritableStream follow the web platform instead of emulating Node's older stream model.
Use it if
- A specific Vite production graph fails on readable-stream circular imports and that graph can be redirected to this fork.
- `vite-plugin-shim-react-pdf` or another fixed dependency path explicitly expects this package name and readable-stream 3 behavior.
- An unchangeable dependency imports Node stream classes in browser code and the complete application build proves this fork works there.
- Callback-based `pipeline` and `finished`, async iteration, and `Readable.from` from the readable-stream 3 era are the required contract.
- You are writing new browser stream code. Native ReadableStream and WritableStream follow the web platform instead of emulating Node's older stream model.
- Only Node is targeted. `node:stream` ships with the runtime and avoids 3 direct dependencies plus a compatibility fork.
- Current Node stream behavior or `node:stream/promises` is required. This fork preserves readable-stream 3.6-era callback APIs.
- First-party TypeScript declarations are mandatory. Our package inspection found no types in version 3.6.1.
- You assume the package name guarantees browser output. Our direct esbuild browser bundle failed, and neither npm nor the repository has shipped an update since April 2022.
Setup reality
We installed vite-compatible-readable-stream 3.6.1 in 0.8 seconds in a fresh Node 22 container. npm left 5 packages and 1 MB on disk. The package is 208 KB unpacked, declares 3 direct dependencies and 0 peers, has no bundled TypeScript declarations, and produced 0 audit findings. require() and ESM import both worked through CommonJS interoperability. Our direct esbuild browser build failed and emitted no bundle.
There are no credentials, native builds, or config files. The integration normally happens through a Vite alias or a plugin's dependency redirect rather than direct application imports. That makes the full production dependency graph the test target. The README promises fixed circular dependencies for Rollup and Vite, but our import * browser check did not compile. Confirm both development and production builds, then exercise actual PDF or stream behavior instead of treating installation success as browser proof.
The package depends on inherits, string_decoder, and util-deprecate. It exposes Node stream semantics, including byte/object high-water marks, flowing mode, backpressure, destruction, and callback completion. Native Web Streams are different objects with different readers, writers, and cancellation rules. This fork does not include modern conversion helpers or node:stream/promises. Attach error listeners before work begins and prefer pipeline() over a bare pipe chain when several stages must be destroyed together.
Version 3.6.1 declares Node 6+ but was published on April 21, 2022, the same day as the repository's latest push. The repository is unarchived with 0 open issues and pull requests, yet there is no release history after the fork landed. Remove the alias once the upstream dependency builds cleanly. Keeping a frozen compatibility layer after its triggering bug disappears leaves old stream behavior and an untyped package in the frontend graph.
Patterns
Load classes from CommonJS import-stream-classes
const { Readable, Writable, Transform, PassThrough } = require('vite-compatible-readable-stream');Version 3.6.1 is CommonJS without an exports map. ESM import worked in Node 22.23.2 through interoperability, but bundler behavior still needs testing.
Implement a finite readable source create-readable
const { Readable } = require('vite-compatible-readable-stream');
let sent = false;
const source = new Readable({
read() {
if (sent) return;
sent = true;
this.push('first\n');
this.push('second\n');
this.push(null);
},
});`_read` may be called more than once, so retain state to avoid duplicate chunks. `push(null)` marks the end.
Create a readable from an iterable read-from-iterable
const { Readable } = require('vite-compatible-readable-stream');
const source = Readable.from(['alpha', 'beta', 'gamma']);This fork includes `Readable.from`, with a browser-specific substituted module. Test it in the same production build that consumes the stream.
Consume UTF-8 chunks in flowing mode consume-data-events
source.setEncoding('utf8');
source.on('error', (error) => console.error(error));
source.on('data', (chunk) => console.log(chunk));
source.on('end', () => console.log('done'));Adding a data listener starts flowing mode. Register the error listener first so a source failure does not become uncaught.
Iterate chunks asynchronously consume-async-iterator
for await (const chunk of source) {
console.log(chunk.toString());
}The runtime needs `Symbol.asyncIterator`. Exiting the loop early destroys the readable under readable-stream 3 behavior.
Apply writable backpressure create-writable
const { Writable } = require('vite-compatible-readable-stream');
const sink = new Writable({
write(chunk, encoding, callback) {
saveChunk(chunk).then(() => callback(), callback);
},
});Invoke the callback exactly once. Holding it until asynchronous storage finishes is what prevents the source from outrunning this sink.
Uppercase each chunk create-transform
const { Transform } = require('vite-compatible-readable-stream');
const upper = new Transform({
transform(chunk, encoding, callback) {
callback(null, chunk.toString().toUpperCase());
},
});Pass a failure as the callback's first argument. An error thrown later in unrelated asynchronous work is not captured automatically.
Transform JavaScript objects use-object-mode
const { Transform } = require('vite-compatible-readable-stream');
const pickId = new Transform({
objectMode: true,
transform(user, encoding, callback) { callback(null, user.id); },
});Object mode counts buffered objects instead of bytes and uses its own high-water-mark default.
Pipe through one transform pipe-streams
source.pipe(upper).pipe(sink);A pipe chain carries backpressure, but a destination error may leave another stage alive. Use `pipeline()` when teardown across stages matters.
Connect stages with coordinated cleanup pipeline-with-callback
const { pipeline } = require('vite-compatible-readable-stream');
pipeline(source, upper, sink, (error) => {
if (error) console.error('pipeline failed', error);
else console.log('pipeline complete');
});This is the callback API. The package does not ship `node:stream/promises`, so leaving off the callback does not return the modern promise form.
Observe writable completion wait-for-finish
const { finished } = require('vite-compatible-readable-stream');
const stopWatching = finished(sink, (error) => {
if (error) console.error(error);
else console.log('sink closed');
});`finished` reports errors and premature close as well as success. Call the returned cleanup if the observer is no longer needed.
Destroy a stream with a cause destroy-on-error
source.on('error', (cause) => console.error(cause.message));
source.destroy(new Error('input rejected'));The passed error is emitted asynchronously. Without an error listener, it can terminate the process as an uncaught exception.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| readable-stream | npm | Use the maintained userland Node stream mirror when your bundler accepts its module graph. |
| stream-browserify | npm | Use it when a browser build expects the conventional Browserify replacement for Node's stream module. |
| web-streams-polyfill | npm | Use it for WHATWG Web Streams on a runtime that lacks the native classes. |
| vite-plugin-node-polyfills | npm | Use it when a Vite application needs several Node core shims and you accept that larger compatibility layer. |
More web frontend guides
postcss · react · react-dom · tailwindcss · htmlparser2 · tailwind-merge · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

