@prairielearn/csv 2.0.16 → 2.0.18
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/CHANGELOG.md +13 -0
- package/README.md +17 -5
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +3 -1
- package/dist/index.test.js.map +1 -1
- package/package.json +5 -5
- package/src/index.test.ts +7 -3
- package/src/index.ts +2 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @prairielearn/csv
|
|
2
2
|
|
|
3
|
+
## 2.0.18
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- b55261c: Upgrade to TypeScript 5.9
|
|
8
|
+
|
|
9
|
+
## 2.0.17
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 7e9fbf0: Require type parameter for stringifyStream
|
|
14
|
+
- 23adb05: Upgrade all JavaScript dependencies
|
|
15
|
+
|
|
3
16
|
## 2.0.16
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -11,17 +11,29 @@ import { stringifyStream } from '@prairielearn/csv';
|
|
|
11
11
|
import { queryCursor } from '@prairielearn/postgres';
|
|
12
12
|
import { pipeline } from 'node:stream/promises';
|
|
13
13
|
import { createWriteStream } from 'node:fs';
|
|
14
|
+
import { IdSchema } from '@prairielearn/zod';
|
|
15
|
+
import { z } from 'zod';
|
|
14
16
|
|
|
15
|
-
const
|
|
17
|
+
const WorkspaceSchema = z.object({
|
|
18
|
+
id: IdSchema,
|
|
19
|
+
hostname: z.string(),
|
|
20
|
+
});
|
|
21
|
+
type Workspace = z.infer<typeof WorkspaceSchema>;
|
|
22
|
+
|
|
23
|
+
const cursor = await queryCursor('SELECT id, hostname FROM workspaces;', WorkspaceSchema);
|
|
16
24
|
const output = createWriteStream('workspaces.csv');
|
|
17
25
|
|
|
18
|
-
const stringifier = stringifyStream({
|
|
26
|
+
const stringifier = stringifyStream<Workspace>({
|
|
19
27
|
header: true,
|
|
20
|
-
columns: [
|
|
28
|
+
columns: [
|
|
29
|
+
{ key: 'id', header: 'ID' },
|
|
30
|
+
{ key: 'hostname', header: 'Hostname' },
|
|
31
|
+
],
|
|
21
32
|
// Optionally provide a function to transform each item in the stream.
|
|
22
33
|
transform(record) {
|
|
23
34
|
return {
|
|
24
|
-
id: `workspace-${id}`,
|
|
35
|
+
id: `workspace-${record.id}`,
|
|
36
|
+
hostname: record.hostname,
|
|
25
37
|
};
|
|
26
38
|
},
|
|
27
39
|
});
|
|
@@ -37,7 +49,7 @@ import { createWriteStream } from 'node:fs';
|
|
|
37
49
|
|
|
38
50
|
const data = Array.from(new Array(100_000), (_, i) => ({ id: i }));
|
|
39
51
|
const output = createWriteStream('numbers.csv');
|
|
40
|
-
stringifyNonblocking(data, {
|
|
52
|
+
stringifyNonblocking<{ id: number }>(data, {
|
|
41
53
|
header: true,
|
|
42
54
|
columns: [{ key: 'id', header: 'ID' }],
|
|
43
55
|
}).pipe(output);
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export interface StringifyNonblockingOptions extends StringifierOptions {
|
|
|
9
9
|
* block the event loop.
|
|
10
10
|
*/
|
|
11
11
|
export declare function stringifyNonblocking(data: any[], options?: StringifyNonblockingOptions): Stringifier;
|
|
12
|
-
interface StringifyOptions<T
|
|
12
|
+
interface StringifyOptions<T, U = any> extends Pick<StringifierOptions, 'columns' | 'header'> {
|
|
13
13
|
transform?: TransformHandler<T, U>;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
@@ -21,4 +21,4 @@ interface StringifyOptions<T = any, U = any> extends Pick<StringifierOptions, 'c
|
|
|
21
21
|
* Works best when combined with the `pipeline` function from
|
|
22
22
|
* `node:stream/promises`, which will help ensure that errors are handled properly.
|
|
23
23
|
*/
|
|
24
|
-
export declare function stringifyStream<T
|
|
24
|
+
export declare function stringifyStream<T, U = any>(options?: StringifyOptions<T, U>): NodeJS.ReadWriteStream;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAsC,SAAS,EAAE,MAAM,eAAe,CAAC;AAC3F,OAAO,SAAS,MAAM,WAAW,CAAC;AAClC,OAAO,EAAoC,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE/E,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC;AAMlC;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,IAAW,EACX,UAAuC,EAAE;IAEzC,MAAM,EAAE,SAAS,GAAG,GAAG,EAAE,GAAG,kBAAkB,EAAE,GAAG,OAAO,CAAC;IAC3D,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,kBAAkB,CAAC,CAAC;IAExD,OAAO,CAAC,QAAQ,CAAC;QACf,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,SAAS,IAAI;YACX,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;gBACnC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;oBACpB,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;oBAC3B,CAAC,IAAI,CAAC,CAAC;gBACT,CAAC;qBAAM,CAAC;oBACN,WAAW,CAAC,GAAG,EAAE,CAAC;oBAClB,OAAO;gBACT,CAAC;YACH,CAAC;YACD,YAAY,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;QACD,IAAI,EAAE,CAAC;IACT,CAAC,CAAC,CAAC;IAEH,OAAO,WAAW,CAAC;AACrB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAsC,SAAS,EAAE,MAAM,eAAe,CAAC;AAC3F,OAAO,SAAS,MAAM,WAAW,CAAC;AAClC,OAAO,EAAoC,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE/E,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC;AAMlC;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,IAAW,EACX,UAAuC,EAAE;IAEzC,MAAM,EAAE,SAAS,GAAG,GAAG,EAAE,GAAG,kBAAkB,EAAE,GAAG,OAAO,CAAC;IAC3D,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,kBAAkB,CAAC,CAAC;IAExD,OAAO,CAAC,QAAQ,CAAC;QACf,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,SAAS,IAAI;YACX,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;gBACnC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;oBACpB,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;oBAC3B,CAAC,IAAI,CAAC,CAAC;gBACT,CAAC;qBAAM,CAAC;oBACN,WAAW,CAAC,GAAG,EAAE,CAAC;oBAClB,OAAO;gBACT,CAAC;YACH,CAAC;YACD,YAAY,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;QACD,IAAI,EAAE,CAAC;IACT,CAAC,CAAC,CAAC;IAEH,OAAO,WAAW,CAAC;AACrB,CAAC;AAMD;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAC7B,UAAkC,EAAE;IAEpC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,kBAAkB,EAAE,GAAG,OAAO,CAAC;IACjE,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,kBAAkB,CAAC,CAAC;IACxD,IAAI,CAAC,UAAU;QAAE,OAAO,WAAW,CAAC;IACpC,2DAA2D;IAC3D,OAAO,SAAS,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,WAAW,CAAC,CAAC;AACvD,CAAC","sourcesContent":["import { Stringifier, type Options as StringifierOptions, stringify } from 'csv-stringify';\nimport multipipe from 'multipipe';\nimport { type Handler as TransformHandler, transform } from 'stream-transform';\n\nexport { stringify, Stringifier };\n\nexport interface StringifyNonblockingOptions extends StringifierOptions {\n batchSize?: number;\n}\n\n/**\n * Streaming transform from an array of objects to a CSV that doesn't\n * block the event loop.\n */\nexport function stringifyNonblocking(\n data: any[],\n options: StringifyNonblockingOptions = {},\n): Stringifier {\n const { batchSize = 100, ...stringifierOptions } = options;\n const stringifier = new Stringifier(stringifierOptions);\n\n process.nextTick(function () {\n let j = 0;\n function loop() {\n for (let i = 0; i < batchSize; i++) {\n if (j < data.length) {\n stringifier.write(data[j]);\n j += 1;\n } else {\n stringifier.end();\n return;\n }\n }\n setImmediate(loop);\n }\n loop();\n });\n\n return stringifier;\n}\n\ninterface StringifyOptions<T, U = any> extends Pick<StringifierOptions, 'columns' | 'header'> {\n transform?: TransformHandler<T, U>;\n}\n\n/**\n * Transforms an object stream into a CSV stream.\n *\n * This is a thin wrapper around `stringify` from the `csv-stringify` package\n * with added support for transforming the input stream.\n *\n * Works best when combined with the `pipeline` function from\n * `node:stream/promises`, which will help ensure that errors are handled properly.\n */\nexport function stringifyStream<T, U = any>(\n options: StringifyOptions<T, U> = {},\n): NodeJS.ReadWriteStream {\n const { transform: _transform, ...stringifierOptions } = options;\n const stringifier = new Stringifier(stringifierOptions);\n if (!_transform) return stringifier;\n // TODO: use native `node:stream#compose` once it's stable.\n return multipipe(transform(_transform), stringifier);\n}\n"]}
|
package/dist/index.test.js
CHANGED
|
@@ -36,7 +36,9 @@ describe('stringifyStream', () => {
|
|
|
36
36
|
{ a: 2, b: 2 },
|
|
37
37
|
{ a: 3, b: 3 },
|
|
38
38
|
]);
|
|
39
|
-
const csvStream = stream.pipe(stringifyStream({
|
|
39
|
+
const csvStream = stream.pipe(stringifyStream({
|
|
40
|
+
transform: (row) => [row.a + 1, row.b + 2],
|
|
41
|
+
}));
|
|
40
42
|
const csv = await streamToString(csvStream);
|
|
41
43
|
assert.equal(csv, '2,3\n3,4\n4,5\n');
|
|
42
44
|
});
|
package/dist/index.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,SAAS,cAAc,CAAC,MAA6B;IACnD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9D,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC3B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;AACL,CAAC;AAED,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,EAAE,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;QAC/C,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC;YAC3B,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;YACd,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;YACd,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;SACf,CAAC,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC;QACjD,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;QAC9C,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC;YAC3B,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;SACX,CAAC,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC;QACjD,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC;YAC3B,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;YACd,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;YACd,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;SACf,CAAC,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { Readable } from 'node:stream';\n\nimport { assert, describe, it } from 'vitest';\n\nimport { stringifyStream } from './index.js';\n\nfunction streamToString(stream: NodeJS.ReadableStream): Promise<string> {\n const chunks: Buffer[] = [];\n return new Promise((resolve, reject) => {\n stream.on('data', (chunk) => chunks.push(Buffer.from(chunk)));\n stream.on('error', reject);\n stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));\n });\n}\n\ndescribe('stringifyStream', () => {\n it('stringifies a stream of objects', async () => {\n const stream = Readable.from([\n { a: 1, b: 1 },\n { a: 2, b: 2 },\n { a: 3, b: 3 },\n ]);\n const csvStream = stream.pipe(stringifyStream());\n const csv = await streamToString(csvStream);\n assert.equal(csv, '1,1\\n2,2\\n3,3\\n');\n });\n\n it('stringifies a stream of arrays', async () => {\n const stream = Readable.from([\n ['1', '1'],\n ['2', '2'],\n ['3', '3'],\n ]);\n const csvStream = stream.pipe(stringifyStream());\n const csv = await streamToString(csvStream);\n assert.equal(csv, '1,1\\n2,2\\n3,3\\n');\n });\n\n it('stringifies a stream with a transform', async () => {\n const stream = Readable.from([\n { a: 1, b: 1 },\n { a: 2, b: 2 },\n { a: 3, b: 3 },\n ]);\n const csvStream = stream.pipe(\n stringifyStream<{ a: number; b: number }>({\n transform: (row) => [row.a + 1, row.b + 2],\n }),\n );\n const csv = await streamToString(csvStream);\n assert.equal(csv, '2,3\\n3,4\\n4,5\\n');\n });\n\n it('stringifies a stream with keyed columns and a transform', async () => {\n const stream = Readable.from([\n { a: 1, b: 1 },\n { a: 2, b: 2 },\n { a: 3, b: 3 },\n ]);\n const stringifier = stringifyStream<{ a: number; b: number }>({\n header: true,\n columns: [\n { key: 'a', header: 'first' },\n { key: 'b', header: 'second' },\n ],\n transform: (row) => [row.a + 1, row.b + 2],\n });\n const csv = await streamToString(stream.pipe(stringifier));\n assert.equal(csv, 'first,second\\n2,3\\n3,4\\n4,5\\n');\n });\n\n it('stringifies a stream with named columns and a transform', async () => {\n const stream = Readable.from([\n { a: 1, b: 1 },\n { a: 2, b: 2 },\n { a: 3, b: 3 },\n ]);\n const stringifier = stringifyStream<{ a: number; b: number }>({\n header: true,\n columns: ['first', 'second'],\n transform: (row) => [row.a + 1, row.b + 2],\n });\n const csv = await streamToString(stream.pipe(stringifier));\n assert.equal(csv, 'first,second\\n2,3\\n3,4\\n4,5\\n');\n });\n});\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prairielearn/csv",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.18",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -14,17 +14,17 @@
|
|
|
14
14
|
"test": "vitest run --coverage"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"csv-stringify": "^6.
|
|
17
|
+
"csv-stringify": "^6.6.0",
|
|
18
18
|
"multipipe": "^4.0.0",
|
|
19
|
-
"stream-transform": "^3.
|
|
19
|
+
"stream-transform": "^3.4.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@prairielearn/tsconfig": "^0.0.0",
|
|
23
23
|
"@types/multipipe": "^3.0.5",
|
|
24
|
-
"@types/node": "^22.
|
|
24
|
+
"@types/node": "^22.17.0",
|
|
25
25
|
"@vitest/coverage-v8": "^3.2.4",
|
|
26
26
|
"tsx": "^4.20.3",
|
|
27
|
-
"typescript": "^5.
|
|
27
|
+
"typescript": "^5.9.2",
|
|
28
28
|
"vitest": "^3.2.4"
|
|
29
29
|
}
|
|
30
30
|
}
|
package/src/index.test.ts
CHANGED
|
@@ -42,7 +42,11 @@ describe('stringifyStream', () => {
|
|
|
42
42
|
{ a: 2, b: 2 },
|
|
43
43
|
{ a: 3, b: 3 },
|
|
44
44
|
]);
|
|
45
|
-
const csvStream = stream.pipe(
|
|
45
|
+
const csvStream = stream.pipe(
|
|
46
|
+
stringifyStream<{ a: number; b: number }>({
|
|
47
|
+
transform: (row) => [row.a + 1, row.b + 2],
|
|
48
|
+
}),
|
|
49
|
+
);
|
|
46
50
|
const csv = await streamToString(csvStream);
|
|
47
51
|
assert.equal(csv, '2,3\n3,4\n4,5\n');
|
|
48
52
|
});
|
|
@@ -53,7 +57,7 @@ describe('stringifyStream', () => {
|
|
|
53
57
|
{ a: 2, b: 2 },
|
|
54
58
|
{ a: 3, b: 3 },
|
|
55
59
|
]);
|
|
56
|
-
const stringifier = stringifyStream({
|
|
60
|
+
const stringifier = stringifyStream<{ a: number; b: number }>({
|
|
57
61
|
header: true,
|
|
58
62
|
columns: [
|
|
59
63
|
{ key: 'a', header: 'first' },
|
|
@@ -71,7 +75,7 @@ describe('stringifyStream', () => {
|
|
|
71
75
|
{ a: 2, b: 2 },
|
|
72
76
|
{ a: 3, b: 3 },
|
|
73
77
|
]);
|
|
74
|
-
const stringifier = stringifyStream({
|
|
78
|
+
const stringifier = stringifyStream<{ a: number; b: number }>({
|
|
75
79
|
header: true,
|
|
76
80
|
columns: ['first', 'second'],
|
|
77
81
|
transform: (row) => [row.a + 1, row.b + 2],
|
package/src/index.ts
CHANGED
|
@@ -39,8 +39,7 @@ export function stringifyNonblocking(
|
|
|
39
39
|
return stringifier;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
interface StringifyOptions<T = any,
|
|
43
|
-
extends Pick<StringifierOptions, 'columns' | 'header'> {
|
|
42
|
+
interface StringifyOptions<T, U = any> extends Pick<StringifierOptions, 'columns' | 'header'> {
|
|
44
43
|
transform?: TransformHandler<T, U>;
|
|
45
44
|
}
|
|
46
45
|
|
|
@@ -53,7 +52,7 @@ interface StringifyOptions<T = any, U = any>
|
|
|
53
52
|
* Works best when combined with the `pipeline` function from
|
|
54
53
|
* `node:stream/promises`, which will help ensure that errors are handled properly.
|
|
55
54
|
*/
|
|
56
|
-
export function stringifyStream<T
|
|
55
|
+
export function stringifyStream<T, U = any>(
|
|
57
56
|
options: StringifyOptions<T, U> = {},
|
|
58
57
|
): NodeJS.ReadWriteStream {
|
|
59
58
|
const { transform: _transform, ...stringifierOptions } = options;
|