readline-pager 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -17
- package/dist/main.cjs +36 -17
- package/dist/main.d.cts +2 -2
- package/dist/main.d.mts +2 -2
- package/dist/main.mjs +34 -16
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -38,6 +38,7 @@ npm install readline-pager
|
|
|
38
38
|
|
|
39
39
|
```ts
|
|
40
40
|
import { createPager } from "readline-pager";
|
|
41
|
+
// const { createPager } = require("readline-pager");
|
|
41
42
|
|
|
42
43
|
const pager = createPager("./bigfile.txt");
|
|
43
44
|
|
|
@@ -51,8 +52,6 @@ for await (const page of pager) {
|
|
|
51
52
|
**Recommended for highest throughput:**
|
|
52
53
|
|
|
53
54
|
```ts
|
|
54
|
-
const pager = createPager("./bigfile.txt");
|
|
55
|
-
|
|
56
55
|
while (true) {
|
|
57
56
|
const page = await pager.next();
|
|
58
57
|
if (!page) break;
|
|
@@ -60,9 +59,7 @@ while (true) {
|
|
|
60
59
|
|
|
61
60
|
// or
|
|
62
61
|
let page;
|
|
63
|
-
while ((page = await pager.next()) !== null) {
|
|
64
|
-
// process page
|
|
65
|
-
}
|
|
62
|
+
while ((page = await pager.next()) !== null) {}
|
|
66
63
|
```
|
|
67
64
|
|
|
68
65
|
- `while + next()` is the fastest iteration method (avoids extra async-iterator overhead).
|
|
@@ -76,14 +73,14 @@ while ((page = await pager.next()) !== null) {
|
|
|
76
73
|
createPager(filepath, {
|
|
77
74
|
chunkSize?: number, // default: 64 * 1024 (64 KiB)
|
|
78
75
|
pageSize?: number, // default: 1_000
|
|
79
|
-
delimiter?: string,
|
|
76
|
+
delimiter?: string, // default: "\n"
|
|
80
77
|
prefetch?: number, // default: 1
|
|
81
78
|
backward?: boolean, // default: false
|
|
82
79
|
useWorker?: boolean, // default: false (forward only)
|
|
83
80
|
});
|
|
84
81
|
```
|
|
85
82
|
|
|
86
|
-
- `chunkSize
|
|
83
|
+
- `chunkSize` — number of bytes read per I/O operation.
|
|
87
84
|
- `pageSize` — number of lines per page.
|
|
88
85
|
- `delimiter` — line separator.
|
|
89
86
|
- `prefetch` — max number of pages buffered internally. Not required for typical use; tuning has little effect once the engine is optimized.
|
|
@@ -103,7 +100,7 @@ Returns the next page or `null` when finished. Empty lines are preserved.
|
|
|
103
100
|
- A completely empty file (`0` bytes) produces `[""]` on the first read.
|
|
104
101
|
- A file with multiple empty lines returns each line as an empty string (e.g., `["", ""]` for two empty lines). Node.js `readline` may emit fewer or no `line` events in these cases.
|
|
105
102
|
|
|
106
|
-
### `pager.close(): void
|
|
103
|
+
### `pager.close(): Promise<void>`
|
|
107
104
|
|
|
108
105
|
Stops reading and releases resources immediately. Safe to call at any time.
|
|
109
106
|
|
|
@@ -121,28 +118,46 @@ Run the included benchmark:
|
|
|
121
118
|
|
|
122
119
|
```bash
|
|
123
120
|
# default run
|
|
124
|
-
|
|
121
|
+
npm run benchmark
|
|
125
122
|
|
|
126
123
|
# or customize with args
|
|
127
124
|
node test/_benchmark.ts --lines=20000 --page-size=500 --backward
|
|
128
125
|
```
|
|
129
126
|
|
|
130
127
|
> Test setup: generated text files with uuid, run on a fast NVMe machine with default options; values are averages from multiple runs. Results are machine-dependent.
|
|
128
|
+
>
|
|
129
|
+
> The **Average Throughput (MB/s)** is computed for two strategies: reading files line by line and page by page.
|
|
130
|
+
>
|
|
131
|
+
> In addition to _Node_, the two other popular JavaScript runtimes were also tested with `readline-pager`.
|
|
132
|
+
|
|
133
|
+
### Line by line
|
|
134
|
+
|
|
135
|
+
| Runtime / Method | 1M lines (35 MB) | 10M lines (353 MB) | 100M lines (3,529 MB) | 1,000M lines (35,286 MB) |
|
|
136
|
+
| ---------------- | ---------------: | -----------------: | --------------------: | -----------------------: |
|
|
137
|
+
| Node — node:line | 369 | 435 | 455 | 455 |
|
|
138
|
+
| Deno — node:line | 203 | 230 | 230 | 229 |
|
|
139
|
+
| Deno — deno:line | 738 | 901 | 915 | 809 |
|
|
140
|
+
| Bun — node:line | 246 | 279 | 283 | 280 |
|
|
141
|
+
| Bun — bun:line | 938 | 1,540 | 1,668 | 1,315 |
|
|
142
|
+
|
|
143
|
+
### Page by page
|
|
131
144
|
|
|
132
|
-
|
|
|
133
|
-
|
|
|
134
|
-
|
|
|
135
|
-
|
|
|
136
|
-
|
|
|
145
|
+
| Runtime / Method | 1M lines (35 MB) | 10M lines (353 MB) | 100M lines (3,529 MB) | 1,000M lines (35,286 MB) |
|
|
146
|
+
| --------------------- | ---------------: | -----------------: | --------------------: | -----------------------: |
|
|
147
|
+
| Node — readline-pager | 1,053 | 1,311 | 1,278 | 936 |
|
|
148
|
+
| Deno — deno:page | 852 | 909 | 908 | 783 |
|
|
149
|
+
| Deno — readline-pager | 1,131 | 1,268 | 1,271 | 911 |
|
|
150
|
+
| Bun — bun:page | 411 | 440 | 449 | 428 |
|
|
151
|
+
| Bun — readline-pager | 827 | 1,021 | 1,040 | 804 |
|
|
137
152
|
|
|
138
|
-
**
|
|
153
|
+
**Runtime Environment:** Node.js v25.6.1 & Bun v1.3.9 & Deno 2.6.10
|
|
139
154
|
|
|
140
155
|
---
|
|
141
156
|
|
|
142
157
|
## 🛠 Development & Contributing
|
|
143
158
|
|
|
144
|
-
- Minimum supported Node.js: **v18.12
|
|
145
|
-
- Development/test environment: **Node v25.6
|
|
159
|
+
- Minimum supported Node.js: **v18.12 (lts/hydrogen)**.
|
|
160
|
+
- Development/test environment: **Node v25.6 & TypeScript v5.9**.
|
|
146
161
|
|
|
147
162
|
Run tests:
|
|
148
163
|
|
package/dist/main.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.
|
|
1
|
+
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
|
|
2
2
|
let node_fs_promises = require("node:fs/promises");
|
|
3
3
|
let node_worker_threads = require("node:worker_threads");
|
|
4
4
|
|
|
@@ -98,7 +98,10 @@ function createBackwardReader(filepath, options) {
|
|
|
98
98
|
closed = true;
|
|
99
99
|
done = true;
|
|
100
100
|
pageQueue.queue.length = 0;
|
|
101
|
-
if (fd)
|
|
101
|
+
if (fd) {
|
|
102
|
+
await fd.close();
|
|
103
|
+
fd = null;
|
|
104
|
+
}
|
|
102
105
|
}
|
|
103
106
|
return {
|
|
104
107
|
next,
|
|
@@ -113,10 +116,14 @@ function createBackwardReader(filepath, options) {
|
|
|
113
116
|
return lastLine;
|
|
114
117
|
},
|
|
115
118
|
async *[Symbol.asyncIterator]() {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
try {
|
|
120
|
+
while (true) {
|
|
121
|
+
const p = await next();
|
|
122
|
+
if (!p) break;
|
|
123
|
+
yield p;
|
|
124
|
+
}
|
|
125
|
+
} finally {
|
|
126
|
+
await close().catch(() => {});
|
|
120
127
|
}
|
|
121
128
|
}
|
|
122
129
|
};
|
|
@@ -187,7 +194,10 @@ function createForwardReader(filepath, options) {
|
|
|
187
194
|
closed = true;
|
|
188
195
|
done = true;
|
|
189
196
|
pageQueue.queue.length = 0;
|
|
190
|
-
if (fd)
|
|
197
|
+
if (fd) {
|
|
198
|
+
await fd.close();
|
|
199
|
+
fd = null;
|
|
200
|
+
}
|
|
191
201
|
}
|
|
192
202
|
return {
|
|
193
203
|
next,
|
|
@@ -202,10 +212,14 @@ function createForwardReader(filepath, options) {
|
|
|
202
212
|
return lastLine;
|
|
203
213
|
},
|
|
204
214
|
async *[Symbol.asyncIterator]() {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
215
|
+
try {
|
|
216
|
+
while (true) {
|
|
217
|
+
const p = await next();
|
|
218
|
+
if (!p) break;
|
|
219
|
+
yield p;
|
|
220
|
+
}
|
|
221
|
+
} finally {
|
|
222
|
+
await close();
|
|
209
223
|
}
|
|
210
224
|
}
|
|
211
225
|
};
|
|
@@ -247,7 +261,7 @@ function createWorkerReader(filepath, options) {
|
|
|
247
261
|
async function close() {
|
|
248
262
|
closed = true;
|
|
249
263
|
done = true;
|
|
250
|
-
worker.terminate();
|
|
264
|
+
await worker.terminate();
|
|
251
265
|
}
|
|
252
266
|
return {
|
|
253
267
|
next,
|
|
@@ -262,10 +276,14 @@ function createWorkerReader(filepath, options) {
|
|
|
262
276
|
return lastLine;
|
|
263
277
|
},
|
|
264
278
|
async *[Symbol.asyncIterator]() {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
279
|
+
try {
|
|
280
|
+
while (true) {
|
|
281
|
+
const p = await next();
|
|
282
|
+
if (!p) break;
|
|
283
|
+
yield p;
|
|
284
|
+
}
|
|
285
|
+
} finally {
|
|
286
|
+
await close();
|
|
269
287
|
}
|
|
270
288
|
}
|
|
271
289
|
};
|
|
@@ -298,4 +316,5 @@ function createPager(filepath, options = {}) {
|
|
|
298
316
|
}
|
|
299
317
|
|
|
300
318
|
//#endregion
|
|
301
|
-
exports.createPager = createPager;
|
|
319
|
+
exports.createPager = createPager;
|
|
320
|
+
exports.default = createPager;
|
package/dist/main.d.cts
CHANGED
|
@@ -11,7 +11,7 @@ interface PagerOptions extends Partial<ReaderOptions> {
|
|
|
11
11
|
}
|
|
12
12
|
interface Pager extends AsyncIterable<string[]> {
|
|
13
13
|
next(): Promise<string[] | null>;
|
|
14
|
-
close(): void
|
|
14
|
+
close(): Promise<void>;
|
|
15
15
|
readonly lineCount: number;
|
|
16
16
|
readonly firstLine: string | null;
|
|
17
17
|
readonly lastLine: string | null;
|
|
@@ -20,4 +20,4 @@ interface Pager extends AsyncIterable<string[]> {
|
|
|
20
20
|
//#region src/main.d.ts
|
|
21
21
|
declare function createPager(filepath: string, options?: PagerOptions): Pager;
|
|
22
22
|
//#endregion
|
|
23
|
-
export { Pager, PagerOptions, ReaderOptions, createPager };
|
|
23
|
+
export { Pager, PagerOptions, ReaderOptions, createPager, createPager as default };
|
package/dist/main.d.mts
CHANGED
|
@@ -11,7 +11,7 @@ interface PagerOptions extends Partial<ReaderOptions> {
|
|
|
11
11
|
}
|
|
12
12
|
interface Pager extends AsyncIterable<string[]> {
|
|
13
13
|
next(): Promise<string[] | null>;
|
|
14
|
-
close(): void
|
|
14
|
+
close(): Promise<void>;
|
|
15
15
|
readonly lineCount: number;
|
|
16
16
|
readonly firstLine: string | null;
|
|
17
17
|
readonly lastLine: string | null;
|
|
@@ -20,4 +20,4 @@ interface Pager extends AsyncIterable<string[]> {
|
|
|
20
20
|
//#region src/main.d.ts
|
|
21
21
|
declare function createPager(filepath: string, options?: PagerOptions): Pager;
|
|
22
22
|
//#endregion
|
|
23
|
-
export { Pager, PagerOptions, ReaderOptions, createPager };
|
|
23
|
+
export { Pager, PagerOptions, ReaderOptions, createPager, createPager as default };
|
package/dist/main.mjs
CHANGED
|
@@ -102,7 +102,10 @@ function createBackwardReader(filepath, options) {
|
|
|
102
102
|
closed = true;
|
|
103
103
|
done = true;
|
|
104
104
|
pageQueue.queue.length = 0;
|
|
105
|
-
if (fd)
|
|
105
|
+
if (fd) {
|
|
106
|
+
await fd.close();
|
|
107
|
+
fd = null;
|
|
108
|
+
}
|
|
106
109
|
}
|
|
107
110
|
return {
|
|
108
111
|
next,
|
|
@@ -117,10 +120,14 @@ function createBackwardReader(filepath, options) {
|
|
|
117
120
|
return lastLine;
|
|
118
121
|
},
|
|
119
122
|
async *[Symbol.asyncIterator]() {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
123
|
+
try {
|
|
124
|
+
while (true) {
|
|
125
|
+
const p = await next();
|
|
126
|
+
if (!p) break;
|
|
127
|
+
yield p;
|
|
128
|
+
}
|
|
129
|
+
} finally {
|
|
130
|
+
await close().catch(() => {});
|
|
124
131
|
}
|
|
125
132
|
}
|
|
126
133
|
};
|
|
@@ -191,7 +198,10 @@ function createForwardReader(filepath, options) {
|
|
|
191
198
|
closed = true;
|
|
192
199
|
done = true;
|
|
193
200
|
pageQueue.queue.length = 0;
|
|
194
|
-
if (fd)
|
|
201
|
+
if (fd) {
|
|
202
|
+
await fd.close();
|
|
203
|
+
fd = null;
|
|
204
|
+
}
|
|
195
205
|
}
|
|
196
206
|
return {
|
|
197
207
|
next,
|
|
@@ -206,10 +216,14 @@ function createForwardReader(filepath, options) {
|
|
|
206
216
|
return lastLine;
|
|
207
217
|
},
|
|
208
218
|
async *[Symbol.asyncIterator]() {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
219
|
+
try {
|
|
220
|
+
while (true) {
|
|
221
|
+
const p = await next();
|
|
222
|
+
if (!p) break;
|
|
223
|
+
yield p;
|
|
224
|
+
}
|
|
225
|
+
} finally {
|
|
226
|
+
await close();
|
|
213
227
|
}
|
|
214
228
|
}
|
|
215
229
|
};
|
|
@@ -251,7 +265,7 @@ function createWorkerReader(filepath, options) {
|
|
|
251
265
|
async function close() {
|
|
252
266
|
closed = true;
|
|
253
267
|
done = true;
|
|
254
|
-
worker.terminate();
|
|
268
|
+
await worker.terminate();
|
|
255
269
|
}
|
|
256
270
|
return {
|
|
257
271
|
next,
|
|
@@ -266,10 +280,14 @@ function createWorkerReader(filepath, options) {
|
|
|
266
280
|
return lastLine;
|
|
267
281
|
},
|
|
268
282
|
async *[Symbol.asyncIterator]() {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
283
|
+
try {
|
|
284
|
+
while (true) {
|
|
285
|
+
const p = await next();
|
|
286
|
+
if (!p) break;
|
|
287
|
+
yield p;
|
|
288
|
+
}
|
|
289
|
+
} finally {
|
|
290
|
+
await close();
|
|
273
291
|
}
|
|
274
292
|
}
|
|
275
293
|
};
|
|
@@ -302,4 +320,4 @@ function createPager(filepath, options = {}) {
|
|
|
302
320
|
}
|
|
303
321
|
|
|
304
322
|
//#endregion
|
|
305
|
-
export { createPager };
|
|
323
|
+
export { createPager, createPager as default };
|
package/package.json
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "readline-pager",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"build": "tsdown",
|
|
6
6
|
"pretest": "npm run build",
|
|
7
7
|
"test": "node --test --experimental-test-coverage test/**/*.test.ts",
|
|
8
|
-
"prepublishOnly": "npm run build && npm run test"
|
|
8
|
+
"prepublishOnly": "npm run build && npm run test",
|
|
9
|
+
"benchmark": "node test/_benchmark.ts; bun test/_benchmark.ts; deno --allow-write --allow-read test/_benchmark.ts"
|
|
9
10
|
},
|
|
10
11
|
"devDependencies": {
|
|
11
12
|
"@types/bun": "~1.3.9",
|
|
13
|
+
"@types/deno": "~2.5.0",
|
|
12
14
|
"@types/node": "~25.3.0",
|
|
13
15
|
"prettier": "~3.8.1",
|
|
14
16
|
"prettier-plugin-organize-imports": "~4.3.0",
|