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,167 +1,164 @@
1
- // Express API example for jtcsv
2
- // Run: node examples/express-api.js
3
- // Then visit: http://localhost:3000/export/users
4
-
5
- const express = require('express');
6
- const { jsonToCsv } = require('../index.js');
7
- const fs = require('fs').promises;
8
- const path = require('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) {
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) {
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) {
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
- module.exports = app;
1
+ // Express API example for jtcsv
2
+ // Run: node examples/express-api.ts
3
+ // Then visit: http://localhost:3000/export/users
165
4
 
5
+ import express from 'express';
6
+ import { jsonToCsv } from '../index';
7
+ import fs from 'fs/promises';
8
+ import path from 'path';
166
9
 
10
+ const app = express();
11
+ const PORT = 3000;
167
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;