web-csv-toolbox 0.0.2 → 0.2.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/README.md +118 -65
- package/lib/index.d.ts +497 -24
- package/lib/index.js +33 -201
- package/package.json +10 -5
package/README.md
CHANGED
|
@@ -1,38 +1,73 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
[](https://github.com/kamiazya/web-csv-toolbox/actions/workflows/node.js.yaml)
|
|
4
|
+
[](https://badge.fury.io/js/web-csv-toolbox)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](http://makeapullrequest.com)
|
|
7
|
+

|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
# `🌐 web-csv-toolbox 💽`
|
|
2
11
|
|
|
3
12
|
A CSV Toolbox utilizing Web Standard APIs.
|
|
4
13
|
|
|
5
|
-
|
|
14
|
+
🔗
|
|
6
15
|
|
|
7
|
-
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- Only web standard APIs are used, so it should work with these Runtimes.
|
|
16
|
+
[](https://github.com/kamiazya/web-csv-toolbox)
|
|
17
|
+
[](https://www.npmjs.com/package/web-csv-toolbox)
|
|
18
|
+
[](https://yarnpkg.com/package/web-csv-toolbox)
|
|
19
|
+
[](https://kamiazya.github.io/web-csv-toolbox/)
|
|
20
|
+
[](https://github.com/sponsors/kamiazya)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
[](https://biomejs.dev/)
|
|
24
|
+
[](https://vitest.dev/)
|
|
25
|
+
[](https://rollupjs.org/)
|
|
18
26
|
|
|
19
|
-
|
|
27
|
+
</div>
|
|
20
28
|
|
|
21
|
-
|
|
22
|
-
- Supports parsing CSV files from strings, `ReadableStream`s, and `Response` objects.
|
|
23
|
-
- Supports parsing CSV files with different delimiters and quotation characters.
|
|
24
|
-
- Defaults to `,` and `"` respectively.
|
|
25
|
-
- Supports parsing TSV files by setting `delimiter` to `\t`.
|
|
26
|
-
- Supports parsing with multi-character/multi-byte delimiters and quotation characters.
|
|
27
|
-
- Supports parsing binary CSV files.
|
|
29
|
+
---
|
|
28
30
|
|
|
29
|
-
##
|
|
31
|
+
## Key Concepts ✨
|
|
32
|
+
|
|
33
|
+
- 🌐 **Web Standards first.**
|
|
34
|
+
- Utilizing the Web Standards APIs, such as the [Web Streams API](https://developer.mozilla.org/en/docs/Web/API/Streams_API).
|
|
35
|
+
- ❤️ **TypeScript friendly & User friendly.**
|
|
36
|
+
- Fully typed and documented.
|
|
37
|
+
- 0️⃣ **Zero dependencies.**
|
|
38
|
+
- Using only Web Standards APIs.
|
|
39
|
+
- 💪 **Property-based testing.**
|
|
40
|
+
- Using [fast-check](https://fast-check.dev/) and [vitest](https://vitest.dev).
|
|
41
|
+
- ✅ **_To Be Tested_** **Cross-platform.**
|
|
42
|
+
- Works on browsers, Node.js, and Deno.
|
|
43
|
+
- Only web standard APIs are used, so it should work with these runtimes.
|
|
44
|
+
|
|
45
|
+
## Key Features 📗
|
|
46
|
+
|
|
47
|
+
- 🌊 **Efficient CSV Parsing with Streams**
|
|
48
|
+
- 💻 Leveraging the [WHATWG Streams API](https://streams.spec.whatwg.org/) and other Web APIs for seamless and efficient data processing.
|
|
49
|
+
- 🎨 **Flexible Source Support**
|
|
50
|
+
- 🧩 Parse CSVs directly from `string`s, `ReadableStream`s, or `Response` objects.
|
|
51
|
+
- ⚙️ **Advanced Parsing Options**: Customize your experience with various delimiters and quotation marks.
|
|
52
|
+
- 🔄 Defaults to `,` and `"` respectively.
|
|
53
|
+
- 🛠️ Use multi-character/multi-byte delimiters and quotations.
|
|
54
|
+
- 💾 **Specialized Binary CSV Parsing**: Leverage Stream-based processing for versatility and strength.
|
|
55
|
+
- 🔄 Flexible BOM handling.
|
|
56
|
+
- 🗜️ Supports various compression formats.
|
|
57
|
+
- 🔤 Charset specification for diverse encoding.
|
|
58
|
+
|
|
59
|
+
## Installation 📥
|
|
30
60
|
|
|
31
61
|
```sh
|
|
32
|
-
|
|
62
|
+
# Install with npm
|
|
63
|
+
$ npm install web-csv-toolbox
|
|
64
|
+
# Or Yarn
|
|
65
|
+
$ yarn add web-csv-toolbox
|
|
66
|
+
# Or pnpm
|
|
67
|
+
$ pnpm add web-csv-toolbox
|
|
33
68
|
```
|
|
34
69
|
|
|
35
|
-
## Usage
|
|
70
|
+
## Usage 📘
|
|
36
71
|
|
|
37
72
|
### Parsing CSV files from strings
|
|
38
73
|
|
|
@@ -51,7 +86,7 @@ for await (const record of parse(csv)) {
|
|
|
51
86
|
// { name: 'Bob', age: '69' }
|
|
52
87
|
```
|
|
53
88
|
|
|
54
|
-
|
|
89
|
+
### Parsing CSV files from `ReadableStream`s
|
|
55
90
|
|
|
56
91
|
```js
|
|
57
92
|
import { parse } from 'web-csv-toolbox';
|
|
@@ -107,7 +142,7 @@ for await (const record of parse(csv, { delimiter: '\t' })) {
|
|
|
107
142
|
// { name: 'Bob', age: '69' }
|
|
108
143
|
```
|
|
109
144
|
|
|
110
|
-
### Parsing CSV files with
|
|
145
|
+
### Parsing CSV files with headers
|
|
111
146
|
|
|
112
147
|
```js
|
|
113
148
|
import { parse } from 'web-csv-toolbox';
|
|
@@ -123,69 +158,87 @@ for await (const record of parse(csv, { headers: ['name', 'age'] })) {
|
|
|
123
158
|
// { name: 'Bob', age: '69' }
|
|
124
159
|
```
|
|
125
160
|
|
|
126
|
-
##
|
|
127
|
-
|
|
128
|
-
### High-level APIs
|
|
129
|
-
|
|
130
|
-
#### `parse(input[, options]): AsyncIterableIterator<Record>` function
|
|
131
|
-
|
|
132
|
-
Returns an asynchronous iterable of records.
|
|
161
|
+
## APIs 🧑💻
|
|
133
162
|
|
|
134
|
-
|
|
163
|
+
### High-level APIs 🚀
|
|
135
164
|
|
|
136
|
-
|
|
165
|
+
These APIs are designed for **Simplicity and Ease of Use**,
|
|
166
|
+
providing an intuitive and straightforward experience for users.
|
|
137
167
|
|
|
138
|
-
|
|
168
|
+
- **`function parse(input[, options]): AsyncIterableIterator<CSVRecord>`**: [📑](https://kamiazya.github.io/web-csv-toolbox/functions/parse-1.html)
|
|
169
|
+
- Parses various CSV input formats into an asynchronous iterable of records.
|
|
170
|
+
- **`function parse.toArray(input[, options]): Promise<CSVRecord[]>`**: [📑](https://kamiazya.github.io/web-csv-toolbox/functions/parse.toArray.html)
|
|
171
|
+
- Parses CSV input into an array of records, ideal for smaller data sets.
|
|
139
172
|
|
|
140
|
-
|
|
173
|
+
The `input` paramater can be a `string`, a [ReadableStream](https://developer.mozilla.org/docs/Web/API/ReadableStream)
|
|
174
|
+
of `string`s or [Uint8Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array)s,
|
|
175
|
+
or a [Response](https://developer.mozilla.org/docs/Web/API/Response) object.
|
|
141
176
|
|
|
142
|
-
|
|
177
|
+
### Middle-level APIs 🧱
|
|
143
178
|
|
|
144
|
-
|
|
179
|
+
These APIs are optimized for **Enhanced Performance and Control**,
|
|
180
|
+
catering to users who need more detailed and fine-tuned functionality.
|
|
145
181
|
|
|
146
|
-
|
|
182
|
+
- **`function parseBinaryStream(stream[, options])`**: [📑](https://kamiazya.github.io/web-csv-toolbox/functions/parseBinaryStream-1.html)
|
|
183
|
+
- Parses binary streams with precise control over data types.
|
|
184
|
+
- **`function parseResponse(response[, options])`**: [📑](https://kamiazya.github.io/web-csv-toolbox/functions/parseResponse-1.html)
|
|
185
|
+
- Customized parsing directly from `Response` objects.
|
|
186
|
+
- **`function parseStream(stream[, options])`**: [📑](https://kamiazya.github.io/web-csv-toolbox/functions/parseStream-1.html)
|
|
187
|
+
- Stream-based parsing for larger or continuous data.
|
|
188
|
+
- **`function parseString(string[, options])`**: [📑](https://kamiazya.github.io/web-csv-toolbox/functions/parseString-1.html)
|
|
189
|
+
- Efficient parsing of CSV strings.
|
|
190
|
+
- **`function parseStringStream(stream[, options])`**: [📑](https://kamiazya.github.io/web-csv-toolbox/functions/parseStringStream-1.html)
|
|
191
|
+
- Combines string-based parsing with stream processing.
|
|
147
192
|
|
|
148
|
-
|
|
193
|
+
### Low-level APIs ⚙️
|
|
149
194
|
|
|
150
|
-
|
|
195
|
+
These APIs are built for **Advanced Customization and Pipeline Design**,
|
|
196
|
+
ideal for developers looking for in-depth control and flexibility.
|
|
151
197
|
|
|
152
|
-
|
|
198
|
+
- **`class LexerTransformer`**: [📑](https://kamiazya.github.io/web-csv-toolbox/classes/LexerTransformer.html)
|
|
199
|
+
- A TransformStream class for lexical analysis of CSV data.
|
|
200
|
+
- **`class RecordAssemblerTransformer`**: [📑](https://kamiazya.github.io/web-csv-toolbox/classes/RecordAssemblerTransformar.html)
|
|
201
|
+
- Handles the assembly of parsed data into records.
|
|
153
202
|
|
|
154
|
-
|
|
203
|
+
## Options Configuration 🛠️
|
|
155
204
|
|
|
156
|
-
|
|
205
|
+
### Common Options ⚙️
|
|
157
206
|
|
|
158
|
-
|
|
207
|
+
| Option | Description | Default | Notes |
|
|
208
|
+
| -------------- | ------------------------------------- | ----------- | ----- |
|
|
209
|
+
| `delimiter` | Character to separate fields | `,` | |
|
|
210
|
+
| `quotation` | Character used for quoting fields | `"` | |
|
|
211
|
+
| `headers` | Custom headers for the parsed records | First row | If not provided, the first row is used as headers |
|
|
159
212
|
|
|
160
|
-
|
|
213
|
+
### Advanced Options (Binary-Specific) 🧬
|
|
161
214
|
|
|
162
|
-
|
|
215
|
+
| Option | Description | Default | Notes |
|
|
216
|
+
| --------------- | ------------------------------------------------- | --------- | ---------------------- |
|
|
217
|
+
| `charset` | Character encoding for binary CSV inputs | `utf-8` | See [Encoding API Compatibility](https://developer.mozilla.org/en-US/docs/Web/API/Encoding_API/Encodings) for the encoding formats that can be specified. |
|
|
218
|
+
| `decompression` | Decompression algorithm for compressed CSV inputs | | See [DecompressionStream Compatibility](https://developer.mozilla.org/en-US/docs/Web/API/DecompressionStream#browser_compatibilit). |
|
|
219
|
+
| `ignoreBOM` | Whether to ignore Byte Order Mark (BOM) | `false` | See [TextDecoderOptions.ignoreBOM](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoderStream/ignoreBOM) for more information about the BOM. |
|
|
220
|
+
| `fatal` | Throw an error on invalid characters | `false` | See [TextDecoderOptions.fatal](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoderStream/fatal) for more information. |
|
|
163
221
|
|
|
164
|
-
|
|
165
|
-
- `deflate`
|
|
166
|
-
- `deflate-raw`
|
|
167
|
-
- Note: This format is supported in Node.js v21.2.0 and above.
|
|
222
|
+
## How to Contribute 💪
|
|
168
223
|
|
|
169
|
-
|
|
224
|
+
## Star ⭐
|
|
170
225
|
|
|
171
|
-
The
|
|
226
|
+
The easiest way to contribute is to use the library and star [repository](https://github.com/kamiazya/web-csv-toolbox/).
|
|
172
227
|
|
|
173
|
-
|
|
228
|
+
### Questions 💭
|
|
174
229
|
|
|
175
|
-
|
|
230
|
+
Feel free to ask questions on [GitHub Discussions](https://github.com/kamiazya/web-csv-toolbox/discussions).
|
|
176
231
|
|
|
177
|
-
|
|
178
|
-
Defaults to `false`.
|
|
232
|
+
### Report bugs / request additional features 💡
|
|
179
233
|
|
|
180
|
-
|
|
234
|
+
Please register at [GitHub Issues](https://github.com/kamiazya/web-csv-toolbox/issues/new/choose).
|
|
181
235
|
|
|
182
|
-
|
|
183
|
-
Defaults to `false`.
|
|
236
|
+
### Financial Support 💸
|
|
184
237
|
|
|
185
|
-
|
|
238
|
+
Please support [kamiazya](https://github.com/sponsors/kamiazya).
|
|
186
239
|
|
|
187
|
-
|
|
240
|
+
> Even just a dollar is enough motivation to develop 😊
|
|
188
241
|
|
|
189
|
-
## License
|
|
242
|
+
## License ⚖️
|
|
190
243
|
|
|
191
|
-
[
|
|
244
|
+
This software is released under the MIT License, see [LICENSE](https://github.com/kamiazya/web-csv-toolbox?tab=MIT-1-ov-file).
|
package/lib/index.d.ts
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FiledDelimiter is a symbol for field delimiter of CSV.
|
|
3
|
+
* @category Constants
|
|
3
4
|
*/
|
|
4
5
|
declare const FieldDelimiter: unique symbol;
|
|
5
6
|
/**
|
|
6
7
|
* RecordDelimiter is a symbol for record delimiter of CSV.
|
|
8
|
+
* @category Constants
|
|
7
9
|
*/
|
|
8
10
|
declare const RecordDelimiter: unique symbol;
|
|
9
11
|
/**
|
|
10
12
|
* Field is a symbol for field of CSV.
|
|
13
|
+
* @category Constants
|
|
11
14
|
*/
|
|
12
15
|
declare const Field: unique symbol;
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* Token is a atomic unit of a CSV file.
|
|
16
19
|
* It can be a field, field delimiter, or record delimiter.
|
|
20
|
+
* @category Types
|
|
17
21
|
*
|
|
18
22
|
* @example
|
|
19
23
|
* ```ts
|
|
@@ -28,10 +32,12 @@ interface Token<T extends TokenType = TokenType> {
|
|
|
28
32
|
}
|
|
29
33
|
/**
|
|
30
34
|
* Type of a token for CSV.
|
|
35
|
+
* @category Types
|
|
31
36
|
*/
|
|
32
37
|
type TokenType = typeof FieldDelimiter | typeof RecordDelimiter | typeof Field;
|
|
33
38
|
/**
|
|
34
39
|
* CSV Common Options.
|
|
40
|
+
* @category Types
|
|
35
41
|
*/
|
|
36
42
|
interface CommonOptions {
|
|
37
43
|
/**
|
|
@@ -56,6 +62,7 @@ interface CommonOptions {
|
|
|
56
62
|
}
|
|
57
63
|
/**
|
|
58
64
|
* CSV Parsing Options for binary.
|
|
65
|
+
* @category Types
|
|
59
66
|
*/
|
|
60
67
|
interface BinaryOptions {
|
|
61
68
|
/**
|
|
@@ -64,6 +71,7 @@ interface BinaryOptions {
|
|
|
64
71
|
*
|
|
65
72
|
* @remarks
|
|
66
73
|
* Make sure the runtime you are running supports stream decompression.
|
|
74
|
+
*
|
|
67
75
|
* See {@link https://developer.mozilla.org/en-US/docs/Web/API/DecompressionStream#browser_compatibility | DecompressionStream Compatibility}.
|
|
68
76
|
*/
|
|
69
77
|
decomposition?: CompressionFormat;
|
|
@@ -71,7 +79,8 @@ interface BinaryOptions {
|
|
|
71
79
|
* You can specify the character encoding of the binary.
|
|
72
80
|
*
|
|
73
81
|
* @remarks
|
|
74
|
-
* {@link TextDecoderStream} is used internally.
|
|
82
|
+
* {@link !TextDecoderStream} is used internally.
|
|
83
|
+
*
|
|
75
84
|
* See {@link https://developer.mozilla.org/en-US/docs/Web/API/Encoding_API/Encodings | Encoding API Compatibility}
|
|
76
85
|
* for the encoding formats that can be specified.
|
|
77
86
|
*
|
|
@@ -92,14 +101,21 @@ interface BinaryOptions {
|
|
|
92
101
|
* If the binary has a invalid character, you can specify whether to throw an error.
|
|
93
102
|
*
|
|
94
103
|
* @remarks
|
|
95
|
-
* If
|
|
96
|
-
*
|
|
97
|
-
*
|
|
104
|
+
* If the property is `true` then a decoder will throw a {@link !TypeError}
|
|
105
|
+
* if it encounters malformed data while decoding.
|
|
106
|
+
*
|
|
107
|
+
* If `false` the decoder will substitute the invalid data
|
|
108
|
+
* with the replacement character `U+FFFD` (�).
|
|
109
|
+
*
|
|
110
|
+
* See {@link https://developer.mozilla.org/en-US/docs/Web/API/TextDecoderStream/fatal | TextDecoderOptions.fatal} for more information.
|
|
111
|
+
*
|
|
112
|
+
* @default false
|
|
98
113
|
*/
|
|
99
114
|
fatal?: boolean;
|
|
100
115
|
}
|
|
101
116
|
/**
|
|
102
117
|
* Record Assembler Options for CSV.
|
|
118
|
+
* @category Types
|
|
103
119
|
*
|
|
104
120
|
* @remarks
|
|
105
121
|
* If you specify `header: ['foo', 'bar']`,
|
|
@@ -125,21 +141,24 @@ interface RecordAssemblerOptions<Header extends ReadonlyArray<string>> {
|
|
|
125
141
|
}
|
|
126
142
|
/**
|
|
127
143
|
* Parse options for CSV string.
|
|
144
|
+
* @category Types
|
|
128
145
|
*/
|
|
129
146
|
interface ParseOptions<Header extends ReadonlyArray<string>>
|
|
130
147
|
extends CommonOptions,
|
|
131
148
|
RecordAssemblerOptions<Header> {}
|
|
132
149
|
/**
|
|
133
150
|
* Parse options for CSV binary.
|
|
151
|
+
* @category Types
|
|
134
152
|
*/
|
|
135
153
|
interface ParseBinaryOptions<Header extends ReadonlyArray<string>>
|
|
136
154
|
extends ParseOptions<Header>,
|
|
137
155
|
BinaryOptions {}
|
|
138
156
|
/**
|
|
139
157
|
* CSV Record.
|
|
158
|
+
* @category Types
|
|
140
159
|
* @template Header Header of the CSV.
|
|
141
160
|
*
|
|
142
|
-
* @example Header is
|
|
161
|
+
* @example Header is ["foo", "bar"]
|
|
143
162
|
* ```ts
|
|
144
163
|
* const record: CSVRecord<["foo", "bar"]> = {
|
|
145
164
|
* foo: "1",
|
|
@@ -155,6 +174,8 @@ type CSVRecord<Header extends ReadonlyArray<string>> = Record<
|
|
|
155
174
|
/**
|
|
156
175
|
* A transform stream that converts a stream of tokens into a stream of rows.
|
|
157
176
|
*
|
|
177
|
+
* @category Low-level API
|
|
178
|
+
*
|
|
158
179
|
* @example Parse a CSV with headers by data
|
|
159
180
|
* ```ts
|
|
160
181
|
* new ReadableStream({
|
|
@@ -189,6 +210,8 @@ declare class LexerTransformer extends TransformStream<string, Token> {
|
|
|
189
210
|
* @template Header The type of the header row.
|
|
190
211
|
* @param options The options for the parser.
|
|
191
212
|
*
|
|
213
|
+
* @category Low-level API
|
|
214
|
+
*
|
|
192
215
|
* @example Parse a CSV with headers by data
|
|
193
216
|
* ```ts
|
|
194
217
|
* new ReadableStream({
|
|
@@ -235,16 +258,55 @@ declare class RecordAssemblerTransformar<
|
|
|
235
258
|
/**
|
|
236
259
|
* Parse CSV string to records.
|
|
237
260
|
*
|
|
261
|
+
* @category Middle-level API
|
|
238
262
|
* @param csv CSV string to parse
|
|
239
263
|
* @param options Parsing options. See {@link ParseOptions}.
|
|
264
|
+
* @returns Async iterable iterator of records.
|
|
265
|
+
*
|
|
266
|
+
* If you want array of records, use {@link parseString.toArray} function.
|
|
267
|
+
* @example Parsing CSV files from strings
|
|
268
|
+
*
|
|
269
|
+
* ```ts
|
|
270
|
+
* import { parseString } from 'web-csv-toolbox';
|
|
271
|
+
*
|
|
272
|
+
* const csv = `name,age
|
|
273
|
+
* Alice,42
|
|
274
|
+
* Bob,69`;
|
|
275
|
+
*
|
|
276
|
+
* for await (const record of parseString(csv)) {
|
|
277
|
+
* console.log(record);
|
|
278
|
+
* }
|
|
279
|
+
* // Prints:
|
|
280
|
+
* // { name: 'Alice', age: '42' }
|
|
281
|
+
* // { name: 'Bob', age: '69' }
|
|
282
|
+
* ```
|
|
240
283
|
*/
|
|
241
|
-
declare function
|
|
284
|
+
declare function parseString<Header extends ReadonlyArray<string>>(
|
|
242
285
|
csv: string,
|
|
243
286
|
options?: ParseOptions<Header>,
|
|
244
287
|
): AsyncIterableIterator<CSVRecord<Header>>;
|
|
245
|
-
declare namespace
|
|
288
|
+
declare namespace parseString {
|
|
289
|
+
/**
|
|
290
|
+
* Parse CSV string to records.
|
|
291
|
+
*
|
|
292
|
+
* @returns Array of records
|
|
293
|
+
*
|
|
294
|
+
* @example
|
|
295
|
+
* ```ts
|
|
296
|
+
* import { parseString } from 'web-csv-toolbox';
|
|
297
|
+
*
|
|
298
|
+
* const csv = `name,age
|
|
299
|
+
* Alice,42
|
|
300
|
+
* Bob,69`;
|
|
301
|
+
*
|
|
302
|
+
* const records = await parseString.toArray(csv);
|
|
303
|
+
* console.log(records);
|
|
304
|
+
* // Prints:
|
|
305
|
+
* // [ { name: 'Alice', age: '42' }, { name: 'Bob', age: '69' } ]
|
|
306
|
+
* ```
|
|
307
|
+
*/
|
|
246
308
|
function toArray<Header extends ReadonlyArray<string>>(
|
|
247
|
-
|
|
309
|
+
csv: string,
|
|
248
310
|
options?: ParseOptions<Header>,
|
|
249
311
|
): Promise<CSVRecord<Header>[]>;
|
|
250
312
|
}
|
|
@@ -253,16 +315,66 @@ declare namespace streamingParse {
|
|
|
253
315
|
* Parse CSV to records.
|
|
254
316
|
* This function is for parsing a binary stream.
|
|
255
317
|
*
|
|
318
|
+
* @category Middle-level API
|
|
256
319
|
* @remarks
|
|
257
|
-
* If you want to parse a string, use {@link
|
|
320
|
+
* If you want to parse a string, use {@link parseStringStream}.
|
|
258
321
|
* @param stream CSV string to parse
|
|
259
322
|
* @param options Parsing options. See {@link ParseBinaryOptions}.
|
|
323
|
+
* @returns Async iterable iterator of records.
|
|
324
|
+
*
|
|
325
|
+
* If you want array of records, use {@link parseBinaryStream.toArray} function.
|
|
326
|
+
*
|
|
327
|
+
* @example Parsing CSV binary
|
|
328
|
+
*
|
|
329
|
+
* ```ts
|
|
330
|
+
* import { parseBinaryStream } from 'web-csv-toolbox';
|
|
331
|
+
*
|
|
332
|
+
* const csv = Uint8Array.from([
|
|
333
|
+
* // ...
|
|
334
|
+
* ]);
|
|
335
|
+
*
|
|
336
|
+
* const stream = new ReadableStream({
|
|
337
|
+
* start(controller) {
|
|
338
|
+
* controller.enqueue(csv);
|
|
339
|
+
* controller.close();
|
|
340
|
+
* },
|
|
341
|
+
* });
|
|
342
|
+
*
|
|
343
|
+
* for await (const record of parseBinaryStream(csv)) {
|
|
344
|
+
* console.log(record);
|
|
345
|
+
* }
|
|
346
|
+
* ```
|
|
260
347
|
*/
|
|
261
348
|
declare function parseBinaryStream<Header extends ReadonlyArray<string>>(
|
|
262
349
|
stream: ReadableStream<Uint8Array>,
|
|
263
350
|
options?: ParseBinaryOptions<Header>,
|
|
264
351
|
): AsyncIterableIterator<CSVRecord<Header>>;
|
|
265
352
|
declare namespace parseBinaryStream {
|
|
353
|
+
/**
|
|
354
|
+
* Parse CSV binary to array of records,
|
|
355
|
+
* ideal for smaller data sets.
|
|
356
|
+
*
|
|
357
|
+
* @returns Array of records
|
|
358
|
+
*
|
|
359
|
+
* @example Parsing CSV binary
|
|
360
|
+
* ```ts
|
|
361
|
+
* import { parseBinaryStream } from 'web-csv-toolbox';
|
|
362
|
+
*
|
|
363
|
+
* const csv = Uint8Array.from([
|
|
364
|
+
* // ...
|
|
365
|
+
* ]);
|
|
366
|
+
*
|
|
367
|
+
* const stream = new ReadableStream({
|
|
368
|
+
* start(controller) {
|
|
369
|
+
* controller.enqueue(csv);
|
|
370
|
+
* controller.close();
|
|
371
|
+
* },
|
|
372
|
+
* });
|
|
373
|
+
*
|
|
374
|
+
* const records = await parseBinaryStream.toArray(stream);
|
|
375
|
+
* console.log(records);
|
|
376
|
+
* ```
|
|
377
|
+
*/
|
|
266
378
|
function toArray<Header extends ReadonlyArray<string>>(
|
|
267
379
|
stream: ReadableStream<Uint8Array>,
|
|
268
380
|
options?: ParseBinaryOptions<Header>,
|
|
@@ -270,48 +382,407 @@ declare namespace parseBinaryStream {
|
|
|
270
382
|
}
|
|
271
383
|
|
|
272
384
|
/**
|
|
273
|
-
* Parse CSV string to records.
|
|
385
|
+
* Parse CSV string stream to records.
|
|
274
386
|
*
|
|
387
|
+
* @category Middle-level API
|
|
275
388
|
* @param stream CSV string stream to parse
|
|
276
|
-
* @param options Parsing options.
|
|
389
|
+
* @param options Parsing options.
|
|
390
|
+
* @returns Async iterable iterator of records.
|
|
391
|
+
*
|
|
392
|
+
* If you want array of records, use {@link parseStringStream.toArray} function.
|
|
393
|
+
*
|
|
394
|
+
* @example Parsing CSV files from strings
|
|
395
|
+
*
|
|
396
|
+
* ```ts
|
|
397
|
+
* import { parseStringStream } from 'web-csv-toolbox';
|
|
398
|
+
*
|
|
399
|
+
* const csv = `name,age
|
|
400
|
+
* Alice,42
|
|
401
|
+
* Bob,69`;
|
|
402
|
+
*
|
|
403
|
+
* const stream = new ReadableStream({
|
|
404
|
+
* start(controller) {
|
|
405
|
+
* controller.enqueue(csv);
|
|
406
|
+
* controller.close();
|
|
407
|
+
* },
|
|
408
|
+
* });
|
|
409
|
+
*
|
|
410
|
+
* for await (const record of parseStringStream(csv)) {
|
|
411
|
+
* console.log(record);
|
|
412
|
+
* }
|
|
413
|
+
* // Prints:
|
|
414
|
+
* // { name: 'Alice', age: '42' }
|
|
415
|
+
* // { name: 'Bob', age: '69' }
|
|
416
|
+
* ```
|
|
277
417
|
*/
|
|
278
418
|
declare function parseStringStream<Header extends ReadonlyArray<string>>(
|
|
279
419
|
stream: ReadableStream<string>,
|
|
280
420
|
options?: ParseOptions<Header>,
|
|
281
421
|
): AsyncIterableIterator<CSVRecord<Header>>;
|
|
282
422
|
declare namespace parseStringStream {
|
|
423
|
+
/**
|
|
424
|
+
* Parse CSV string stream to records.
|
|
425
|
+
*
|
|
426
|
+
* @returns Array of records
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
*
|
|
430
|
+
* ```ts
|
|
431
|
+
* import { parseStringStream } from 'web-csv-toolbox';
|
|
432
|
+
*
|
|
433
|
+
* const csv = `name,age
|
|
434
|
+
* Alice,42
|
|
435
|
+
* Bob,69`;
|
|
436
|
+
*
|
|
437
|
+
* const stream = new ReadableStream({
|
|
438
|
+
* start(controller) {
|
|
439
|
+
* controller.enqueue(csv);
|
|
440
|
+
* controller.close();
|
|
441
|
+
* },
|
|
442
|
+
* });
|
|
443
|
+
*
|
|
444
|
+
* const records = await parseStringStream.toArray(stream);
|
|
445
|
+
* console.log(records);
|
|
446
|
+
* // Prints:
|
|
447
|
+
* // [ { name: 'Alice', age: '42' }, { name: 'Bob', age: '69' } ]
|
|
448
|
+
* ```
|
|
449
|
+
*/
|
|
283
450
|
function toArray<Header extends ReadonlyArray<string>>(
|
|
284
|
-
stream: ReadableStream<
|
|
451
|
+
stream: ReadableStream<string>,
|
|
285
452
|
options?: ParseOptions<Header>,
|
|
286
453
|
): Promise<CSVRecord<Header>[]>;
|
|
287
454
|
}
|
|
288
455
|
|
|
289
456
|
/**
|
|
290
|
-
* Parse CSV to records
|
|
457
|
+
* Parse HTTP Response what contains CSV to records,
|
|
458
|
+
* ideal for smaller data sets.
|
|
459
|
+
*
|
|
460
|
+
* @remarks
|
|
461
|
+
* This function automatically treats response headers.
|
|
291
462
|
*
|
|
292
|
-
*
|
|
463
|
+
* - If `Content-Type` header is not set, it assumes `text/csv`.
|
|
464
|
+
* - If `Content-Type` header is not `text/csv`, it throws an error.
|
|
465
|
+
* - If `Content-Type` header has charset parameter, it uses it for decoding.
|
|
466
|
+
* - If `Content-Encoding` header is set, it decompresses the response.
|
|
467
|
+
* - Should there be any conflicting information between the header and the options, the option's value will take precedence.
|
|
468
|
+
*
|
|
469
|
+
* @category Middle-level API
|
|
470
|
+
* @param response
|
|
471
|
+
* @param options
|
|
472
|
+
* @returns Async iterable iterator of records.
|
|
473
|
+
*
|
|
474
|
+
* If you want array of records, use {@link parseResponse.toArray} function.
|
|
475
|
+
*
|
|
476
|
+
* @example Parsing CSV Response
|
|
477
|
+
*
|
|
478
|
+
* ```ts
|
|
479
|
+
* import { parseResponse } from 'web-csv-toolbox';
|
|
480
|
+
*
|
|
481
|
+
* const response = await fetch('https://example.com/data.csv');
|
|
482
|
+
*
|
|
483
|
+
* for await (const record of parseResponse(response)) {
|
|
484
|
+
* console.log(record);
|
|
485
|
+
* }
|
|
486
|
+
* ```
|
|
487
|
+
*/
|
|
488
|
+
declare function parseResponse<Header extends ReadonlyArray<string>>(
|
|
489
|
+
response: Response,
|
|
490
|
+
options?: ParseOptions<Header>,
|
|
491
|
+
): AsyncIterableIterator<CSVRecord<Header>>;
|
|
492
|
+
declare namespace parseResponse {
|
|
493
|
+
/**
|
|
494
|
+
* Parse CSV Response to array of records.
|
|
495
|
+
*
|
|
496
|
+
* @returns Array of records
|
|
497
|
+
*
|
|
498
|
+
* @example Parsing CSV Response
|
|
499
|
+
*
|
|
500
|
+
* ```ts
|
|
501
|
+
* import { parseResponse } from 'web-csv-toolbox';
|
|
502
|
+
*
|
|
503
|
+
* const response = await fetch('https://example.com/data.csv');
|
|
504
|
+
*
|
|
505
|
+
* const records = await parseResponse.toArray(response);
|
|
506
|
+
* console.log(records);
|
|
507
|
+
* ```
|
|
508
|
+
*/
|
|
509
|
+
function toArray<Header extends ReadonlyArray<string>>(
|
|
510
|
+
response: Response,
|
|
511
|
+
options?: ParseOptions<Header>,
|
|
512
|
+
): Promise<CSVRecord<Header>[]>;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Parse CSV Stream to records,
|
|
517
|
+
* ideal for smaller data sets.
|
|
518
|
+
*
|
|
519
|
+
* {@link !ReadableStream} of {@link !String} and {@link !Uint8Array} are supported.
|
|
293
520
|
*
|
|
294
521
|
* @remarks
|
|
295
|
-
* {@link
|
|
296
|
-
* {@link parseStringStream} and {@link parseResponse} are used internally.
|
|
522
|
+
* {@link parseStringStream} and {@link parseBinaryStream} are used internally.
|
|
297
523
|
* If you known the type of the stream, it performs better to use them directly.
|
|
298
524
|
*
|
|
299
|
-
* If you want to parse a string, use {@link
|
|
300
|
-
* If you want to parse a Uint8Array, use {@link
|
|
301
|
-
* If you want to parse a ReadableStream<string>, use {@link parseStringStream}.
|
|
302
|
-
* If you want to parse a ReadableStream<Uint8Array>, use {@link parseBinaryStream}.
|
|
303
|
-
* If you want to parse a Response, use {@link parseResponse}.
|
|
525
|
+
* If you want to parse a string, use {@link parseStringStream}.
|
|
526
|
+
* If you want to parse a Uint8Array, use {@link parseBinaryStream}.
|
|
304
527
|
*
|
|
528
|
+
* @category Middle-level API
|
|
305
529
|
* @param csv CSV string to parse
|
|
306
530
|
* @param options Parsing options. See {@link ParseOptions}.
|
|
531
|
+
* @returns Async iterable iterator of records.
|
|
532
|
+
*
|
|
533
|
+
* If you want array of records, use {@link parseStream.toArray} function.
|
|
534
|
+
*
|
|
535
|
+
* @example Parsing CSV string stream
|
|
536
|
+
*
|
|
537
|
+
* ```ts
|
|
538
|
+
*
|
|
539
|
+
* import { parseStream } from 'web-csv-toolbox';
|
|
540
|
+
*
|
|
541
|
+
* const csv = `name,age
|
|
542
|
+
* Alice,42
|
|
543
|
+
* Bob,69`;
|
|
544
|
+
*
|
|
545
|
+
* const stream = new ReadableStream({
|
|
546
|
+
* start(controller) {
|
|
547
|
+
* controller.enqueue(csv);
|
|
548
|
+
* controller.close();
|
|
549
|
+
* },
|
|
550
|
+
* });
|
|
551
|
+
*
|
|
552
|
+
* for await (const record of parseStream(stream)) {
|
|
553
|
+
* console.log(record);
|
|
554
|
+
* }
|
|
555
|
+
* // Prints:
|
|
556
|
+
* // { name: 'Alice', age: '42' }
|
|
557
|
+
* // { name: 'Bob', age: '69' }
|
|
558
|
+
* ```
|
|
559
|
+
*
|
|
560
|
+
* @example Parsing CSV binary stream
|
|
561
|
+
*
|
|
562
|
+
* ```ts
|
|
563
|
+
* import { parseStream } from 'web-csv-toolbox';
|
|
564
|
+
*
|
|
565
|
+
* const csv = Uint8Array.from([
|
|
566
|
+
* // ...
|
|
567
|
+
* ]);
|
|
568
|
+
*
|
|
569
|
+
* const stream = new ReadableStream({
|
|
570
|
+
* start(controller) {
|
|
571
|
+
* controller.enqueue(csv);
|
|
572
|
+
* controller.close();
|
|
573
|
+
* },
|
|
574
|
+
* });
|
|
575
|
+
*
|
|
576
|
+
* for await (const record of parseStream(stream)) {
|
|
577
|
+
* console.log(record);
|
|
578
|
+
* }
|
|
579
|
+
* ```
|
|
580
|
+
*/
|
|
581
|
+
declare function parseStream<Header extends ReadonlyArray<string>>(
|
|
582
|
+
stream: ReadableStream<Uint8Array | string>,
|
|
583
|
+
options?: ParseBinaryOptions<Header>,
|
|
584
|
+
): AsyncIterableIterator<CSVRecord<Header>>;
|
|
585
|
+
declare namespace parseStream {
|
|
586
|
+
/**
|
|
587
|
+
* Parse CSV Stream to array of records.
|
|
588
|
+
*
|
|
589
|
+
* @returns Array of records
|
|
590
|
+
*/
|
|
591
|
+
function toArray<Header extends ReadonlyArray<string>>(
|
|
592
|
+
stream: ReadableStream<Uint8Array>,
|
|
593
|
+
options?: ParseBinaryOptions<Header>,
|
|
594
|
+
): Promise<CSVRecord<Header>[]>;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Parse CSV to records.
|
|
599
|
+
*
|
|
600
|
+
* {@link !String}, {@link !ReadableStream}<string | {@link !Uint8Array}> and {@link !Response} are supported.
|
|
601
|
+
*
|
|
602
|
+
*
|
|
603
|
+
* @typeParam Header Header type like `['name', 'age']`.
|
|
604
|
+
*
|
|
605
|
+
* @param csv CSV string to parse.
|
|
606
|
+
* @param options Parsing options for CSV string parsing.
|
|
607
|
+
* @returns Async iterable iterator of records.
|
|
608
|
+
*
|
|
609
|
+
* If you want array of records, use {@link parse.toArray} function.
|
|
610
|
+
* @category High-level API
|
|
611
|
+
*
|
|
612
|
+
* @remarks
|
|
613
|
+
* {@link parseString}, {@link parseBinaryStream},
|
|
614
|
+
* {@link parseStringStream} and {@link parseResponse} are used internally.
|
|
615
|
+
*
|
|
616
|
+
* If you known the type of the CSV, it performs better to use them directly.
|
|
617
|
+
*
|
|
618
|
+
* | If you want to parse a... | Use... | Data are treated as... |
|
|
619
|
+
* | ----------------------------------- | ------------------------- | ---------------------- |
|
|
620
|
+
* | {@link !String} | {@link parseString} | String |
|
|
621
|
+
* | {@link !ReadableStream}<string> | {@link parseStringStream} | String |
|
|
622
|
+
* | {@link !ReadableStream}<Uint8Array> | {@link parseBinaryStream} | Binary |
|
|
623
|
+
* | {@link !Response} | {@link parseResponse} | Binary |
|
|
624
|
+
*
|
|
625
|
+
* @example Parsing CSV files from strings
|
|
626
|
+
*
|
|
627
|
+
* ```ts
|
|
628
|
+
* import { parse } from 'web-csv-toolbox';
|
|
629
|
+
*
|
|
630
|
+
* const csv = `name,age
|
|
631
|
+
* Alice,42
|
|
632
|
+
* Bob,69`;
|
|
633
|
+
*
|
|
634
|
+
* for await (const record of parse(csv)) {
|
|
635
|
+
* console.log(record);
|
|
636
|
+
* }
|
|
637
|
+
* // Prints:
|
|
638
|
+
* // { name: 'Alice', age: '42' }
|
|
639
|
+
* // { name: 'Bob', age: '69' }
|
|
640
|
+
* ```
|
|
641
|
+
*
|
|
642
|
+
* @example Parsing CSV files from streams
|
|
643
|
+
*
|
|
644
|
+
* ```ts
|
|
645
|
+
* import { parse } from 'web-csv-toolbox';
|
|
646
|
+
*
|
|
647
|
+
* const csv = `name,age
|
|
648
|
+
* Alice,42
|
|
649
|
+
* Bob,69`;
|
|
650
|
+
*
|
|
651
|
+
* const stream = new ReadableStream({
|
|
652
|
+
* start(controller) {
|
|
653
|
+
* controller.enqueue(csv);
|
|
654
|
+
* controller.close();
|
|
655
|
+
* }
|
|
656
|
+
* });
|
|
657
|
+
*
|
|
658
|
+
* for await (const record of parse(stream)) {
|
|
659
|
+
* console.log(record);
|
|
660
|
+
* }
|
|
661
|
+
* // Prints:
|
|
662
|
+
* // { name: 'Alice', age: '42' }
|
|
663
|
+
* // { name: 'Bob', age: '69' }
|
|
664
|
+
* ```
|
|
665
|
+
*
|
|
666
|
+
*
|
|
667
|
+
* @example Parsing CSV files with headers
|
|
668
|
+
*
|
|
669
|
+
* ```ts
|
|
670
|
+
* import { parse } from 'web-csv-toolbox';
|
|
671
|
+
*
|
|
672
|
+
* // This CSV has no header.
|
|
673
|
+
* const csv = `Alice,42
|
|
674
|
+
* Bob,69`;
|
|
675
|
+
*
|
|
676
|
+
* for await (const record of parse(csv, { header: ['name', 'age'] })) {
|
|
677
|
+
* console.log(record);
|
|
678
|
+
* }
|
|
679
|
+
* // Prints:
|
|
680
|
+
* // { name: 'Alice', age: '42' }
|
|
681
|
+
* // { name: 'Bob', age: '69' }
|
|
682
|
+
* ```
|
|
683
|
+
*
|
|
684
|
+
* @example Parsing CSV files with different delimiters characters
|
|
685
|
+
*
|
|
686
|
+
* ```ts
|
|
687
|
+
* import { parse } from 'web-csv-toolbox';
|
|
688
|
+
*
|
|
689
|
+
* const csv = `name\tage
|
|
690
|
+
* Alice\t42
|
|
691
|
+
* Bob\t69`;
|
|
692
|
+
*
|
|
693
|
+
* for await (const record of parse(csv, { delimiter: '\t' })) {
|
|
694
|
+
* console.log(record);
|
|
695
|
+
* }
|
|
696
|
+
* // Prints:
|
|
697
|
+
* // { name: 'Alice', age: '42' }
|
|
698
|
+
* // { name: 'Bob', age: '69' }
|
|
699
|
+
* ```
|
|
307
700
|
*/
|
|
308
701
|
declare function parse<Header extends ReadonlyArray<string>>(
|
|
309
|
-
csv: string | ReadableStream<
|
|
702
|
+
csv: string | ReadableStream<string>,
|
|
310
703
|
options?: ParseOptions<Header>,
|
|
311
704
|
): AsyncIterableIterator<CSVRecord<Header>>;
|
|
705
|
+
/**
|
|
706
|
+
* Parse CSV binary to records.
|
|
707
|
+
*
|
|
708
|
+
* @param csv CSV binary to parse.
|
|
709
|
+
* @param options Parsing options for CSV binary parsing.
|
|
710
|
+
*
|
|
711
|
+
* @example Parsing CSV files from responses
|
|
712
|
+
*
|
|
713
|
+
* ```ts
|
|
714
|
+
* import { parse } from 'web-csv-toolbox';
|
|
715
|
+
*
|
|
716
|
+
* // This CSV data is not gzipped and encoded in utf-8.
|
|
717
|
+
* const response = await fetch('https://example.com/data.csv');
|
|
718
|
+
*
|
|
719
|
+
* for await (const record of parse(response)) {
|
|
720
|
+
* // ...
|
|
721
|
+
* }
|
|
722
|
+
* ```
|
|
723
|
+
*
|
|
724
|
+
* @example Parsing CSV files with options spcialized for binary
|
|
725
|
+
*
|
|
726
|
+
* ```ts
|
|
727
|
+
* import { parse } from 'web-csv-toolbox';
|
|
728
|
+
*
|
|
729
|
+
* // This CSV data is gzipped and encoded in shift-jis and has BOM.
|
|
730
|
+
* const response = await fetch('https://example.com/data.csv.gz');
|
|
731
|
+
*
|
|
732
|
+
* for await (const record of parse(response, {
|
|
733
|
+
* charset: 'shift-jis',
|
|
734
|
+
* ignoreBOM: true,
|
|
735
|
+
* decomposition: 'gzip',
|
|
736
|
+
* })) {
|
|
737
|
+
* // ...
|
|
738
|
+
* }
|
|
739
|
+
* ```
|
|
740
|
+
*/
|
|
741
|
+
declare function parse<Header extends ReadonlyArray<string>>(
|
|
742
|
+
csv: ReadableStream<Uint8Array> | Response,
|
|
743
|
+
options?: ParseBinaryOptions<Header>,
|
|
744
|
+
): AsyncIterableIterator<CSVRecord<Header>>;
|
|
312
745
|
declare namespace parse {
|
|
746
|
+
/**
|
|
747
|
+
* Parse CSV string to array of records,
|
|
748
|
+
* ideal for smaller data sets.
|
|
749
|
+
*
|
|
750
|
+
* @example Parse a CSV as array of records
|
|
751
|
+
*
|
|
752
|
+
* ```ts
|
|
753
|
+
* import { parse } from 'web-csv-toolbox';
|
|
754
|
+
*
|
|
755
|
+
* const csv = `name,age
|
|
756
|
+
* Alice,42
|
|
757
|
+
* Bob,69`;
|
|
758
|
+
*
|
|
759
|
+
* const records = await parse.toArray(csv);
|
|
760
|
+
* console.log(records);
|
|
761
|
+
* // Prints:
|
|
762
|
+
* // [ { name: 'Alice', age: '42' }, { name: 'Bob', age: '69' } ]
|
|
763
|
+
* ```
|
|
764
|
+
*/
|
|
765
|
+
function toArray<Header extends ReadonlyArray<string>>(
|
|
766
|
+
csv: string | ReadableStream<string>,
|
|
767
|
+
options?: ParseOptions<Header>,
|
|
768
|
+
): Promise<CSVRecord<Header>[]>;
|
|
769
|
+
/**
|
|
770
|
+
* Parse CSV string to array of records,
|
|
771
|
+
* ideal for smaller data sets.
|
|
772
|
+
*
|
|
773
|
+
* @example Parse a CSV as array of records
|
|
774
|
+
*
|
|
775
|
+
* ```ts
|
|
776
|
+
* import { parse } from 'web-csv-toolbox';
|
|
777
|
+
*
|
|
778
|
+
* const response = await fetch('https://example.com/data.csv');
|
|
779
|
+
*
|
|
780
|
+
* const records = await parse.toArray(response);
|
|
781
|
+
* console.log(records);
|
|
782
|
+
* ```
|
|
783
|
+
*/
|
|
313
784
|
function toArray<Header extends ReadonlyArray<string>>(
|
|
314
|
-
csv:
|
|
785
|
+
csv: ReadableStream<Uint8Array> | Response,
|
|
315
786
|
options?: ParseOptions<Header>,
|
|
316
787
|
): Promise<CSVRecord<Header>[]>;
|
|
317
788
|
}
|
|
@@ -332,6 +803,8 @@ export {
|
|
|
332
803
|
type TokenType,
|
|
333
804
|
parse,
|
|
334
805
|
parseBinaryStream,
|
|
806
|
+
parseResponse,
|
|
807
|
+
parseStream,
|
|
808
|
+
parseString,
|
|
335
809
|
parseStringStream,
|
|
336
|
-
streamingParse,
|
|
337
810
|
};
|
package/lib/index.js
CHANGED
|
@@ -1,33 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FiledDelimiter is a symbol for field delimiter of CSV.
|
|
3
|
-
*/
|
|
4
1
|
const FieldDelimiter = Symbol.for("web-streams-csv.FieldDelimiter");
|
|
5
|
-
/**
|
|
6
|
-
* RecordDelimiter is a symbol for record delimiter of CSV.
|
|
7
|
-
*/
|
|
8
2
|
const RecordDelimiter = Symbol.for("web-streams-csv.RecordDelimiter");
|
|
9
|
-
/**
|
|
10
|
-
* Field is a symbol for field of CSV.
|
|
11
|
-
*/
|
|
12
3
|
const Field = Symbol.for("web-streams-csv.Field");
|
|
13
4
|
|
|
14
5
|
const CR = "\r";
|
|
15
6
|
const CRLF = "\r\n";
|
|
16
7
|
const LF = "\n";
|
|
17
|
-
/**
|
|
18
|
-
* COMMA is a symbol for comma(,).
|
|
19
|
-
*/
|
|
20
8
|
const COMMA = ",";
|
|
21
|
-
/**
|
|
22
|
-
* DOUBLE_QUATE is a symbol for double quate(").
|
|
23
|
-
*/
|
|
24
9
|
const DOUBLE_QUATE = '"';
|
|
25
10
|
|
|
26
|
-
/**
|
|
27
|
-
* Assert that the options are valid.
|
|
28
|
-
*
|
|
29
|
-
* @param options The options to assert.
|
|
30
|
-
*/
|
|
31
11
|
function assertCommonOptions(options) {
|
|
32
12
|
if (typeof options.quotation === "string" && options.quotation.length === 0) {
|
|
33
13
|
throw new Error("quotation must not be empty");
|
|
@@ -51,41 +31,10 @@ function assertCommonOptions(options) {
|
|
|
51
31
|
}
|
|
52
32
|
}
|
|
53
33
|
|
|
54
|
-
/**
|
|
55
|
-
* Escape a string for use in a regular expression.
|
|
56
|
-
*
|
|
57
|
-
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions#escaping Regular expressions#Escaping | MDN}
|
|
58
|
-
* @param v string to escape
|
|
59
|
-
* @returns escaped string
|
|
60
|
-
*/
|
|
61
34
|
function escapeRegExp(v) {
|
|
62
35
|
return v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
63
36
|
}
|
|
64
37
|
|
|
65
|
-
/**
|
|
66
|
-
* A transform stream that converts a stream of tokens into a stream of rows.
|
|
67
|
-
*
|
|
68
|
-
* @example Parse a CSV with headers by data
|
|
69
|
-
* ```ts
|
|
70
|
-
* new ReadableStream({
|
|
71
|
-
* start(controller) {
|
|
72
|
-
* controller.enqueue("name,age\r\n");
|
|
73
|
-
* controller.enqueue("Alice,20\r\n");
|
|
74
|
-
* controller.close();
|
|
75
|
-
* }
|
|
76
|
-
* })
|
|
77
|
-
* .pipeThrough(new LexerTransformer())
|
|
78
|
-
* .pipeTo(new WritableStream({ write(token) { console.log(token); }}));
|
|
79
|
-
* // { type: Field, value: "name" }
|
|
80
|
-
* // { type: FieldDelimiter, value: "," }
|
|
81
|
-
* // { type: Field, value: "age" }
|
|
82
|
-
* // { type: RecordDelimiter, value: "\r\n" }
|
|
83
|
-
* // { type: Field, value: "Alice" }
|
|
84
|
-
* // { type: FieldDelimiter, value: "," }
|
|
85
|
-
* // { type: Field, value: "20" }
|
|
86
|
-
* // { type: RecordDelimiter, value: "\r\n" }
|
|
87
|
-
* ```
|
|
88
|
-
*/
|
|
89
38
|
class LexerTransformer extends TransformStream {
|
|
90
39
|
#demiliter;
|
|
91
40
|
#demiliterLength;
|
|
@@ -161,49 +110,38 @@ class LexerTransformer extends TransformStream {
|
|
|
161
110
|
if (this.#buffer.length === 0) {
|
|
162
111
|
return null;
|
|
163
112
|
}
|
|
164
|
-
// Check for CRLF
|
|
165
113
|
if (this.#buffer.startsWith(CRLF)) {
|
|
166
114
|
this.#buffer = this.#buffer.slice(2);
|
|
167
115
|
return { type: RecordDelimiter, value: CRLF };
|
|
168
116
|
}
|
|
169
|
-
// Check for LF
|
|
170
117
|
if (this.#buffer.startsWith(LF)) {
|
|
171
118
|
this.#buffer = this.#buffer.slice(1);
|
|
172
119
|
return { type: RecordDelimiter, value: LF };
|
|
173
120
|
}
|
|
174
|
-
// Check for Delimiter
|
|
175
121
|
if (this.#buffer.startsWith(this.#demiliter)) {
|
|
176
122
|
this.#buffer = this.#buffer.slice(this.#demiliterLength);
|
|
177
123
|
return { type: FieldDelimiter, value: this.#demiliter };
|
|
178
124
|
}
|
|
179
|
-
// Check for Quoted String
|
|
180
125
|
if (this.#buffer.startsWith(this.#quotation)) {
|
|
181
|
-
// If we're flushing and the buffer doesn't end with a quote, then return null
|
|
182
|
-
// because we're not done with the quoted string
|
|
183
126
|
if (flush === false && this.#buffer.endsWith(this.#quotation)) {
|
|
184
127
|
return null;
|
|
185
128
|
}
|
|
186
129
|
return this.extractQuotedString();
|
|
187
130
|
}
|
|
188
|
-
// Check for Unquoted String
|
|
189
131
|
const match = this.#matcher.exec(this.#buffer);
|
|
190
132
|
if (match) {
|
|
191
|
-
// If we're flushing and the match doesn't consume the entire buffer,
|
|
192
|
-
// then return null
|
|
193
133
|
if (flush === false && match[0].length === this.#buffer.length) {
|
|
194
134
|
return null;
|
|
195
135
|
}
|
|
196
136
|
this.#buffer = this.#buffer.slice(match[0].length);
|
|
197
137
|
return { type: Field, value: match[0] };
|
|
198
138
|
}
|
|
199
|
-
// Otherwise, return null
|
|
200
139
|
return null;
|
|
201
140
|
}
|
|
202
141
|
extractQuotedString() {
|
|
203
|
-
let end = this.#quotationLength;
|
|
142
|
+
let end = this.#quotationLength;
|
|
204
143
|
let value = "";
|
|
205
144
|
while (end < this.#buffer.length) {
|
|
206
|
-
// Escaped quote
|
|
207
145
|
if (
|
|
208
146
|
this.#buffer.slice(end, end + this.#quotationLength) ===
|
|
209
147
|
this.quotation &&
|
|
@@ -216,7 +154,6 @@ class LexerTransformer extends TransformStream {
|
|
|
216
154
|
end += this.#quotationLength * 2;
|
|
217
155
|
continue;
|
|
218
156
|
}
|
|
219
|
-
// Closing quote
|
|
220
157
|
if (
|
|
221
158
|
this.#buffer.slice(end, end + this.#quotationLength) === this.quotation
|
|
222
159
|
) {
|
|
@@ -226,52 +163,10 @@ class LexerTransformer extends TransformStream {
|
|
|
226
163
|
value += this.#buffer[end];
|
|
227
164
|
end++;
|
|
228
165
|
}
|
|
229
|
-
// If we get here, we've reached the end of the buffer
|
|
230
166
|
return null;
|
|
231
167
|
}
|
|
232
168
|
}
|
|
233
169
|
|
|
234
|
-
/**
|
|
235
|
-
* A transform stream that converts a stream of tokens into a stream of rows.
|
|
236
|
-
* @template Header The type of the header row.
|
|
237
|
-
* @param options The options for the parser.
|
|
238
|
-
*
|
|
239
|
-
* @example Parse a CSV with headers by data
|
|
240
|
-
* ```ts
|
|
241
|
-
* new ReadableStream({
|
|
242
|
-
* start(controller) {
|
|
243
|
-
* controller.enqueue("name,age\r\n");
|
|
244
|
-
* controller.enqueue("Alice,20\r\n");
|
|
245
|
-
* controller.enqueue("Bob,25\r\n");
|
|
246
|
-
* controller.enqueue("Charlie,30\r\n");
|
|
247
|
-
* controller.close();
|
|
248
|
-
* })
|
|
249
|
-
* .pipeThrough(new LexerTransformer())
|
|
250
|
-
* .pipeThrough(new RecordAssemblerTransformar())
|
|
251
|
-
* .pipeTo(new WritableStream({ write(row) { console.log(row); }}));
|
|
252
|
-
* // { name: "Alice", age: "20" }
|
|
253
|
-
* // { name: "Bob", age: "25" }
|
|
254
|
-
* // { name: "Charlie", age: "30" }
|
|
255
|
-
* ```
|
|
256
|
-
*
|
|
257
|
-
* @example Parse a CSV with headers by options
|
|
258
|
-
* ```ts
|
|
259
|
-
* new ReadableStream({
|
|
260
|
-
* start(controller) {
|
|
261
|
-
* controller.enqueue("Alice,20\r\n");
|
|
262
|
-
* controller.enqueue("Bob,25\r\n");
|
|
263
|
-
* controller.enqueue("Charlie,30\r\n");
|
|
264
|
-
* controller.close();
|
|
265
|
-
* }
|
|
266
|
-
* })
|
|
267
|
-
* .pipeThrough(new LexerTransformer())
|
|
268
|
-
* .pipeThrough(new RecordAssemblerTransformar({ header: ["name", "age"] }))
|
|
269
|
-
* .pipeTo(new WritableStream({ write(row) { console.log(row); }}));
|
|
270
|
-
* // { name: "Alice", age: "20" }
|
|
271
|
-
* // { name: "Bob", age: "25" }
|
|
272
|
-
* // { name: "Charlie", age: "30" }
|
|
273
|
-
* ```
|
|
274
|
-
*/
|
|
275
170
|
class RecordAssemblerTransformar extends TransformStream {
|
|
276
171
|
#fieldIndex = 0;
|
|
277
172
|
#row = [];
|
|
@@ -301,7 +196,6 @@ class RecordAssemblerTransformar extends TransformStream {
|
|
|
301
196
|
controller.enqueue(record);
|
|
302
197
|
}
|
|
303
198
|
}
|
|
304
|
-
// Reset the row fields buffer.
|
|
305
199
|
this.#fieldIndex = 0;
|
|
306
200
|
this.#row = new Array(this.#header?.length);
|
|
307
201
|
this.#darty = false;
|
|
@@ -310,7 +204,6 @@ class RecordAssemblerTransformar extends TransformStream {
|
|
|
310
204
|
},
|
|
311
205
|
flush: (controller) => {
|
|
312
206
|
if (this.#fieldIndex !== 0 && this.#header !== undefined) {
|
|
313
|
-
// console.log('B', this.#row)
|
|
314
207
|
if (this.#darty) {
|
|
315
208
|
const record = Object.fromEntries(
|
|
316
209
|
this.#header
|
|
@@ -356,12 +249,6 @@ async function toArray(...args) {
|
|
|
356
249
|
return rows;
|
|
357
250
|
}
|
|
358
251
|
|
|
359
|
-
/**
|
|
360
|
-
* Parse CSV string to records.
|
|
361
|
-
*
|
|
362
|
-
* @param stream CSV string stream to parse
|
|
363
|
-
* @param options Parsing options. See {@link ParseOptions}.
|
|
364
|
-
*/
|
|
365
252
|
async function* parseStringStream(stream, options) {
|
|
366
253
|
let controller;
|
|
367
254
|
const readable = new ReadableStream({
|
|
@@ -387,48 +274,30 @@ async function* parseStringStream(stream, options) {
|
|
|
387
274
|
reader.releaseLock();
|
|
388
275
|
}
|
|
389
276
|
}
|
|
390
|
-
(
|
|
391
|
-
parseStringStream
|
|
392
|
-
);
|
|
393
|
-
parseStringStream.toArray = toArray;
|
|
277
|
+
((parseStringStream) => {
|
|
278
|
+
parseStringStream.toArray = toArray;
|
|
279
|
+
})(parseStringStream || (parseStringStream = {}));
|
|
394
280
|
|
|
395
|
-
|
|
396
|
-
* Parse CSV string to records.
|
|
397
|
-
*
|
|
398
|
-
* @param csv CSV string to parse
|
|
399
|
-
* @param options Parsing options. See {@link ParseOptions}.
|
|
400
|
-
*/
|
|
401
|
-
async function* streamingParse(csv, options) {
|
|
281
|
+
async function* parseString(csv, options) {
|
|
402
282
|
yield* parseStringStream(new SingleValueReadableStream(csv), options);
|
|
403
283
|
}
|
|
404
|
-
(
|
|
405
|
-
|
|
284
|
+
((parseString) => {
|
|
285
|
+
parseString.toArray = toArray;
|
|
286
|
+
})(parseString || (parseString = {}));
|
|
406
287
|
|
|
407
|
-
/**
|
|
408
|
-
* Parse CSV to records.
|
|
409
|
-
* This function is for parsing a binary stream.
|
|
410
|
-
*
|
|
411
|
-
* @remarks
|
|
412
|
-
* If you want to parse a string, use {@link streamingParse}.
|
|
413
|
-
* @param stream CSV string to parse
|
|
414
|
-
* @param options Parsing options. See {@link ParseBinaryOptions}.
|
|
415
|
-
*/
|
|
416
288
|
async function* parseBinaryStream(stream, options) {
|
|
417
289
|
const { charset, fatal, ignoreBOM, decomposition } = options ?? {};
|
|
418
290
|
yield* parseStringStream(
|
|
419
291
|
[
|
|
420
|
-
// NOTE: if decompression is undefined, it will be ignored.
|
|
421
292
|
...(decomposition ? [new DecompressionStream(decomposition)] : []),
|
|
422
|
-
// NOTE: if charset is undefined, it will be decoded as utf-8.
|
|
423
293
|
new TextDecoderStream(charset, { fatal, ignoreBOM }),
|
|
424
294
|
].reduce((stream, transformer) => stream.pipeThrough(transformer), stream),
|
|
425
295
|
options,
|
|
426
296
|
);
|
|
427
297
|
}
|
|
428
|
-
(
|
|
429
|
-
parseBinaryStream
|
|
430
|
-
);
|
|
431
|
-
parseBinaryStream.toArray = toArray;
|
|
298
|
+
((parseBinaryStream) => {
|
|
299
|
+
parseBinaryStream.toArray = toArray;
|
|
300
|
+
})(parseBinaryStream || (parseBinaryStream = {}));
|
|
432
301
|
|
|
433
302
|
function parseMime(contentType) {
|
|
434
303
|
const [type, ...parameters] = contentType.split(";");
|
|
@@ -452,8 +321,6 @@ function parseResponse(response, options) {
|
|
|
452
321
|
}
|
|
453
322
|
const decomposition = headers.get("content-encoding") ?? undefined;
|
|
454
323
|
const charset = mime.parameters.charset ?? "utf-8";
|
|
455
|
-
// TODO: Support header=present and header=absent
|
|
456
|
-
// const header = mime.parameters.header ?? "present";
|
|
457
324
|
if (response.body === null) {
|
|
458
325
|
throw new Error("Response body is null");
|
|
459
326
|
}
|
|
@@ -463,74 +330,37 @@ function parseResponse(response, options) {
|
|
|
463
330
|
...options,
|
|
464
331
|
});
|
|
465
332
|
}
|
|
466
|
-
(
|
|
467
|
-
parseResponse.toArray = toArray;
|
|
333
|
+
((parseResponse) => {
|
|
334
|
+
parseResponse.toArray = toArray;
|
|
335
|
+
})(parseResponse || (parseResponse = {}));
|
|
468
336
|
|
|
469
|
-
/**
|
|
470
|
-
* Parse CSV Stream to records.
|
|
471
|
-
* string and Uint8Array are supported.
|
|
472
|
-
*
|
|
473
|
-
* @remarks
|
|
474
|
-
* {@link parseStringStream} and {@link parseBinaryStream} are used internally.
|
|
475
|
-
* If you known the type of the stream, it performs better to use them directly.
|
|
476
|
-
*
|
|
477
|
-
* If you want to parse a string, use {@link parseStringStream}.
|
|
478
|
-
* If you want to parse a Uint8Array, use {@link parseBinaryStream}.
|
|
479
|
-
*
|
|
480
|
-
* @param csv CSV string to parse
|
|
481
|
-
* @param options Parsing options. See {@link ParserOptions}.
|
|
482
|
-
*/
|
|
483
337
|
async function* parseStream(stream, options) {
|
|
484
338
|
const [branch1, branch2] = stream.tee();
|
|
485
339
|
const reader1 = branch1.getReader();
|
|
486
340
|
const { value: firstChunk } = await reader1.read();
|
|
487
341
|
reader1.releaseLock();
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
case firstChunk instanceof Uint8Array:
|
|
493
|
-
yield* parseBinaryStream(branch2, options);
|
|
494
|
-
break;
|
|
342
|
+
if (typeof firstChunk === "string") {
|
|
343
|
+
yield* parseStringStream(branch2, options);
|
|
344
|
+
} else if (firstChunk instanceof Uint8Array) {
|
|
345
|
+
yield* parseBinaryStream(branch2, options);
|
|
495
346
|
}
|
|
496
347
|
}
|
|
497
|
-
(
|
|
498
|
-
parseStream.toArray = toArray;
|
|
348
|
+
((parseStream) => {
|
|
349
|
+
parseStream.toArray = toArray;
|
|
350
|
+
})(parseStream || (parseStream = {}));
|
|
499
351
|
|
|
500
|
-
/**
|
|
501
|
-
* Parse CSV to records.
|
|
502
|
-
*
|
|
503
|
-
* {@link String}, {@link Uint8Array}, ReadableStream<string | Uint8Array> and Response are supported.
|
|
504
|
-
*
|
|
505
|
-
* @remarks
|
|
506
|
-
* {@link streamingParse}, {@link parseBinaryStream},
|
|
507
|
-
* {@link parseStringStream} and {@link parseResponse} are used internally.
|
|
508
|
-
* If you known the type of the stream, it performs better to use them directly.
|
|
509
|
-
*
|
|
510
|
-
* If you want to parse a string, use {@link streamingParse}.
|
|
511
|
-
* If you want to parse a Uint8Array, use {@link parseStream}.
|
|
512
|
-
* If you want to parse a ReadableStream<string>, use {@link parseStringStream}.
|
|
513
|
-
* If you want to parse a ReadableStream<Uint8Array>, use {@link parseBinaryStream}.
|
|
514
|
-
* If you want to parse a Response, use {@link parseResponse}.
|
|
515
|
-
*
|
|
516
|
-
* @param csv CSV string to parse
|
|
517
|
-
* @param options Parsing options. See {@link ParseOptions}.
|
|
518
|
-
*/
|
|
519
352
|
async function* parse(csv, options) {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
break;
|
|
527
|
-
case csv instanceof Response:
|
|
528
|
-
yield* parseResponse(csv, options);
|
|
529
|
-
break;
|
|
353
|
+
if (typeof csv === "string") {
|
|
354
|
+
yield* parseString(csv, options);
|
|
355
|
+
} else if (csv instanceof ReadableStream) {
|
|
356
|
+
yield* parseStream(csv, options);
|
|
357
|
+
} else if (csv instanceof Response) {
|
|
358
|
+
yield* parseResponse(csv, options);
|
|
530
359
|
}
|
|
531
360
|
}
|
|
532
|
-
(
|
|
533
|
-
parse.toArray = toArray;
|
|
361
|
+
((parse) => {
|
|
362
|
+
parse.toArray = toArray;
|
|
363
|
+
})(parse || (parse = {}));
|
|
534
364
|
|
|
535
365
|
export {
|
|
536
366
|
Field,
|
|
@@ -540,6 +370,8 @@ export {
|
|
|
540
370
|
RecordDelimiter,
|
|
541
371
|
parse,
|
|
542
372
|
parseBinaryStream,
|
|
373
|
+
parseResponse,
|
|
374
|
+
parseStream,
|
|
375
|
+
parseString,
|
|
543
376
|
parseStringStream,
|
|
544
|
-
streamingParse,
|
|
545
377
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "web-csv-toolbox",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A CSV Toolbox utilizing Web Standard APIs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -21,12 +21,13 @@
|
|
|
21
21
|
"README.md"
|
|
22
22
|
],
|
|
23
23
|
"scripts": {
|
|
24
|
+
"doc": "typedoc",
|
|
24
25
|
"test": "vitest",
|
|
25
26
|
"format": "biome format . --write",
|
|
26
27
|
"lint": "biome lint .",
|
|
27
28
|
"check": "biome check src --apply",
|
|
28
29
|
"check:no-apply": "biome check src",
|
|
29
|
-
"build": "rollup -c rollup.config.ts --configPlugin rollup-plugin-typescript2 && biome
|
|
30
|
+
"build": "rollup -c rollup.config.ts --configPlugin rollup-plugin-typescript2 && biome check lib --apply",
|
|
30
31
|
"prepare": "husky install"
|
|
31
32
|
},
|
|
32
33
|
"repository": {
|
|
@@ -44,11 +45,13 @@
|
|
|
44
45
|
"bugs": {
|
|
45
46
|
"url": "https://github.com/kamiazya/web-csv-toolbox/issues"
|
|
46
47
|
},
|
|
47
|
-
"homepage": "https://github.
|
|
48
|
+
"homepage": "https://kamiazya.github.io/web-csv-toolbox/",
|
|
48
49
|
"devDependencies": {
|
|
49
50
|
"@biomejs/biome": "1.4.1",
|
|
51
|
+
"@changesets/changelog-github": "^0.5.0",
|
|
50
52
|
"@changesets/cli": "^2.27.1",
|
|
51
|
-
"@fast-check/vitest": "^0.0.
|
|
53
|
+
"@fast-check/vitest": "^0.0.9",
|
|
54
|
+
"changesets-github-release": "^0.1.0",
|
|
52
55
|
"husky": "^8.0.0",
|
|
53
56
|
"jsdom": "^23.0.1",
|
|
54
57
|
"lint-staged": "^15.2.0",
|
|
@@ -56,7 +59,9 @@
|
|
|
56
59
|
"rollup-plugin-delete": "^2.0.0",
|
|
57
60
|
"rollup-plugin-dts": "^6.1.0",
|
|
58
61
|
"rollup-plugin-typescript2": "^0.36.0",
|
|
62
|
+
"typedoc": "^0.25.4",
|
|
63
|
+
"typedoc-plugin-mdn-links": "^3.1.9",
|
|
59
64
|
"typescript": "^5.3.2",
|
|
60
|
-
"vitest": "^
|
|
65
|
+
"vitest": "^1.1.0"
|
|
61
66
|
}
|
|
62
67
|
}
|