jtcsv 2.2.7 → 3.0.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 (140) hide show
  1. package/README.md +31 -1
  2. package/bin/jtcsv.js +891 -821
  3. package/bin/jtcsv.ts +2534 -0
  4. package/csv-to-json.js +168 -145
  5. package/dist/jtcsv-core.cjs.js +1407 -0
  6. package/dist/jtcsv-core.cjs.js.map +1 -0
  7. package/dist/jtcsv-core.esm.js +1379 -0
  8. package/dist/jtcsv-core.esm.js.map +1 -0
  9. package/dist/jtcsv-core.umd.js +1413 -0
  10. package/dist/jtcsv-core.umd.js.map +1 -0
  11. package/dist/jtcsv-full.cjs.js +1912 -0
  12. package/dist/jtcsv-full.cjs.js.map +1 -0
  13. package/dist/jtcsv-full.esm.js +1880 -0
  14. package/dist/jtcsv-full.esm.js.map +1 -0
  15. package/dist/jtcsv-full.umd.js +1918 -0
  16. package/dist/jtcsv-full.umd.js.map +1 -0
  17. package/dist/jtcsv-workers.esm.js +759 -0
  18. package/dist/jtcsv-workers.esm.js.map +1 -0
  19. package/dist/jtcsv-workers.umd.js +773 -0
  20. package/dist/jtcsv-workers.umd.js.map +1 -0
  21. package/dist/jtcsv.cjs.js +61 -19
  22. package/dist/jtcsv.cjs.js.map +1 -1
  23. package/dist/jtcsv.esm.js +61 -19
  24. package/dist/jtcsv.esm.js.map +1 -1
  25. package/dist/jtcsv.umd.js +61 -19
  26. package/dist/jtcsv.umd.js.map +1 -1
  27. package/errors.js +188 -2
  28. package/examples/advanced/conditional-transformations.js +446 -0
  29. package/examples/advanced/conditional-transformations.ts +446 -0
  30. package/examples/advanced/csv-parser.worker.js +89 -0
  31. package/examples/advanced/csv-parser.worker.ts +89 -0
  32. package/examples/advanced/nested-objects-example.js +306 -0
  33. package/examples/advanced/nested-objects-example.ts +306 -0
  34. package/examples/advanced/performance-optimization.js +504 -0
  35. package/examples/advanced/performance-optimization.ts +504 -0
  36. package/examples/advanced/run-demo-server.js +116 -0
  37. package/examples/advanced/run-demo-server.ts +116 -0
  38. package/examples/advanced/web-worker-usage.html +874 -0
  39. package/examples/async-multithreaded-example.ts +335 -0
  40. package/examples/cli-advanced-usage.md +288 -0
  41. package/examples/cli-batch-processing.ts +38 -0
  42. package/examples/cli-tool.js +0 -3
  43. package/examples/cli-tool.ts +183 -0
  44. package/examples/error-handling.js +21 -7
  45. package/examples/error-handling.ts +356 -0
  46. package/examples/express-api.js +0 -3
  47. package/examples/express-api.ts +164 -0
  48. package/examples/large-dataset-example.js +0 -3
  49. package/examples/large-dataset-example.ts +204 -0
  50. package/examples/ndjson-processing.js +1 -1
  51. package/examples/ndjson-processing.ts +456 -0
  52. package/examples/plugin-excel-exporter.js +3 -4
  53. package/examples/plugin-excel-exporter.ts +406 -0
  54. package/examples/react-integration.tsx +637 -0
  55. package/examples/schema-validation.ts +640 -0
  56. package/examples/simple-usage.js +254 -254
  57. package/examples/simple-usage.ts +194 -0
  58. package/examples/streaming-example.js +4 -5
  59. package/examples/streaming-example.ts +419 -0
  60. package/examples/web-workers-advanced.ts +28 -0
  61. package/index.d.ts +1 -3
  62. package/index.js +15 -1
  63. package/json-save.js +9 -3
  64. package/json-to-csv.js +168 -21
  65. package/package.json +69 -10
  66. package/plugins/express-middleware/README.md +21 -2
  67. package/plugins/express-middleware/example.js +3 -4
  68. package/plugins/express-middleware/example.ts +135 -0
  69. package/plugins/express-middleware/index.d.ts +1 -1
  70. package/plugins/express-middleware/index.js +270 -118
  71. package/plugins/express-middleware/index.ts +557 -0
  72. package/plugins/fastify-plugin/index.js +2 -4
  73. package/plugins/fastify-plugin/index.ts +443 -0
  74. package/plugins/hono/index.ts +226 -0
  75. package/plugins/nestjs/index.ts +201 -0
  76. package/plugins/nextjs-api/examples/ConverterComponent.tsx +386 -0
  77. package/plugins/nextjs-api/examples/api-convert.js +0 -2
  78. package/plugins/nextjs-api/examples/api-convert.ts +67 -0
  79. package/plugins/nextjs-api/index.tsx +339 -0
  80. package/plugins/nextjs-api/route.js +2 -3
  81. package/plugins/nextjs-api/route.ts +370 -0
  82. package/plugins/nuxt/index.ts +94 -0
  83. package/plugins/nuxt/runtime/composables/useJtcsv.ts +100 -0
  84. package/plugins/nuxt/runtime/plugin.ts +71 -0
  85. package/plugins/remix/index.js +1 -1
  86. package/plugins/remix/index.ts +260 -0
  87. package/plugins/sveltekit/index.js +1 -1
  88. package/plugins/sveltekit/index.ts +301 -0
  89. package/plugins/trpc/index.ts +267 -0
  90. package/src/browser/browser-functions.ts +402 -0
  91. package/src/browser/core.js +92 -0
  92. package/src/browser/core.ts +152 -0
  93. package/src/browser/csv-to-json-browser.d.ts +3 -0
  94. package/src/browser/csv-to-json-browser.js +36 -14
  95. package/src/browser/csv-to-json-browser.ts +264 -0
  96. package/src/browser/errors-browser.ts +303 -0
  97. package/src/browser/extensions/plugins.js +92 -0
  98. package/src/browser/extensions/plugins.ts +93 -0
  99. package/src/browser/extensions/workers.js +39 -0
  100. package/src/browser/extensions/workers.ts +39 -0
  101. package/src/browser/globals.d.ts +5 -0
  102. package/src/browser/index.ts +192 -0
  103. package/src/browser/json-to-csv-browser.d.ts +3 -0
  104. package/src/browser/json-to-csv-browser.js +13 -3
  105. package/src/browser/json-to-csv-browser.ts +262 -0
  106. package/src/browser/streams.js +12 -2
  107. package/src/browser/streams.ts +336 -0
  108. package/src/browser/workers/csv-parser.worker.ts +377 -0
  109. package/src/browser/workers/worker-pool.ts +548 -0
  110. package/src/core/delimiter-cache.js +22 -8
  111. package/src/core/delimiter-cache.ts +310 -0
  112. package/src/core/node-optimizations.ts +449 -0
  113. package/src/core/plugin-system.js +29 -11
  114. package/src/core/plugin-system.ts +400 -0
  115. package/src/core/transform-hooks.ts +558 -0
  116. package/src/engines/fast-path-engine-new.ts +347 -0
  117. package/src/engines/fast-path-engine.ts +854 -0
  118. package/src/errors.ts +72 -0
  119. package/src/formats/ndjson-parser.ts +469 -0
  120. package/src/formats/tsv-parser.ts +334 -0
  121. package/src/index-with-plugins.js +16 -9
  122. package/src/index-with-plugins.ts +395 -0
  123. package/src/types/index.ts +255 -0
  124. package/src/utils/bom-utils.js +259 -0
  125. package/src/utils/bom-utils.ts +373 -0
  126. package/src/utils/encoding-support.js +124 -0
  127. package/src/utils/encoding-support.ts +155 -0
  128. package/src/utils/schema-validator.js +19 -19
  129. package/src/utils/schema-validator.ts +819 -0
  130. package/src/utils/transform-loader.js +1 -1
  131. package/src/utils/transform-loader.ts +389 -0
  132. package/src/utils/zod-adapter.js +170 -0
  133. package/src/utils/zod-adapter.ts +280 -0
  134. package/src/web-server/index.js +10 -10
  135. package/src/web-server/index.ts +683 -0
  136. package/src/workers/csv-multithreaded.ts +310 -0
  137. package/src/workers/csv-parser.worker.ts +227 -0
  138. package/src/workers/worker-pool.ts +409 -0
  139. package/stream-csv-to-json.js +26 -8
  140. package/stream-json-to-csv.js +1 -0
@@ -0,0 +1,164 @@
1
+ // Express API example for jtcsv
2
+ // Run: node examples/express-api.ts
3
+ // Then visit: http://localhost:3000/export/users
4
+
5
+ import express from 'express';
6
+ import { jsonToCsv } from '../index';
7
+ import fs from 'fs/promises';
8
+ import path from 'path';
9
+
10
+ const app = express();
11
+ const PORT = 3000;
12
+
13
+ // Mock database
14
+ const mockUsers = Array.from({ length: 100 }, (_, i) => ({
15
+ id: i + 1,
16
+ name: `User ${i + 1}`,
17
+ email: `user${i + 1}@example.com`,
18
+ role: i % 3 === 0 ? 'admin' : i % 3 === 1 ? 'moderator' : 'user',
19
+ status: i % 5 === 0 ? 'inactive' : 'active',
20
+ createdAt: new Date(Date.now() - Math.random() * 31536000000).toISOString(),
21
+ profile: {
22
+ age: Math.floor(Math.random() * 50) + 18,
23
+ country: ['USA', 'UK', 'Germany', 'France', 'Japan'][Math.floor(Math.random() * 5)],
24
+ bio: `Bio for user ${i + 1}`
25
+ },
26
+ tags: ['customer', `tier${Math.floor(Math.random() * 3) + 1}`]
27
+ }));
28
+
29
+ // Middleware
30
+ app.use(express.json());
31
+
32
+ // Health check
33
+ app.get('/', (req, res) => {
34
+ res.json({
35
+ message: 'jtcsv Express API Example',
36
+ endpoints: [
37
+ 'GET /export/users - Export users as CSV',
38
+ 'GET /export/users/download - Download users CSV file',
39
+ 'POST /export/custom - Convert custom JSON to CSV'
40
+ ]
41
+ });
42
+ });
43
+
44
+ // Export users as CSV (inline)
45
+ app.get('/export/users', (req, res) => {
46
+ try {
47
+ const csv = jsonToCsv(mockUsers, {
48
+ delimiter: ',',
49
+ renameMap: {
50
+ id: 'ID',
51
+ name: 'Full Name',
52
+ email: 'Email Address',
53
+ role: 'Role',
54
+ status: 'Status',
55
+ 'profile.age': 'Age',
56
+ 'profile.country': 'Country',
57
+ 'profile.bio': 'Bio',
58
+ 'createdAt': 'Created At'
59
+ }
60
+ });
61
+
62
+ res.set('Content-Type', 'text/plain');
63
+ res.send(csv);
64
+ } catch (error: any) {
65
+ res.status(500).json({ error: error.message });
66
+ }
67
+ });
68
+
69
+ // Download users CSV file
70
+ app.get('/export/users/download', async (req, res) => {
71
+ try {
72
+ const csv = jsonToCsv(mockUsers, {
73
+ delimiter: ',',
74
+ renameMap: {
75
+ id: 'ID',
76
+ name: 'Full Name',
77
+ email: 'Email Address'
78
+ }
79
+ });
80
+
81
+ const filename = `users-export-${Date.now()}.csv`;
82
+ const filePath = path.join(__dirname, 'temp', filename);
83
+
84
+ // Ensure temp directory exists
85
+ await fs.mkdir(path.join(__dirname, 'temp'), { recursive: true });
86
+
87
+ // Write file
88
+ await fs.writeFile(filePath, csv, 'utf8');
89
+
90
+ // Send file for download
91
+ res.download(filePath, filename, (err) => {
92
+ // Clean up temp file
93
+ fs.unlink(filePath).catch(() => {});
94
+ });
95
+ } catch (error: any) {
96
+ res.status(500).json({ error: error.message });
97
+ }
98
+ });
99
+
100
+ // Convert custom JSON to CSV
101
+ app.post('/export/custom', (req, res) => {
102
+ try {
103
+ const { data, options = {} } = req.body;
104
+
105
+ if (!Array.isArray(data)) {
106
+ return res.status(400).json({ error: 'Data must be an array' });
107
+ }
108
+
109
+ // Limit to 10,000 records for safety
110
+ const safeData = data.slice(0, 10000);
111
+
112
+ const csv = jsonToCsv(safeData, {
113
+ delimiter: options.delimiter || ',',
114
+ renameMap: options.renameMap || {}
115
+ });
116
+
117
+ res.json({
118
+ success: true,
119
+ records: safeData.length,
120
+ csvSize: csv.length,
121
+ csv: options.returnCsv !== false ? csv : undefined
122
+ });
123
+ } catch (error: any) {
124
+ res.status(500).json({
125
+ success: false,
126
+ error: error.message,
127
+ type: error.constructor.name
128
+ });
129
+ }
130
+ });
131
+
132
+ // Example with CSV injection protection
133
+ app.get('/export/safe', (req, res) => {
134
+ const dangerousData = [
135
+ { id: 1, input: '=cmd|"/c calc.exe"!A0', note: 'Malicious formula' },
136
+ { id: 2, input: '@SUM(A1:A10)', note: 'Excel function' },
137
+ { id: 3, input: '+1-1', note: 'Another formula' },
138
+ { id: 4, input: '-2+2', note: 'Formula with minus' },
139
+ { id: 5, input: 'Normal text', note: 'Safe content' },
140
+ { id: 6, input: 'Text with, commas', note: 'With comma' },
141
+ { id: 7, input: 'Text with\nnewlines', note: 'With newline' },
142
+ { id: 8, input: 'Text with "quotes"', note: 'With quotes' }
143
+ ];
144
+
145
+ const csv = jsonToCsv(dangerousData, { delimiter: ',' });
146
+
147
+ res.set('Content-Type', 'text/plain');
148
+ res.send(`# CSV Injection Protection Demo\n\n${csv}`);
149
+ });
150
+
151
+ // Start server
152
+ app.listen(PORT, () => {
153
+ console.log(`šŸš€ Express API running at http://localhost:${PORT}`);
154
+ console.log('šŸ“Š Try these endpoints:');
155
+ console.log(` http://localhost:${PORT}/export/users`);
156
+ console.log(` http://localhost:${PORT}/export/users/download`);
157
+ console.log(` http://localhost:${PORT}/export/safe`);
158
+ console.log('\nExample POST to /export/custom:');
159
+ console.log(`curl -X POST http://localhost:${PORT}/export/custom \\
160
+ -H "Content-Type: application/json" \\
161
+ -d '{"data":[{"name":"John","age":30}],"options":{"delimiter":";"}}'`);
162
+ });
163
+
164
+ export default app;
@@ -180,6 +180,3 @@ module.exports = {
180
180
  benchmarkConversion,
181
181
  saveCsvInChunks
182
182
  };
183
-
184
-
185
-
@@ -0,0 +1,204 @@
1
+ // Large dataset example for jtcsv
2
+ // Demonstrates handling of large datasets and memory efficiency
3
+
4
+ import { jsonToCsv } from '../index-core.js';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+
8
+ interface DatasetRecord {
9
+ id: number;
10
+ timestamp: string;
11
+ userId: string;
12
+ action: string;
13
+ amount: number;
14
+ success: boolean;
15
+ metadata: {
16
+ ip: string;
17
+ userAgent: string;
18
+ sessionId: string;
19
+ };
20
+ tags: string[];
21
+ }
22
+
23
+ interface MemoryUsage {
24
+ rss: number;
25
+ heapTotal: number;
26
+ heapUsed: number;
27
+ external: number;
28
+ }
29
+
30
+ // Generate large dataset
31
+ export function generateLargeDataset(rows: number): DatasetRecord[] {
32
+ console.log(`Generating ${rows.toLocaleString()} records...`);
33
+
34
+ const dataset: DatasetRecord[] = [];
35
+ const batchSize = 10000;
36
+ const batches = Math.ceil(rows / batchSize);
37
+
38
+ for (let batch = 0; batch < batches; batch++) {
39
+ const start = batch * batchSize;
40
+ const end = Math.min(start + batchSize, rows);
41
+
42
+ for (let i = start; i < end; i++) {
43
+ dataset.push({
44
+ id: i + 1,
45
+ timestamp: new Date(Date.now() - Math.random() * 31536000000).toISOString(),
46
+ userId: `user_${Math.floor(Math.random() * 10000)}`,
47
+ action: ['login', 'purchase', 'view', 'click', 'logout'][Math.floor(Math.random() * 5)],
48
+ amount: Math.random() * 1000,
49
+ success: Math.random() > 0.1,
50
+ metadata: {
51
+ ip: `${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}`,
52
+ userAgent: `Browser/${Math.floor(Math.random() * 100)}`,
53
+ sessionId: `session_${Math.random().toString(36).substr(2, 9)}`
54
+ },
55
+ tags: Array.from({ length: Math.floor(Math.random() * 5) + 1 },
56
+ () => ['tag' + Math.floor(Math.random() * 20), 'cat' + Math.floor(Math.random() * 10)]
57
+ ).flat()
58
+ });
59
+ }
60
+
61
+ if ((batch + 1) % 10 === 0 || batch === batches - 1) {
62
+ console.log(` Generated ${Math.min((batch + 1) * batchSize, rows).toLocaleString()} records`);
63
+ }
64
+ }
65
+
66
+ return dataset;
67
+ }
68
+
69
+ // Memory usage helper
70
+ export function getMemoryUsage(): MemoryUsage {
71
+ const used = process.memoryUsage();
72
+ return {
73
+ rss: Math.round(used.rss / 1024 / 1024),
74
+ heapTotal: Math.round(used.heapTotal / 1024 / 1024),
75
+ heapUsed: Math.round(used.heapUsed / 1024 / 1024),
76
+ external: Math.round(used.external / 1024 / 1024)
77
+ };
78
+ }
79
+
80
+ // Benchmark conversion
81
+ export async function benchmarkConversion(dataset: DatasetRecord[], name: string): Promise<string | null> {
82
+ console.log(`\n${name}:`);
83
+ console.log(` Records: ${dataset.length.toLocaleString()}`);
84
+
85
+ const memBefore = getMemoryUsage();
86
+ console.log(` Memory before: ${memBefore.heapUsed}MB heap`);
87
+
88
+ const startTime = performance.now();
89
+
90
+ try {
91
+ const csv = jsonToCsv(dataset, {
92
+ delimiter: ','
93
+ });
94
+
95
+ const endTime = performance.now();
96
+ const memAfter = getMemoryUsage();
97
+
98
+ console.log(` Conversion time: ${(endTime - startTime).toFixed(2)}ms`);
99
+ console.log(` Memory after: ${memAfter.heapUsed}MB heap`);
100
+ console.log(` Memory delta: ${(memAfter.heapUsed - memBefore.heapUsed)}MB`);
101
+ console.log(` CSV size: ${(csv.length / 1024 / 1024).toFixed(2)}MB`);
102
+ console.log(` Rows/second: ${Math.round((dataset.length / (endTime - startTime)) * 1000).toLocaleString()}`);
103
+
104
+ return csv;
105
+ } catch (error: unknown) {
106
+ console.error(` Error: ${(error as Error).message}`);
107
+ return null;
108
+ }
109
+ }
110
+
111
+ // Save CSV in chunks (for very large files)
112
+ export function saveCsvInChunks(csv: string, filename: string, chunkSize = 10 * 1024 * 1024): Promise<void> { // 10MB chunks
113
+ console.log(`\nSaving CSV in chunks to ${filename}...`);
114
+
115
+ const totalChunks = Math.ceil(csv.length / chunkSize);
116
+ const writeStream = fs.createWriteStream(filename);
117
+
118
+ return new Promise((resolve, reject) => {
119
+ writeStream.on('error', reject);
120
+ writeStream.on('finish', resolve);
121
+
122
+ for (let i = 0; i < totalChunks; i++) {
123
+ const start = i * chunkSize;
124
+ const end = Math.min(start + chunkSize, csv.length);
125
+ const chunk = csv.substring(start, end);
126
+
127
+ if (!writeStream.write(chunk)) {
128
+ // Wait for drain if buffer is full
129
+ writeStream.once('drain', () => {
130
+ if (i % 10 === 0 || i === totalChunks - 1) {
131
+ console.log(` Written chunk ${i + 1}/${totalChunks} (${Math.round((i + 1) / totalChunks * 100)}%)`);
132
+ }
133
+ });
134
+ } else {
135
+ if (i % 10 === 0 || i === totalChunks - 1) {
136
+ console.log(` Written chunk ${i + 1}/${totalChunks} (${Math.round((i + 1) / totalChunks * 100)}%)`);
137
+ }
138
+ }
139
+ }
140
+
141
+ writeStream.end();
142
+ });
143
+ }
144
+
145
+ // Main function
146
+ export async function main(): Promise<void> {
147
+ console.log('šŸ“Š jtcsv Large Dataset Demo');
148
+ console.log('=' .repeat(50));
149
+
150
+ // Test with different dataset sizes
151
+ const testSizes = [1000, 10000, 50000];
152
+
153
+ for (const size of testSizes) {
154
+ const dataset = generateLargeDataset(size);
155
+ const csv = await benchmarkConversion(dataset, `Dataset: ${size.toLocaleString()} records`);
156
+
157
+ if (csv && size === 50000) {
158
+ // Save the largest dataset as example
159
+ const filename = `large-dataset-${size}-records.csv`;
160
+ await saveCsvInChunks(csv, filename);
161
+
162
+ const stats = fs.statSync(filename);
163
+ console.log(`\nāœ… Saved to ${filename}`);
164
+ console.log(` File size: ${(stats.size / 1024 / 1024).toFixed(2)}MB`);
165
+
166
+ // Show first few lines
167
+ const sample = csv.split('\n').slice(0, 3).join('\n');
168
+ console.log('\nSample output (first 3 lines):');
169
+ console.log(sample);
170
+ console.log('...');
171
+ }
172
+
173
+ // Force garbage collection if available
174
+ if (global.gc) {
175
+ (global.gc as () => void)();
176
+ }
177
+ }
178
+
179
+ console.log('\nšŸŽÆ Performance Summary:');
180
+ console.log('| Records | Approx. Time | Memory Usage | CSV Size |');
181
+ console.log('|---------|--------------|--------------|----------|');
182
+ console.log('| 1,000 | ~5ms | ~2MB | ~0.5MB |');
183
+ console.log('| 10,000 | ~50ms | ~10MB | ~5MB |');
184
+ console.log('| 50,000 | ~250ms | ~40MB | ~25MB |');
185
+ console.log('| 100,000 | ~500ms* | ~80MB* | ~50MB* |');
186
+ console.log('\n* Estimated values');
187
+
188
+ console.log('\nšŸ’” Tips for very large datasets:');
189
+ console.log('1. Use the maxRecords option optionally to limit processing');
190
+ console.log('2. Process data in batches if memory is limited');
191
+ console.log('3. Use saveAsCsv() for secure file writing');
192
+ console.log('4. Monitor memory usage with process.memoryUsage()');
193
+ }
194
+
195
+ // Run if called directly
196
+ if (require.main === module) {
197
+ main().catch(console.error);
198
+ }
199
+
200
+ export default {
201
+ generateLargeDataset,
202
+ benchmarkConversion,
203
+ saveCsvInChunks
204
+ };
@@ -168,7 +168,7 @@ async function ndjsonToCsvConversion() {
168
168
  });
169
169
 
170
170
  // Collect CSV output
171
- let csvOutput = '';
171
+ const csvOutput = '';
172
172
  transformStream.writable.getWriter();
173
173
 
174
174
  // Manual stream processing for demo