web-csv-toolbox 0.13.0-next-c3f786a4e480ec1609f1e5d305b955e18f6a1ac4 → 0.13.0-next-bd865d6ddb1cf9691d7b9a83d0790651f074dd47
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 +2 -2
- package/dist/{Lexer.d.ts → CSVLexer.d.ts} +16 -11
- package/dist/{Lexer.js → CSVLexer.js} +10 -17
- package/dist/CSVLexer.js.map +1 -0
- package/dist/{LexerTransformer.d.ts → CSVLexerTransformer.d.ts} +7 -7
- package/dist/{LexerTransformer.js → CSVLexerTransformer.js} +7 -7
- package/dist/CSVLexerTransformer.js.map +1 -0
- package/dist/CSVRecordAssembler.d.ts +27 -0
- package/dist/CSVRecordAssembler.js +111 -0
- package/dist/CSVRecordAssembler.js.map +1 -0
- package/dist/{RecordAssemblerTransformer.d.ts → CSVRecordAssemblerTransformer.d.ts} +10 -10
- package/dist/{RecordAssemblerTransformer.js → CSVRecordAssemblerTransformer.js} +7 -7
- package/dist/CSVRecordAssemblerTransformer.js.map +1 -0
- package/dist/_virtual/web_csv_toolbox_wasm_bg.wasm.js +1 -1
- package/dist/common/types.d.ts +5 -5
- package/dist/parseStringStreamToStream.js +4 -4
- package/dist/parseStringStreamToStream.js.map +1 -1
- package/dist/parseStringToArraySync.js +4 -4
- package/dist/parseStringToArraySync.js.map +1 -1
- package/dist/parseStringToIterableIterator.js +4 -4
- package/dist/parseStringToIterableIterator.js.map +1 -1
- package/dist/parseStringToStream.js +4 -4
- package/dist/parseStringToStream.js.map +1 -1
- package/dist/parseUint8ArrayStreamToStream.js +6 -6
- package/dist/parseUint8ArrayStreamToStream.js.map +1 -1
- package/dist/web-csv-toolbox.d.ts +4 -2
- package/dist/web-csv-toolbox.js +4 -2
- package/dist/web-csv-toolbox.js.map +1 -1
- package/dist/web_csv_toolbox_wasm_bg.wasm +0 -0
- package/package.json +1 -1
- package/dist/Lexer.js.map +0 -1
- package/dist/LexerTransformer.js.map +0 -1
- package/dist/RecordAssembler.d.ts +0 -7
- package/dist/RecordAssembler.js +0 -105
- package/dist/RecordAssembler.js.map +0 -1
- package/dist/RecordAssemblerTransformer.js.map +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { CSVLexer } from './CSVLexer.js';
|
|
2
|
+
import { CSVRecordAssembler } from './CSVRecordAssembler.js';
|
|
3
3
|
import { commonParseErrorHandling } from './commonParseErrorHandling.js';
|
|
4
4
|
|
|
5
5
|
function parseStringToArraySync(csv, options) {
|
|
6
6
|
try {
|
|
7
|
-
const lexer = new
|
|
8
|
-
const assembler = new
|
|
7
|
+
const lexer = new CSVLexer(options);
|
|
8
|
+
const assembler = new CSVRecordAssembler(options);
|
|
9
9
|
const tokens = lexer.lex(csv);
|
|
10
10
|
return [...assembler.assemble(tokens)];
|
|
11
11
|
} catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseStringToArraySync.js","sources":["../src/parseStringToArraySync.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"parseStringToArraySync.js","sources":["../src/parseStringToArraySync.ts"],"sourcesContent":["import { CSVLexer } from \"./CSVLexer.ts\";\nimport { CSVRecordAssembler } from \"./CSVRecordAssembler.ts\";\nimport type { CSVRecord, ParseOptions } from \"./common/types.ts\";\nimport { commonParseErrorHandling } from \"./commonParseErrorHandling.ts\";\nimport type { DEFAULT_DELIMITER, DEFAULT_QUOTATION } from \"./constants.ts\";\nimport type { PickCSVHeader } from \"./utils/types.ts\";\n\n/**\n * Synchronously parses a CSV string into an array of records.\n *\n * @param csv - The CSV string to parse.\n * @param options - Parsing options including delimiter, quotation, header, etc.\n * @returns An array of CSV records.\n * @throws {ParseError} If the CSV data is malformed.\n *\n * @remarks\n * **WARNING**: This function loads all parsed records into memory as an array.\n * For CSV data with a large number of records, consider using `parseStringToIterableIterator()`\n * to iterate over records without loading them all into memory at once.\n *\n * @example\n * ```ts\n * const csv = \"name,age\\nAlice,30\\nBob,25\";\n * const records = parseStringToArraySync(csv);\n * // [{ name: \"Alice\", age: \"30\" }, { name: \"Bob\", age: \"25\" }]\n * ```\n */\nexport function parseStringToArraySync<\n const CSVSource extends string,\n const Delimiter extends string = DEFAULT_DELIMITER,\n const Quotation extends string = DEFAULT_QUOTATION,\n const Header extends ReadonlyArray<string> = PickCSVHeader<\n CSVSource,\n Delimiter,\n Quotation\n >,\n>(\n csv: CSVSource,\n options: ParseOptions<Header, Delimiter, Quotation>,\n): CSVRecord<Header>[];\nexport function parseStringToArraySync<\n const CSVSource extends string,\n const Header extends ReadonlyArray<string> = PickCSVHeader<CSVSource>,\n>(csv: CSVSource, options?: ParseOptions<Header>): CSVRecord<Header>[];\nexport function parseStringToArraySync<\n const Header extends ReadonlyArray<string>,\n>(csv: string, options?: ParseOptions<Header>): CSVRecord<Header>[];\nexport function parseStringToArraySync<\n const Header extends ReadonlyArray<string>,\n>(csv: string, options?: ParseOptions<Header>): CSVRecord<Header>[] {\n try {\n const lexer = new CSVLexer(options);\n const assembler = new CSVRecordAssembler(options);\n const tokens = lexer.lex(csv);\n return [...assembler.assemble(tokens)];\n } catch (error) {\n commonParseErrorHandling(error);\n }\n}\n"],"names":[],"mappings":";;;;AA+CgB,SAAA,sBAAA,CAEd,KAAa,OAAqD,EAAA;AAClE,EAAI,IAAA;AACF,IAAM,MAAA,KAAA,GAAQ,IAAI,QAAA,CAAS,OAAO,CAAA;AAClC,IAAM,MAAA,SAAA,GAAY,IAAI,kBAAA,CAAmB,OAAO,CAAA;AAChD,IAAM,MAAA,MAAA,GAAS,KAAM,CAAA,GAAA,CAAI,GAAG,CAAA;AAC5B,IAAA,OAAO,CAAC,GAAG,SAAU,CAAA,QAAA,CAAS,MAAM,CAAC,CAAA;AAAA,WAC9B,KAAO,EAAA;AACd,IAAA,wBAAA,CAAyB,KAAK,CAAA;AAAA;AAElC;;;;"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { CSVLexer } from './CSVLexer.js';
|
|
2
|
+
import { CSVRecordAssembler } from './CSVRecordAssembler.js';
|
|
3
3
|
import { commonParseErrorHandling } from './commonParseErrorHandling.js';
|
|
4
4
|
|
|
5
5
|
function parseStringToIterableIterator(csv, options) {
|
|
6
6
|
try {
|
|
7
|
-
const lexer = new
|
|
8
|
-
const assembler = new
|
|
7
|
+
const lexer = new CSVLexer(options);
|
|
8
|
+
const assembler = new CSVRecordAssembler(options);
|
|
9
9
|
const tokens = lexer.lex(csv);
|
|
10
10
|
return assembler.assemble(tokens);
|
|
11
11
|
} catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseStringToIterableIterator.js","sources":["../src/parseStringToIterableIterator.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"parseStringToIterableIterator.js","sources":["../src/parseStringToIterableIterator.ts"],"sourcesContent":["import { CSVLexer } from \"./CSVLexer.ts\";\nimport { CSVRecordAssembler } from \"./CSVRecordAssembler.ts\";\nimport type { CSVRecord, ParseOptions } from \"./common/types.ts\";\nimport { commonParseErrorHandling } from \"./commonParseErrorHandling.ts\";\nimport type { DEFAULT_DELIMITER, DEFAULT_QUOTATION } from \"./constants.ts\";\nimport type { PickCSVHeader } from \"./utils/types.ts\";\n\nexport function parseStringToIterableIterator<\n const CSVSource extends string,\n const Delimiter extends string = DEFAULT_DELIMITER,\n const Quotation extends string = DEFAULT_QUOTATION,\n const Header extends ReadonlyArray<string> = PickCSVHeader<\n CSVSource,\n Delimiter,\n Quotation\n >,\n>(\n stream: CSVSource,\n options: ParseOptions<Header, Delimiter, Quotation>,\n): IterableIterator<CSVRecord<Header>>;\nexport function parseStringToIterableIterator<\n const CSVSource extends string,\n const Header extends ReadonlyArray<string> = PickCSVHeader<CSVSource>,\n>(\n stream: CSVSource,\n options?: ParseOptions<Header>,\n): IterableIterator<CSVRecord<Header>>;\nexport function parseStringToIterableIterator<\n const Header extends ReadonlyArray<string>,\n>(\n stream: string,\n options?: ParseOptions<Header>,\n): IterableIterator<CSVRecord<Header>>;\nexport function parseStringToIterableIterator<\n const Header extends ReadonlyArray<string>,\n>(\n csv: string,\n options?: ParseOptions<Header>,\n): IterableIterator<CSVRecord<Header>> {\n try {\n const lexer = new CSVLexer(options);\n const assembler = new CSVRecordAssembler(options);\n const tokens = lexer.lex(csv);\n return assembler.assemble(tokens);\n } catch (error) {\n commonParseErrorHandling(error);\n }\n}\n"],"names":[],"mappings":";;;;AAiCgB,SAAA,6BAAA,CAGd,KACA,OACqC,EAAA;AACrC,EAAI,IAAA;AACF,IAAM,MAAA,KAAA,GAAQ,IAAI,QAAA,CAAS,OAAO,CAAA;AAClC,IAAM,MAAA,SAAA,GAAY,IAAI,kBAAA,CAAmB,OAAO,CAAA;AAChD,IAAM,MAAA,MAAA,GAAS,KAAM,CAAA,GAAA,CAAI,GAAG,CAAA;AAC5B,IAAO,OAAA,SAAA,CAAU,SAAS,MAAM,CAAA;AAAA,WACzB,KAAO,EAAA;AACd,IAAA,wBAAA,CAAyB,KAAK,CAAA;AAAA;AAElC;;;;"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { CSVLexer } from './CSVLexer.js';
|
|
2
|
+
import { CSVRecordAssembler } from './CSVRecordAssembler.js';
|
|
3
3
|
import { commonParseErrorHandling } from './commonParseErrorHandling.js';
|
|
4
4
|
|
|
5
5
|
function parseStringToStream(csv, options) {
|
|
6
6
|
try {
|
|
7
|
-
const lexer = new
|
|
8
|
-
const assembler = new
|
|
7
|
+
const lexer = new CSVLexer(options);
|
|
8
|
+
const assembler = new CSVRecordAssembler(options);
|
|
9
9
|
return new ReadableStream({
|
|
10
10
|
start(controller) {
|
|
11
11
|
const tokens = lexer.lex(csv);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseStringToStream.js","sources":["../src/parseStringToStream.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"parseStringToStream.js","sources":["../src/parseStringToStream.ts"],"sourcesContent":["import { CSVLexer } from \"./CSVLexer.ts\";\nimport { CSVRecordAssembler } from \"./CSVRecordAssembler.ts\";\nimport type { CSVRecord, ParseOptions } from \"./common/types.ts\";\nimport { commonParseErrorHandling } from \"./commonParseErrorHandling.ts\";\nimport type { DEFAULT_DELIMITER, DEFAULT_QUOTATION } from \"./constants.ts\";\nimport type { PickCSVHeader } from \"./utils/types.ts\";\n\nexport function parseStringToStream<\n const CSVSource extends string,\n const Delimiter extends string = DEFAULT_DELIMITER,\n const Quotation extends string = DEFAULT_QUOTATION,\n const Header extends ReadonlyArray<string> = PickCSVHeader<\n CSVSource,\n Delimiter,\n Quotation\n >,\n>(\n stream: CSVSource,\n options: ParseOptions<Header, Delimiter, Quotation>,\n): ReadableStream<CSVRecord<Header>>;\nexport function parseStringToStream<\n const CSVSource extends string,\n const Header extends ReadonlyArray<string> = PickCSVHeader<CSVSource>,\n>(\n stream: CSVSource,\n options?: ParseOptions<Header>,\n): ReadableStream<CSVRecord<Header>>;\nexport function parseStringToStream<const Header extends ReadonlyArray<string>>(\n stream: string,\n options?: ParseOptions<Header>,\n): ReadableStream<CSVRecord<Header>>;\nexport function parseStringToStream<const Header extends ReadonlyArray<string>>(\n csv: string,\n options?: ParseOptions<Header>,\n): ReadableStream<CSVRecord<Header>> {\n try {\n const lexer = new CSVLexer(options);\n const assembler = new CSVRecordAssembler(options);\n return new ReadableStream({\n start(controller) {\n const tokens = lexer.lex(csv);\n for (const record of assembler.assemble(tokens)) {\n controller.enqueue(record);\n }\n controller.close();\n },\n });\n } catch (error) {\n commonParseErrorHandling(error);\n }\n}\n"],"names":[],"mappings":";;;;AA+BgB,SAAA,mBAAA,CACd,KACA,OACmC,EAAA;AACnC,EAAI,IAAA;AACF,IAAM,MAAA,KAAA,GAAQ,IAAI,QAAA,CAAS,OAAO,CAAA;AAClC,IAAM,MAAA,SAAA,GAAY,IAAI,kBAAA,CAAmB,OAAO,CAAA;AAChD,IAAA,OAAO,IAAI,cAAe,CAAA;AAAA,MACxB,MAAM,UAAY,EAAA;AAChB,QAAM,MAAA,MAAA,GAAS,KAAM,CAAA,GAAA,CAAI,GAAG,CAAA;AAC5B,QAAA,KAAA,MAAW,MAAU,IAAA,SAAA,CAAU,QAAS,CAAA,MAAM,CAAG,EAAA;AAC/C,UAAA,UAAA,CAAW,QAAQ,MAAM,CAAA;AAAA;AAE3B,QAAA,UAAA,CAAW,KAAM,EAAA;AAAA;AACnB,KACD,CAAA;AAAA,WACM,KAAO,EAAA;AACd,IAAA,wBAAA,CAAyB,KAAK,CAAA;AAAA;AAElC;;;;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { CSVLexerTransformer } from './CSVLexerTransformer.js';
|
|
2
|
+
import { CSVRecordAssemblerTransformer } from './CSVRecordAssemblerTransformer.js';
|
|
3
3
|
import { pipeline } from './utils/pipeline.js';
|
|
4
4
|
|
|
5
5
|
function parseUint8ArrayStreamToStream(stream, options) {
|
|
@@ -8,13 +8,13 @@ function parseUint8ArrayStreamToStream(stream, options) {
|
|
|
8
8
|
stream,
|
|
9
9
|
new DecompressionStream(decomposition),
|
|
10
10
|
new TextDecoderStream(charset, { fatal, ignoreBOM }),
|
|
11
|
-
new
|
|
12
|
-
new
|
|
11
|
+
new CSVLexerTransformer(options),
|
|
12
|
+
new CSVRecordAssemblerTransformer(options)
|
|
13
13
|
) : pipeline(
|
|
14
14
|
stream,
|
|
15
15
|
new TextDecoderStream(charset, { fatal, ignoreBOM }),
|
|
16
|
-
new
|
|
17
|
-
new
|
|
16
|
+
new CSVLexerTransformer(options),
|
|
17
|
+
new CSVRecordAssemblerTransformer(options)
|
|
18
18
|
);
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseUint8ArrayStreamToStream.js","sources":["../src/parseUint8ArrayStreamToStream.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"parseUint8ArrayStreamToStream.js","sources":["../src/parseUint8ArrayStreamToStream.ts"],"sourcesContent":["import { CSVLexerTransformer } from \"./CSVLexerTransformer.ts\";\nimport { CSVRecordAssemblerTransformer } from \"./CSVRecordAssemblerTransformer.ts\";\nimport type { CSVRecord, ParseBinaryOptions } from \"./common/types.ts\";\nimport { pipeline } from \"./utils/pipeline.ts\";\n\nexport function parseUint8ArrayStreamToStream<Header extends readonly string[]>(\n stream: ReadableStream<Uint8Array>,\n options?: ParseBinaryOptions<Header>,\n): ReadableStream<CSVRecord<Header>> {\n const { charset, fatal, ignoreBOM, decomposition } = options ?? {};\n return decomposition\n ? pipeline(\n stream,\n new DecompressionStream(decomposition),\n new TextDecoderStream(charset, { fatal, ignoreBOM }),\n new CSVLexerTransformer(options),\n new CSVRecordAssemblerTransformer(options),\n )\n : pipeline(\n stream,\n new TextDecoderStream(charset, { fatal, ignoreBOM }),\n new CSVLexerTransformer(options),\n new CSVRecordAssemblerTransformer(options),\n );\n}\n"],"names":[],"mappings":";;;;AAKgB,SAAA,6BAAA,CACd,QACA,OACmC,EAAA;AACnC,EAAA,MAAM,EAAE,OAAS,EAAA,KAAA,EAAO,WAAW,aAAc,EAAA,GAAI,WAAW,EAAC;AACjE,EAAA,OAAO,aACH,GAAA,QAAA;AAAA,IACE,MAAA;AAAA,IACA,IAAI,oBAAoB,aAAa,CAAA;AAAA,IACrC,IAAI,iBAAkB,CAAA,OAAA,EAAS,EAAE,KAAA,EAAO,WAAW,CAAA;AAAA,IACnD,IAAI,oBAAoB,OAAO,CAAA;AAAA,IAC/B,IAAI,8BAA8B,OAAO;AAAA,GAE3C,GAAA,QAAA;AAAA,IACE,MAAA;AAAA,IACA,IAAI,iBAAkB,CAAA,OAAA,EAAS,EAAE,KAAA,EAAO,WAAW,CAAA;AAAA,IACnD,IAAI,oBAAoB,OAAO,CAAA;AAAA,IAC/B,IAAI,8BAA8B,OAAO;AAAA,GAC3C;AACN;;;;"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export * from './common/constants.ts';
|
|
2
2
|
export * from './common/errors.ts';
|
|
3
3
|
export * from './common/types.ts';
|
|
4
|
-
export * from './
|
|
4
|
+
export * from './CSVLexer.ts';
|
|
5
|
+
export * from './CSVLexerTransformer.ts';
|
|
5
6
|
export * from './loadWASM.ts';
|
|
6
7
|
export * from './parse.ts';
|
|
7
8
|
export * from './parseBinary.ts';
|
|
@@ -10,4 +11,5 @@ export * from './parseString.ts';
|
|
|
10
11
|
export * from './parseStringStream.ts';
|
|
11
12
|
export * from './parseStringToArraySyncWASM.ts';
|
|
12
13
|
export * from './parseUint8ArrayStream.ts';
|
|
13
|
-
export * from './
|
|
14
|
+
export * from './CSVRecordAssembler.ts';
|
|
15
|
+
export * from './CSVRecordAssemblerTransformer.ts';
|
package/dist/web-csv-toolbox.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { Field, FieldDelimiter, RecordDelimiter } from './common/constants.js';
|
|
2
2
|
export { ParseError } from './common/errors.js';
|
|
3
|
-
export {
|
|
3
|
+
export { CSVLexer, DEFAULT_MAX_BUFFER_SIZE } from './CSVLexer.js';
|
|
4
|
+
export { CSVLexerTransformer } from './CSVLexerTransformer.js';
|
|
4
5
|
export { loadWASM } from './loadWASM.js';
|
|
5
6
|
export { parse } from './parse.js';
|
|
6
7
|
export { parseBinary } from './parseBinary.js';
|
|
@@ -9,5 +10,6 @@ export { parseString } from './parseString.js';
|
|
|
9
10
|
export { parseStringStream } from './parseStringStream.js';
|
|
10
11
|
export { parseStringToArraySyncWASM } from './parseStringToArraySyncWASM.js';
|
|
11
12
|
export { parseUint8ArrayStream } from './parseUint8ArrayStream.js';
|
|
12
|
-
export {
|
|
13
|
+
export { CSVRecordAssembler } from './CSVRecordAssembler.js';
|
|
14
|
+
export { CSVRecordAssemblerTransformer } from './CSVRecordAssemblerTransformer.js';
|
|
13
15
|
//# sourceMappingURL=web-csv-toolbox.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-csv-toolbox.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"web-csv-toolbox.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;"}
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "web-csv-toolbox",
|
|
3
|
-
"version": "0.13.0-next-
|
|
3
|
+
"version": "0.13.0-next-bd865d6ddb1cf9691d7b9a83d0790651f074dd47",
|
|
4
4
|
"description": "A CSV Toolbox utilizing Web Standard APIs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "dist/web-csv-toolbox.js",
|
package/dist/Lexer.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Lexer.js","sources":["../src/Lexer.ts"],"sourcesContent":["import { assertCommonOptions } from \"./assertCommonOptions.ts\";\nimport { Field, FieldDelimiter, RecordDelimiter } from \"./common/constants.ts\";\nimport { ParseError } from \"./common/errors.ts\";\nimport type {\n AbortSignalOptions,\n CommonOptions,\n Position,\n RecordDelimiterToken,\n Token,\n} from \"./common/types.ts\";\nimport { CRLF, DEFAULT_DELIMITER, DEFAULT_QUOTATION, LF } from \"./constants.ts\";\nimport { escapeRegExp } from \"./utils/escapeRegExp.ts\";\n\n/**\n * Default maximum buffer size in characters (UTF-16 code units).\n * Approximately 10MB for ASCII text, but may vary for non-ASCII characters.\n */\nexport const DEFAULT_MAX_BUFFER_SIZE = 10 * 1024 * 1024;\n\n/**\n * CSV Lexer.\n *\n * Lexer tokenizes CSV data into fields and records.\n */\nexport class Lexer<\n Delimiter extends string = DEFAULT_DELIMITER,\n Quotation extends string = DEFAULT_QUOTATION,\n> {\n #delimiter: string;\n #quotation: string;\n #buffer = \"\";\n #flush = false;\n #matcher: RegExp;\n #fieldDelimiterLength: number;\n #maxBufferSize: number;\n\n #cursor: Position = {\n line: 1,\n column: 1,\n offset: 0,\n };\n #rowNumber = 1;\n\n #signal?: AbortSignal;\n\n /**\n * Constructs a new Lexer instance.\n * @param options - The common options for the lexer.\n */\n constructor(\n options: CommonOptions<Delimiter, Quotation> & AbortSignalOptions = {},\n ) {\n const {\n delimiter = DEFAULT_DELIMITER,\n quotation = DEFAULT_QUOTATION,\n maxBufferSize = DEFAULT_MAX_BUFFER_SIZE,\n signal,\n } = options;\n assertCommonOptions({ delimiter, quotation, maxBufferSize });\n this.#delimiter = delimiter;\n this.#quotation = quotation;\n this.#fieldDelimiterLength = delimiter.length;\n this.#maxBufferSize = maxBufferSize;\n const d = escapeRegExp(delimiter);\n const q = escapeRegExp(quotation);\n this.#matcher = new RegExp(\n `^(?:(?!${q})(?!${d})(?![\\\\r\\\\n]))([\\\\S\\\\s\\\\uFEFF\\\\xA0]+?)(?=${q}|${d}|\\\\r|\\\\n|$)`,\n );\n if (signal) {\n this.#signal = signal;\n }\n }\n\n /**\n * Lexes the given chunk of CSV data.\n * @param chunk - The chunk of CSV data to be lexed.\n * @param buffering - Indicates whether the lexer is buffering or not.\n * @returns An iterable iterator of tokens.\n */\n public lex(chunk: string | null, buffering = false): IterableIterator<Token> {\n if (!buffering) {\n this.#flush = true;\n }\n if (typeof chunk === \"string\" && chunk.length !== 0) {\n this.#buffer += chunk;\n this.#checkBufferSize();\n }\n\n return this.#tokens();\n }\n\n /**\n * Flushes the lexer and returns any remaining tokens.\n * @returns An array of tokens.\n */\n public flush(): Token[] {\n this.#flush = true;\n return [...this.#tokens()];\n }\n\n /**\n * Generates tokens from the buffered CSV data.\n * @yields Tokens from the buffered CSV data.\n */\n *#tokens(): Generator<Token> {\n if (this.#flush) {\n // Trim the last CRLF or LF\n if (this.#buffer.endsWith(CRLF)) {\n this.#buffer = this.#buffer.slice(0, -2 /* -CRLF.length */);\n } else if (this.#buffer.endsWith(LF)) {\n this.#buffer = this.#buffer.slice(0, -1 /* -LF.length */);\n }\n }\n let token: Token | null;\n while ((token = this.#nextToken())) {\n yield token;\n }\n }\n\n /**\n * Checks if the buffer size exceeds the maximum allowed size.\n * @throws {RangeError} If the buffer size exceeds the maximum.\n */\n #checkBufferSize(): void {\n if (this.#buffer.length > this.#maxBufferSize) {\n throw new RangeError(\n `Buffer size (${this.#buffer.length} characters) exceeded maximum allowed size of ${this.#maxBufferSize} characters`,\n );\n }\n }\n\n /**\n * Retrieves the next token from the buffered CSV data.\n * @returns The next token or null if there are no more tokens.\n */\n #nextToken(): Token | null {\n this.#signal?.throwIfAborted();\n if (this.#buffer.length === 0) {\n return null;\n }\n // Buffer is Record Delimiter, defer to the next iteration.\n if (\n this.#flush === false &&\n (this.#buffer === CRLF || this.#buffer === LF)\n ) {\n return null;\n }\n\n // Check for CRLF\n if (this.#buffer.startsWith(CRLF)) {\n this.#buffer = this.#buffer.slice(2);\n const start: Position = { ...this.#cursor };\n this.#cursor.line++;\n this.#cursor.column = 1;\n this.#cursor.offset += 2; // CRLF.length\n const token: RecordDelimiterToken = {\n type: RecordDelimiter,\n value: CRLF,\n location: {\n start,\n end: { ...this.#cursor },\n rowNumber: this.#rowNumber++,\n },\n };\n return token;\n }\n\n // Check for LF\n if (this.#buffer.startsWith(LF)) {\n this.#buffer = this.#buffer.slice(1);\n const start: Position = { ...this.#cursor };\n this.#cursor.line++;\n this.#cursor.column = 1;\n this.#cursor.offset += 1; // LF.length\n const token: RecordDelimiterToken = {\n type: RecordDelimiter,\n value: LF,\n location: {\n start,\n end: { ...this.#cursor },\n rowNumber: this.#rowNumber++,\n },\n };\n return token;\n }\n\n // Check for Delimiter\n if (this.#buffer.startsWith(this.#delimiter)) {\n this.#buffer = this.#buffer.slice(1);\n const start: Position = { ...this.#cursor };\n this.#cursor.column += this.#fieldDelimiterLength;\n this.#cursor.offset += this.#fieldDelimiterLength;\n return {\n type: FieldDelimiter,\n value: this.#delimiter,\n location: {\n start,\n end: { ...this.#cursor },\n rowNumber: this.#rowNumber,\n },\n };\n }\n\n // Check for Quoted String\n if (this.#buffer.startsWith(this.#quotation)) {\n /**\n * Extract Quoted field.\n *\n * The following code is equivalent to the following:\n *\n * If the next character is a quote:\n * - If the character after that is a quote, then append a quote to the value and skip two characters.\n * - Otherwise, return the quoted string.\n * Otherwise, append the character to the value and skip one character.\n *\n * ```plaintext\n * | `i` | `i + 1` | `i + 2` |\n * |------------|------------|----------|\n * | cur | next | | => Variable names\n * | #quotation | #quotation | | => Escaped quote\n * | #quotation | (EOF) | | => Closing quote\n * | #quotation | undefined | | => End of buffer\n * | undefined | | | => End of buffer\n * ```\n */\n let value = \"\";\n let offset = 1; // Skip the opening quote\n let column = 2; // Skip the opening quote\n let line = 0;\n\n // Define variables\n let cur: string = this.#buffer[offset];\n let next: string | undefined = this.#buffer[offset + 1];\n do {\n // If the current character is a quote, check the next characters for closing quotes.\n if (cur === this.#quotation) {\n // If the cur character is a quote and the next character is a quote,\n // then append a quote to the value and skip two characters.\n if (next === this.#quotation) {\n // Append a quote to the value and skip two characters.\n value += this.#quotation;\n offset += 2;\n cur = this.#buffer[offset];\n next = this.#buffer[offset + 1];\n\n // Update the diff\n column += 2;\n continue;\n }\n\n // If the cur character is a quote and the next character is undefined,\n // then return null.\n if (next === undefined && this.#flush === false) {\n return null;\n }\n\n // Otherwise, return the quoted string.\n // Update the buffer and return the token\n offset++;\n this.#buffer = this.#buffer.slice(offset);\n const start: Position = { ...this.#cursor };\n this.#cursor.column += column;\n this.#cursor.offset += offset;\n this.#cursor.line += line;\n return {\n type: Field,\n value,\n location: {\n start,\n end: { ...this.#cursor },\n rowNumber: this.#rowNumber,\n },\n };\n }\n\n // Append the character to the value.\n value += cur;\n\n // Prepare for the next iteration\n if (cur === LF) {\n // If the current character is a LF,\n // then increment the line number and reset the column number.\n line++;\n column = 1;\n } else {\n // Otherwise, increment the column number and offset.\n column++;\n }\n\n offset++;\n cur = next;\n next = this.#buffer[offset + 1];\n } while (cur !== undefined);\n\n if (this.#flush) {\n throw new ParseError(\"Unexpected EOF while parsing quoted field.\", {\n position: { ...this.#cursor },\n });\n }\n return null;\n }\n\n // Check for Unquoted String\n const match = this.#matcher.exec(this.#buffer);\n if (match) {\n // If we're flushing and the match doesn't consume the entire buffer,\n // then return null\n if (this.#flush === false && match[0].length === this.#buffer.length) {\n return null;\n }\n const value = match[1];\n this.#buffer = this.#buffer.slice(value.length);\n const start: Position = { ...this.#cursor };\n this.#cursor.column += value.length;\n this.#cursor.offset += value.length;\n return {\n type: Field,\n value,\n location: {\n start,\n end: { ...this.#cursor },\n rowNumber: this.#rowNumber,\n },\n };\n }\n\n // Otherwise, return null\n return null;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LexerTransformer.js","sources":["../src/LexerTransformer.ts"],"sourcesContent":["import { Lexer } from \"./Lexer.ts\";\nimport type { LexerTransformerOptions, Token } from \"./common/types.ts\";\nimport type { DEFAULT_DELIMITER, DEFAULT_QUOTATION } from \"./constants.ts\";\n\n/**\n * A transform stream that converts a stream of tokens into a stream of rows.\n *\n * @category Low-level API\n *\n * @example Parse a CSV with headers by data\n * ```ts\n * new ReadableStream({\n * start(controller) {\n * controller.enqueue(\"name,age\\r\\n\");\n * controller.enqueue(\"Alice,20\\r\\n\");\n * controller.close();\n * }\n * })\n * .pipeThrough(new LexerTransformer())\n * .pipeTo(new WritableStream({ write(tokens) {\n * for (const token of tokens) {\n * console.log(token);\n * }\n * }}));\n * // { type: Field, value: \"name\", location: {...} }\n * // { type: FieldDelimiter, value: \",\", location: {...} }\n * // { type: Field, value: \"age\", location: {...} }\n * // { type: RecordDelimiter, value: \"\\r\\n\", location: {...} }\n * // { type: Field, value: \"Alice\", location: {...} }\n * // { type: FieldDelimiter, value: \",\", location: {...} }\n * // { type: Field, value: \"20\" }\n * // { type: RecordDelimiter, value: \"\\r\\n\", location: {...} }\n * ```\n */\nexport class LexerTransformer<\n Delimiter extends string = DEFAULT_DELIMITER,\n Quotation extends string = DEFAULT_QUOTATION,\n> extends TransformStream<string, Token[]> {\n public readonly lexer: Lexer<Delimiter, Quotation>;\n constructor(options: LexerTransformerOptions<Delimiter, Quotation> = {}) {\n const lexer = new Lexer(options);\n super({\n transform: (chunk, controller) => {\n if (chunk.length !== 0) {\n try {\n controller.enqueue([...lexer.lex(chunk, true)]);\n } catch (error) {\n controller.error(error);\n }\n }\n },\n flush: (controller) => {\n try {\n controller.enqueue(lexer.flush());\n } catch (error) {\n controller.error(error);\n }\n },\n });\n this.lexer = lexer;\n }\n}\n"],"names":[],"mappings":";;AAkCO,MAAM,yBAGH,eAAiC,CAAA;AAAA,EACzB,KAAA;AAAA,EAChB,WAAA,CAAY,OAAyD,GAAA,EAAI,EAAA;AACvE,IAAM,MAAA,KAAA,GAAQ,IAAI,KAAA,CAAM,OAAO,CAAA;AAC/B,IAAM,KAAA,CAAA;AAAA,MACJ,SAAA,EAAW,CAAC,KAAA,EAAO,UAAe,KAAA;AAChC,QAAI,IAAA,KAAA,CAAM,WAAW,CAAG,EAAA;AACtB,UAAI,IAAA;AACF,YAAW,UAAA,CAAA,OAAA,CAAQ,CAAC,GAAG,KAAA,CAAM,IAAI,KAAO,EAAA,IAAI,CAAC,CAAC,CAAA;AAAA,mBACvC,KAAO,EAAA;AACd,YAAA,UAAA,CAAW,MAAM,KAAK,CAAA;AAAA;AACxB;AACF,OACF;AAAA,MACA,KAAA,EAAO,CAAC,UAAe,KAAA;AACrB,QAAI,IAAA;AACF,UAAW,UAAA,CAAA,OAAA,CAAQ,KAAM,CAAA,KAAA,EAAO,CAAA;AAAA,iBACzB,KAAO,EAAA;AACd,UAAA,UAAA,CAAW,MAAM,KAAK,CAAA;AAAA;AACxB;AACF,KACD,CAAA;AACD,IAAA,IAAA,CAAK,KAAQ,GAAA,KAAA;AAAA;AAEjB;;;;"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { CSVRecord, RecordAssemblerOptions, Token } from './common/types.ts';
|
|
2
|
-
export declare class RecordAssembler<Header extends ReadonlyArray<string>> {
|
|
3
|
-
#private;
|
|
4
|
-
constructor(options?: RecordAssemblerOptions<Header>);
|
|
5
|
-
assemble(tokens: Iterable<Token>, flush?: boolean): IterableIterator<CSVRecord<Header>>;
|
|
6
|
-
flush(): Generator<CSVRecord<Header>>;
|
|
7
|
-
}
|
package/dist/RecordAssembler.js
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { RecordDelimiter, FieldDelimiter } from './common/constants.js';
|
|
2
|
-
import { ParseError } from './common/errors.js';
|
|
3
|
-
|
|
4
|
-
const DEFAULT_MAX_FIELD_COUNT = 1e5;
|
|
5
|
-
class RecordAssembler {
|
|
6
|
-
#fieldIndex = 0;
|
|
7
|
-
#row = [];
|
|
8
|
-
#header;
|
|
9
|
-
#dirty = false;
|
|
10
|
-
#signal;
|
|
11
|
-
#maxFieldCount;
|
|
12
|
-
#skipEmptyLines;
|
|
13
|
-
constructor(options = {}) {
|
|
14
|
-
const mfc = options.maxFieldCount ?? DEFAULT_MAX_FIELD_COUNT;
|
|
15
|
-
if (!(Number.isFinite(mfc) || mfc === Number.POSITIVE_INFINITY) || Number.isFinite(mfc) && (mfc < 1 || !Number.isInteger(mfc))) {
|
|
16
|
-
throw new RangeError(
|
|
17
|
-
"maxFieldCount must be a positive integer or Number.POSITIVE_INFINITY"
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
this.#maxFieldCount = mfc;
|
|
21
|
-
this.#skipEmptyLines = options.skipEmptyLines ?? false;
|
|
22
|
-
if (options.header !== void 0 && Array.isArray(options.header)) {
|
|
23
|
-
this.#setHeader(options.header);
|
|
24
|
-
}
|
|
25
|
-
if (options.signal) {
|
|
26
|
-
this.#signal = options.signal;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
*assemble(tokens, flush = true) {
|
|
30
|
-
for (const token of tokens) {
|
|
31
|
-
this.#signal?.throwIfAborted();
|
|
32
|
-
switch (token.type) {
|
|
33
|
-
case FieldDelimiter:
|
|
34
|
-
this.#fieldIndex++;
|
|
35
|
-
this.#checkFieldCount();
|
|
36
|
-
this.#dirty = true;
|
|
37
|
-
break;
|
|
38
|
-
case RecordDelimiter:
|
|
39
|
-
if (this.#header === void 0) {
|
|
40
|
-
this.#setHeader(this.#row);
|
|
41
|
-
} else {
|
|
42
|
-
if (this.#dirty) {
|
|
43
|
-
yield Object.fromEntries(
|
|
44
|
-
this.#header.map((header, index) => [
|
|
45
|
-
header,
|
|
46
|
-
this.#row.at(index)
|
|
47
|
-
])
|
|
48
|
-
);
|
|
49
|
-
} else {
|
|
50
|
-
if (this.#skipEmptyLines) {
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
yield Object.fromEntries(
|
|
54
|
-
this.#header.map((header) => [header, ""])
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
this.#fieldIndex = 0;
|
|
59
|
-
this.#row = new Array(this.#header?.length).fill("");
|
|
60
|
-
this.#dirty = false;
|
|
61
|
-
break;
|
|
62
|
-
default:
|
|
63
|
-
this.#dirty = true;
|
|
64
|
-
this.#row[this.#fieldIndex] = token.value;
|
|
65
|
-
break;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
if (flush) {
|
|
69
|
-
yield* this.flush();
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
*flush() {
|
|
73
|
-
if (this.#header !== void 0) {
|
|
74
|
-
if (this.#dirty) {
|
|
75
|
-
yield Object.fromEntries(
|
|
76
|
-
this.#header.filter((v) => v).map((header, index) => [header, this.#row.at(index)])
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
#checkFieldCount() {
|
|
82
|
-
if (this.#fieldIndex + 1 > this.#maxFieldCount) {
|
|
83
|
-
throw new RangeError(
|
|
84
|
-
`Field count (${this.#fieldIndex + 1}) exceeded maximum allowed count of ${this.#maxFieldCount}`
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
#setHeader(header) {
|
|
89
|
-
if (header.length > this.#maxFieldCount) {
|
|
90
|
-
throw new RangeError(
|
|
91
|
-
`Header field count (${header.length}) exceeded maximum allowed count of ${this.#maxFieldCount}`
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
this.#header = header;
|
|
95
|
-
if (this.#header.length === 0) {
|
|
96
|
-
throw new ParseError("The header must not be empty.");
|
|
97
|
-
}
|
|
98
|
-
if (new Set(this.#header).size !== this.#header.length) {
|
|
99
|
-
throw new ParseError("The header must not contain duplicate fields.");
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export { RecordAssembler };
|
|
105
|
-
//# sourceMappingURL=RecordAssembler.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"RecordAssembler.js","sources":["../src/RecordAssembler.ts"],"sourcesContent":["import { FieldDelimiter, RecordDelimiter } from \"./common/constants.ts\";\nimport { ParseError } from \"./common/errors.ts\";\nimport type {\n CSVRecord,\n RecordAssemblerOptions,\n Token,\n} from \"./common/types.ts\";\n\n/**\n * Default maximum field count per record (100,000 fields).\n */\nconst DEFAULT_MAX_FIELD_COUNT = 100_000;\n\nexport class RecordAssembler<Header extends ReadonlyArray<string>> {\n #fieldIndex = 0;\n #row: string[] = [];\n #header: Header | undefined;\n #dirty = false;\n #signal?: AbortSignal;\n #maxFieldCount: number;\n #skipEmptyLines: boolean;\n\n constructor(options: RecordAssemblerOptions<Header> = {}) {\n const mfc = options.maxFieldCount ?? DEFAULT_MAX_FIELD_COUNT;\n // Validate maxFieldCount\n if (\n !(Number.isFinite(mfc) || mfc === Number.POSITIVE_INFINITY) ||\n (Number.isFinite(mfc) && (mfc < 1 || !Number.isInteger(mfc)))\n ) {\n throw new RangeError(\n \"maxFieldCount must be a positive integer or Number.POSITIVE_INFINITY\",\n );\n }\n this.#maxFieldCount = mfc;\n this.#skipEmptyLines = options.skipEmptyLines ?? false;\n if (options.header !== undefined && Array.isArray(options.header)) {\n this.#setHeader(options.header);\n }\n if (options.signal) {\n this.#signal = options.signal;\n }\n }\n\n public *assemble(\n tokens: Iterable<Token>,\n flush = true,\n ): IterableIterator<CSVRecord<Header>> {\n for (const token of tokens) {\n this.#signal?.throwIfAborted();\n switch (token.type) {\n case FieldDelimiter:\n this.#fieldIndex++;\n this.#checkFieldCount();\n this.#dirty = true;\n break;\n case RecordDelimiter:\n if (this.#header === undefined) {\n this.#setHeader(this.#row as unknown as Header);\n } else {\n if (this.#dirty) {\n yield Object.fromEntries(\n this.#header.map((header, index) => [\n header,\n this.#row.at(index),\n ]),\n ) as unknown as CSVRecord<Header>;\n } else {\n if (this.#skipEmptyLines) {\n continue;\n }\n yield Object.fromEntries(\n this.#header.map((header) => [header, \"\"]),\n ) as CSVRecord<Header>;\n }\n }\n // Reset the row fields buffer.\n this.#fieldIndex = 0;\n this.#row = new Array(this.#header?.length).fill(\"\");\n this.#dirty = false;\n break;\n default:\n this.#dirty = true;\n this.#row[this.#fieldIndex] = token.value;\n break;\n }\n }\n\n if (flush) {\n yield* this.flush();\n }\n }\n\n public *flush(): Generator<CSVRecord<Header>> {\n if (this.#header !== undefined) {\n if (this.#dirty) {\n yield Object.fromEntries(\n this.#header\n .filter((v) => v)\n .map((header, index) => [header, this.#row.at(index)]),\n ) as unknown as CSVRecord<Header>;\n }\n }\n }\n\n #checkFieldCount(): void {\n if (this.#fieldIndex + 1 > this.#maxFieldCount) {\n throw new RangeError(\n `Field count (${this.#fieldIndex + 1}) exceeded maximum allowed count of ${this.#maxFieldCount}`,\n );\n }\n }\n\n #setHeader(header: Header) {\n if (header.length > this.#maxFieldCount) {\n throw new RangeError(\n `Header field count (${header.length}) exceeded maximum allowed count of ${this.#maxFieldCount}`,\n );\n }\n this.#header = header;\n if (this.#header.length === 0) {\n throw new ParseError(\"The header must not be empty.\");\n }\n if (new Set(this.#header).size !== this.#header.length) {\n throw new ParseError(\"The header must not contain duplicate fields.\");\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"RecordAssemblerTransformer.js","sources":["../src/RecordAssemblerTransformer.ts"],"sourcesContent":["import { RecordAssembler } from \"./RecordAssembler.ts\";\nimport type {\n CSVRecord,\n RecordAssemblerOptions,\n Token,\n} from \"./common/types.ts\";\n\n/**\n * A transform stream that converts a stream of tokens into a stream of rows.\n * @template Header The type of the header row.\n * @param options The options for the parser.\n *\n * @category Low-level API\n *\n * @example Parse a CSV with headers by data\n * ```ts\n * new ReadableStream({\n * start(controller) {\n * controller.enqueue(\"name,age\\r\\n\");\n * controller.enqueue(\"Alice,20\\r\\n\");\n * controller.enqueue(\"Bob,25\\r\\n\");\n * controller.enqueue(\"Charlie,30\\r\\n\");\n * controller.close();\n * })\n * .pipeThrough(new LexerTransformer())\n * .pipeThrough(new RecordAssemblerTransformer())\n * .pipeTo(new WritableStream({ write(row) { console.log(row); }}));\n * // { name: \"Alice\", age: \"20\" }\n * // { name: \"Bob\", age: \"25\" }\n * // { name: \"Charlie\", age: \"30\" }\n * ```\n *\n * @example Parse a CSV with headers by options\n * ```ts\n * new ReadableStream({\n * start(controller) {\n * controller.enqueue(\"Alice,20\\r\\n\");\n * controller.enqueue(\"Bob,25\\r\\n\");\n * controller.enqueue(\"Charlie,30\\r\\n\");\n * controller.close();\n * }\n * })\n * .pipeThrough(new LexerTransformer())\n * .pipeThrough(new RecordAssemblerTransformer({ header: [\"name\", \"age\"] }))\n * .pipeTo(new WritableStream({ write(row) { console.log(row); }}));\n * // { name: \"Alice\", age: \"20\" }\n * // { name: \"Bob\", age: \"25\" }\n * // { name: \"Charlie\", age: \"30\" }\n * ```\n */\nexport class RecordAssemblerTransformer<\n Header extends ReadonlyArray<string>,\n> extends TransformStream<Token[], CSVRecord<Header>> {\n public readonly assembler: RecordAssembler<Header>;\n\n constructor(options: RecordAssemblerOptions<Header> = {}) {\n const assembler = new RecordAssembler(options);\n super({\n transform: (tokens, controller) => {\n try {\n for (const token of assembler.assemble(tokens, false)) {\n controller.enqueue(token);\n }\n } catch (error) {\n controller.error(error);\n }\n },\n flush: (controller) => {\n try {\n for (const token of assembler.flush()) {\n controller.enqueue(token);\n }\n } catch (error) {\n controller.error(error);\n }\n },\n });\n this.assembler = assembler;\n }\n}\n"],"names":[],"mappings":";;AAkDO,MAAM,mCAEH,eAA4C,CAAA;AAAA,EACpC,SAAA;AAAA,EAEhB,WAAA,CAAY,OAA0C,GAAA,EAAI,EAAA;AACxD,IAAM,MAAA,SAAA,GAAY,IAAI,eAAA,CAAgB,OAAO,CAAA;AAC7C,IAAM,KAAA,CAAA;AAAA,MACJ,SAAA,EAAW,CAAC,MAAA,EAAQ,UAAe,KAAA;AACjC,QAAI,IAAA;AACF,UAAA,KAAA,MAAW,KAAS,IAAA,SAAA,CAAU,QAAS,CAAA,MAAA,EAAQ,KAAK,CAAG,EAAA;AACrD,YAAA,UAAA,CAAW,QAAQ,KAAK,CAAA;AAAA;AAC1B,iBACO,KAAO,EAAA;AACd,UAAA,UAAA,CAAW,MAAM,KAAK,CAAA;AAAA;AACxB,OACF;AAAA,MACA,KAAA,EAAO,CAAC,UAAe,KAAA;AACrB,QAAI,IAAA;AACF,UAAW,KAAA,MAAA,KAAA,IAAS,SAAU,CAAA,KAAA,EAAS,EAAA;AACrC,YAAA,UAAA,CAAW,QAAQ,KAAK,CAAA;AAAA;AAC1B,iBACO,KAAO,EAAA;AACd,UAAA,UAAA,CAAW,MAAM,KAAK,CAAA;AAAA;AACxB;AACF,KACD,CAAA;AACD,IAAA,IAAA,CAAK,SAAY,GAAA,SAAA;AAAA;AAErB;;;;"}
|