csv-stream-lite 0.1.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/dist/parser.js ADDED
@@ -0,0 +1,596 @@
1
+ import { ByteBuffer } from './byte-buffer.js';
2
+ import { DEFAULT_CHUNK_SIZE } from './defaults.js';
3
+ import { TooFewColumnsError, TooManyColumnsError } from './errors.js';
4
+ import { CsvStringify } from './stringify.js';
5
+ import { bytesToString } from './utils.js';
6
+ const UTF_8_BOM = [0xef, 0xbb, 0xbf];
7
+ /**
8
+ * Map of character names to their byte values for CSV parsing.
9
+ * Contains commonly used characters in CSV syntax.
10
+ */
11
+ const BYTE_MAP = {
12
+ quotes: 34,
13
+ comma: 44,
14
+ backslash: 92,
15
+ space: 32,
16
+ tab: 9,
17
+ carriageReturn: 13,
18
+ lineFeed: 10,
19
+ };
20
+ const isLineEnd = (byte) => {
21
+ return byte === BYTE_MAP.lineFeed || byte === BYTE_MAP.carriageReturn;
22
+ };
23
+ /**
24
+ * Abstract base class for CSV entities that supports both synchronous and asynchronous parsing.
25
+ * Provides common functionality for reading and streaming CSV data.
26
+ *
27
+ * @typeParam T - The type returned by read operations
28
+ * @typeParam S - The type yielded by stream operations (defaults to T)
29
+ */
30
+ export class CsvEntity {
31
+ byteBuffer;
32
+ separator = ',';
33
+ escapeChar = '"';
34
+ consumed = false;
35
+ /**
36
+ * Creates a new CSV entity.
37
+ *
38
+ * @param asyncIterable - Optional byte stream or buffer to parse
39
+ * @param options - Configuration options for parsing
40
+ */
41
+ constructor(asyncIterable, options) {
42
+ this.byteBuffer =
43
+ asyncIterable instanceof ByteBuffer
44
+ ? asyncIterable
45
+ : new ByteBuffer(asyncIterable);
46
+ if (options?.separator) {
47
+ this.separator = options.separator;
48
+ }
49
+ if (options?.escapeChar) {
50
+ this.escapeChar = options.escapeChar;
51
+ }
52
+ }
53
+ set maxBufferSize(size) {
54
+ this.byteBuffer.maxBufferSize = size;
55
+ }
56
+ get maxBufferSize() {
57
+ return this.byteBuffer.maxBufferSize;
58
+ }
59
+ set allowBufferToBeExceeded(value) {
60
+ this.byteBuffer.allowBufferToBeExceeded = value;
61
+ }
62
+ get allowBufferToBeExceeded() {
63
+ return this.byteBuffer.allowBufferToBeExceeded;
64
+ }
65
+ /**
66
+ * Reads and parses the entire entity synchronously.
67
+ * Marks the entity as consumed after reading.
68
+ *
69
+ * @returns The parsed result of type T
70
+ */
71
+ read() {
72
+ const res = this.parse();
73
+ this.consumed = true;
74
+ return res;
75
+ }
76
+ /**
77
+ * Reads and parses the entire entity asynchronously.
78
+ * Marks the entity as consumed after reading.
79
+ *
80
+ * @returns A promise that resolves to the parsed result of type T
81
+ */
82
+ async readAsync() {
83
+ const res = await this.parseAsync();
84
+ this.consumed = true;
85
+ return res;
86
+ }
87
+ /**
88
+ * Returns a synchronous generator that yields chunks of type S.
89
+ * Marks the entity as consumed when iteration completes.
90
+ *
91
+ * @returns A generator that yields values of type S
92
+ */
93
+ *stream() {
94
+ const res = yield* this.streamImpl();
95
+ this.consumed = true;
96
+ return res;
97
+ }
98
+ /**
99
+ * Returns an asynchronous generator that yields chunks of type S.
100
+ * Handles buffering and automatically reads more data as needed.
101
+ *
102
+ * @returns An async generator that yields values of type S
103
+ */
104
+ async *streamAsync() {
105
+ while (true) {
106
+ const stream = this.stream();
107
+ let currentChunk = undefined;
108
+ while (true) {
109
+ currentChunk = this.byteBuffer.resetOnFail(() => stream.next());
110
+ if (currentChunk === undefined) {
111
+ break; // Need more data
112
+ }
113
+ if (currentChunk.done) {
114
+ return; // No more data
115
+ }
116
+ yield currentChunk.value;
117
+ }
118
+ await this.byteBuffer.readStreamAsync();
119
+ }
120
+ }
121
+ /**
122
+ * Consumes the entity if it hasn't been consumed yet.
123
+ * This ensures the buffer advances to the end of this entity.
124
+ */
125
+ consume() {
126
+ if (!this.consumed) {
127
+ this.read();
128
+ }
129
+ }
130
+ /**
131
+ * Asynchronously consumes the entity if it hasn't been consumed yet.
132
+ * This ensures the buffer advances to the end of this entity.
133
+ *
134
+ * @returns A promise that resolves when consumption is complete
135
+ */
136
+ async consumeAsync() {
137
+ if (!this.consumed) {
138
+ await this.readAsync();
139
+ }
140
+ }
141
+ /**
142
+ * Makes this entity iterable using the synchronous stream.
143
+ *
144
+ * @returns A generator that yields values of type S
145
+ */
146
+ [Symbol.iterator]() {
147
+ return this.stream();
148
+ }
149
+ /**
150
+ * Makes this entity async iterable using the asynchronous stream.
151
+ *
152
+ * @returns An async generator that yields values of type S
153
+ */
154
+ [Symbol.asyncIterator]() {
155
+ return this.streamAsync();
156
+ }
157
+ }
158
+ /**
159
+ * Represents a single CSV cell that can be read as a string or streamed in chunks.
160
+ * Handles quoted cells and escape sequences according to CSV standards.
161
+ */
162
+ export class CsvCell extends CsvEntity {
163
+ chunkSize = DEFAULT_CHUNK_SIZE;
164
+ endOfLineReached = false;
165
+ parse() {
166
+ let str = '';
167
+ for (const part of this) {
168
+ str += part;
169
+ }
170
+ return str;
171
+ }
172
+ async parseAsync() {
173
+ let str = '';
174
+ for await (const part of this) {
175
+ str += part;
176
+ }
177
+ return str;
178
+ }
179
+ /**
180
+ * Reads the cell value and transforms it using the provided function.
181
+ * Special handling for Boolean transformer: converts 'true'/'false' strings to boolean.
182
+ *
183
+ * @typeParam T - The type to transform the cell value into
184
+ * @param transform - Function to transform the cell string into type T
185
+ * @returns The transformed value of type T
186
+ */
187
+ readAs(transform) {
188
+ const cellStr = this.read();
189
+ if (transform === Boolean) {
190
+ return (String(cellStr).toLowerCase() === 'true');
191
+ }
192
+ return transform(cellStr);
193
+ }
194
+ /**
195
+ * Asynchronously reads the cell value and transforms it using the provided function.
196
+ * Special handling for Boolean transformer: converts 'true'/'false' strings to boolean.
197
+ *
198
+ * @typeParam T - The type to transform the cell value into
199
+ * @param transform - Function to transform the cell string into type T (can be async)
200
+ * @returns A promise that resolves to the transformed value of type T
201
+ */
202
+ async readAsAsync(transform) {
203
+ const cellStr = await this.readAsync();
204
+ if (transform === Boolean) {
205
+ return (cellStr.toLowerCase() === 'true');
206
+ }
207
+ return await transform(cellStr);
208
+ }
209
+ *streamImpl() {
210
+ const separator = this.separator.charCodeAt(0);
211
+ const escapeChar = this.escapeChar.charCodeAt(0);
212
+ let chunk = [];
213
+ let hadData = false;
214
+ let isEscaped = false;
215
+ const next = this.byteBuffer.peek();
216
+ if (next === null && this.byteBuffer.eof) {
217
+ throw new Error('No more data to read');
218
+ }
219
+ if (next === escapeChar) {
220
+ isEscaped = true;
221
+ this.byteBuffer.expect(escapeChar); // consume opening quote
222
+ }
223
+ while (this.byteBuffer.peek() !== null) {
224
+ const next = this.byteBuffer.peek();
225
+ if (isEscaped) {
226
+ if (next === escapeChar) {
227
+ // Possible end of quoted cell
228
+ const lookahead = this.byteBuffer.peek(1);
229
+ if (lookahead === escapeChar) {
230
+ // Escaped quote
231
+ this.byteBuffer.expect(escapeChar); // consume first quote
232
+ this.byteBuffer.expect(escapeChar); // consume second quote
233
+ chunk.push(escapeChar);
234
+ continue;
235
+ }
236
+ else {
237
+ // End of quoted cell
238
+ break;
239
+ }
240
+ }
241
+ }
242
+ else {
243
+ if (next === separator || isLineEnd(next)) {
244
+ break;
245
+ }
246
+ }
247
+ const nextByte = this.byteBuffer.next();
248
+ chunk.push(nextByte);
249
+ if (chunk.length >= this.chunkSize) {
250
+ yield bytesToString(new Uint8Array(chunk));
251
+ chunk = [];
252
+ hadData = true;
253
+ }
254
+ }
255
+ if (isEscaped) {
256
+ this.byteBuffer.expect(escapeChar); // consume closing quote
257
+ }
258
+ if (this.byteBuffer.peek() === separator) {
259
+ this.byteBuffer.expect(separator); // consume separator
260
+ }
261
+ while (isLineEnd(this.byteBuffer.peek())) {
262
+ this.byteBuffer.next(); // consume line ending
263
+ this.endOfLineReached = true;
264
+ }
265
+ if (!hadData || chunk.length > 0)
266
+ yield bytesToString(new Uint8Array(chunk));
267
+ }
268
+ }
269
+ /**
270
+ * Represents a single CSV row that can be read as an array of strings or streamed as individual cells.
271
+ * Can also be parsed into an object using a shape definition.
272
+ *
273
+ * @typeParam T - The object type when reading as an object
274
+ * @typeParam O - The output type after optional transformation (defaults to T)
275
+ */
276
+ export class CsvRow extends CsvEntity {
277
+ parse() {
278
+ const cells = [];
279
+ for (const cell of this) {
280
+ cells.push(cell.read());
281
+ }
282
+ return cells;
283
+ }
284
+ async parseAsync() {
285
+ const cells = [];
286
+ for await (const cell of this) {
287
+ cells.push(await cell.readAsync());
288
+ }
289
+ return cells;
290
+ }
291
+ *streamImpl() {
292
+ while (this.byteBuffer.peek() !== null) {
293
+ const cell = new CsvCell(this.byteBuffer, this);
294
+ yield cell;
295
+ cell.consume(); // Advance the buffer
296
+ if (cell.endOfLineReached) {
297
+ break;
298
+ }
299
+ }
300
+ }
301
+ /**
302
+ * Reads the row as an object using the provided shape definition.
303
+ * Handles column count validation and extra cells based on options.
304
+ *
305
+ * @param options - Configuration for reading the row as an object
306
+ * @returns The parsed object of type O
307
+ * @throws {TooManyColumnsError} If strictColumns is true and extra cells are found
308
+ * @throws {TooFewColumnsError} If strictColumns is true and cells are missing
309
+ */
310
+ readObject(options) {
311
+ let { shape, headers } = options;
312
+ let obj = {};
313
+ let i = 0;
314
+ const includeExtraCells = options?.includeExtraCells ?? false;
315
+ const strictColumns = options?.strictColumns ?? false;
316
+ if (Array.isArray(shape)) {
317
+ shape = shape.reduce((acc, header) => {
318
+ acc[header] = String;
319
+ return acc;
320
+ }, {});
321
+ }
322
+ for (const cell of this) {
323
+ if (!includeExtraCells && i >= headers.length) {
324
+ if (strictColumns) {
325
+ throw new TooManyColumnsError(`Extra cells found${options?.rowIndex ? ` in row ${options.rowIndex}` : ''} but strictColumns is enabled`);
326
+ }
327
+ cell.consume();
328
+ }
329
+ else {
330
+ const header = i >= headers.length
331
+ ? `extra_cell_${i - headers.length + 1}`
332
+ : headers[i];
333
+ obj[header] = cell.read();
334
+ }
335
+ i++;
336
+ if (cell.endOfLineReached) {
337
+ break;
338
+ }
339
+ }
340
+ for (; i < headers.length; i++) {
341
+ if (strictColumns) {
342
+ throw new TooFewColumnsError(`Not enough cells${options?.rowIndex ? ` in row ${options.rowIndex}` : ''} to match headers when strictColumns is enabled`);
343
+ }
344
+ const header = headers[i];
345
+ obj[header] = undefined;
346
+ }
347
+ if (options.transform) {
348
+ obj = options.transform(obj);
349
+ }
350
+ for (const transform in shape) {
351
+ const transformer = shape[transform];
352
+ if (obj[transform] !== undefined) {
353
+ if (transformer === Boolean) {
354
+ obj[transform] = String(obj[transform]) === 'true';
355
+ }
356
+ else {
357
+ obj[transform] = transformer(obj[transform]);
358
+ }
359
+ }
360
+ }
361
+ this.consumed = true;
362
+ return obj;
363
+ }
364
+ /**
365
+ * Asynchronously reads the row as an object using the provided shape definition.
366
+ * Automatically handles buffer refills as needed.
367
+ *
368
+ * @param options - Configuration for reading the row as an object
369
+ * @returns A promise that resolves to the parsed object of type O
370
+ */
371
+ async readObjectAsync(options) {
372
+ while (true) {
373
+ const value = this.byteBuffer.resetOnFail(() => {
374
+ return this.readObject(options);
375
+ });
376
+ if (value !== undefined) {
377
+ return value;
378
+ }
379
+ await this.byteBuffer.readStreamAsync();
380
+ }
381
+ }
382
+ }
383
+ /**
384
+ * Main CSV parser class for parsing complete CSV documents.
385
+ * Supports reading headers, streaming rows, and converting rows to objects.
386
+ *
387
+ * @typeParam T - The object type for each row when reading as objects
388
+ * @typeParam O - The output type after optional transformation (defaults to T)
389
+ *
390
+ * @example
391
+ * ```typescript
392
+ * // Parse CSV with headers
393
+ * const csv = new Csv(fileStream)
394
+ * for await (const row of csv.streamObjectsAsync()) {
395
+ * console.log(row)
396
+ * }
397
+ * ```
398
+ */
399
+ export class Csv extends CsvEntity {
400
+ includeExtraCells = false;
401
+ ignoreUtf8Bom = true;
402
+ headers;
403
+ shape;
404
+ readHeaders = true;
405
+ strictColumns = false;
406
+ transform;
407
+ /**
408
+ * Creates a new CSV parser.
409
+ *
410
+ * @param asyncIterable - Optional byte stream or buffer containing CSV data
411
+ * @param options - Configuration options for CSV parsing
412
+ * @throws {Error} If both headers and shape options are specified
413
+ */
414
+ constructor(asyncIterable, options) {
415
+ super(asyncIterable, options);
416
+ if (options?.includeExtraCells) {
417
+ this.includeExtraCells = options.includeExtraCells;
418
+ }
419
+ if (options?.ignoreUtf8Bom !== undefined) {
420
+ this.ignoreUtf8Bom = options.ignoreUtf8Bom;
421
+ }
422
+ if (options?.headers) {
423
+ this.headers = options.headers;
424
+ }
425
+ if (options?.readHeaders !== undefined) {
426
+ this.readHeaders = options.readHeaders;
427
+ }
428
+ if (options?.strictColumns !== undefined) {
429
+ this.strictColumns = options.strictColumns;
430
+ }
431
+ if (options?.shape && this.headers) {
432
+ throw new Error('Cannot specify both headers and shape options');
433
+ }
434
+ this.shape = options?.shape;
435
+ this.transform = options?.transform;
436
+ }
437
+ readBom() {
438
+ // Skip UTF-8 BOM if present
439
+ while (this.ignoreUtf8Bom &&
440
+ this.byteBuffer.peek() === UTF_8_BOM[0] &&
441
+ this.byteBuffer.peek(1) === UTF_8_BOM[1] &&
442
+ this.byteBuffer.peek(2) === UTF_8_BOM[2]) {
443
+ this.byteBuffer.next();
444
+ this.byteBuffer.next();
445
+ this.byteBuffer.next();
446
+ }
447
+ }
448
+ parse() {
449
+ return Array.from(this.streamObjects());
450
+ }
451
+ async parseAsync() {
452
+ const results = [];
453
+ for await (const obj of this.streamObjectsAsync()) {
454
+ results.push(obj);
455
+ }
456
+ return results;
457
+ }
458
+ *streamImpl() {
459
+ // Skip UTF-8 BOM if present
460
+ this.readBom();
461
+ while (this.byteBuffer.peek() !== null) {
462
+ const row = new CsvRow(this.byteBuffer, this);
463
+ yield row;
464
+ row.consume(); // Advance the buffer
465
+ }
466
+ this.consumed = true;
467
+ }
468
+ /**
469
+ * Synchronously streams CSV rows as objects.
470
+ * Reads headers from the first row if readHeaders is true.
471
+ *
472
+ * @returns A generator that yields parsed objects of type O
473
+ *
474
+ * @example
475
+ * ```typescript
476
+ * const csv = new Csv(csvString)
477
+ * for (const row of csv.streamObjects()) {
478
+ * console.log(row)
479
+ * }
480
+ * ```
481
+ */
482
+ *streamObjects() {
483
+ const stream = this.stream();
484
+ let headers = this.headers ?? [];
485
+ if (this.readHeaders) {
486
+ const headerRow = stream.next();
487
+ if (headerRow.done) {
488
+ return; // No data
489
+ }
490
+ const foundHeaders = headerRow.value.read();
491
+ if (headers.length === 0) {
492
+ headers = foundHeaders;
493
+ }
494
+ }
495
+ let rowIndex = this.readHeaders ? 2 : 1;
496
+ while (true) {
497
+ const currentRow = stream.next();
498
+ if (currentRow.done) {
499
+ return; // No more data
500
+ }
501
+ yield currentRow.value.readObject({
502
+ headers: headers,
503
+ shape: this.shape,
504
+ includeExtraCells: this.includeExtraCells,
505
+ strictColumns: this.strictColumns,
506
+ rowIndex: rowIndex++,
507
+ transform: this.transform,
508
+ });
509
+ }
510
+ }
511
+ /**
512
+ * Asynchronously streams CSV rows as objects.
513
+ * Automatically handles buffer refills as needed.
514
+ *
515
+ * @returns An async generator that yields parsed objects of type O
516
+ *
517
+ * @example
518
+ * ```typescript
519
+ * const csv = new Csv(fileStream)
520
+ * for await (const row of csv.streamObjectsAsync()) {
521
+ * console.log(row)
522
+ * }
523
+ * ```
524
+ */
525
+ async *streamObjectsAsync() {
526
+ while (true) {
527
+ const stream = this.streamObjects();
528
+ let currentRow = undefined;
529
+ while (true) {
530
+ currentRow = this.byteBuffer.resetOnFail(() => stream.next());
531
+ if (currentRow === undefined) {
532
+ break; // Need more data
533
+ }
534
+ if (currentRow.done) {
535
+ return; // No more data
536
+ }
537
+ yield currentRow.value;
538
+ }
539
+ await this.byteBuffer.readStreamAsync();
540
+ }
541
+ }
542
+ /**
543
+ * Static method to stringify an array of objects into CSV format.
544
+ * Returns a synchronous generator that yields CSV string chunks.
545
+ *
546
+ * @typeParam T - The input object type
547
+ * @typeParam O - The output object type after optional transformation (defaults to T)
548
+ * @param values - Array of objects to convert to CSV
549
+ * @param options - Optional configuration for CSV stringification
550
+ * @returns A generator that yields CSV string chunks
551
+ *
552
+ * @example
553
+ * ```typescript
554
+ * const data = [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]
555
+ * for (const chunk of Csv.stringify(data, { headers: ['name', 'age'] })) {
556
+ * process.stdout.write(chunk)
557
+ * }
558
+ * ```
559
+ */
560
+ static stringify(values, options) {
561
+ return new CsvStringify(values, options).toStream();
562
+ }
563
+ /**
564
+ * Static method to stringify an array of objects into CSV format asynchronously.
565
+ * Returns an asynchronous generator that yields CSV string chunks.
566
+ *
567
+ * @typeParam T - The input object type
568
+ * @typeParam O - The output object type after optional transformation (defaults to T)
569
+ * @param values - Array of objects to convert to CSV
570
+ * @param options - Optional configuration for CSV stringification
571
+ * @returns An async generator that yields CSV string chunks
572
+ *
573
+ * @example
574
+ * ```typescript
575
+ * const data = [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]
576
+ * for await (const chunk of Csv.stringifyAsync(data)) {
577
+ * process.stdout.write(chunk)
578
+ * }
579
+ * ```
580
+ */
581
+ static stringifyAsync(values, options) {
582
+ return new CsvStringify(values, options).toStreamAsync();
583
+ }
584
+ /**
585
+ * Static method to create a CsvStringify instance for advanced usage.
586
+ *
587
+ * @typeParam T - The input object type
588
+ * @typeParam O - The output object type after optional transformation (defaults to T)
589
+ * @param values - Array of objects to convert to CSV
590
+ * @param options - Optional configuration for CSV stringification
591
+ * @returns A CsvStringify instance
592
+ */
593
+ static stringifier(values, options) {
594
+ return new CsvStringify(values, options);
595
+ }
596
+ }
@@ -0,0 +1,91 @@
1
+ import { CsvObjectShape, CsvString } from './types.js';
2
+ export interface CsvStringifyOptions<T extends object = object, O extends object = T> {
3
+ /** Optional array of headers to use as the first row. Optionally, pass in a CsvObjectShape to be used as headers */
4
+ headers?: string[] | CsvObjectShape<T>;
5
+ /** Character to separate fields (default: ',') */
6
+ delimiter?: string;
7
+ /** Character to escape special characters (default: '"') */
8
+ escapeChar?: string;
9
+ /** Character to quote fields (default: '"') */
10
+ quoteChar?: string;
11
+ /** Newline character (default: '\n') */
12
+ newline?: string;
13
+ /** Whether to write a UTF-8 BOM at the start of the output (default: false) */
14
+ writeBom?: boolean;
15
+ /** Whether to always write headers, even if there are no records (default: false) */
16
+ alwaysWriteHeaders?: boolean;
17
+ /** Optional transform function to modify each record before stringifying */
18
+ transform?: (row: T) => O;
19
+ }
20
+ /**
21
+ * CSV stringifier class for converting JavaScript objects into CSV format.
22
+ * Supports both synchronous and asynchronous streaming of CSV output.
23
+ *
24
+ * @typeParam T - The input object type
25
+ * @typeParam O - The output object type after optional transformation (defaults to T)
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const data = [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]
30
+ * const stringifier = new CsvStringify(data, { headers: ['name', 'age'] })
31
+ * const csvString = stringifier.toString()
32
+ * ```
33
+ */
34
+ export declare class CsvStringify<T extends object = object, O extends object = T> {
35
+ private values;
36
+ headers?: string[];
37
+ delimiter: string;
38
+ escapeChar: string;
39
+ quoteChar: string;
40
+ newline: string;
41
+ writeBom: boolean;
42
+ alwaysWriteHeaders: boolean;
43
+ transform?: (row: T) => O;
44
+ /**
45
+ * Creates a new CSV stringifier.
46
+ *
47
+ * @param values - Iterable or async iterable of objects to stringify
48
+ * @param options - Optional configuration for CSV stringification
49
+ */
50
+ constructor(values: Iterable<T> | AsyncIterable<T>, options?: CsvStringifyOptions<T, O>);
51
+ private formatField;
52
+ private recordToString;
53
+ private forceWriteHeaders;
54
+ /**
55
+ * Returns a synchronous generator that yields CSV string chunks.
56
+ *
57
+ * @returns A generator that yields CSV strings
58
+ * @throws {Error} If values is not iterable
59
+ */
60
+ toStream(): Generator<CsvString<O>>;
61
+ /**
62
+ * Returns an asynchronous generator that yields CSV string chunks.
63
+ *
64
+ * @returns An async generator that yields CSV strings
65
+ */
66
+ toStreamAsync(): AsyncGenerator<CsvString<O>>;
67
+ /**
68
+ * Converts all values to a single CSV string synchronously.
69
+ *
70
+ * @returns The complete CSV string
71
+ */
72
+ toString(): CsvString<O>;
73
+ /**
74
+ * Converts all values to a single CSV string asynchronously.
75
+ *
76
+ * @returns A promise that resolves to the complete CSV string
77
+ */
78
+ toStringAsync(): Promise<CsvString<O>>;
79
+ /**
80
+ * Makes this stringifier iterable using the synchronous stream.
81
+ *
82
+ * @returns A generator that yields CSV strings
83
+ */
84
+ [Symbol.iterator](): Generator<CsvString<O>>;
85
+ /**
86
+ * Makes this stringifier async iterable using the asynchronous stream.
87
+ *
88
+ * @returns An async generator that yields CSV strings
89
+ */
90
+ [Symbol.asyncIterator](): AsyncGenerator<CsvString<O>>;
91
+ }