@yuhere/js-readline
v1.0.0
Published
readline modules to read lines from a readable stream.
Downloads
38
Readme
@yuhere/js-readline
ES-only TypeScript library for reading lines from web-platform input sources — Response, ReadableStream<Uint8Array>, Blob, and File.
- Forward & backward iteration
- Around mode — surrounding-line context for editors/viewers
- Abort control — cancel mid-stream
- Node.js & browser — single ESM bundle
Installation
npm install @yuhere/js-readlineQuick start
Forward (top → bottom)
import { readline_fw } from "@yuhere/js-readline";
const [ctrl, lines] = readline_fw(new Blob(["hello\nworld"]));
for await (const { line, lineNo } of lines) {
console.log(lineNo, line); // 1 hello, 2 world
}Backward (bottom → top)
import { readline_bw } from "@yuhere/js-readline";
const [ctrl, lines] = readline_bw(new Blob(["first\nsecond\nthird"]));
for await (const { line, lineNo } of lines) {
console.log(lineNo, line); // -1 third, -2 second, -3 first
}Around mode (with context)
import { around_lines_fw } from "@yuhere/js-readline";
const [ctrl, around_lines, gen] = await around_lines_fw({
input: new Blob(["line1\nline2\nline3\nline4\nline5"]),
AROUND_LIMIT: 10,
});
for await (const { line, lineNo } of gen) {
const ctx = around_lines(-1, 1); // [prev, current, next]
console.log(lineNo, line, ctx);
}Streaming from fetch
const res = await fetch("https://example.com/data.ndjson");
const [ctrl, lines] = readline_fw(res);
for await (const { line } of lines) {
const data = JSON.parse(line);
if (data.done) ctrl.abort();
}vs Node.js built-in readline
Bottom line: Node.js
readlineis 3–10× faster (C++ implementation), but only works in Node.js, only reads forward, and doesn't provide byte offsets or context windows. Choose based on your needs.
Feature comparison
| | @yuhere/js-readline | Node.js readline |
|---|---|---|
| Runtime | Browser + Node.js | Node.js only |
| Direction | Forward & backward | Forward only |
| Around mode | ✓ sliding window context | — |
| Abort control | ✓ AbortController | ✓ close() |
| Byte locations | ✓ {start, end} per line | — |
| Input types | Response, ReadableStream, Blob, File | Node.js Readable streams |
| Performance | 60–500 MB/s (pure JS) | 150–4,700 MB/s (C++) |
| Lines returned | {line, lineNo, loc, size} | string only |
| Pause / resume | Via abort + re-create | ✓ built-in |
| Dependencies | Zero | Built into Node.js |
When to use each
Use @yuhere/js-readline when you need:
- Browser support (files, fetch responses, Blob APIs)
- Backward line reading (tail -r, log viewers)
- Around-mode context (code editors, grep -C, source viewers)
- Precise byte offsets for each line
- A consistent API across Node.js and the browser
Use Node.js readline when you need:
- Maximum throughput on large files (Node.js only)
- Pause/resume during iteration
- The standard, built-in solution with zero dependencies
Performance snapshot
10 MB file, 200 B/line, Stream input — npm run bench:vs-node
| | MB/s | lines/s | |---|---|---| | js-readline | ~141 MB/s | ~735 K | | Node.js readline | ~1,549 MB/s | ~8.1 M | | Ratio | 0.1× | 0.1× |
The gap comes from three JS-level costs Node.js avoids in C++:
TextDecoder.decode()called per line (JS→native overhead × N)Uint8Array.slice()allocates new memory per line (GC pressure)- Async generator
yieldper line + chunk boundary handling
Full benchmark suite: npm run bench
API
readline_fw(input, options?)
Read lines top to bottom. Returns [AbortController, AsyncGenerator].
| Param | Type | Description |
|---|---|---|
| input | Response \| ReadableStream<Uint8Array> \| Blob \| File | Source to read from |
| options.encoding | string | TextDecoder encoding (default "utf-8") |
| options.controller | AbortController | Reuse an existing controller |
| options.size | number | Known byte size (ignored for Blob/File; NaN when unknown) |
readline_bw(input, options?)
Read lines bottom to top. Only accepts Blob | File. Returns [AbortController, AsyncGenerator]. Line numbers are negative (-1 = last line).
| Param | Type | Description |
|---|---|---|
| input | Blob \| File | Source to read from |
| options.encoding | string | TextDecoder encoding (default "utf-8") |
| options.controller | AbortController | Reuse an existing controller |
around_lines_fw(options)
Forward around-mode. Returns Promise<[AbortController, AroundLinesFunc, AsyncGenerator]>.
| Option | Type | Default | Description |
|---|---|---|---|
| input | Response \| ReadableStream \| Blob \| File | — | Source to read from |
| AROUND_LIMIT | number | 30 | One-sided look-behind/ahead bound (clamped ≥ 0) |
| encoding | string | "utf-8" | TextDecoder encoding |
| size | number | NaN | Known byte size |
around_lines_bw(options)
Backward around-mode. Only accepts Blob | File. Same options as around_lines_fw (except size). Line numbers are negative.
around_lines(before?, after?)
The context-lookup function returned by around_lines_fw / around_lines_bw. Returns a string[] slice of buffered lines relative to the current iteration position.
| Call | Result |
|---|---|
| around_lines() | All buffered lines |
| around_lines(-1, 0) | [prev, current] |
| around_lines(0, 1) | [current, next] |
| around_lines(-1, 1) | [prev, current, next] |
| around_lines(0) | [current, ..., end] |
| around_lines(-1) | [prev, ..., end] |
Throws if after < before.
Yielded value — StreamReadlineValue
Each line is yielded as an object:
{
loc: Location; // {start: number, end: number} — byte range including newline
size: number; // total input bytes (NaN if unknown)
lineNo: number; // 1-based: positive forward, negative backward
line: string; // decoded text with trailing \r?\n removed
}Types
| Export | Description |
|---|---|
| StreamReadlineValue | Shape of each yielded line |
| StreamReadlineResult | [AbortController, AsyncGenerator] |
| Location | {start: number, end: number} byte range |
| ReadlineOptions | Shared options type (encoding, controller) |
| AroundLinesFunc | Signature of the around_lines() callback |
| AroundLinesResult | [AbortController, AroundLinesFunc, AsyncGenerator] |
| LinesAroundFwOptions | Options for around_lines_fw |
| LinesAroundBwOptions | Options for around_lines_bw |
License
MIT
