jtcsv 2.2.8 → 3.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.
Files changed (246) hide show
  1. package/README.md +204 -115
  2. package/bin/jtcsv.ts +2612 -0
  3. package/browser.d.ts +142 -0
  4. package/dist/benchmark.js +446 -0
  5. package/dist/benchmark.js.map +1 -0
  6. package/dist/bin/jtcsv.js +1940 -0
  7. package/dist/bin/jtcsv.js.map +1 -0
  8. package/dist/csv-to-json.js +1262 -0
  9. package/dist/csv-to-json.js.map +1 -0
  10. package/dist/errors.js +291 -0
  11. package/dist/errors.js.map +1 -0
  12. package/dist/eslint.config.js +147 -0
  13. package/dist/eslint.config.js.map +1 -0
  14. package/dist/index-core.js +95 -0
  15. package/dist/index-core.js.map +1 -0
  16. package/dist/index.js +93 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/json-save.js +229 -0
  19. package/dist/json-save.js.map +1 -0
  20. package/dist/json-to-csv.js +576 -0
  21. package/dist/json-to-csv.js.map +1 -0
  22. package/dist/jtcsv-core.cjs.js +1736 -0
  23. package/dist/jtcsv-core.cjs.js.map +1 -0
  24. package/dist/jtcsv-core.esm.js +1708 -0
  25. package/dist/jtcsv-core.esm.js.map +1 -0
  26. package/dist/jtcsv-core.umd.js +1742 -0
  27. package/dist/jtcsv-core.umd.js.map +1 -0
  28. package/dist/jtcsv-full.cjs.js +2241 -0
  29. package/dist/jtcsv-full.cjs.js.map +1 -0
  30. package/dist/jtcsv-full.esm.js +2209 -0
  31. package/dist/jtcsv-full.esm.js.map +1 -0
  32. package/dist/jtcsv-full.umd.js +2247 -0
  33. package/dist/jtcsv-full.umd.js.map +1 -0
  34. package/dist/jtcsv-workers.esm.js +768 -0
  35. package/dist/jtcsv-workers.esm.js.map +1 -0
  36. package/dist/jtcsv-workers.umd.js +782 -0
  37. package/dist/jtcsv-workers.umd.js.map +1 -0
  38. package/dist/jtcsv.cjs.js +1996 -2048
  39. package/dist/jtcsv.cjs.js.map +1 -1
  40. package/dist/jtcsv.esm.js +1992 -2048
  41. package/dist/jtcsv.esm.js.map +1 -1
  42. package/dist/jtcsv.umd.js +2157 -2209
  43. package/dist/jtcsv.umd.js.map +1 -1
  44. package/dist/plugins/express-middleware/index.js +350 -0
  45. package/dist/plugins/express-middleware/index.js.map +1 -0
  46. package/dist/plugins/fastify-plugin/index.js +315 -0
  47. package/dist/plugins/fastify-plugin/index.js.map +1 -0
  48. package/dist/plugins/hono/index.js +111 -0
  49. package/dist/plugins/hono/index.js.map +1 -0
  50. package/dist/plugins/nestjs/index.js +112 -0
  51. package/dist/plugins/nestjs/index.js.map +1 -0
  52. package/dist/plugins/nuxt/index.js +53 -0
  53. package/dist/plugins/nuxt/index.js.map +1 -0
  54. package/dist/plugins/remix/index.js +133 -0
  55. package/dist/plugins/remix/index.js.map +1 -0
  56. package/dist/plugins/sveltekit/index.js +155 -0
  57. package/dist/plugins/sveltekit/index.js.map +1 -0
  58. package/dist/plugins/trpc/index.js +136 -0
  59. package/dist/plugins/trpc/index.js.map +1 -0
  60. package/dist/run-demo.js +49 -0
  61. package/dist/run-demo.js.map +1 -0
  62. package/dist/src/browser/browser-functions.js +193 -0
  63. package/dist/src/browser/browser-functions.js.map +1 -0
  64. package/dist/src/browser/core.js +123 -0
  65. package/dist/src/browser/core.js.map +1 -0
  66. package/dist/src/browser/csv-to-json-browser.js +353 -0
  67. package/dist/src/browser/csv-to-json-browser.js.map +1 -0
  68. package/dist/src/browser/errors-browser.js +219 -0
  69. package/dist/src/browser/errors-browser.js.map +1 -0
  70. package/dist/src/browser/extensions/plugins.js +106 -0
  71. package/dist/src/browser/extensions/plugins.js.map +1 -0
  72. package/dist/src/browser/extensions/workers.js +66 -0
  73. package/dist/src/browser/extensions/workers.js.map +1 -0
  74. package/dist/src/browser/index.js +140 -0
  75. package/dist/src/browser/index.js.map +1 -0
  76. package/dist/src/browser/json-to-csv-browser.js +225 -0
  77. package/dist/src/browser/json-to-csv-browser.js.map +1 -0
  78. package/dist/src/browser/streams.js +340 -0
  79. package/dist/src/browser/streams.js.map +1 -0
  80. package/dist/src/browser/workers/csv-parser.worker.js +264 -0
  81. package/dist/src/browser/workers/csv-parser.worker.js.map +1 -0
  82. package/dist/src/browser/workers/worker-pool.js +338 -0
  83. package/dist/src/browser/workers/worker-pool.js.map +1 -0
  84. package/dist/src/core/delimiter-cache.js +196 -0
  85. package/dist/src/core/delimiter-cache.js.map +1 -0
  86. package/dist/src/core/node-optimizations.js +279 -0
  87. package/dist/src/core/node-optimizations.js.map +1 -0
  88. package/dist/src/core/plugin-system.js +399 -0
  89. package/dist/src/core/plugin-system.js.map +1 -0
  90. package/dist/src/core/transform-hooks.js +348 -0
  91. package/dist/src/core/transform-hooks.js.map +1 -0
  92. package/dist/src/engines/fast-path-engine-new.js +262 -0
  93. package/dist/src/engines/fast-path-engine-new.js.map +1 -0
  94. package/dist/src/engines/fast-path-engine.js +671 -0
  95. package/dist/src/engines/fast-path-engine.js.map +1 -0
  96. package/dist/src/errors.js +18 -0
  97. package/dist/src/errors.js.map +1 -0
  98. package/dist/src/formats/ndjson-parser.js +332 -0
  99. package/dist/src/formats/ndjson-parser.js.map +1 -0
  100. package/dist/src/formats/tsv-parser.js +230 -0
  101. package/dist/src/formats/tsv-parser.js.map +1 -0
  102. package/dist/src/index-with-plugins.js +259 -0
  103. package/dist/src/index-with-plugins.js.map +1 -0
  104. package/dist/src/types/index.js +3 -0
  105. package/dist/src/types/index.js.map +1 -0
  106. package/dist/src/utils/bom-utils.js +267 -0
  107. package/dist/src/utils/bom-utils.js.map +1 -0
  108. package/dist/src/utils/encoding-support.js +77 -0
  109. package/dist/src/utils/encoding-support.js.map +1 -0
  110. package/dist/src/utils/schema-validator.js +609 -0
  111. package/dist/src/utils/schema-validator.js.map +1 -0
  112. package/dist/src/utils/transform-loader.js +281 -0
  113. package/dist/src/utils/transform-loader.js.map +1 -0
  114. package/dist/src/utils/validators.js +40 -0
  115. package/dist/src/utils/validators.js.map +1 -0
  116. package/dist/src/utils/zod-adapter.js +144 -0
  117. package/dist/src/utils/zod-adapter.js.map +1 -0
  118. package/dist/src/web-server/index.js +648 -0
  119. package/dist/src/web-server/index.js.map +1 -0
  120. package/dist/src/workers/csv-multithreaded.js +211 -0
  121. package/dist/src/workers/csv-multithreaded.js.map +1 -0
  122. package/dist/src/workers/csv-parser.worker.js +179 -0
  123. package/dist/src/workers/csv-parser.worker.js.map +1 -0
  124. package/dist/src/workers/worker-pool.js +228 -0
  125. package/dist/src/workers/worker-pool.js.map +1 -0
  126. package/dist/stream-csv-to-json.js +665 -0
  127. package/dist/stream-csv-to-json.js.map +1 -0
  128. package/dist/stream-json-to-csv.js +389 -0
  129. package/dist/stream-json-to-csv.js.map +1 -0
  130. package/examples/advanced/conditional-transformations.ts +446 -0
  131. package/examples/advanced/csv-parser.worker.ts +89 -0
  132. package/examples/advanced/nested-objects-example.ts +306 -0
  133. package/examples/advanced/performance-optimization.ts +504 -0
  134. package/examples/advanced/run-demo-server.ts +116 -0
  135. package/examples/advanced/web-worker-usage.html +874 -0
  136. package/examples/async-multithreaded-example.ts +335 -0
  137. package/examples/cli-advanced-usage.md +290 -0
  138. package/examples/{cli-batch-processing.js → cli-batch-processing.ts} +38 -38
  139. package/examples/{cli-tool.js → cli-tool.ts} +5 -8
  140. package/examples/{error-handling.js → error-handling.ts} +356 -324
  141. package/examples/{express-api.js → express-api.ts} +161 -164
  142. package/examples/{large-dataset-example.js → large-dataset-example.ts} +201 -182
  143. package/examples/{ndjson-processing.js → ndjson-processing.ts} +456 -434
  144. package/examples/{plugin-excel-exporter.js → plugin-excel-exporter.ts} +6 -7
  145. package/examples/react-integration.tsx +637 -0
  146. package/examples/{schema-validation.js → schema-validation.ts} +2 -2
  147. package/examples/simple-usage.ts +194 -0
  148. package/examples/{streaming-example.js → streaming-example.ts} +12 -12
  149. package/index.d.ts +187 -18
  150. package/package.json +75 -81
  151. package/plugins.d.ts +37 -0
  152. package/schema.d.ts +103 -0
  153. package/src/browser/browser-functions.ts +402 -0
  154. package/src/browser/core.ts +152 -0
  155. package/src/browser/csv-to-json-browser.d.ts +3 -0
  156. package/src/browser/csv-to-json-browser.ts +494 -0
  157. package/src/browser/{errors-browser.js → errors-browser.ts} +305 -197
  158. package/src/browser/extensions/plugins.ts +93 -0
  159. package/src/browser/extensions/workers.ts +39 -0
  160. package/src/browser/globals.d.ts +5 -0
  161. package/src/browser/index.ts +192 -0
  162. package/src/browser/json-to-csv-browser.d.ts +3 -0
  163. package/src/browser/json-to-csv-browser.ts +338 -0
  164. package/src/browser/streams.ts +403 -0
  165. package/src/browser/workers/{csv-parser.worker.js → csv-parser.worker.ts} +3 -3
  166. package/src/browser/workers/{worker-pool.js → worker-pool.ts} +51 -30
  167. package/src/core/delimiter-cache.ts +320 -0
  168. package/src/core/{node-optimizations.js → node-optimizations.ts} +448 -407
  169. package/src/core/plugin-system.ts +588 -0
  170. package/src/core/transform-hooks.ts +566 -0
  171. package/src/engines/{fast-path-engine-new.js → fast-path-engine-new.ts} +11 -2
  172. package/src/engines/{fast-path-engine.js → fast-path-engine.ts} +79 -53
  173. package/src/errors.ts +1 -0
  174. package/src/formats/{ndjson-parser.js → ndjson-parser.ts} +24 -16
  175. package/src/formats/{tsv-parser.js → tsv-parser.ts} +18 -17
  176. package/src/{index-with-plugins.js → index-with-plugins.ts} +381 -357
  177. package/src/types/index.ts +275 -0
  178. package/src/utils/bom-utils.ts +373 -0
  179. package/src/utils/encoding-support.ts +155 -0
  180. package/src/utils/{schema-validator.js → schema-validator.ts} +814 -589
  181. package/src/utils/transform-loader.ts +389 -0
  182. package/src/utils/validators.ts +35 -0
  183. package/src/utils/zod-adapter.ts +280 -0
  184. package/src/web-server/{index.js → index.ts} +19 -19
  185. package/src/workers/csv-multithreaded.ts +310 -0
  186. package/src/workers/csv-parser.worker.ts +227 -0
  187. package/src/workers/worker-pool.ts +409 -0
  188. package/bin/jtcsv.js +0 -2462
  189. package/csv-to-json.js +0 -688
  190. package/errors.js +0 -208
  191. package/examples/simple-usage.js +0 -282
  192. package/index.js +0 -68
  193. package/json-save.js +0 -254
  194. package/json-to-csv.js +0 -526
  195. package/plugins/README.md +0 -91
  196. package/plugins/express-middleware/README.md +0 -64
  197. package/plugins/express-middleware/example.js +0 -136
  198. package/plugins/express-middleware/index.d.ts +0 -114
  199. package/plugins/express-middleware/index.js +0 -360
  200. package/plugins/express-middleware/package.json +0 -52
  201. package/plugins/fastify-plugin/index.js +0 -406
  202. package/plugins/fastify-plugin/package.json +0 -55
  203. package/plugins/hono/README.md +0 -28
  204. package/plugins/hono/index.d.ts +0 -12
  205. package/plugins/hono/index.js +0 -36
  206. package/plugins/hono/package.json +0 -35
  207. package/plugins/nestjs/README.md +0 -35
  208. package/plugins/nestjs/index.d.ts +0 -25
  209. package/plugins/nestjs/index.js +0 -77
  210. package/plugins/nestjs/package.json +0 -37
  211. package/plugins/nextjs-api/README.md +0 -57
  212. package/plugins/nextjs-api/examples/ConverterComponent.jsx +0 -386
  213. package/plugins/nextjs-api/examples/api-convert.js +0 -69
  214. package/plugins/nextjs-api/index.js +0 -387
  215. package/plugins/nextjs-api/package.json +0 -63
  216. package/plugins/nextjs-api/route.js +0 -371
  217. package/plugins/nuxt/README.md +0 -24
  218. package/plugins/nuxt/index.js +0 -21
  219. package/plugins/nuxt/package.json +0 -35
  220. package/plugins/nuxt/runtime/composables/useJtcsv.js +0 -6
  221. package/plugins/nuxt/runtime/plugin.js +0 -6
  222. package/plugins/remix/README.md +0 -26
  223. package/plugins/remix/index.d.ts +0 -16
  224. package/plugins/remix/index.js +0 -62
  225. package/plugins/remix/package.json +0 -35
  226. package/plugins/sveltekit/README.md +0 -28
  227. package/plugins/sveltekit/index.d.ts +0 -17
  228. package/plugins/sveltekit/index.js +0 -54
  229. package/plugins/sveltekit/package.json +0 -33
  230. package/plugins/trpc/README.md +0 -25
  231. package/plugins/trpc/index.d.ts +0 -7
  232. package/plugins/trpc/index.js +0 -32
  233. package/plugins/trpc/package.json +0 -34
  234. package/src/browser/browser-functions.js +0 -219
  235. package/src/browser/csv-to-json-browser.js +0 -700
  236. package/src/browser/index.js +0 -113
  237. package/src/browser/json-to-csv-browser.js +0 -309
  238. package/src/browser/streams.js +0 -393
  239. package/src/core/delimiter-cache.js +0 -186
  240. package/src/core/plugin-system.js +0 -476
  241. package/src/core/transform-hooks.js +0 -350
  242. package/src/errors.js +0 -26
  243. package/src/utils/transform-loader.js +0 -205
  244. package/stream-csv-to-json.js +0 -542
  245. package/stream-json-to-csv.js +0 -464
  246. /package/examples/{web-workers-advanced.js → web-workers-advanced.ts} +0 -0
@@ -1,464 +0,0 @@
1
- /**
2
- * Stream JSON to CSV Converter - Node.js Module
3
- *
4
- * A streaming implementation for converting JSON data to CSV format
5
- * with memory-efficient processing for large files.
6
- *
7
- * @module stream-json-to-csv
8
- */
9
-
10
- const {
11
- ValidationError,
12
- SecurityError,
13
- LimitError,
14
- ConfigurationError,
15
- safeExecute
16
- } = require('./errors');
17
-
18
- const { Transform, Readable, Writable } = require('stream');
19
- const { pipeline } = require('stream/promises');
20
-
21
- // Import schema validator from utils
22
- const { createSchemaValidators } = require('./src/utils/schema-validator');
23
-
24
- /**
25
- * Creates a transform stream that converts JSON objects to CSV rows
26
- *
27
- * @param {Object} options - Configuration options
28
- * @param {string} [options.delimiter=';'] - CSV delimiter character
29
- * @param {boolean} [options.includeHeaders=true] - Whether to include headers row
30
- * @param {Object} [options.renameMap={}] - Map for renaming column headers (oldKey: newKey)
31
- * @param {Object} [options.template={}] - Template object to ensure consistent column order
32
- * @param {number} [options.maxRecords=Infinity] - Maximum number of records to process
33
- * @param {Function} [options.transform] - Custom transform function for each row
34
- * @param {Object} [options.schema] - JSON schema for validation and formatting
35
- * @returns {Transform} Transform stream
36
- *
37
- * @example
38
- * const { createJsonToCsvStream } = require('./stream-json-to-csv');
39
- *
40
- * const transformStream = createJsonToCsvStream({
41
- * delimiter: ',',
42
- * renameMap: { id: 'ID', name: 'Full Name' }
43
- * });
44
- *
45
- * // Pipe JSON objects to CSV
46
- * jsonReadableStream.pipe(transformStream).pipe(csvWritableStream);
47
- */
48
- /* istanbul ignore next */
49
- function createJsonToCsvStream(options = {}) {
50
- return safeExecute(() => {
51
- /* istanbul ignore next */
52
- const opts = options && typeof options === 'object' ? options : {};
53
-
54
- const {
55
- delimiter = ';',
56
- includeHeaders = true,
57
- renameMap = {},
58
- template = {},
59
- maxRecords = Infinity,
60
- transform = null,
61
- schema = null
62
- } = opts;
63
-
64
- // Validate options
65
- if (delimiter && typeof delimiter !== 'string') {
66
- throw new ConfigurationError('Delimiter must be a string');
67
- }
68
-
69
- if (delimiter && delimiter.length !== 1) {
70
- throw new ConfigurationError('Delimiter must be a single character');
71
- }
72
-
73
- if (renameMap && typeof renameMap !== 'object') {
74
- throw new ConfigurationError('renameMap must be an object');
75
- }
76
-
77
- if (template && typeof template !== 'object') {
78
- throw new ConfigurationError('template must be an object');
79
- }
80
-
81
- if (maxRecords !== Infinity && (typeof maxRecords !== 'number' || maxRecords <= 0)) {
82
- throw new ConfigurationError('maxRecords must be a positive number or Infinity');
83
- }
84
-
85
- if (transform && typeof transform !== 'function') {
86
- throw new ConfigurationError('transform must be a function');
87
- }
88
-
89
- if (schema && typeof schema !== 'object') {
90
- throw new ConfigurationError('schema must be an object');
91
- }
92
-
93
- let headers = null;
94
- let headersWritten = false;
95
- let recordCount = 0;
96
- let reverseRenameMap = {};
97
- let finalHeaders = [];
98
- let schemaValidators = null;
99
-
100
- // Initialize schema validators if schema is provided
101
- if (schema) {
102
- schemaValidators = createSchemaValidators(schema);
103
- }
104
-
105
- /**
106
- * Escapes a value for CSV format with CSV injection protection
107
- *
108
- * @private
109
- * @param {*} value - The value to escape
110
- * @returns {string} Escaped CSV value
111
- */
112
- const escapeValue = (value) => {
113
- if (value === null || value === undefined || value === '') {
114
- return '';
115
- }
116
-
117
- const stringValue = String(value);
118
-
119
- // CSV Injection protection - escape formulas
120
- let escapedValue = stringValue;
121
- if (/^[=+\-@]/.test(stringValue)) {
122
- // Prepend single quote to prevent formula execution in Excel
123
- escapedValue = "'" + stringValue;
124
- }
125
-
126
- // Check if value needs escaping (contains delimiter, quotes, or newlines)
127
- if (
128
- escapedValue.includes(delimiter) ||
129
- escapedValue.includes('"') ||
130
- escapedValue.includes('\n') ||
131
- escapedValue.includes('\r')
132
- ) {
133
- // Escape double quotes by doubling them
134
- return `"${escapedValue.replace(/"/g, '""')}"`;
135
- }
136
-
137
- return escapedValue;
138
- };
139
-
140
- /**
141
- * Formats value based on schema
142
- *
143
- * @private
144
- * @param {*} value - The value to format
145
- * @param {string} key - The key/field name
146
- * @returns {*} Formatted value
147
- */
148
- const formatValue = (value, key) => {
149
- if (!schemaValidators || !schemaValidators[key]) {
150
- return value;
151
- }
152
-
153
- const validator = schemaValidators[key];
154
-
155
- // Apply formatting if available
156
- if (validator.format) {
157
- return validator.format(value);
158
- }
159
-
160
- return value;
161
- };
162
-
163
- /**
164
- * Validates value against schema
165
- *
166
- * @private
167
- * @param {*} value - The value to validate
168
- * @param {string} key - The key/field name
169
- * @returns {boolean} True if valid
170
- */
171
- const validateValue = (value, key) => {
172
- if (!schemaValidators || !schemaValidators[key]) {
173
- return true;
174
- }
175
-
176
- const validator = schemaValidators[key];
177
-
178
- // Apply validation if available
179
- /* istanbul ignore next */
180
- if (validator.validate) {
181
- return validator.validate(value);
182
- }
183
-
184
- /* istanbul ignore next */
185
- return true;
186
- };
187
-
188
- return new Transform({
189
- objectMode: true,
190
-
191
- transform(chunk, encoding, callback) {
192
- try {
193
- // Check record limit
194
- if (recordCount >= maxRecords) {
195
- return callback(new LimitError(
196
- `Data size exceeds maximum limit of ${maxRecords} records`,
197
- maxRecords,
198
- recordCount
199
- ));
200
- }
201
-
202
- // Validate chunk is an object
203
- if (!chunk || typeof chunk !== 'object') {
204
- return callback(new ValidationError('Input data must be objects'));
205
- }
206
-
207
- // Apply custom transform if provided
208
- let item = chunk;
209
- if (transform) {
210
- try {
211
- item = transform(chunk);
212
- /* istanbul ignore next */
213
- if (!item || typeof item !== 'object') {
214
- return callback(new ValidationError('Transform function must return an object'));
215
- }
216
- } catch (error) {
217
- return callback(new ValidationError(`Transform function error: ${error.message}`));
218
- }
219
- }
220
-
221
- // Initialize headers on first record
222
- if (!headers) {
223
- const allKeys = new Set();
224
- Object.keys(item).forEach(key => allKeys.add(key));
225
- const originalKeys = Array.from(allKeys);
226
-
227
- // Apply rename map to create header names
228
- headers = originalKeys.map(key => renameMap[key] || key);
229
-
230
- // Create reverse mapping
231
- reverseRenameMap = {};
232
- originalKeys.forEach((key, index) => {
233
- reverseRenameMap[headers[index]] = key;
234
- });
235
-
236
- // Apply template ordering if provided
237
- finalHeaders = headers;
238
- if (Object.keys(template).length > 0) {
239
- const templateHeaders = Object.keys(template).map(key => renameMap[key] || key);
240
- const extraHeaders = headers.filter(h => !templateHeaders.includes(h));
241
- finalHeaders = [...templateHeaders, ...extraHeaders];
242
- }
243
-
244
- // Write headers if requested
245
- if (includeHeaders && finalHeaders.length > 0 && !headersWritten) {
246
- this.push(finalHeaders.join(delimiter) + '\n');
247
- headersWritten = true;
248
- }
249
- }
250
-
251
- // Build CSV row
252
- const rowValues = finalHeaders.map(header => {
253
- // Get the original key for this header
254
- /* istanbul ignore next */
255
- const originalKey = reverseRenameMap[header] || header;
256
- let value = item[originalKey];
257
-
258
- // Format value based on schema
259
- value = formatValue(value, originalKey);
260
-
261
- // Validate value against schema
262
- if (!validateValue(value, originalKey)) {
263
- throw new ValidationError(`Invalid value for field '${originalKey}': ${value}`);
264
- }
265
-
266
- return escapeValue(value);
267
- });
268
-
269
- const row = rowValues.join(delimiter) + '\n';
270
- this.push(row);
271
- recordCount++;
272
-
273
- callback();
274
- } catch (error) {
275
- callback(error);
276
- }
277
- },
278
-
279
- flush(callback) {
280
- // If no data was processed but headers were requested, write empty headers
281
- if (includeHeaders && !headersWritten) {
282
- /* istanbul ignore next */
283
- if (Object.keys(template).length > 0) {
284
- const templateHeaders = Object.keys(template).map(key => renameMap[key] || key);
285
- /* istanbul ignore next */
286
- if (templateHeaders.length > 0) {
287
- this.push(templateHeaders.join(delimiter) + '\n');
288
- }
289
- }
290
- }
291
- callback();
292
- }
293
- });
294
- }, 'STREAM_CREATION_ERROR', { function: 'createJsonToCsvStream' });
295
- }
296
-
297
- /**
298
- * Converts a readable stream of JSON objects to CSV and writes to a writable stream
299
- *
300
- * @param {Readable} inputStream - Readable stream of JSON objects
301
- * @param {Writable} outputStream - Writable stream for CSV output
302
- * @param {Object} options - Configuration options (same as createJsonToCsvStream)
303
- * @returns {Promise<void>}
304
- *
305
- * @example
306
- * const { streamJsonToCsv } = require('./stream-json-to-csv');
307
- *
308
- * await streamJsonToCsv(jsonStream, csvStream, {
309
- * delimiter: ',',
310
- * schema: {
311
- * properties: {
312
- * id: { type: 'integer' },
313
- * name: { type: 'string', minLength: 1 },
314
- * date: { type: 'string', format: 'date-time' }
315
- * }
316
- * }
317
- * });
318
- */
319
- /* istanbul ignore next */
320
- async function streamJsonToCsv(inputStream, outputStream, options = {}) {
321
- return safeExecute(async () => {
322
- const transformStream = createJsonToCsvStream(options);
323
-
324
- await pipeline(
325
- inputStream,
326
- transformStream,
327
- outputStream
328
- );
329
- }, 'STREAM_PROCESSING_ERROR', { function: 'streamJsonToCsv' });
330
- }
331
-
332
- /**
333
- * Converts JSON to CSV and saves it to a file using streaming
334
- *
335
- * @param {Readable} inputStream - Readable stream of JSON objects
336
- * @param {string} filePath - Path to save the CSV file
337
- * @param {Object} options - Configuration options (same as createJsonToCsvStream)
338
- * @returns {Promise<void>}
339
- *
340
- * @example
341
- * const { saveJsonStreamAsCsv } = require('./stream-json-to-csv');
342
- *
343
- * await saveJsonStreamAsCsv(jsonStream, './output.csv', {
344
- * delimiter: ',',
345
- * includeHeaders: true
346
- * });
347
- */
348
- async function saveJsonStreamAsCsv(inputStream, filePath, options = {}) {
349
- return safeExecute(async () => {
350
- const fs = require('fs');
351
- const path = require('path');
352
-
353
- // Validate file path
354
- if (typeof filePath !== 'string' || filePath.trim() === '') {
355
- throw new ValidationError('File path must be a non-empty string');
356
- }
357
-
358
- if (!filePath.toLowerCase().endsWith('.csv')) {
359
- throw new ValidationError('File must have .csv extension');
360
- }
361
-
362
- // Prevent directory traversal attacks
363
- const normalizedPath = path.normalize(filePath);
364
- if (normalizedPath.includes('..') ||
365
- /\\\.\.\\|\/\.\.\//.test(filePath) ||
366
- filePath.startsWith('..') ||
367
- filePath.includes('/..')) {
368
- throw new SecurityError('Directory traversal detected in file path');
369
- }
370
-
371
- const safePath = path.resolve(filePath);
372
-
373
- // Ensure directory exists
374
- const dir = path.dirname(safePath);
375
- await fs.promises.mkdir(dir, { recursive: true });
376
-
377
- // Create write stream with BOM for Excel UTF-8 support
378
- const writeStream = fs.createWriteStream(safePath, 'utf8');
379
-
380
- // Add UTF-8 BOM for Excel compatibility if requested
381
- if (options.addBOM !== false) {
382
- writeStream.write('\uFEFF');
383
- }
384
-
385
- const transformStream = createJsonToCsvStream(options);
386
-
387
- await pipeline(
388
- inputStream,
389
- transformStream,
390
- writeStream
391
- );
392
-
393
- return safePath;
394
- }, 'FILE_STREAM_ERROR', { function: 'saveJsonStreamAsCsv' });
395
- }
396
-
397
- /**
398
- * Creates a readable stream from an array of JSON objects
399
- *
400
- * @param {Array<Object>} data - Array of JSON objects
401
- * @returns {Readable} Readable stream
402
- */
403
- function createJsonReadableStream(data) {
404
- return new Readable({
405
- objectMode: true,
406
- read() {
407
- /* istanbul ignore next */
408
- if (!this._data) {
409
- this._data = Array.isArray(data) ? [...data] : [];
410
- this._index = 0;
411
- }
412
-
413
- while (this._index < this._data.length) {
414
- const item = this._data[this._index];
415
- this._index++;
416
-
417
- if (!this.push(item)) {
418
- // Stream buffer is full, wait for next read
419
- return;
420
- }
421
- }
422
-
423
- // End of data
424
- this.push(null);
425
- }
426
- });
427
- }
428
-
429
- /**
430
- * Creates a writable stream that collects CSV data
431
- *
432
- * @returns {Writable} Writable stream that collects data
433
- */
434
- function createCsvCollectorStream() {
435
- let collectedData = '';
436
-
437
- return new Writable({
438
- write(chunk, encoding, callback) {
439
- collectedData += chunk.toString();
440
- callback();
441
- },
442
-
443
- final(callback) {
444
- this._collectedData = collectedData;
445
- callback();
446
- }
447
- });
448
- }
449
-
450
- module.exports = {
451
- createJsonToCsvStream,
452
- streamJsonToCsv,
453
- saveJsonStreamAsCsv,
454
- createJsonReadableStream,
455
- createCsvCollectorStream
456
- // Note: createSchemaValidators is no longer exported from here
457
- // It should be imported directly from './src/utils/schema-validator'
458
- };
459
-
460
- // For ES6 module compatibility
461
- /* istanbul ignore next */
462
- if (typeof module !== 'undefined' && module.exports) {
463
- module.exports.default = createJsonToCsvStream;
464
- }