readline-pager 0.4.9 → 0.5.1

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 CHANGED
@@ -85,6 +85,7 @@ createPager(filepath, {
85
85
  prefetch?: number, // default: 8
86
86
  backward?: boolean, // default: false
87
87
  useWorker?: boolean, // default: false
88
+ tryNative?: boolean, // default: true
88
89
  });
89
90
  ```
90
91
 
@@ -94,6 +95,10 @@ createPager(filepath, {
94
95
  - `prefetch` — maximum number of pages buffered internally.
95
96
  - `backward` — read the file from end to start.
96
97
  - `useWorker` — offload reading to a worker thread (forward reading only).
98
+ - `tryNative` — attempts to use the native reader, falls back to the non-native version if it fails.
99
+
100
+ > **Note:**
101
+ > `createNativePager` supports only `pageSize`, `delimiter`, and `backward` options and does **not** support multi-character delimiters.
97
102
 
98
103
  ---
99
104
 
@@ -107,7 +112,11 @@ Returns `null` when the end of the file is reached.
107
112
 
108
113
  Empty lines are preserved.
109
114
 
110
- ---
115
+ > **Note:**
116
+ > Unlike Node.js `readline`, which may skip empty files or leading empty lines, `readline-pager` always returns all lines.
117
+ >
118
+ > - A completely empty file (`0` bytes) produces `[""]` on the first read.
119
+ > - A file containing multiple empty lines returns each line as an empty string.
111
120
 
112
121
  ### `pager.nextSync(): string[] | null`
113
122
 
@@ -115,30 +124,12 @@ Synchronous version of `pager.next()`.
115
124
 
116
125
  Returns the next page immediately or `null` when the end of the file is reached.
117
126
 
118
- ---
119
-
120
127
  ### `pager.close(): Promise<void>`
121
128
 
122
129
  Stops reading and releases resources asynchronously. Safe to call at any time.
123
130
 
124
131
  ---
125
132
 
126
- ### `createNativePager(filepath, options?): Pager`
127
-
128
- Creates a pager backed by the optional native C++ addon.
129
-
130
- If the native addon is not available for the current platform, this function throws.
131
-
132
- ---
133
-
134
- > **Note:**
135
- > Unlike Node.js `readline`, which may skip empty files or leading empty lines, `readline-pager` always returns all lines.
136
- >
137
- > - A completely empty file (`0` bytes) produces `[""]` on the first read.
138
- > - A file containing multiple empty lines returns each line as an empty string.
139
-
140
- ---
141
-
142
133
  ## 📊 Benchmark
143
134
 
144
135
  Run the benchmark locally:
package/dist/main.cjs CHANGED
@@ -532,18 +532,35 @@ function createWorkerReader(filepath, options) {
532
532
  //#endregion
533
533
  //#region src/main.ts
534
534
  function createPager(filepath, options = {}) {
535
- const { chunkSize = 64 * 1024, pageSize = 1e3, delimiter = "\n", prefetch = 8, backward = false, useWorker = false } = options;
535
+ const { chunkSize = 64 * 1024, pageSize = 1e3, delimiter = "\n", prefetch = 8, backward = false, useWorker = false, tryNative = true } = options;
536
536
  if (!filepath) throw new Error("filepath required");
537
537
  if (pageSize < 1) throw new RangeError("pageSize must be >= 1");
538
538
  if (prefetch < 1) throw new RangeError("prefetch must be >= 1");
539
- if (backward && useWorker) throw new Error("backward not supported with useWorker");
539
+ if (useWorker) {
540
+ if (backward) throw new Error("backward not supported with useWorker");
541
+ if (tryNative) throw new Error("tryNative not supported with useWorker");
542
+ }
543
+ if (tryNative) {
544
+ if (delimiter.length !== 1) throw new RangeError("native reader only supports single-character delimiters");
545
+ }
540
546
  const _options = {
541
547
  chunkSize,
542
548
  pageSize,
543
549
  prefetch,
544
550
  delimiter
545
551
  };
546
- const reader = useWorker ? createWorkerReader(filepath, _options) : backward ? createBackwardReader(filepath, _options) : createForwardReader(filepath, _options);
552
+ let nativeReader;
553
+ if (tryNative) {
554
+ const _nativeOptions = {
555
+ pageSize,
556
+ delimiter,
557
+ backward
558
+ };
559
+ try {
560
+ nativeReader = require_native.createNativePager(filepath, _nativeOptions);
561
+ } catch {}
562
+ }
563
+ const reader = tryNative && nativeReader ? nativeReader : useWorker ? createWorkerReader(filepath, _options) : backward ? createBackwardReader(filepath, _options) : createForwardReader(filepath, _options);
547
564
  if (process.env.TEST_CLEANUPS) {
548
565
  globalThis.__test_cleanups__ ??= [];
549
566
  globalThis.__test_cleanups__.push(reader.close);
package/dist/main.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- import { i as ReaderOptions, n as Pager, r as PagerOptions, t as createNativePager } from "./native-BNwCco1j.cjs";
1
+ import { a as NativeReaderOptions, c as ReaderOptions, i as NativeAddon, n as AddonData, o as Pager, r as AddonFD, s as PagerOptions, t as createNativePager } from "./native-46pCT8Rc.cjs";
2
2
 
3
3
  //#region src/main.d.ts
4
4
  declare function createPager(filepath: string, options?: PagerOptions): Pager;
5
5
  //#endregion
6
- export { Pager, PagerOptions, ReaderOptions, createNativePager, createPager, createPager as default };
6
+ export { AddonData, AddonFD, NativeAddon, NativeReaderOptions, Pager, PagerOptions, ReaderOptions, createNativePager, createPager, createPager as default };
package/dist/main.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { i as ReaderOptions, n as Pager, r as PagerOptions, t as createNativePager } from "./native-BWytCdQz.mjs";
1
+ import { a as NativeReaderOptions, c as ReaderOptions, i as NativeAddon, n as AddonData, o as Pager, r as AddonFD, s as PagerOptions, t as createNativePager } from "./native-DeBXdY3U.mjs";
2
2
 
3
3
  //#region src/main.d.ts
4
4
  declare function createPager(filepath: string, options?: PagerOptions): Pager;
5
5
  //#endregion
6
- export { Pager, PagerOptions, ReaderOptions, createNativePager, createPager, createPager as default };
6
+ export { AddonData, AddonFD, NativeAddon, NativeReaderOptions, Pager, PagerOptions, ReaderOptions, createNativePager, createPager, createPager as default };
package/dist/main.mjs CHANGED
@@ -532,18 +532,35 @@ function createWorkerReader(filepath, options) {
532
532
  //#endregion
533
533
  //#region src/main.ts
534
534
  function createPager(filepath, options = {}) {
535
- const { chunkSize = 64 * 1024, pageSize = 1e3, delimiter = "\n", prefetch = 8, backward = false, useWorker = false } = options;
535
+ const { chunkSize = 64 * 1024, pageSize = 1e3, delimiter = "\n", prefetch = 8, backward = false, useWorker = false, tryNative = true } = options;
536
536
  if (!filepath) throw new Error("filepath required");
537
537
  if (pageSize < 1) throw new RangeError("pageSize must be >= 1");
538
538
  if (prefetch < 1) throw new RangeError("prefetch must be >= 1");
539
- if (backward && useWorker) throw new Error("backward not supported with useWorker");
539
+ if (useWorker) {
540
+ if (backward) throw new Error("backward not supported with useWorker");
541
+ if (tryNative) throw new Error("tryNative not supported with useWorker");
542
+ }
543
+ if (tryNative) {
544
+ if (delimiter.length !== 1) throw new RangeError("native reader only supports single-character delimiters");
545
+ }
540
546
  const _options = {
541
547
  chunkSize,
542
548
  pageSize,
543
549
  prefetch,
544
550
  delimiter
545
551
  };
546
- const reader = useWorker ? createWorkerReader(filepath, _options) : backward ? createBackwardReader(filepath, _options) : createForwardReader(filepath, _options);
552
+ let nativeReader;
553
+ if (tryNative) {
554
+ const _nativeOptions = {
555
+ pageSize,
556
+ delimiter,
557
+ backward
558
+ };
559
+ try {
560
+ nativeReader = createNativePager(filepath, _nativeOptions);
561
+ } catch {}
562
+ }
563
+ const reader = tryNative && nativeReader ? nativeReader : useWorker ? createWorkerReader(filepath, _options) : backward ? createBackwardReader(filepath, _options) : createForwardReader(filepath, _options);
547
564
  if (process.env.TEST_CLEANUPS) {
548
565
  globalThis.__test_cleanups__ ??= [];
549
566
  globalThis.__test_cleanups__.push(reader.close);
@@ -0,0 +1,35 @@
1
+ //#region src/types.d.ts
2
+ interface ReaderOptions {
3
+ chunkSize: number;
4
+ pageSize: number;
5
+ delimiter: string;
6
+ prefetch: number;
7
+ }
8
+ interface PagerOptions extends Partial<ReaderOptions> {
9
+ backward?: boolean;
10
+ useWorker?: boolean;
11
+ tryNative?: boolean;
12
+ }
13
+ interface Pager extends AsyncIterable<string[]>, Iterable<string[]> {
14
+ next(): Promise<string[] | null>;
15
+ nextSync(): string[] | null;
16
+ close(): Promise<void>;
17
+ }
18
+ interface NativeReaderOptions {
19
+ pageSize: number;
20
+ delimiter: string;
21
+ backward: boolean;
22
+ }
23
+ type AddonFD = object | null;
24
+ type AddonData = Buffer | null;
25
+ interface NativeAddon {
26
+ open: (filepath: string, pageSize: number, delimiter: string, backward: boolean) => AddonFD;
27
+ next: (fd: AddonFD) => Promise<AddonData>;
28
+ nextSync: (fd: AddonFD) => AddonData;
29
+ close: (fd: AddonFD) => Promise<void>;
30
+ }
31
+ //#endregion
32
+ //#region src/native.d.ts
33
+ declare function createNativePager(filepath: string, options?: Partial<NativeReaderOptions>): Pager;
34
+ //#endregion
35
+ export { NativeReaderOptions as a, ReaderOptions as c, NativeAddon as i, AddonData as n, Pager as o, AddonFD as r, PagerOptions as s, createNativePager as t };
@@ -0,0 +1,35 @@
1
+ //#region src/types.d.ts
2
+ interface ReaderOptions {
3
+ chunkSize: number;
4
+ pageSize: number;
5
+ delimiter: string;
6
+ prefetch: number;
7
+ }
8
+ interface PagerOptions extends Partial<ReaderOptions> {
9
+ backward?: boolean;
10
+ useWorker?: boolean;
11
+ tryNative?: boolean;
12
+ }
13
+ interface Pager extends AsyncIterable<string[]>, Iterable<string[]> {
14
+ next(): Promise<string[] | null>;
15
+ nextSync(): string[] | null;
16
+ close(): Promise<void>;
17
+ }
18
+ interface NativeReaderOptions {
19
+ pageSize: number;
20
+ delimiter: string;
21
+ backward: boolean;
22
+ }
23
+ type AddonFD = object | null;
24
+ type AddonData = Buffer | null;
25
+ interface NativeAddon {
26
+ open: (filepath: string, pageSize: number, delimiter: string, backward: boolean) => AddonFD;
27
+ next: (fd: AddonFD) => Promise<AddonData>;
28
+ nextSync: (fd: AddonFD) => AddonData;
29
+ close: (fd: AddonFD) => Promise<void>;
30
+ }
31
+ //#endregion
32
+ //#region src/native.d.ts
33
+ declare function createNativePager(filepath: string, options?: Partial<NativeReaderOptions>): Pager;
34
+ //#endregion
35
+ export { NativeReaderOptions as a, ReaderOptions as c, NativeAddon as i, AddonData as n, Pager as o, AddonFD as r, PagerOptions as s, createNativePager as t };
package/dist/native.cjs CHANGED
@@ -29,29 +29,33 @@ function loadNativeAddon() {
29
29
  throw new Error(UNAVAILABLE);
30
30
  }
31
31
  }
32
- function createNativePager(filepath, { pageSize = 1e3, delimiter = "\n" } = {}) {
33
- const pagerNative = loadNativeAddon();
32
+ function createNativePager(filepath, options) {
33
+ const { pageSize = 1e3, delimiter = "\n", backward = false } = options ?? {};
34
+ if (!filepath) throw new Error("filepath required");
35
+ if (pageSize < 1) throw new RangeError("pageSize must be >= 1");
36
+ if (delimiter.length !== 1) throw new RangeError("native reader only supports single-character delimiters");
37
+ const nativePager = loadNativeAddon();
34
38
  let fd = null;
35
39
  let closed = false;
36
40
  const init = () => {
37
- fd = pagerNative.open(filepath, pageSize, delimiter);
41
+ fd = nativePager.open(filepath, pageSize, delimiter, backward);
38
42
  };
39
43
  const next = async () => {
40
44
  if (closed || !fd) return null;
41
- const data = await pagerNative.next(fd);
45
+ const data = await nativePager.next(fd);
42
46
  if (!data) return null;
43
47
  return data.toString("utf8").split(delimiter);
44
48
  };
45
49
  const nextSync = () => {
46
50
  if (closed || !fd) return null;
47
- const data = pagerNative.nextSync(fd);
51
+ const data = nativePager.nextSync(fd);
48
52
  if (!data) return null;
49
53
  return data.toString("utf8").split(delimiter);
50
54
  };
51
55
  const close = async () => {
52
56
  if (!closed || fd) {
53
57
  closed = true;
54
- await pagerNative.close(fd);
58
+ await nativePager.close(fd);
55
59
  fd = null;
56
60
  }
57
61
  };
package/dist/native.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- import { t as createNativePager } from "./native-BNwCco1j.cjs";
1
+ import { t as createNativePager } from "./native-46pCT8Rc.cjs";
2
2
  export { createNativePager };
package/dist/native.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { t as createNativePager } from "./native-BWytCdQz.mjs";
1
+ import { t as createNativePager } from "./native-DeBXdY3U.mjs";
2
2
  export { createNativePager };
package/dist/native.mjs CHANGED
@@ -28,29 +28,33 @@ function loadNativeAddon() {
28
28
  throw new Error(UNAVAILABLE);
29
29
  }
30
30
  }
31
- function createNativePager(filepath, { pageSize = 1e3, delimiter = "\n" } = {}) {
32
- const pagerNative = loadNativeAddon();
31
+ function createNativePager(filepath, options) {
32
+ const { pageSize = 1e3, delimiter = "\n", backward = false } = options ?? {};
33
+ if (!filepath) throw new Error("filepath required");
34
+ if (pageSize < 1) throw new RangeError("pageSize must be >= 1");
35
+ if (delimiter.length !== 1) throw new RangeError("native reader only supports single-character delimiters");
36
+ const nativePager = loadNativeAddon();
33
37
  let fd = null;
34
38
  let closed = false;
35
39
  const init = () => {
36
- fd = pagerNative.open(filepath, pageSize, delimiter);
40
+ fd = nativePager.open(filepath, pageSize, delimiter, backward);
37
41
  };
38
42
  const next = async () => {
39
43
  if (closed || !fd) return null;
40
- const data = await pagerNative.next(fd);
44
+ const data = await nativePager.next(fd);
41
45
  if (!data) return null;
42
46
  return data.toString("utf8").split(delimiter);
43
47
  };
44
48
  const nextSync = () => {
45
49
  if (closed || !fd) return null;
46
- const data = pagerNative.nextSync(fd);
50
+ const data = nativePager.nextSync(fd);
47
51
  if (!data) return null;
48
52
  return data.toString("utf8").split(delimiter);
49
53
  };
50
54
  const close = async () => {
51
55
  if (!closed || fd) {
52
56
  closed = true;
53
- await pagerNative.close(fd);
57
+ await nativePager.close(fd);
54
58
  fd = null;
55
59
  }
56
60
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "readline-pager",
3
- "version": "0.4.9",
3
+ "version": "0.5.1",
4
4
  "scripts": {
5
5
  "build:js": "tsdown",
6
6
  "build:native": "node-gyp rebuild",
@@ -24,10 +24,10 @@
24
24
  "typescript": "~6.0.2"
25
25
  },
26
26
  "optionalDependencies": {
27
- "@devmor-j/readline-pager-linux-arm64": "0.4.9",
28
- "@devmor-j/readline-pager-linux-x64": "0.4.9",
29
- "@devmor-j/readline-pager-linux-musl-arm64": "0.4.9",
30
- "@devmor-j/readline-pager-linux-musl-x64": "0.4.9"
27
+ "@devmor-j/readline-pager-linux-arm64": "0.5.1",
28
+ "@devmor-j/readline-pager-linux-musl-arm64": "0.5.1",
29
+ "@devmor-j/readline-pager-linux-musl-x64": "0.5.1",
30
+ "@devmor-j/readline-pager-linux-x64": "0.5.1"
31
31
  },
32
32
  "gypfile": false,
33
33
  "type": "module",
@@ -1,27 +0,0 @@
1
- //#region src/types.d.ts
2
- interface ReaderOptions {
3
- chunkSize: number;
4
- pageSize: number;
5
- delimiter: string;
6
- prefetch: number;
7
- }
8
- interface PagerOptions extends Partial<ReaderOptions> {
9
- backward?: boolean;
10
- useWorker?: boolean;
11
- }
12
- interface Pager extends AsyncIterable<string[]>, Iterable<string[]> {
13
- next(): Promise<string[] | null>;
14
- nextSync(): string[] | null;
15
- close(): Promise<void>;
16
- }
17
- //#endregion
18
- //#region src/native.d.ts
19
- declare function createNativePager(filepath: string, {
20
- pageSize,
21
- delimiter
22
- }?: {
23
- pageSize?: number | undefined;
24
- delimiter?: string | undefined;
25
- }): Pager;
26
- //#endregion
27
- export { ReaderOptions as i, Pager as n, PagerOptions as r, createNativePager as t };
@@ -1,27 +0,0 @@
1
- //#region src/types.d.ts
2
- interface ReaderOptions {
3
- chunkSize: number;
4
- pageSize: number;
5
- delimiter: string;
6
- prefetch: number;
7
- }
8
- interface PagerOptions extends Partial<ReaderOptions> {
9
- backward?: boolean;
10
- useWorker?: boolean;
11
- }
12
- interface Pager extends AsyncIterable<string[]>, Iterable<string[]> {
13
- next(): Promise<string[] | null>;
14
- nextSync(): string[] | null;
15
- close(): Promise<void>;
16
- }
17
- //#endregion
18
- //#region src/native.d.ts
19
- declare function createNativePager(filepath: string, {
20
- pageSize,
21
- delimiter
22
- }?: {
23
- pageSize?: number | undefined;
24
- delimiter?: string | undefined;
25
- }): Pager;
26
- //#endregion
27
- export { ReaderOptions as i, Pager as n, PagerOptions as r, createNativePager as t };