Ardis Lu [Sat, 12 Jul 2025 06:22:50 +0000 (23:22 -0700)]
Re-export callback types
These types were initially missed because they are defined with the `callback` annotation and not `typedef`. For future completeness, search for `export type` in the generated declaration files.
Ardis Lu [Sat, 12 Jul 2025 05:09:25 +0000 (22:09 -0700)]
Add `useLiteralNames` option to disable column name normalization
This behavior is useful to preserve data when an input CSV has multiple columns with the same normalized name. By default, `CSVNormalizer` will remove these duplicate columns (the data from the last column is used).
Note that columns with the same *literal* name are still removed even when `useLiteralNames` is `true`. If you need to preserve columns with the same literal name, use a lower-level transformation (e.g., `CSVTransformer`).
Ardis Lu [Fri, 11 Jul 2025 04:49:00 +0000 (21:49 -0700)]
Re-export all types in `index.js`
Currently, the types are accessible internally in the library and within `index.js`, but NOT accessible from consumers importing from the `index.js` file. A re-export is required to make the types accessible to end-users.
Additionally, the JSDoc import will only work for types declared at the module scope, so `CSVWriterStatus` was moved to the outer scope.
Ardis Lu [Thu, 10 Jul 2025 05:10:20 +0000 (22:10 -0700)]
Ignore `handleHeaders` if the row has been normalized
This behavior is desirable when `CSVTransformer` interacts with `CSVNormalizer` or any other transformation which converts the row from `Array<string>` into `Array<Object>`, because it avoids the need to pass `handleHeaders: true` to `CSVTransformer` (which is counterintuitive). It is more accurate to say the header row has been deleted by the transformation and must by re-inserted into the write stream later.
The loosening of the type checking is not a breaking change. Since the row is passed directly to `JSON.parse()`, the fields could have always been type `Object` instead of `string`.
Ardis Lu [Thu, 10 Jul 2025 04:33:12 +0000 (21:33 -0700)]
Fall back on `name` if `displayName` is undefined
This behavior is useful when passing rows to `CSVDenormalizer` after a transformation *other* than `CSVNormalizer` (which automatically creates `displayName`).
Ardis Lu [Thu, 10 Jul 2025 04:12:44 +0000 (21:12 -0700)]
Set default normalization type to `'string'`
This behavior is helpful to perform minimal normalization tasks but otherwise preserve the default CSV transformation behavior (i.e., have all input values be a string).
This is not a breaking change. Previously, `type` would also default to `'string'` after logging a warning in console.
Ardis Lu [Fri, 4 Jul 2025 05:02:25 +0000 (22:02 -0700)]
Increase `highWaterMark` for `CSVWriter`'s queuing strategy
This change improves the 100MB Write benchmark from ~9s to ~7s.
Bumping `highWaterMark` to >1 also changes the stream's interaction with `AbortController`, so now the chunk processed during an abort is included in the write.
I believe this behavior has to do with the task queue order of the callback for the `AbortSignal`'s `abort` event - so now with multiple chunks in the queue, the last chunk is written before the `abort` event is processed. I was unable to force this writing code to occur after the `abort` event in a deterministic way.
This situation is a rare edge case so letting it through.
Ardis Lu [Fri, 4 Jul 2025 03:45:55 +0000 (20:45 -0700)]
Simplify and harden tick indicator
Relying on physical timing is flaky (e.g., it will fail if executing on a slow machine). Sending a simple `tick` increment into the task queue is more reliable.
Ardis Lu [Tue, 1 Jul 2025 04:19:16 +0000 (21:19 -0700)]
Increase default chunk size from 1 KiB to 64 KiB
Benchmarks showed improvements or no change across all tests. Notably, 100MB Read improved from ~13s to ~3s.
The magic value of 64 KiB was selected based on Node.js's default of 16 KiB in similar code [1] and manually testing some multiples over that (Node.js tends to be conservative).
Ardis Lu [Wed, 4 Jun 2025 06:45:32 +0000 (23:45 -0700)]
Simplify release notes to be auto generated
Reasons in favor of minimal release notes: (1) Releases is a GitHub functionality, it's better not to invest too much time into vendor-specific configurations. (2) There is no binary associated with this library so Releases has reduced significance. (3) Meaningful release notes are still contained in git tag annotations, which can be carried across hosts.
One downside is losing meaningful messages in email notifications if people subscribe to follow releases on GitHub.
Ardis Lu [Tue, 27 May 2025 05:01:57 +0000 (22:01 -0700)]
Refactor to pass options through to private fields
This change makes the code consistent with previous refactoring on the core code, and also prevents unexpected behavior from mutating the options object after instantiation.
Ardis Lu [Sun, 25 May 2025 04:55:24 +0000 (21:55 -0700)]
Explicitly import `ReadableStream` to make TS understand `ReadableStream.from`
This change does not affect the code execution, but it makes the TS language server understand the `ReadableStream.from` static function. Without the explicit import, it is not possible using only JSDoc to merge the `ReadableStream` instance interface with the `ReadableStream` constructor because they share the same name.
Ardis Lu [Sun, 25 May 2025 04:24:08 +0000 (21:24 -0700)]
Migrate from CLI options to explicit `tsconfig.json`
This change makes it easier to use an explicit `lib` configuration and types, which resolves type issues resulting from subtle mismatches between implied type imports (e.g., some files importing `ReadableStream` from TS's built-in Web API types and others importing from Node.js's `streams/web`).
This is the cleanest way to make a JS library importable to a TS project. This approach is similar to Svelte's approach.
Note the dev dependency on TS is only used for `.d.ts` generation. The source code itself still does not use TS (or any dependencies other than Node.js) at all.
Ardis Lu [Mon, 28 Apr 2025 06:07:57 +0000 (23:07 -0700)]
Block `cancel()` until `start()` is finished
If `cancel()` is called immediately after the stream is instantiated, it is possible that `start()` is not yet finished setting up the file handle before the `cancel()` logic is executed.
Ardis Lu [Sun, 27 Apr 2025 08:38:12 +0000 (01:38 -0700)]
Refactor to unpack `options` into private properties
This change makes these properties consistent with other classes in this library. This change is also the simplest way to let the TypeScript language server understand that these properties are not null.
Ardis Lu [Sun, 27 Apr 2025 08:30:01 +0000 (01:30 -0700)]
Refactor to simplify for language server
The TypeScript language server is not yet capable of understanding that the nullish coalescing assignment will make the property non-null, so it will show a type error. Another workaround would be to assert the property is non-null using a JSDoc equivalent to TypeScript's `!` but that is also not yet supported by JSDoc.
Ardis Lu [Fri, 4 Apr 2025 07:12:25 +0000 (00:12 -0700)]
Test UTF-16 BE decoding
`utf-16be` is not supported by Node.js's decoder in the `fs` module, but it is supported by the web standard Encoding API. So this is a good test to demonstrate the difference between the two.
Ardis Lu [Fri, 4 Apr 2025 06:51:45 +0000 (23:51 -0700)]
Remove dependency on Node.js `createReadStream`
This change decouples the file reading from the character decoding, which enables use of the web standard Encoding API (`TextDecoderStream`) instead of Node.js's internal decoder. The web standard allows a broader set of encodings.