web-csv-toolbox 0.12.1-next-53113e3c3ee5a6ec969f90ac8d8c87cc929fe86b → 0.13.0-next-76eec9027400dc77264be2be8a252d284f00dc6a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -376,12 +376,13 @@ console.log(result);
376
376
 
377
377
  ### Advanced Options (Binary-Specific) 🧬
378
378
 
379
- | Option | Description | Default | Notes |
380
- | --------------- | ------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
381
- | `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. |
382
- | `decompression` | Decompression algorithm for compressed CSV inputs | | See [DecompressionStream Compatibility](https://developer.mozilla.org/en-US/docs/Web/API/DecompressionStream#browser_compatibilit). |
383
- | `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. |
384
- | `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. |
379
+ | Option | Description | Default | Notes |
380
+ | --------------------------------- | ------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
381
+ | `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. |
382
+ | `decompression` | Decompression algorithm for compressed CSV inputs | | See [DecompressionStream Compatibility](https://developer.mozilla.org/en-US/docs/Web/API/DecompressionStream#browser_compatibilit). Supports: gzip, deflate, deflate-raw |
383
+ | `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. |
384
+ | `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. |
385
+ | `allowExperimentalCompressions` | Allow experimental/future compression formats | `false` | When enabled, passes unknown compression formats to runtime. Use cautiously. See example below. |
385
386
 
386
387
  ## Performance & Best Practices ⚡
387
388
 
@@ -507,6 +508,81 @@ For production use with untrusted input, consider:
507
508
  - Implementing file size limits at the application level
508
509
  - Validating parsed data before use
509
510
 
511
+ #### Implementing Size Limits for Untrusted Sources
512
+
513
+ When processing CSV files from untrusted sources (especially compressed files), you can implement size limits using a custom TransformStream:
514
+
515
+ ```js
516
+ import { parse } from 'web-csv-toolbox';
517
+
518
+ // Create a size-limiting TransformStream
519
+ class SizeLimitStream extends TransformStream {
520
+ constructor(maxBytes) {
521
+ let bytesRead = 0;
522
+ super({
523
+ transform(chunk, controller) {
524
+ bytesRead += chunk.length;
525
+ if (bytesRead > maxBytes) {
526
+ controller.error(new Error(`Size limit exceeded: ${maxBytes} bytes`));
527
+ } else {
528
+ controller.enqueue(chunk);
529
+ }
530
+ }
531
+ });
532
+ }
533
+ }
534
+
535
+ // Example: Limit decompressed data to 10MB
536
+ const response = await fetch('https://untrusted-source.com/data.csv.gz');
537
+ const limitedStream = response.body
538
+ .pipeThrough(new DecompressionStream('gzip'))
539
+ .pipeThrough(new SizeLimitStream(10 * 1024 * 1024)); // 10MB limit
540
+
541
+ try {
542
+ for await (const record of parse(limitedStream)) {
543
+ console.log(record);
544
+ }
545
+ } catch (error) {
546
+ if (error.message.includes('Size limit exceeded')) {
547
+ console.error('File too large - possible compression bomb attack');
548
+ }
549
+ }
550
+ ```
551
+
552
+ **Note**: The library automatically validates Content-Encoding headers when parsing Response objects, rejecting unsupported compression formats.
553
+
554
+ #### Using Experimental Compression Formats
555
+
556
+ By default, the library only supports well-tested compression formats: `gzip`, `deflate`, and `deflate-raw`. If you need to use newer formats (like Brotli) that your runtime supports but the library hasn't explicitly added yet, you can enable experimental mode:
557
+
558
+ ```js
559
+ import { parse } from 'web-csv-toolbox';
560
+
561
+ // ✅ Default behavior: Only known formats
562
+ const response = await fetch('data.csv.gz');
563
+ await parse(response); // Works
564
+
565
+ // ⚠️ Experimental: Allow future formats
566
+ const response2 = await fetch('data.csv.br'); // Brotli compression
567
+ try {
568
+ await parse(response2, { allowExperimentalCompressions: true });
569
+ // Works if runtime supports Brotli
570
+ } catch (error) {
571
+ // Runtime will throw if format is unsupported
572
+ console.error('Runtime does not support this compression format');
573
+ }
574
+ ```
575
+
576
+ **When to use this:**
577
+ - Your runtime supports a newer compression format (e.g., Brotli in modern browsers)
578
+ - You want to use the format before this library explicitly supports it
579
+ - You trust the compression format source
580
+
581
+ **Cautions:**
582
+ - Error messages will come from the runtime, not this library
583
+ - No library-level validation for unknown formats
584
+ - You must verify your runtime supports the format
585
+
510
586
  ## How to Contribute 💪
511
587
 
512
588
  ## Star ⭐
@@ -208,6 +208,36 @@ export interface BinaryOptions {
208
208
  * @default false
209
209
  */
210
210
  fatal?: boolean;
211
+ /**
212
+ * Allow experimental or future compression formats not explicitly supported by this library.
213
+ *
214
+ * @remarks
215
+ * When `true`, unknown compression formats from Content-Encoding headers will be passed
216
+ * to the runtime's DecompressionStream without validation. This allows using newer
217
+ * compression formats (like Brotli) if your runtime supports them, even before this
218
+ * library is updated to explicitly support them.
219
+ *
220
+ * When `false` (default), only known formats are allowed: gzip, deflate, deflate-raw.
221
+ *
222
+ * **Use with caution**: Enabling this bypasses library validation and relies entirely
223
+ * on runtime error handling. If the runtime doesn't support the format, you'll get
224
+ * a runtime error instead of a clear validation error from this library.
225
+ *
226
+ * @default false
227
+ *
228
+ * @example
229
+ * ```ts
230
+ * // Safe mode (default): Only known formats
231
+ * const response = await fetch('data.csv.gz');
232
+ * await parse(response); // ✓ Works
233
+ *
234
+ * // Experimental mode: Allow future formats
235
+ * const response = await fetch('data.csv.br'); // Brotli
236
+ * await parse(response, { allowExperimentalCompressions: true });
237
+ * // Works if runtime supports Brotli, otherwise throws runtime error
238
+ * ```
239
+ */
240
+ allowExperimentalCompressions?: boolean;
211
241
  }
212
242
  /**
213
243
  * Record Assembler Options for CSV.
@@ -5,6 +5,6 @@ import { ParseBinaryOptions } from './common/types.ts';
5
5
  * @param response - The response object from which to extract the options.
6
6
  * @param options - The options to merge with the extracted options.
7
7
  * @returns The options extracted from the response.
8
- * @throws {RangeError} - The content type is not supported.
8
+ * @throws {TypeError} - The content type is not supported or the content-encoding is invalid.
9
9
  */
10
10
  export declare function getOptionsFromResponse<Header extends ReadonlyArray<string>>(response: Response, options?: ParseBinaryOptions<Header>): ParseBinaryOptions<Header>;
@@ -1,13 +1,38 @@
1
1
  import { parseMime } from './utils/parseMime.js';
2
2
 
3
+ const SUPPORTED_COMPRESSIONS = /* @__PURE__ */ new Set([
4
+ "gzip",
5
+ "deflate",
6
+ "deflate-raw"
7
+ ]);
3
8
  function getOptionsFromResponse(response, options = {}) {
4
9
  const { headers } = response;
5
10
  const contentType = headers.get("content-type") ?? "text/csv";
6
11
  const mime = parseMime(contentType);
7
12
  if (mime.type !== "text/csv") {
8
- throw new RangeError(`Invalid mime type: "${contentType}"`);
13
+ throw new TypeError(`Invalid mime type: "${contentType}"`);
14
+ }
15
+ const contentEncoding = headers.get("content-encoding");
16
+ let decomposition;
17
+ if (contentEncoding) {
18
+ const normalizedEncoding = contentEncoding.trim().toLowerCase();
19
+ if (normalizedEncoding.includes(",")) {
20
+ throw new TypeError(
21
+ `Multiple content-encodings are not supported: "${contentEncoding}"`
22
+ );
23
+ }
24
+ if (SUPPORTED_COMPRESSIONS.has(normalizedEncoding)) {
25
+ decomposition = normalizedEncoding;
26
+ } else if (normalizedEncoding) {
27
+ if (options.allowExperimentalCompressions) {
28
+ decomposition = normalizedEncoding;
29
+ } else {
30
+ throw new TypeError(
31
+ `Unsupported content-encoding: "${contentEncoding}". Supported formats: ${Array.from(SUPPORTED_COMPRESSIONS).join(", ")}. To use experimental formats, set allowExperimentalCompressions: true`
32
+ );
33
+ }
34
+ }
9
35
  }
10
- const decomposition = headers.get("content-encoding") ?? void 0;
11
36
  const charset = mime.parameters.charset ?? "utf-8";
12
37
  return {
13
38
  decomposition,
@@ -1 +1 @@
1
- {"version":3,"file":"getOptionsFromResponse.js","sources":["../src/getOptionsFromResponse.ts"],"sourcesContent":["import type { ParseBinaryOptions } from \"./common/types.ts\";\nimport { parseMime } from \"./utils/parseMime.ts\";\n\n/**\n * Extracts the options from the response object.\n *\n * @param response - The response object from which to extract the options.\n * @param options - The options to merge with the extracted options.\n * @returns The options extracted from the response.\n * @throws {RangeError} - The content type is not supported.\n */\nexport function getOptionsFromResponse<Header extends ReadonlyArray<string>>(\n response: Response,\n options: ParseBinaryOptions<Header> = {},\n): ParseBinaryOptions<Header> {\n const { headers } = response;\n const contentType = headers.get(\"content-type\") ?? \"text/csv\";\n const mime = parseMime(contentType);\n if (mime.type !== \"text/csv\") {\n throw new RangeError(`Invalid mime type: \"${contentType}\"`);\n }\n const decomposition =\n (headers.get(\"content-encoding\") as CompressionFormat) ?? undefined;\n const charset = mime.parameters.charset ?? \"utf-8\";\n // TODO: Support header=present and header=absent\n // const header = mime.parameters.header ?? \"present\";\n return {\n decomposition,\n charset,\n ...options,\n };\n}\n"],"names":[],"mappings":";;AAWO,SAAS,sBACd,CAAA,QAAA,EACA,OAAsC,GAAA,EACV,EAAA;AAC5B,EAAM,MAAA,EAAE,SAAY,GAAA,QAAA;AACpB,EAAA,MAAM,WAAc,GAAA,OAAA,CAAQ,GAAI,CAAA,cAAc,CAAK,IAAA,UAAA;AACnD,EAAM,MAAA,IAAA,GAAO,UAAU,WAAW,CAAA;AAClC,EAAI,IAAA,IAAA,CAAK,SAAS,UAAY,EAAA;AAC5B,IAAA,MAAM,IAAI,UAAA,CAAW,CAAuB,oBAAA,EAAA,WAAW,CAAG,CAAA,CAAA,CAAA;AAAA;AAE5D,EAAA,MAAM,aACH,GAAA,OAAA,CAAQ,GAAI,CAAA,kBAAkB,CAA2B,IAAA,MAAA;AAC5D,EAAM,MAAA,OAAA,GAAU,IAAK,CAAA,UAAA,CAAW,OAAW,IAAA,OAAA;AAG3C,EAAO,OAAA;AAAA,IACL,aAAA;AAAA,IACA,OAAA;AAAA,IACA,GAAG;AAAA,GACL;AACF;;;;"}
1
+ {"version":3,"file":"getOptionsFromResponse.js","sources":["../src/getOptionsFromResponse.ts"],"sourcesContent":["import type { ParseBinaryOptions } from \"./common/types.ts\";\nimport { parseMime } from \"./utils/parseMime.ts\";\n\n/**\n * Supported compression formats for CSV decompression.\n * These correspond to the Web Standard CompressionFormat values.\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CompressionFormat | CompressionFormat}\n */\nconst SUPPORTED_COMPRESSIONS: ReadonlySet<CompressionFormat> = new Set([\n \"gzip\",\n \"deflate\",\n \"deflate-raw\",\n]);\n\n/**\n * Extracts the options from the response object.\n *\n * @param response - The response object from which to extract the options.\n * @param options - The options to merge with the extracted options.\n * @returns The options extracted from the response.\n * @throws {TypeError} - The content type is not supported or the content-encoding is invalid.\n */\nexport function getOptionsFromResponse<Header extends ReadonlyArray<string>>(\n response: Response,\n options: ParseBinaryOptions<Header> = {},\n): ParseBinaryOptions<Header> {\n const { headers } = response;\n const contentType = headers.get(\"content-type\") ?? \"text/csv\";\n const mime = parseMime(contentType);\n if (mime.type !== \"text/csv\") {\n throw new TypeError(`Invalid mime type: \"${contentType}\"`);\n }\n\n const contentEncoding = headers.get(\"content-encoding\");\n let decomposition: CompressionFormat | undefined;\n\n if (contentEncoding) {\n const normalizedEncoding = contentEncoding.trim().toLowerCase();\n\n if (normalizedEncoding.includes(\",\")) {\n throw new TypeError(\n `Multiple content-encodings are not supported: \"${contentEncoding}\"`,\n );\n }\n\n if (SUPPORTED_COMPRESSIONS.has(normalizedEncoding as CompressionFormat)) {\n decomposition = normalizedEncoding as CompressionFormat;\n } else if (normalizedEncoding) {\n // Unknown compression format\n if (options.allowExperimentalCompressions) {\n // Allow runtime to handle experimental/future formats\n decomposition = normalizedEncoding as CompressionFormat;\n } else {\n throw new TypeError(\n `Unsupported content-encoding: \"${contentEncoding}\". Supported formats: ${Array.from(SUPPORTED_COMPRESSIONS).join(\", \")}. To use experimental formats, set allowExperimentalCompressions: true`,\n );\n }\n }\n }\n\n const charset = mime.parameters.charset ?? \"utf-8\";\n // TODO: Support header=present and header=absent\n // const header = mime.parameters.header ?? \"present\";\n return {\n decomposition,\n charset,\n ...options,\n };\n}\n"],"names":[],"mappings":";;AASA,MAAM,sBAAA,uBAA6D,GAAI,CAAA;AAAA,EACrE,MAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAC,CAAA;AAUM,SAAS,sBACd,CAAA,QAAA,EACA,OAAsC,GAAA,EACV,EAAA;AAC5B,EAAM,MAAA,EAAE,SAAY,GAAA,QAAA;AACpB,EAAA,MAAM,WAAc,GAAA,OAAA,CAAQ,GAAI,CAAA,cAAc,CAAK,IAAA,UAAA;AACnD,EAAM,MAAA,IAAA,GAAO,UAAU,WAAW,CAAA;AAClC,EAAI,IAAA,IAAA,CAAK,SAAS,UAAY,EAAA;AAC5B,IAAA,MAAM,IAAI,SAAA,CAAU,CAAuB,oBAAA,EAAA,WAAW,CAAG,CAAA,CAAA,CAAA;AAAA;AAG3D,EAAM,MAAA,eAAA,GAAkB,OAAQ,CAAA,GAAA,CAAI,kBAAkB,CAAA;AACtD,EAAI,IAAA,aAAA;AAEJ,EAAA,IAAI,eAAiB,EAAA;AACnB,IAAA,MAAM,kBAAqB,GAAA,eAAA,CAAgB,IAAK,EAAA,CAAE,WAAY,EAAA;AAE9D,IAAI,IAAA,kBAAA,CAAmB,QAAS,CAAA,GAAG,CAAG,EAAA;AACpC,MAAA,MAAM,IAAI,SAAA;AAAA,QACR,kDAAkD,eAAe,CAAA,CAAA;AAAA,OACnE;AAAA;AAGF,IAAI,IAAA,sBAAA,CAAuB,GAAI,CAAA,kBAAuC,CAAG,EAAA;AACvE,MAAgB,aAAA,GAAA,kBAAA;AAAA,eACP,kBAAoB,EAAA;AAE7B,MAAA,IAAI,QAAQ,6BAA+B,EAAA;AAEzC,QAAgB,aAAA,GAAA,kBAAA;AAAA,OACX,MAAA;AACL,QAAA,MAAM,IAAI,SAAA;AAAA,UACR,CAAA,+BAAA,EAAkC,eAAe,CAAyB,sBAAA,EAAA,KAAA,CAAM,KAAK,sBAAsB,CAAA,CAAE,IAAK,CAAA,IAAI,CAAC,CAAA,sEAAA;AAAA,SACzH;AAAA;AACF;AACF;AAGF,EAAM,MAAA,OAAA,GAAU,IAAK,CAAA,UAAA,CAAW,OAAW,IAAA,OAAA;AAG3C,EAAO,OAAA;AAAA,IACL,aAAA;AAAA,IACA,OAAA;AAAA,IACA,GAAG;AAAA,GACL;AACF;;;;"}
@@ -8,7 +8,7 @@ function parseResponse(response, options) {
8
8
  try {
9
9
  const options_ = getOptionsFromResponse(response, options);
10
10
  if (response.body === null) {
11
- throw new RangeError("Response body is null");
11
+ throw new TypeError("Response body is null");
12
12
  }
13
13
  return parseUint8ArrayStream(response.body, options_);
14
14
  } catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"parseResponse.js","sources":["../src/parseResponse.ts"],"sourcesContent":["import type { CSVRecord, ParseOptions } from \"./common/types.ts\";\nimport { commonParseErrorHandling } from \"./commonParseErrorHandling.ts\";\nimport { getOptionsFromResponse } from \"./getOptionsFromResponse.ts\";\nimport { parseResponseToStream } from \"./parseResponseToStream.ts\";\nimport { parseUint8ArrayStream } from \"./parseUint8ArrayStream.ts\";\nimport * as internal from \"./utils/convertThisAsyncIterableIteratorToArray.ts\";\n\n/**\n * Parse HTTP Response what contains CSV to records,\n * ideal for smaller data sets.\n *\n * @remarks\n * This function automatically treats response headers.\n *\n * - If `Content-Type` header is not set, it assumes `text/csv`.\n * - If `Content-Type` header is not `text/csv`, it throws an error.\n * - If `Content-Type` header has charset parameter, it uses it for decoding.\n * - If `Content-Encoding` header is set, it decompresses the response.\n * - Should there be any conflicting information between the header and the options, the option's value will take precedence.\n *\n * @category Middle-level API\n * @param response\n * @param options\n * @returns Async iterable iterator of records.\n *\n * If you want array of records, use {@link parseResponse.toArray} function.\n *\n * @example Parsing CSV Response\n *\n * ```ts\n * import { parseResponse } from 'web-csv-toolbox';\n *\n * const response = await fetch('https://example.com/data.csv');\n *\n * for await (const record of parseResponse(response)) {\n * console.log(record);\n * }\n * ```\n */\nexport function parseResponse<Header extends ReadonlyArray<string>>(\n response: Response,\n options?: ParseOptions<Header>,\n): AsyncIterableIterator<CSVRecord<Header>> {\n try {\n const options_ = getOptionsFromResponse(response, options);\n if (response.body === null) {\n throw new RangeError(\"Response body is null\");\n }\n return parseUint8ArrayStream(response.body, options_);\n } catch (error) {\n commonParseErrorHandling(error);\n }\n}\n\nexport declare namespace parseResponse {\n /**\n * Parse CSV Response to array of records.\n *\n * @returns Array of records\n *\n * @example Parsing CSV Response\n *\n * ```ts\n * import { parseResponse } from 'web-csv-toolbox';\n *\n * const response = await fetch('https://example.com/data.csv');\n *\n * const records = await parseResponse.toArray(response);\n * console.log(records);\n * ```\n */\n export function toArray<Header extends ReadonlyArray<string>>(\n response: Response,\n options?: ParseOptions<Header>,\n ): Promise<CSVRecord<Header>[]>;\n /**\n * Parse CSV Response to stream of records.\n *\n * @param response Response to parse\n * @returns Stream of records\n *\n * @example Parsing CSV Response\n *\n * ```ts\n * import { parseResponse } from 'web-csv-toolbox';\n *\n * const response = await fetch('https://example.com/data.csv');\n *\n * await parseResponse.toStream(response)\n * .pipeTo(\n * new WritableStream({\n * write(record) {\n * console.log(record);\n * },\n * }),\n * );\n * // Prints:\n * // { name: 'Alice', age: '42' }\n * // { name: 'Bob', age: '69' }\n * ```\n */\n export function toStream<Header extends ReadonlyArray<string>>(\n response: Response,\n options?: ParseOptions<Header>,\n ): ReadableStream<CSVRecord<Header>[]>;\n}\n\nObject.defineProperties(parseResponse, {\n toArray: {\n enumerable: true,\n writable: false,\n value: internal.convertThisAsyncIterableIteratorToArray,\n },\n toStreamSync: {\n enumerable: true,\n writable: false,\n value: parseResponseToStream,\n },\n});\n"],"names":["internal.convertThisAsyncIterableIteratorToArray"],"mappings":";;;;;;AAuCgB,SAAA,aAAA,CACd,UACA,OAC0C,EAAA;AAC1C,EAAI,IAAA;AACF,IAAM,MAAA,QAAA,GAAW,sBAAuB,CAAA,QAAA,EAAU,OAAO,CAAA;AACzD,IAAI,IAAA,QAAA,CAAS,SAAS,IAAM,EAAA;AAC1B,MAAM,MAAA,IAAI,WAAW,uBAAuB,CAAA;AAAA;AAE9C,IAAO,OAAA,qBAAA,CAAsB,QAAS,CAAA,IAAA,EAAM,QAAQ,CAAA;AAAA,WAC7C,KAAO,EAAA;AACd,IAAA,wBAAA,CAAyB,KAAK,CAAA;AAAA;AAElC;AAuDA,MAAA,CAAO,iBAAiB,aAAe,EAAA;AAAA,EACrC,OAAS,EAAA;AAAA,IACP,UAAY,EAAA,IAAA;AAAA,IACZ,QAAU,EAAA,KAAA;AAAA,IACV,OAAOA;AAAS,GAClB;AAAA,EACA,YAAc,EAAA;AAAA,IACZ,UAAY,EAAA,IAAA;AAAA,IACZ,QAAU,EAAA,KAAA;AAAA,IACV,KAAO,EAAA;AAAA;AAEX,CAAC,CAAA;;;;"}
1
+ {"version":3,"file":"parseResponse.js","sources":["../src/parseResponse.ts"],"sourcesContent":["import type { CSVRecord, ParseOptions } from \"./common/types.ts\";\nimport { commonParseErrorHandling } from \"./commonParseErrorHandling.ts\";\nimport { getOptionsFromResponse } from \"./getOptionsFromResponse.ts\";\nimport { parseResponseToStream } from \"./parseResponseToStream.ts\";\nimport { parseUint8ArrayStream } from \"./parseUint8ArrayStream.ts\";\nimport * as internal from \"./utils/convertThisAsyncIterableIteratorToArray.ts\";\n\n/**\n * Parse HTTP Response what contains CSV to records,\n * ideal for smaller data sets.\n *\n * @remarks\n * This function automatically treats response headers.\n *\n * - If `Content-Type` header is not set, it assumes `text/csv`.\n * - If `Content-Type` header is not `text/csv`, it throws an error.\n * - If `Content-Type` header has charset parameter, it uses it for decoding.\n * - If `Content-Encoding` header is set, it decompresses the response.\n * - Should there be any conflicting information between the header and the options, the option's value will take precedence.\n *\n * @category Middle-level API\n * @param response\n * @param options\n * @returns Async iterable iterator of records.\n *\n * If you want array of records, use {@link parseResponse.toArray} function.\n *\n * @example Parsing CSV Response\n *\n * ```ts\n * import { parseResponse } from 'web-csv-toolbox';\n *\n * const response = await fetch('https://example.com/data.csv');\n *\n * for await (const record of parseResponse(response)) {\n * console.log(record);\n * }\n * ```\n */\nexport function parseResponse<Header extends ReadonlyArray<string>>(\n response: Response,\n options?: ParseOptions<Header>,\n): AsyncIterableIterator<CSVRecord<Header>> {\n try {\n const options_ = getOptionsFromResponse(response, options);\n if (response.body === null) {\n throw new TypeError(\"Response body is null\");\n }\n return parseUint8ArrayStream(response.body, options_);\n } catch (error) {\n commonParseErrorHandling(error);\n }\n}\n\nexport declare namespace parseResponse {\n /**\n * Parse CSV Response to array of records.\n *\n * @returns Array of records\n *\n * @example Parsing CSV Response\n *\n * ```ts\n * import { parseResponse } from 'web-csv-toolbox';\n *\n * const response = await fetch('https://example.com/data.csv');\n *\n * const records = await parseResponse.toArray(response);\n * console.log(records);\n * ```\n */\n export function toArray<Header extends ReadonlyArray<string>>(\n response: Response,\n options?: ParseOptions<Header>,\n ): Promise<CSVRecord<Header>[]>;\n /**\n * Parse CSV Response to stream of records.\n *\n * @param response Response to parse\n * @returns Stream of records\n *\n * @example Parsing CSV Response\n *\n * ```ts\n * import { parseResponse } from 'web-csv-toolbox';\n *\n * const response = await fetch('https://example.com/data.csv');\n *\n * await parseResponse.toStream(response)\n * .pipeTo(\n * new WritableStream({\n * write(record) {\n * console.log(record);\n * },\n * }),\n * );\n * // Prints:\n * // { name: 'Alice', age: '42' }\n * // { name: 'Bob', age: '69' }\n * ```\n */\n export function toStream<Header extends ReadonlyArray<string>>(\n response: Response,\n options?: ParseOptions<Header>,\n ): ReadableStream<CSVRecord<Header>[]>;\n}\n\nObject.defineProperties(parseResponse, {\n toArray: {\n enumerable: true,\n writable: false,\n value: internal.convertThisAsyncIterableIteratorToArray,\n },\n toStreamSync: {\n enumerable: true,\n writable: false,\n value: parseResponseToStream,\n },\n});\n"],"names":["internal.convertThisAsyncIterableIteratorToArray"],"mappings":";;;;;;AAuCgB,SAAA,aAAA,CACd,UACA,OAC0C,EAAA;AAC1C,EAAI,IAAA;AACF,IAAM,MAAA,QAAA,GAAW,sBAAuB,CAAA,QAAA,EAAU,OAAO,CAAA;AACzD,IAAI,IAAA,QAAA,CAAS,SAAS,IAAM,EAAA;AAC1B,MAAM,MAAA,IAAI,UAAU,uBAAuB,CAAA;AAAA;AAE7C,IAAO,OAAA,qBAAA,CAAsB,QAAS,CAAA,IAAA,EAAM,QAAQ,CAAA;AAAA,WAC7C,KAAO,EAAA;AACd,IAAA,wBAAA,CAAyB,KAAK,CAAA;AAAA;AAElC;AAuDA,MAAA,CAAO,iBAAiB,aAAe,EAAA;AAAA,EACrC,OAAS,EAAA;AAAA,IACP,UAAY,EAAA,IAAA;AAAA,IACZ,QAAU,EAAA,KAAA;AAAA,IACV,OAAOA;AAAS,GAClB;AAAA,EACA,YAAc,EAAA;AAAA,IACZ,UAAY,EAAA,IAAA;AAAA,IACZ,QAAU,EAAA,KAAA;AAAA,IACV,KAAO,EAAA;AAAA;AAEX,CAAC,CAAA;;;;"}
@@ -6,7 +6,7 @@ function parseResponseToStream(response, options) {
6
6
  try {
7
7
  const options_ = getOptionsFromResponse(response, options);
8
8
  if (response.body === null) {
9
- throw new RangeError("Response body is null");
9
+ throw new TypeError("Response body is null");
10
10
  }
11
11
  return parseUint8ArrayStreamToStream(response.body, options_);
12
12
  } catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"parseResponseToStream.js","sources":["../src/parseResponseToStream.ts"],"sourcesContent":["import type { CSVRecord, ParseBinaryOptions } from \"./common/types.ts\";\nimport { commonParseErrorHandling } from \"./commonParseErrorHandling.ts\";\nimport { getOptionsFromResponse } from \"./getOptionsFromResponse.ts\";\nimport { parseUint8ArrayStreamToStream } from \"./parseUint8ArrayStreamToStream.ts\";\n\nexport function parseResponseToStream<Header extends ReadonlyArray<string>>(\n response: Response,\n options?: ParseBinaryOptions<Header>,\n): ReadableStream<CSVRecord<Header>> {\n try {\n const options_ = getOptionsFromResponse(response, options);\n if (response.body === null) {\n throw new RangeError(\"Response body is null\");\n }\n return parseUint8ArrayStreamToStream(response.body, options_);\n } catch (error) {\n commonParseErrorHandling(error);\n }\n}\n"],"names":[],"mappings":";;;;AAKgB,SAAA,qBAAA,CACd,UACA,OACmC,EAAA;AACnC,EAAI,IAAA;AACF,IAAM,MAAA,QAAA,GAAW,sBAAuB,CAAA,QAAA,EAAU,OAAO,CAAA;AACzD,IAAI,IAAA,QAAA,CAAS,SAAS,IAAM,EAAA;AAC1B,MAAM,MAAA,IAAI,WAAW,uBAAuB,CAAA;AAAA;AAE9C,IAAO,OAAA,6BAAA,CAA8B,QAAS,CAAA,IAAA,EAAM,QAAQ,CAAA;AAAA,WACrD,KAAO,EAAA;AACd,IAAA,wBAAA,CAAyB,KAAK,CAAA;AAAA;AAElC;;;;"}
1
+ {"version":3,"file":"parseResponseToStream.js","sources":["../src/parseResponseToStream.ts"],"sourcesContent":["import type { CSVRecord, ParseBinaryOptions } from \"./common/types.ts\";\nimport { commonParseErrorHandling } from \"./commonParseErrorHandling.ts\";\nimport { getOptionsFromResponse } from \"./getOptionsFromResponse.ts\";\nimport { parseUint8ArrayStreamToStream } from \"./parseUint8ArrayStreamToStream.ts\";\n\nexport function parseResponseToStream<Header extends ReadonlyArray<string>>(\n response: Response,\n options?: ParseBinaryOptions<Header>,\n): ReadableStream<CSVRecord<Header>> {\n try {\n const options_ = getOptionsFromResponse(response, options);\n if (response.body === null) {\n throw new TypeError(\"Response body is null\");\n }\n return parseUint8ArrayStreamToStream(response.body, options_);\n } catch (error) {\n commonParseErrorHandling(error);\n }\n}\n"],"names":[],"mappings":";;;;AAKgB,SAAA,qBAAA,CACd,UACA,OACmC,EAAA;AACnC,EAAI,IAAA;AACF,IAAM,MAAA,QAAA,GAAW,sBAAuB,CAAA,QAAA,EAAU,OAAO,CAAA;AACzD,IAAI,IAAA,QAAA,CAAS,SAAS,IAAM,EAAA;AAC1B,MAAM,MAAA,IAAI,UAAU,uBAAuB,CAAA;AAAA;AAE7C,IAAO,OAAA,6BAAA,CAA8B,QAAS,CAAA,IAAA,EAAM,QAAQ,CAAA;AAAA,WACrD,KAAO,EAAA;AACd,IAAA,wBAAA,CAAyB,KAAK,CAAA;AAAA;AAElC;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-csv-toolbox",
3
- "version": "0.12.1-next-53113e3c3ee5a6ec969f90ac8d8c87cc929fe86b",
3
+ "version": "0.13.0-next-76eec9027400dc77264be2be8a252d284f00dc6a",
4
4
  "description": "A CSV Toolbox utilizing Web Standard APIs.",
5
5
  "type": "module",
6
6
  "module": "dist/web-csv-toolbox.js",