Chain functions, generators, Node streams, and Web streams into a pipeline with backpressure support.
JavaScript
100
433 commits
updated Oct 6, 2026
stream-chain processes streams of objects — records, not raw text or bytes. It is especially designed for huge streams: multi-gigabyte database dumps, append-only logs, message-queue and other live feeds generated continuously and far too large to hold in memory. You handle one record at a time while the library keeps memory flat, propagating backpressure from end to end.
Each stage of the pipeline is an ordinary function. stream-chain wires your functions — alongside any Node or Web streams you already use — into a single Duplex stream, so the whole pipeline behaves like one stream you can compose further.
Why it might be for you:
chain.none to drop it. The model goes further — a stage can emit any number of values per input, from zero to many. A generator function does this naturally by yielding; a plain function can return chain.many([...]) instead, but that holds every value in memory, so generators are the leaner choice for fan-out.async functions, generators, async generators, and real streams in one chain — on whichever substrate you want: Node streams (the default), native Web Streams (/web), or substrate-free async iterables (/core).Read a huge JSONL dump, transform each record, write the result back out — record by record, in constant memory:
import chain from 'stream-chain';
import parseFile from 'stream-chain/jsonl/file/parser.js';
import stringerToFile from 'stream-chain/jsonl/file/stringer.js';
const pipeline = chain([
parseFile(), // read line-by-line -> {key, value} per record
({value}) => (value.active ? value : chain.none), // drop inactive records
async user => ({...user, plan: await lookupPlan(user.id)}), // enrich asynchronously
stringerToFile('active-users.jsonl') // write results back out
]);
pipeline.on('finish', () => console.log('done'));
pipeline.end('users.jsonl'); // a 10 TB file is fine
The middle stages are plain functions; stream-chain handles the streaming, backpressure, and file handles. The source and sink don't have to be JSONL — any object stream works: a database cursor, an HTTP response, stream-json assembling tokens into objects, your own generator. JSONL just happens to be the most common on-disk format for object streams, so it ships in the box. See Intro by examples for more.
npm i --save stream-chain
Duplex pipeline.chain(), and the safe default for unbounded pipelines.Duplex.take, skip, …), reduce (fold, scan, …), stream adapters (readableFrom, stream pullers), and more. See utils.parser() / stringer(), Node and Web wrappers, fused local-file edges (parseFile() / stringerToFile()), and per-line error handling via errorIndicator. See jsonl.stream-chain / stream-chain/node (default Node streams), stream-chain/web (native Web Streams, browser-safe), stream-chain/core (substrate-free async iterables).Full documentation is in the wiki — browse the index, or search it by name.
BSD-3-Clause
fun() keeps overlapping calls' outputs apart (thx oss-security-shopify), lines() handles a CRLF split across chunks, asyncBlockWriter refuses calls after a failed write instead of truncating its file.asyncBlockWriter releases its FileHandle when its own write fails, instead of leaking it.pipe() propagates the original error intact.AggregateError.stream-chain/node/jsonl & stream-chain/web/jsonl carry .asStream / .asWebStream. Bugfix: removed checkedParse(), mistakenly exposed in 4.2.0.parseFile(), stringerToFile() + errorIndicator option. Bugfix: now /core chain passes strings as a single value.readableWebStreamFrom(), reduceWebStream(), parserWebStream(), stringerWebStream() + dataSource() + minor bugfix.fixUtf8Stream() now works on browsers./node (default), /web (native Web Streams), /core (substrate-free). New asWebStream() adapter with per-item backpressure. See the Migration guide.ChainOutput<W, R> propagates R to events and methods (thx Scover). Updated deps.fun() (~2.5× faster for sync pipelines, now returns Many | Promise<Many>). Sync fast path in gen() (~1.6× faster). Documented null/undefined handling differences. Wiki: renamed V2 files for Windows compatibility.finish event not firing after stop. Web stream detection uses duck-typing instead of instanceof (supports non-standard web streams) (thx Alex Yang). Performance: unified fast path in asStream().combineMany() and combineManyMut(). Fixed readableFrom() unhandled rejection bug. Improved TS typings, docs, and package.json metadata.The full release notes are in the wiki: Release notes.
Overall
Maintained
Code review
npm
Latest release
Releases this year
3,911 followers · starred May 2022
70 followers · starred Jan 2025
Chain functions, generators, Node streams, and Web streams into a pipeline with backpressure support.
JavaScript
100
433 commits
updated Oct 6, 2026
stream-chain processes streams of objects — records, not raw text or bytes. It is especially designed for huge streams: multi-gigabyte database dumps, append-only logs, message-queue and other live feeds generated continuously and far too large to hold in memory. You handle one record at a time while the library keeps memory flat, propagating backpressure from end to end.
Each stage of the pipeline is an ordinary function. stream-chain wires your functions — alongside any Node or Web streams you already use — into a single Duplex stream, so the whole pipeline behaves like one stream you can compose further.
Why it might be for you:
chain.none to drop it. The model goes further — a stage can emit any number of values per input, from zero to many. A generator function does this naturally by yielding; a plain function can return chain.many([...]) instead, but that holds every value in memory, so generators are the leaner choice for fan-out.async functions, generators, async generators, and real streams in one chain — on whichever substrate you want: Node streams (the default), native Web Streams (/web), or substrate-free async iterables (/core).Read a huge JSONL dump, transform each record, write the result back out — record by record, in constant memory:
import chain from 'stream-chain';
import parseFile from 'stream-chain/jsonl/file/parser.js';
import stringerToFile from 'stream-chain/jsonl/file/stringer.js';
const pipeline = chain([
parseFile(), // read line-by-line -> {key, value} per record
({value}) => (value.active ? value : chain.none), // drop inactive records
async user => ({...user, plan: await lookupPlan(user.id)}), // enrich asynchronously
stringerToFile('active-users.jsonl') // write results back out
]);
pipeline.on('finish', () => console.log('done'));
pipeline.end('users.jsonl'); // a 10 TB file is fine
The middle stages are plain functions; stream-chain handles the streaming, backpressure, and file handles. The source and sink don't have to be JSONL — any object stream works: a database cursor, an HTTP response, stream-json assembling tokens into objects, your own generator. JSONL just happens to be the most common on-disk format for object streams, so it ships in the box. See Intro by examples for more.
npm i --save stream-chain
Duplex pipeline.chain(), and the safe default for unbounded pipelines.Duplex.take, skip, …), reduce (fold, scan, …), stream adapters (readableFrom, stream pullers), and more. See utils.parser() / stringer(), Node and Web wrappers, fused local-file edges (parseFile() / stringerToFile()), and per-line error handling via errorIndicator. See jsonl.stream-chain / stream-chain/node (default Node streams), stream-chain/web (native Web Streams, browser-safe), stream-chain/core (substrate-free async iterables).Full documentation is in the wiki — browse the index, or search it by name.
BSD-3-Clause
fun() keeps overlapping calls' outputs apart (thx oss-security-shopify), lines() handles a CRLF split across chunks, asyncBlockWriter refuses calls after a failed write instead of truncating its file.asyncBlockWriter releases its FileHandle when its own write fails, instead of leaking it.pipe() propagates the original error intact.AggregateError.stream-chain/node/jsonl & stream-chain/web/jsonl carry .asStream / .asWebStream. Bugfix: removed checkedParse(), mistakenly exposed in 4.2.0.parseFile(), stringerToFile() + errorIndicator option. Bugfix: now /core chain passes strings as a single value.readableWebStreamFrom(), reduceWebStream(), parserWebStream(), stringerWebStream() + dataSource() + minor bugfix.fixUtf8Stream() now works on browsers./node (default), /web (native Web Streams), /core (substrate-free). New asWebStream() adapter with per-item backpressure. See the Migration guide.ChainOutput<W, R> propagates R to events and methods (thx Scover). Updated deps.fun() (~2.5× faster for sync pipelines, now returns Many | Promise<Many>). Sync fast path in gen() (~1.6× faster). Documented null/undefined handling differences. Wiki: renamed V2 files for Windows compatibility.finish event not firing after stop. Web stream detection uses duck-typing instead of instanceof (supports non-standard web streams) (thx Alex Yang). Performance: unified fast path in asStream().combineMany() and combineManyMut(). Fixed readableFrom() unhandled rejection bug. Improved TS typings, docs, and package.json metadata.The full release notes are in the wiki: Release notes.
Overall
Maintained
Code review
npm
Latest release
Releases this year
3,911 followers · starred May 2022
70 followers · starred Jan 2025