spliterator 1.5.0
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/LICENSE.md +650 -0
- package/README.md +119 -0
- package/out/index.d.ts +21 -0
- package/out/index.d.ts.map +1 -0
- package/out/index.js +21 -0
- package/out/index.js.map +1 -0
- package/out/lib/AsyncSpliterator.d.ts +127 -0
- package/out/lib/AsyncSpliterator.d.ts.map +1 -0
- package/out/lib/AsyncSpliterator.js +332 -0
- package/out/lib/AsyncSpliterator.js.map +1 -0
- package/out/lib/BufferController.d.ts +62 -0
- package/out/lib/BufferController.d.ts.map +1 -0
- package/out/lib/BufferController.js +93 -0
- package/out/lib/BufferController.js.map +1 -0
- package/out/lib/CSVSpliterator.d.ts +104 -0
- package/out/lib/CSVSpliterator.d.ts.map +1 -0
- package/out/lib/CSVSpliterator.js +83 -0
- package/out/lib/CSVSpliterator.js.map +1 -0
- package/out/lib/CharacterSequence.d.ts +103 -0
- package/out/lib/CharacterSequence.d.ts.map +1 -0
- package/out/lib/CharacterSequence.js +184 -0
- package/out/lib/CharacterSequence.js.map +1 -0
- package/out/lib/CompositeDataView.d.ts +104 -0
- package/out/lib/CompositeDataView.d.ts.map +1 -0
- package/out/lib/CompositeDataView.js +242 -0
- package/out/lib/CompositeDataView.js.map +1 -0
- package/out/lib/DelimitedChunkReader.d.ts +67 -0
- package/out/lib/DelimitedChunkReader.d.ts.map +1 -0
- package/out/lib/DelimitedChunkReader.js +113 -0
- package/out/lib/DelimitedChunkReader.js.map +1 -0
- package/out/lib/DelimiterTransformer.d.ts +27 -0
- package/out/lib/DelimiterTransformer.d.ts.map +1 -0
- package/out/lib/DelimiterTransformer.js +26 -0
- package/out/lib/DelimiterTransformer.js.map +1 -0
- package/out/lib/IndexQueue.d.ts +48 -0
- package/out/lib/IndexQueue.d.ts.map +1 -0
- package/out/lib/IndexQueue.js +80 -0
- package/out/lib/IndexQueue.js.map +1 -0
- package/out/lib/JSONSpliterator.d.ts +19 -0
- package/out/lib/JSONSpliterator.d.ts.map +1 -0
- package/out/lib/JSONSpliterator.js +55 -0
- package/out/lib/JSONSpliterator.js.map +1 -0
- package/out/lib/SlidingWindow.d.ts +93 -0
- package/out/lib/SlidingWindow.d.ts.map +1 -0
- package/out/lib/SlidingWindow.js +176 -0
- package/out/lib/SlidingWindow.js.map +1 -0
- package/out/lib/Spliterator.d.ts +51 -0
- package/out/lib/Spliterator.d.ts.map +1 -0
- package/out/lib/Spliterator.js +238 -0
- package/out/lib/Spliterator.js.map +1 -0
- package/out/lib/TextSpliterator.d.ts +40 -0
- package/out/lib/TextSpliterator.d.ts.map +1 -0
- package/out/lib/TextSpliterator.js +56 -0
- package/out/lib/TextSpliterator.js.map +1 -0
- package/out/lib/casing.d.ts +28 -0
- package/out/lib/casing.d.ts.map +1 -0
- package/out/lib/casing.js +69 -0
- package/out/lib/casing.js.map +1 -0
- package/out/lib/node/fs.d.ts +98 -0
- package/out/lib/node/fs.d.ts.map +1 -0
- package/out/lib/node/fs.js +162 -0
- package/out/lib/node/fs.js.map +1 -0
- package/out/lib/shared.d.ts +167 -0
- package/out/lib/shared.d.ts.map +1 -0
- package/out/lib/shared.js +92 -0
- package/out/lib/shared.js.map +1 -0
- package/out/lib/take.d.ts +26 -0
- package/out/lib/take.d.ts.map +1 -0
- package/out/lib/take.js +71 -0
- package/out/lib/take.js.map +1 -0
- package/out/lib/writer.d.ts +34 -0
- package/out/lib/writer.d.ts.map +1 -0
- package/out/lib/writer.js +41 -0
- package/out/lib/writer.js.map +1 -0
- package/package.json +100 -0
package/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Ribbon 🎀
|
|
2
|
+
|
|
3
|
+
Ribbon makes newline-delimited files easy to work with.
|
|
4
|
+
|
|
5
|
+
Let's say you have a huge newline-delimited JSON file that can't fit into memory.
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
{"name": "Jessie", "age": 30}
|
|
9
|
+
{"name": "Kelly", "age": 40}
|
|
10
|
+
{"name": "Loren", "age": 50}
|
|
11
|
+
// Several hundred thousand more lines...
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Ribbon can help you read it line-by-line:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { JSONSpliterator } from "spliterator"
|
|
18
|
+
|
|
19
|
+
interface Person {
|
|
20
|
+
name: string
|
|
21
|
+
age: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const reader = JSONSpliterator.fromAsync("example.jsonl")
|
|
25
|
+
|
|
26
|
+
for await (const line of reader) {
|
|
27
|
+
console.log(line) // {"name": "Alice", "age": 30}, etc.
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
[](https://www.npmjs.com/package/spliterator)
|
|
32
|
+
|
|
33
|
+
# Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
yarn add spliterator
|
|
37
|
+
# or
|
|
38
|
+
npm install spliterator
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
# Usage
|
|
42
|
+
|
|
43
|
+
## Character-delimited files
|
|
44
|
+
|
|
45
|
+
While Ribbon supports any delimited byte stream, it's particularly useful for character-delimited content such as comma-separated values (CSV), tab-separated values (TSV) – or any other delimiter you can think of.
|
|
46
|
+
|
|
47
|
+
```csv
|
|
48
|
+
Full Name, Occupation, Age
|
|
49
|
+
Morgan, Developer, 30
|
|
50
|
+
Nataly, Designer, 40
|
|
51
|
+
Orlando, Manager, 50
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { CSVSpliterator } from "spliterator"
|
|
56
|
+
|
|
57
|
+
const reader = CSVSpliterator.fromAsync("people.csv")
|
|
58
|
+
|
|
59
|
+
for await (const columns of reader) {
|
|
60
|
+
console.log(columns) // ["Full Name", "Occupation", "Age"], ["Morgan", "Developer", 30], etc.
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
CSV files can also be emitted as objects with headers as keys, with some quality-of-life features, such as normalizing property keys:
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { CSVSpliterator } from "spliterator"
|
|
68
|
+
|
|
69
|
+
interface Person {
|
|
70
|
+
full_name: string
|
|
71
|
+
occupation: string
|
|
72
|
+
age: number
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const reader = CSVSpliterator.fromAsync<Person>("people.csv", { mode: "object" })
|
|
76
|
+
|
|
77
|
+
for await (const columns of reader) {
|
|
78
|
+
console.log(columns) // { full_name: "Morgan", occupation: "Developer", age: 30 }, etc.
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Advanced usage
|
|
83
|
+
|
|
84
|
+
Ribbon is designed to be simple to use, but also flexible and powerful.
|
|
85
|
+
For more advanced use cases, check out our tests in the `test` directory, or our fully-annotated source code.
|
|
86
|
+
|
|
87
|
+
### Reading from a stream
|
|
88
|
+
|
|
89
|
+
All Ribbon generators implement the `Generator` and `AsyncGenerator` interfaces, so you can use them in `for...of` and `for await...of` loops, as well the web-native [ReadableStreams](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream), so you can use them in `for await...of` loops, as well as piping them through transformations to avoid nested and partially materialized streams.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { JSONSpliterator } from "spliterator"
|
|
93
|
+
|
|
94
|
+
const people = [
|
|
95
|
+
{ name: "Alice", age: 30 },
|
|
96
|
+
{ name: "Bob", age: 40 },
|
|
97
|
+
{ name: "Charlie", age: 50 },
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
const generator = JSONSpliterator.from(people.map(JSON.stringify).join("\n"))
|
|
101
|
+
const stream = ReadableStream.from(generator)
|
|
102
|
+
|
|
103
|
+
for await (const line of stream) {
|
|
104
|
+
console.log(line) // {"name": "Alice", "age": 30}, etc.
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Custom generators
|
|
109
|
+
|
|
110
|
+
While Ribbon includes premade exports for most use-cases, custom generators can be created via `DelimitedGenerator`. This class is a low-level interface that allows you to create your own generators for any kind of delimited content.
|
|
111
|
+
|
|
112
|
+
# License
|
|
113
|
+
|
|
114
|
+
Ribbon is licensed under the AGPL-3.0 license. Generally,
|
|
115
|
+
this means that you can use the software for free, but you must share
|
|
116
|
+
any modifications you make to the software.
|
|
117
|
+
|
|
118
|
+
For more information on commercial usage licensing, please contact us at
|
|
119
|
+
`hello@sister.software`
|
package/out/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*/
|
|
6
|
+
export * from "./lib/BufferController.js";
|
|
7
|
+
export * from "./lib/casing.js";
|
|
8
|
+
export * from "./lib/CharacterSequence.js";
|
|
9
|
+
export * from "./lib/CompositeDataView.js";
|
|
10
|
+
export * from "./lib/CSVSpliterator.js";
|
|
11
|
+
export * from "./lib/DelimitedChunkReader.js";
|
|
12
|
+
export * from "./lib/DelimiterTransformer.js";
|
|
13
|
+
export * from "./lib/IndexQueue.js";
|
|
14
|
+
export * from "./lib/JSONSpliterator.js";
|
|
15
|
+
export * from "./lib/shared.js";
|
|
16
|
+
export * from "./lib/SlidingWindow.js";
|
|
17
|
+
export * from "./lib/Spliterator.js";
|
|
18
|
+
export * from "./lib/take.js";
|
|
19
|
+
export * from "./lib/TextSpliterator.js";
|
|
20
|
+
export * from "./lib/writer.js";
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,2BAA2B,CAAA;AACzC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,4BAA4B,CAAA;AAC1C,cAAc,4BAA4B,CAAA;AAC1C,cAAc,yBAAyB,CAAA;AACvC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,qBAAqB,CAAA;AACnC,cAAc,0BAA0B,CAAA;AACxC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,wBAAwB,CAAA;AACtC,cAAc,sBAAsB,CAAA;AACpC,cAAc,eAAe,CAAA;AAC7B,cAAc,0BAA0B,CAAA;AACxC,cAAc,iBAAiB,CAAA"}
|
package/out/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*/
|
|
6
|
+
export * from "./lib/BufferController.js";
|
|
7
|
+
export * from "./lib/casing.js";
|
|
8
|
+
export * from "./lib/CharacterSequence.js";
|
|
9
|
+
export * from "./lib/CompositeDataView.js";
|
|
10
|
+
export * from "./lib/CSVSpliterator.js";
|
|
11
|
+
export * from "./lib/DelimitedChunkReader.js";
|
|
12
|
+
export * from "./lib/DelimiterTransformer.js";
|
|
13
|
+
export * from "./lib/IndexQueue.js";
|
|
14
|
+
export * from "./lib/JSONSpliterator.js";
|
|
15
|
+
export * from "./lib/shared.js";
|
|
16
|
+
export * from "./lib/SlidingWindow.js";
|
|
17
|
+
export * from "./lib/Spliterator.js";
|
|
18
|
+
export * from "./lib/take.js";
|
|
19
|
+
export * from "./lib/TextSpliterator.js";
|
|
20
|
+
export * from "./lib/writer.js";
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
package/out/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,2BAA2B,CAAA;AACzC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,4BAA4B,CAAA;AAC1C,cAAc,4BAA4B,CAAA;AAC1C,cAAc,yBAAyB,CAAA;AACvC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,qBAAqB,CAAA;AACnC,cAAc,0BAA0B,CAAA;AACxC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,wBAAwB,CAAA;AACtC,cAAc,sBAAsB,CAAA;AACpC,cAAc,eAAe,CAAA;AAC7B,cAAc,0BAA0B,CAAA;AACxC,cAAc,iBAAiB,CAAA"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*/
|
|
6
|
+
import { CharacterSequenceInput } from "./CharacterSequence.js";
|
|
7
|
+
import { AsyncDataResource, type FileResourceLike } from "./shared.js";
|
|
8
|
+
/**
|
|
9
|
+
* Initialization options for creating a spliterator.
|
|
10
|
+
*/
|
|
11
|
+
export interface SpliteratorInit {
|
|
12
|
+
/**
|
|
13
|
+
* The character to delimit by. Typically a newline or comma.
|
|
14
|
+
*/
|
|
15
|
+
delimiter?: CharacterSequenceInput;
|
|
16
|
+
/**
|
|
17
|
+
* The byte offset to start reading from.
|
|
18
|
+
*
|
|
19
|
+
* This is an advanced option that is not typically used.
|
|
20
|
+
*
|
|
21
|
+
* @default 0
|
|
22
|
+
*/
|
|
23
|
+
position?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Whether to emit repeated delimiters as empty buffers.
|
|
26
|
+
*
|
|
27
|
+
* Setting this to `false` matches the behavior of `String.prototype.split`.
|
|
28
|
+
*
|
|
29
|
+
* @default true
|
|
30
|
+
*/
|
|
31
|
+
skipEmpty?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Count of skipped matches before emitting a data.
|
|
34
|
+
*
|
|
35
|
+
* @default 0
|
|
36
|
+
*/
|
|
37
|
+
drop?: number;
|
|
38
|
+
/**
|
|
39
|
+
* The maximum number of data slices to yield.
|
|
40
|
+
*
|
|
41
|
+
* Useful for limiting the number of emitted slices, i.e. lines in a file.
|
|
42
|
+
*
|
|
43
|
+
* Note that skipped slices are not counted towards the limit.
|
|
44
|
+
*
|
|
45
|
+
* @default Infinity
|
|
46
|
+
*/
|
|
47
|
+
take?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Whether to emit debug information.
|
|
50
|
+
*/
|
|
51
|
+
debug?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Initialization options for creating an asynchronous spliterator.
|
|
55
|
+
*/
|
|
56
|
+
export interface AsyncSpliteratorInit extends SpliteratorInit {
|
|
57
|
+
/**
|
|
58
|
+
* The buffer chunk size to read from the file, i.e. the high-water mark for the file read
|
|
59
|
+
* operation.
|
|
60
|
+
*
|
|
61
|
+
* @default BlockSize * 16
|
|
62
|
+
* @minimum The delimiter byte length * 4 or block size of the file system. Whichever is greater.
|
|
63
|
+
* @maximum The byte length of the file.
|
|
64
|
+
*/
|
|
65
|
+
highWaterMark?: number;
|
|
66
|
+
/**
|
|
67
|
+
* Whether to close the file handle after completion.
|
|
68
|
+
*
|
|
69
|
+
* @default true
|
|
70
|
+
*/
|
|
71
|
+
autoClose?: boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* An asynchronous spliterator for reading delimited byte streams.
|
|
75
|
+
*
|
|
76
|
+
* ```ts
|
|
77
|
+
* import { AsyncSpliterator } from "spliterator"
|
|
78
|
+
*
|
|
79
|
+
* const file = await AsyncSpliterator.from("data.csv")
|
|
80
|
+
*
|
|
81
|
+
* for await (const line of file) {
|
|
82
|
+
* console.log(line)
|
|
83
|
+
* }
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* @see {@linkcode Spliterator} for the synchronous version.
|
|
87
|
+
*/
|
|
88
|
+
export declare class AsyncSpliterator implements AsyncIterableIterator<Uint8Array>, AsyncDisposable {
|
|
89
|
+
#private;
|
|
90
|
+
/**
|
|
91
|
+
* Create a new delimited generator from a data resource.
|
|
92
|
+
*
|
|
93
|
+
* Unlike the constructor, this method can be used to create a generator from a file path or URL.
|
|
94
|
+
*
|
|
95
|
+
* @param source - The data resource to read from.
|
|
96
|
+
* @param init - The initialization options for the generator.
|
|
97
|
+
*/
|
|
98
|
+
static from(source: AsyncDataResource, init?: AsyncSpliteratorInit): Promise<AsyncSpliterator>;
|
|
99
|
+
/**
|
|
100
|
+
* Dispose of the spliterator, closing the file handle if necessary.
|
|
101
|
+
*/
|
|
102
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Create a new delimited generator from a file handle.
|
|
105
|
+
*
|
|
106
|
+
* @param file - The file to read from.
|
|
107
|
+
* @param init - The initialization options for the generator.
|
|
108
|
+
* @see {@linkcode AsyncSpliterator.from} to create a new generator from a data resource.
|
|
109
|
+
*/
|
|
110
|
+
constructor(file: FileResourceLike, init?: AsyncSpliteratorInit);
|
|
111
|
+
next(): Promise<IteratorResult<Uint8Array>>;
|
|
112
|
+
/**
|
|
113
|
+
* Collect all the byte ranges from the file.
|
|
114
|
+
*
|
|
115
|
+
* **This method will read the entire file into memory.**
|
|
116
|
+
*/
|
|
117
|
+
toArray(): Promise<Uint8Array[]>;
|
|
118
|
+
/**
|
|
119
|
+
* Collect all the byte ranges from the file as a string.
|
|
120
|
+
*
|
|
121
|
+
* **This method will read the entire file into memory.**
|
|
122
|
+
*/
|
|
123
|
+
toDecodedArray(decoder?: import("util").TextDecoder): Promise<string[]>;
|
|
124
|
+
[Symbol.asyncIterator](): this;
|
|
125
|
+
return(): Promise<IteratorReturnResult<undefined>>;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=AsyncSpliterator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AsyncSpliterator.d.ts","sourceRoot":"","sources":["../../lib/AsyncSpliterator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAqB,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AAElF,OAAO,EAEN,iBAAiB,EAGjB,KAAK,gBAAgB,EAErB,MAAM,aAAa,CAAA;AAEpB;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B;;OAEG;IACH,SAAS,CAAC,EAAE,sBAAsB,CAAA;IAElC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IAEnB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IAEb;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IAEb;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CACf;AAED;;GAEG;AACH,MAAM,WAAW,oBAAqB,SAAQ,eAAe;IAC5D;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAiB,YAAW,qBAAqB,CAAC,UAAU,CAAC,EAAE,eAAe;;IAG1F;;;;;;;OAOG;WACU,IAAI,CAAC,MAAM,EAAE,iBAAiB,EAAE,IAAI,GAAE,oBAAyB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAaxG;;OAEG;IACU,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;IASnD;;;;;;OAMG;gBAES,IAAI,EAAE,gBAAgB,EAAE,IAAI,GAAE,oBAAyB;IA+RtD,IAAI,IAAI,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IAgCxD;;;;OAIG;IACI,OAAO,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAIvC;;;;OAIG;IACI,cAAc,CAAC,OAAO,6BAAoB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAI9D,CAAC,MAAM,CAAC,aAAa,CAAC;IAItB,MAAM,IAAI,OAAO,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC;CAGzD"}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*/
|
|
6
|
+
import { BufferController } from "./BufferController.js";
|
|
7
|
+
import { CharacterSequence } from "./CharacterSequence.js";
|
|
8
|
+
import { IndexQueue } from "./IndexQueue.js";
|
|
9
|
+
import { applyReaderPolyfill, isFileResourceLike, } from "./shared.js";
|
|
10
|
+
/**
|
|
11
|
+
* An asynchronous spliterator for reading delimited byte streams.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { AsyncSpliterator } from "spliterator"
|
|
15
|
+
*
|
|
16
|
+
* const file = await AsyncSpliterator.from("data.csv")
|
|
17
|
+
*
|
|
18
|
+
* for await (const line of file) {
|
|
19
|
+
* console.log(line)
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @see {@linkcode Spliterator} for the synchronous version.
|
|
24
|
+
*/
|
|
25
|
+
export class AsyncSpliterator {
|
|
26
|
+
//#region Lifecycle
|
|
27
|
+
/**
|
|
28
|
+
* Create a new delimited generator from a data resource.
|
|
29
|
+
*
|
|
30
|
+
* Unlike the constructor, this method can be used to create a generator from a file path or URL.
|
|
31
|
+
*
|
|
32
|
+
* @param source - The data resource to read from.
|
|
33
|
+
* @param init - The initialization options for the generator.
|
|
34
|
+
*/
|
|
35
|
+
static async from(source, init = {}) {
|
|
36
|
+
let file;
|
|
37
|
+
if (isFileResourceLike(source)) {
|
|
38
|
+
file = source;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const { NodeFileResource } = await import("spliterator/node/fs");
|
|
42
|
+
file = await NodeFileResource.open(source);
|
|
43
|
+
}
|
|
44
|
+
return new AsyncSpliterator(file, init);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Dispose of the spliterator, closing the file handle if necessary.
|
|
48
|
+
*/
|
|
49
|
+
async [Symbol.asyncDispose]() {
|
|
50
|
+
this.#indices.clear();
|
|
51
|
+
this.#controller.clear();
|
|
52
|
+
if (this.#autoClose) {
|
|
53
|
+
await this.#file[Symbol.asyncDispose]?.();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Create a new delimited generator from a file handle.
|
|
58
|
+
*
|
|
59
|
+
* @param file - The file to read from.
|
|
60
|
+
* @param init - The initialization options for the generator.
|
|
61
|
+
* @see {@linkcode AsyncSpliterator.from} to create a new generator from a data resource.
|
|
62
|
+
*/
|
|
63
|
+
constructor(file, init = {}) {
|
|
64
|
+
applyReaderPolyfill(file);
|
|
65
|
+
this.#file = file;
|
|
66
|
+
this.#autoClose = init.autoClose ?? false;
|
|
67
|
+
this.#needle = new CharacterSequence(init.delimiter);
|
|
68
|
+
this.#readPosition = init.position ?? 0;
|
|
69
|
+
this.#totalByteSize = file.size;
|
|
70
|
+
this.#yieldByteEstimate = this.#totalByteSize - this.#readPosition;
|
|
71
|
+
this.#highWaterMark = Math.max(this.#needle.length * 4, init.highWaterMark ?? 4096);
|
|
72
|
+
this.#yieldDropCount = Math.max(0, init.drop ?? 0);
|
|
73
|
+
this.#yieldStopCount = Math.max(init.take ?? Infinity, 0) + this.#yieldDropCount;
|
|
74
|
+
this.#skipEmpty = init.skipEmpty ?? true;
|
|
75
|
+
this.#controller = new BufferController({
|
|
76
|
+
initialBufferSize: Math.max(this.#needle.length * 4, this.#highWaterMark),
|
|
77
|
+
});
|
|
78
|
+
this.#debug = init.debug ?? false;
|
|
79
|
+
this.#log = this.#debug ? console.debug.bind(console) : () => void 0;
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region Private Properties
|
|
83
|
+
/**
|
|
84
|
+
* The file to read from.
|
|
85
|
+
*/
|
|
86
|
+
#file;
|
|
87
|
+
/**
|
|
88
|
+
* Whether to close the file handle when the iterator is completed or disposed.
|
|
89
|
+
*/
|
|
90
|
+
#autoClose;
|
|
91
|
+
/**
|
|
92
|
+
* A queue of index tuples marking the start and end of delimiter positions.
|
|
93
|
+
*
|
|
94
|
+
* Note that indices are relative to the buffer, not the file.
|
|
95
|
+
*
|
|
96
|
+
* This means that the start index is always 0, and the end index is the byte length of the
|
|
97
|
+
* buffer.
|
|
98
|
+
*/
|
|
99
|
+
#indices = new IndexQueue();
|
|
100
|
+
/**
|
|
101
|
+
* The byte sequence to search for, i.e. an encoded delimiter.
|
|
102
|
+
*/
|
|
103
|
+
#needle;
|
|
104
|
+
/**
|
|
105
|
+
* The buffer containing the current chunks of data. This will be resized as needed to accommodate
|
|
106
|
+
* the file read operation.
|
|
107
|
+
*/
|
|
108
|
+
#controller;
|
|
109
|
+
/**
|
|
110
|
+
* The total byte size of the file.
|
|
111
|
+
*/
|
|
112
|
+
#totalByteSize;
|
|
113
|
+
/**
|
|
114
|
+
* The total number of bytes we expect to yield.
|
|
115
|
+
*
|
|
116
|
+
* We use this to ensure that we don't miss any data when the file is read in chunks.
|
|
117
|
+
*/
|
|
118
|
+
#yieldByteEstimate;
|
|
119
|
+
/**
|
|
120
|
+
* How many yields to skip.
|
|
121
|
+
*/
|
|
122
|
+
#yieldDropCount;
|
|
123
|
+
/**
|
|
124
|
+
* How many yields to allow before stopping.
|
|
125
|
+
*/
|
|
126
|
+
#yieldStopCount;
|
|
127
|
+
/**
|
|
128
|
+
* Whether to skip empty yields.
|
|
129
|
+
*/
|
|
130
|
+
#skipEmpty;
|
|
131
|
+
/**
|
|
132
|
+
* The high water mark for the buffer.
|
|
133
|
+
*
|
|
134
|
+
* This defines the size of each read operation, as well as the total size of indices to keep in
|
|
135
|
+
* memory.
|
|
136
|
+
*/
|
|
137
|
+
#highWaterMark;
|
|
138
|
+
/**
|
|
139
|
+
* The total number of bytes we've yielded.
|
|
140
|
+
*/
|
|
141
|
+
#yieldedByteLength = 0;
|
|
142
|
+
/**
|
|
143
|
+
* The total number of yields.
|
|
144
|
+
*/
|
|
145
|
+
#yieldCount = 0;
|
|
146
|
+
/**
|
|
147
|
+
* The current byte index to perform read operations from.
|
|
148
|
+
*/
|
|
149
|
+
#readPosition;
|
|
150
|
+
/**
|
|
151
|
+
* The last byte range seen while draining the buffer.
|
|
152
|
+
*/
|
|
153
|
+
#lastByteRangeSeen;
|
|
154
|
+
/**
|
|
155
|
+
* Whether to output debug information.
|
|
156
|
+
*/
|
|
157
|
+
#debug;
|
|
158
|
+
/**
|
|
159
|
+
* Whether the iterator is done.
|
|
160
|
+
*/
|
|
161
|
+
#done = false;
|
|
162
|
+
//#endregion
|
|
163
|
+
//#region Private Methods
|
|
164
|
+
#log;
|
|
165
|
+
/**
|
|
166
|
+
* Find and enqueue delimiter positions.
|
|
167
|
+
*/
|
|
168
|
+
#search() {
|
|
169
|
+
const lastByteRange = this.#indices.peekLast();
|
|
170
|
+
let searchCursor = lastByteRange ? lastByteRange[1] + this.#needle.length : 0;
|
|
171
|
+
while (searchCursor <= this.#controller.bytesWritten) {
|
|
172
|
+
const delimiterIndex = this.#needle.search(this.#controller.bytes, searchCursor);
|
|
173
|
+
if (delimiterIndex === -1) {
|
|
174
|
+
this.#log(`No viable byte range found in buffer. Breaking.`);
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
this.#log("Found byte range", [searchCursor, delimiterIndex]);
|
|
178
|
+
this.#indices.enqueue([searchCursor, delimiterIndex]);
|
|
179
|
+
searchCursor = delimiterIndex + this.#needle.length;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Read a chunk of data from the file.
|
|
184
|
+
*/
|
|
185
|
+
async #read() {
|
|
186
|
+
// We're extra careful here to ensure that we don't read past the end of the file.
|
|
187
|
+
// And because `file.read` doesn't expose the number of bytes read,
|
|
188
|
+
// we keep track of this ourselves.
|
|
189
|
+
const nextReadLength = Math.min(this.#highWaterMark, this.#totalByteSize - this.#readPosition);
|
|
190
|
+
// Offset in this context means the byte offset to begin writing to the buffer.
|
|
191
|
+
const offset = this.#controller.bytesWritten;
|
|
192
|
+
// Before reading we grow the buffer to ensure that we have enough space.
|
|
193
|
+
const nextBufferSize = Math.max(this.#controller.byteLengthMinimum, offset + nextReadLength);
|
|
194
|
+
this.#controller.grow(nextBufferSize);
|
|
195
|
+
this.#log("Before read", { cursor: this.#readPosition, nextReadLength, nextBufferSize, offset });
|
|
196
|
+
await this.#file.read({
|
|
197
|
+
buffer: this.#controller.bytes,
|
|
198
|
+
offset,
|
|
199
|
+
position: this.#readPosition,
|
|
200
|
+
length: nextReadLength,
|
|
201
|
+
});
|
|
202
|
+
this.#controller.bytesWritten += nextReadLength;
|
|
203
|
+
// this.#log(
|
|
204
|
+
// "Inspecting buffer",
|
|
205
|
+
// debugAsVisibleCharacters(this.#controller.bytes.subarray(0, this.#controller.bytesWritten))
|
|
206
|
+
// )
|
|
207
|
+
this.#readPosition += nextReadLength;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Fill the buffer with data and search for delimiters.
|
|
211
|
+
*/
|
|
212
|
+
async #fill() {
|
|
213
|
+
if (this.#readPosition === this.#totalByteSize) {
|
|
214
|
+
this.#log("Reached end of file. Preparing to finalize.");
|
|
215
|
+
// There's a few special cases we could get this far and not have drained the buffer.
|
|
216
|
+
const lastByteRange = this.#lastByteRangeSeen;
|
|
217
|
+
if (lastByteRange) {
|
|
218
|
+
const lastByteIndex = lastByteRange[1];
|
|
219
|
+
// There's a special case where the last delimiter is at the end of the *file*.
|
|
220
|
+
if (lastByteIndex < this.#controller.bytesWritten) {
|
|
221
|
+
// We need to determine if we're at the end of the *file* and there's no delimiter.
|
|
222
|
+
const possibleDelimiterIndex = this.#needle.search(this.#controller.bytes, this.#controller.bytesWritten - this.#needle.length);
|
|
223
|
+
if (possibleDelimiterIndex !== -1) {
|
|
224
|
+
this.#log("Inserted byte range at the last delimiter", [
|
|
225
|
+
possibleDelimiterIndex,
|
|
226
|
+
this.#controller.bytesWritten,
|
|
227
|
+
]);
|
|
228
|
+
// We found a delimiter at the end of the file, so we enqueue the byte range.
|
|
229
|
+
this.#indices.enqueue([possibleDelimiterIndex + this.#needle.length, this.#controller.bytesWritten]);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
this.#log("Inserted byte range at the last byte", [lastByteIndex, this.#controller.bytesWritten]);
|
|
233
|
+
// The file didn't end with a delimiter, so we just enqueue the last byte range.
|
|
234
|
+
this.#indices.enqueue([lastByteIndex + this.#needle.length, this.#controller.bytesWritten]);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
this.#log(`Weird situation. We're at the end of the file, but the last byte range ${lastByteIndex} is greater than the buffer length ${this.#controller.bytesWritten}.`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
else if (this.#yieldCount === 0) {
|
|
242
|
+
// We didn't find any delimiters in the file, so we just enqueue the whole buffer.
|
|
243
|
+
this.#indices.enqueue([0, this.#controller.bytesWritten]);
|
|
244
|
+
}
|
|
245
|
+
this.#done = true;
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
// We're effectively done with that window of the buffer.
|
|
249
|
+
// So we can compress it to save memory.
|
|
250
|
+
const nextBufferStart = this.#lastByteRangeSeen ? this.#lastByteRangeSeen[1] + this.#needle.length : 0;
|
|
251
|
+
this.#log("Compressing buffer", { nextBufferStart });
|
|
252
|
+
this.#controller.compress(nextBufferStart);
|
|
253
|
+
while (this.#readPosition < this.#totalByteSize && this.#indices.byteLength < this.#highWaterMark) {
|
|
254
|
+
await this.#read();
|
|
255
|
+
this.#search();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
async #finalize() {
|
|
259
|
+
if (this.#debug) {
|
|
260
|
+
/**
|
|
261
|
+
* The total number of bytes we expect to be omitted from the buffer. This is derived from the
|
|
262
|
+
* number of of yields and the length of the delimiter.
|
|
263
|
+
*/
|
|
264
|
+
const expectedYieldedDelimitedBytes = (this.#yieldCount - 1) * this.#needle.length;
|
|
265
|
+
const omittedBytes = this.#yieldByteEstimate - (this.#yieldedByteLength + expectedYieldedDelimitedBytes);
|
|
266
|
+
this.#log({
|
|
267
|
+
totalByteSize: this.#totalByteSize,
|
|
268
|
+
readPosition: this.#readPosition,
|
|
269
|
+
yieldByteEstimate: this.#yieldByteEstimate,
|
|
270
|
+
yieldedByteLength: this.#yieldedByteLength,
|
|
271
|
+
yieldCount: this.#yieldCount,
|
|
272
|
+
expectedYieldedDelimitedBytes,
|
|
273
|
+
omittedBytes,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
if (this.#autoClose) {
|
|
277
|
+
await this.#file[Symbol.asyncDispose]?.();
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
done: true,
|
|
281
|
+
value: undefined,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
//#endregion
|
|
285
|
+
//#region Iterator Methods
|
|
286
|
+
async next() {
|
|
287
|
+
if (this.#done || this.#yieldCount >= this.#yieldStopCount)
|
|
288
|
+
return this.#finalize();
|
|
289
|
+
if (this.#indices.size === 0) {
|
|
290
|
+
await this.#fill();
|
|
291
|
+
}
|
|
292
|
+
const currentByteRange = this.#indices.dequeue();
|
|
293
|
+
this.#lastByteRangeSeen = currentByteRange;
|
|
294
|
+
const [start, end] = currentByteRange;
|
|
295
|
+
const slice = this.#controller.subarray(start, end);
|
|
296
|
+
if (slice.length === 0 && this.#skipEmpty) {
|
|
297
|
+
return this.next();
|
|
298
|
+
}
|
|
299
|
+
this.#yieldCount++;
|
|
300
|
+
if (this.#yieldCount <= this.#yieldDropCount) {
|
|
301
|
+
return this.next();
|
|
302
|
+
}
|
|
303
|
+
this.#yieldedByteLength += end - start;
|
|
304
|
+
return {
|
|
305
|
+
value: slice,
|
|
306
|
+
done: false,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Collect all the byte ranges from the file.
|
|
311
|
+
*
|
|
312
|
+
* **This method will read the entire file into memory.**
|
|
313
|
+
*/
|
|
314
|
+
toArray() {
|
|
315
|
+
return Array.fromAsync(this);
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Collect all the byte ranges from the file as a string.
|
|
319
|
+
*
|
|
320
|
+
* **This method will read the entire file into memory.**
|
|
321
|
+
*/
|
|
322
|
+
toDecodedArray(decoder = new TextDecoder()) {
|
|
323
|
+
return Array.fromAsync(this, (bytes) => decoder.decode(bytes));
|
|
324
|
+
}
|
|
325
|
+
[Symbol.asyncIterator]() {
|
|
326
|
+
return this;
|
|
327
|
+
}
|
|
328
|
+
return() {
|
|
329
|
+
return this.#finalize();
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
//# sourceMappingURL=AsyncSpliterator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AsyncSpliterator.js","sourceRoot":"","sources":["../../lib/AsyncSpliterator.ts"],"names":[],"mappings":"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"}
|