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
@@ -6,7 +6,7 @@
6
6
  * @date 2026-01-22
7
7
  */
8
8
 
9
- const ExcelJS = require('exceljs');
9
+ import ExcelJS from "exceljs";
10
10
 
11
11
  /**
12
12
  * Плагин для экспорта в Excel формат
@@ -249,7 +249,9 @@ async function importFromExcel(filePath, options = {}) {
249
249
  let headers = [];
250
250
 
251
251
  worksheet.eachRow((row, rowNumber) => {
252
- if (rowNumber <= skipRows) return;
252
+ if (rowNumber <= skipRows) {
253
+ return;
254
+ }
253
255
 
254
256
  if (hasHeaders && rowNumber === skipRows + 1) {
255
257
  // Первая строка - заголовки
@@ -289,7 +291,7 @@ async function exampleUsage() {
289
291
  console.log('🚀 Пример использования плагинов JTCSV\n');
290
292
 
291
293
  // Создаем экземпляр JTCSV с плагинами
292
- const JtcsvWithPlugins = require('../src/index-with-plugins');
294
+ import JtcsvWithPlugins from "../src/index-with-plugins";
293
295
  const jtcsv = JtcsvWithPlugins.create({
294
296
  enablePlugins: true,
295
297
  enableFastPath: true
@@ -390,7 +392,7 @@ async function exampleUsage() {
390
392
  }
391
393
 
392
394
  // Экспортируем плагины и функции
393
- module.exports = {
395
+ export default {
394
396
  excelExporterPlugin,
395
397
  excelImporterPlugin,
396
398
  exportToExcel,
@@ -402,6 +404,3 @@ module.exports = {
402
404
  if (require.main === module) {
403
405
  exampleUsage().catch(console.error);
404
406
  }
405
-
406
-
407
-
@@ -0,0 +1,637 @@
1
+ /**
2
+ * React Integration Example for jtcsv
3
+ *
4
+ * This file demonstrates how to use jtcsv in React applications
5
+ * for CSV import/export functionality.
6
+ *
7
+ * To use: npm install jtcsv react react-dom
8
+ */
9
+
10
+ import React, { useState, useCallback, useMemo } from 'react';
11
+ import { csvToJson, jsonToCsv, ValidationError, ParsingError } from 'jtcsv';
12
+
13
+ // =============================================================================
14
+ // Example 1: CSV Import Component
15
+ // =============================================================================
16
+
17
+ export function CsvImporter({ onImport, columns, parseOptions = {} }) {
18
+ const [error, setError] = useState(null);
19
+ const [preview, setPreview] = useState(null);
20
+ const [isProcessing, setIsProcessing] = useState(false);
21
+
22
+ const handleFileSelect = useCallback(async (event) => {
23
+ const file = event.target.files?.[0];
24
+ if (!file) return;
25
+
26
+ setError(null);
27
+ setIsProcessing(true);
28
+
29
+ try {
30
+ const text = await file.text();
31
+ const data = csvToJson(text, {
32
+ hasHeaders: true,
33
+ trim: true,
34
+ parseNumbers: true,
35
+ parseBooleans: true,
36
+ ...parseOptions
37
+ });
38
+
39
+ // Show preview of first 5 rows
40
+ setPreview({
41
+ total: data.length,
42
+ sample: data.slice(0, 5),
43
+ columns: data.length > 0 ? Object.keys(data[0]) : []
44
+ });
45
+
46
+ // Call onImport with full data
47
+ if (onImport) {
48
+ onImport(data);
49
+ }
50
+ } catch (err) {
51
+ if (err instanceof ParsingError) {
52
+ setError(`Parsing error at line ${err.lineNumber}: ${err.message}`);
53
+ } else if (err instanceof ValidationError) {
54
+ setError(`Validation error: ${err.message}`);
55
+ } else {
56
+ setError(`Error: ${err.message}`);
57
+ }
58
+ setPreview(null);
59
+ } finally {
60
+ setIsProcessing(false);
61
+ }
62
+ }, [onImport, parseOptions]);
63
+
64
+ const clearPreview = useCallback(() => {
65
+ setPreview(null);
66
+ setError(null);
67
+ }, []);
68
+
69
+ return (
70
+ <div className="csv-importer">
71
+ <div className="upload-area">
72
+ <input
73
+ type="file"
74
+ accept=".csv,.tsv,.txt"
75
+ onChange={handleFileSelect}
76
+ disabled={isProcessing}
77
+ />
78
+ {isProcessing && <span>Processing...</span>}
79
+ </div>
80
+
81
+ {error && (
82
+ <div className="error-message" style={{ color: 'red' }}>
83
+ {error}
84
+ </div>
85
+ )}
86
+
87
+ {preview && (
88
+ <div className="preview">
89
+ <h4>Preview ({preview.total} rows total)</h4>
90
+ <p>Columns: {preview.columns.join(', ')}</p>
91
+ <table>
92
+ <thead>
93
+ <tr>
94
+ {preview.columns.map(col => (
95
+ <th key={col}>{col}</th>
96
+ ))}
97
+ </tr>
98
+ </thead>
99
+ <tbody>
100
+ {preview.sample.map((row, i) => (
101
+ <tr key={i}>
102
+ {preview.columns.map(col => (
103
+ <td key={col}>{String(row[col])}</td>
104
+ ))}
105
+ </tr>
106
+ ))}
107
+ </tbody>
108
+ </table>
109
+ <button onClick={clearPreview}>Clear</button>
110
+ </div>
111
+ )}
112
+ </div>
113
+ );
114
+ }
115
+
116
+ // =============================================================================
117
+ // Example 2: CSV Export Component
118
+ // =============================================================================
119
+
120
+ export function CsvExporter({ data, filename = 'export.csv', options = {} }) {
121
+ const [isExporting, setIsExporting] = useState(false);
122
+
123
+ const handleExport = useCallback(() => {
124
+ setIsExporting(true);
125
+
126
+ try {
127
+ const csv = jsonToCsv(data, {
128
+ delimiter: ',',
129
+ includeHeaders: true,
130
+ preventCsvInjection: true,
131
+ ...options
132
+ });
133
+
134
+ // Create blob and download
135
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
136
+ const url = URL.createObjectURL(blob);
137
+ const link = document.createElement('a');
138
+ link.href = url;
139
+ link.download = filename;
140
+ document.body.appendChild(link);
141
+ link.click();
142
+ document.body.removeChild(link);
143
+ URL.revokeObjectURL(url);
144
+ } catch (err) {
145
+ console.error('Export failed:', err);
146
+ alert(`Export failed: ${err.message}`);
147
+ } finally {
148
+ setIsExporting(false);
149
+ }
150
+ }, [data, filename, options]);
151
+
152
+ return (
153
+ <button
154
+ onClick={handleExport}
155
+ disabled={isExporting || !data || data.length === 0}
156
+ >
157
+ {isExporting ? 'Exporting...' : `Export CSV (${data?.length || 0} rows)`}
158
+ </button>
159
+ );
160
+ }
161
+
162
+ // =============================================================================
163
+ // Example 3: Custom Hook for CSV Operations
164
+ // =============================================================================
165
+
166
+ export function useCsv(initialData = []) {
167
+ const [data, setData] = useState(initialData);
168
+ const [error, setError] = useState(null);
169
+ const [isLoading, setIsLoading] = useState(false);
170
+
171
+ const importCsv = useCallback(async (csvString, options = {}) => {
172
+ setIsLoading(true);
173
+ setError(null);
174
+
175
+ try {
176
+ const parsed = csvToJson(csvString, {
177
+ hasHeaders: true,
178
+ trim: true,
179
+ ...options
180
+ });
181
+ setData(parsed);
182
+ return parsed;
183
+ } catch (err) {
184
+ setError(err);
185
+ throw err;
186
+ } finally {
187
+ setIsLoading(false);
188
+ }
189
+ }, []);
190
+
191
+ const exportCsv = useCallback((options = {}) => {
192
+ try {
193
+ return jsonToCsv(data, {
194
+ delimiter: ',',
195
+ includeHeaders: true,
196
+ ...options
197
+ });
198
+ } catch (err) {
199
+ setError(err);
200
+ throw err;
201
+ }
202
+ }, [data]);
203
+
204
+ const importFromFile = useCallback(async (file, options = {}) => {
205
+ const text = await file.text();
206
+ return importCsv(text, options);
207
+ }, [importCsv]);
208
+
209
+ const downloadCsv = useCallback((filename = 'data.csv', options = {}) => {
210
+ const csv = exportCsv(options);
211
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
212
+ const url = URL.createObjectURL(blob);
213
+ const link = document.createElement('a');
214
+ link.href = url;
215
+ link.download = filename;
216
+ link.click();
217
+ URL.revokeObjectURL(url);
218
+ }, [exportCsv]);
219
+
220
+ const clearData = useCallback(() => {
221
+ setData([]);
222
+ setError(null);
223
+ }, []);
224
+
225
+ return {
226
+ data,
227
+ setData,
228
+ error,
229
+ isLoading,
230
+ importCsv,
231
+ exportCsv,
232
+ importFromFile,
233
+ downloadCsv,
234
+ clearData
235
+ };
236
+ }
237
+
238
+ // =============================================================================
239
+ // Example 4: Data Table with CSV Support
240
+ // =============================================================================
241
+
242
+ export function DataTableWithCsv({
243
+ initialData = [],
244
+ columns,
245
+ editable = false,
246
+ onDataChange
247
+ }) {
248
+ const {
249
+ data,
250
+ setData,
251
+ importFromFile,
252
+ downloadCsv,
253
+ isLoading,
254
+ error
255
+ } = useCsv(initialData);
256
+
257
+ const handleFileChange = useCallback(async (event) => {
258
+ const file = event.target.files?.[0];
259
+ if (file) {
260
+ try {
261
+ const imported = await importFromFile(file, {
262
+ parseNumbers: true,
263
+ parseBooleans: true
264
+ });
265
+ if (onDataChange) onDataChange(imported);
266
+ } catch (err) {
267
+ // Error is handled by useCsv hook
268
+ }
269
+ }
270
+ }, [importFromFile, onDataChange]);
271
+
272
+ const handleCellEdit = useCallback((rowIndex, column, value) => {
273
+ const newData = [...data];
274
+ newData[rowIndex] = { ...newData[rowIndex], [column]: value };
275
+ setData(newData);
276
+ if (onDataChange) onDataChange(newData);
277
+ }, [data, setData, onDataChange]);
278
+
279
+ const handleAddRow = useCallback(() => {
280
+ const emptyRow = columns.reduce((acc, col) => ({ ...acc, [col]: '' }), {});
281
+ const newData = [...data, emptyRow];
282
+ setData(newData);
283
+ if (onDataChange) onDataChange(newData);
284
+ }, [data, columns, setData, onDataChange]);
285
+
286
+ const handleDeleteRow = useCallback((rowIndex) => {
287
+ const newData = data.filter((_, i) => i !== rowIndex);
288
+ setData(newData);
289
+ if (onDataChange) onDataChange(newData);
290
+ }, [data, setData, onDataChange]);
291
+
292
+ const displayColumns = columns || (data.length > 0 ? Object.keys(data[0]) : []);
293
+
294
+ return (
295
+ <div className="data-table">
296
+ <div className="toolbar">
297
+ <input
298
+ type="file"
299
+ accept=".csv"
300
+ onChange={handleFileChange}
301
+ disabled={isLoading}
302
+ />
303
+ <button onClick={() => downloadCsv('export.csv')}>
304
+ Export CSV
305
+ </button>
306
+ {editable && (
307
+ <button onClick={handleAddRow}>Add Row</button>
308
+ )}
309
+ </div>
310
+
311
+ {error && (
312
+ <div className="error" style={{ color: 'red' }}>
313
+ {error.message}
314
+ </div>
315
+ )}
316
+
317
+ {isLoading && <div>Loading...</div>}
318
+
319
+ <table>
320
+ <thead>
321
+ <tr>
322
+ {displayColumns.map(col => (
323
+ <th key={col}>{col}</th>
324
+ ))}
325
+ {editable && <th>Actions</th>}
326
+ </tr>
327
+ </thead>
328
+ <tbody>
329
+ {data.map((row, rowIndex) => (
330
+ <tr key={rowIndex}>
331
+ {displayColumns.map(col => (
332
+ <td key={col}>
333
+ {editable ? (
334
+ <input
335
+ value={row[col] ?? ''}
336
+ onChange={(e) => handleCellEdit(rowIndex, col, e.target.value)}
337
+ />
338
+ ) : (
339
+ String(row[col] ?? '')
340
+ )}
341
+ </td>
342
+ ))}
343
+ {editable && (
344
+ <td>
345
+ <button onClick={() => handleDeleteRow(rowIndex)}>Delete</button>
346
+ </td>
347
+ )}
348
+ </tr>
349
+ ))}
350
+ </tbody>
351
+ </table>
352
+ </div>
353
+ );
354
+ }
355
+
356
+ // =============================================================================
357
+ // Example 5: CSV Converter Tool Component
358
+ // =============================================================================
359
+
360
+ export function CsvConverterTool() {
361
+ const [inputText, setInputText] = useState('');
362
+ const [outputText, setOutputText] = useState('');
363
+ const [mode, setMode] = useState('csv-to-json'); // or 'json-to-csv'
364
+ const [error, setError] = useState(null);
365
+ const [options, setOptions] = useState({
366
+ delimiter: ',',
367
+ parseNumbers: true,
368
+ parseBooleans: true,
369
+ prettyPrint: true
370
+ });
371
+
372
+ const convert = useCallback(() => {
373
+ setError(null);
374
+
375
+ try {
376
+ if (mode === 'csv-to-json') {
377
+ const data = csvToJson(inputText, {
378
+ delimiter: options.delimiter,
379
+ parseNumbers: options.parseNumbers,
380
+ parseBooleans: options.parseBooleans
381
+ });
382
+ setOutputText(
383
+ options.prettyPrint
384
+ ? JSON.stringify(data, null, 2)
385
+ : JSON.stringify(data)
386
+ );
387
+ } else {
388
+ const data = JSON.parse(inputText);
389
+ const csv = jsonToCsv(data, {
390
+ delimiter: options.delimiter
391
+ });
392
+ setOutputText(csv);
393
+ }
394
+ } catch (err) {
395
+ setError(err.message);
396
+ }
397
+ }, [inputText, mode, options]);
398
+
399
+ return (
400
+ <div className="csv-converter">
401
+ <div className="mode-selector">
402
+ <label>
403
+ <input
404
+ type="radio"
405
+ value="csv-to-json"
406
+ checked={mode === 'csv-to-json'}
407
+ onChange={() => setMode('csv-to-json')}
408
+ />
409
+ CSV to JSON
410
+ </label>
411
+ <label>
412
+ <input
413
+ type="radio"
414
+ value="json-to-csv"
415
+ checked={mode === 'json-to-csv'}
416
+ onChange={() => setMode('json-to-csv')}
417
+ />
418
+ JSON to CSV
419
+ </label>
420
+ </div>
421
+
422
+ <div className="options">
423
+ <label>
424
+ Delimiter:
425
+ <select
426
+ value={options.delimiter}
427
+ onChange={(e) => setOptions({ ...options, delimiter: e.target.value })}
428
+ >
429
+ <option value=",">Comma (,)</option>
430
+ <option value=";">Semicolon (;)</option>
431
+ <option value="\t">Tab</option>
432
+ <option value="|">Pipe (|)</option>
433
+ </select>
434
+ </label>
435
+
436
+ {mode === 'csv-to-json' && (
437
+ <>
438
+ <label>
439
+ <input
440
+ type="checkbox"
441
+ checked={options.parseNumbers}
442
+ onChange={(e) => setOptions({ ...options, parseNumbers: e.target.checked })}
443
+ />
444
+ Parse Numbers
445
+ </label>
446
+ <label>
447
+ <input
448
+ type="checkbox"
449
+ checked={options.parseBooleans}
450
+ onChange={(e) => setOptions({ ...options, parseBooleans: e.target.checked })}
451
+ />
452
+ Parse Booleans
453
+ </label>
454
+ <label>
455
+ <input
456
+ type="checkbox"
457
+ checked={options.prettyPrint}
458
+ onChange={(e) => setOptions({ ...options, prettyPrint: e.target.checked })}
459
+ />
460
+ Pretty Print
461
+ </label>
462
+ </>
463
+ )}
464
+ </div>
465
+
466
+ <div className="converter-panels">
467
+ <div className="input-panel">
468
+ <h4>Input ({mode === 'csv-to-json' ? 'CSV' : 'JSON'})</h4>
469
+ <textarea
470
+ value={inputText}
471
+ onChange={(e) => setInputText(e.target.value)}
472
+ placeholder={mode === 'csv-to-json'
473
+ ? 'Paste CSV here...\nname,age\nJohn,30'
474
+ : 'Paste JSON array here...\n[{"name":"John","age":30}]'
475
+ }
476
+ rows={10}
477
+ />
478
+ </div>
479
+
480
+ <div className="convert-button">
481
+ <button onClick={convert}>Convert →</button>
482
+ </div>
483
+
484
+ <div className="output-panel">
485
+ <h4>Output ({mode === 'csv-to-json' ? 'JSON' : 'CSV'})</h4>
486
+ <textarea
487
+ value={outputText}
488
+ readOnly
489
+ rows={10}
490
+ />
491
+ </div>
492
+ </div>
493
+
494
+ {error && (
495
+ <div className="error" style={{ color: 'red' }}>
496
+ Error: {error}
497
+ </div>
498
+ )}
499
+ </div>
500
+ );
501
+ }
502
+
503
+ // =============================================================================
504
+ // Example 6: Async Data Loader with CSV Support
505
+ // =============================================================================
506
+
507
+ export function useAsyncCsvLoader(url) {
508
+ const [data, setData] = useState([]);
509
+ const [isLoading, setIsLoading] = useState(false);
510
+ const [error, setError] = useState(null);
511
+
512
+ const load = useCallback(async (parseOptions = {}) => {
513
+ setIsLoading(true);
514
+ setError(null);
515
+
516
+ try {
517
+ const response = await fetch(url);
518
+ if (!response.ok) {
519
+ throw new Error(`HTTP error: ${response.status}`);
520
+ }
521
+ const csvText = await response.text();
522
+ const parsed = csvToJson(csvText, {
523
+ hasHeaders: true,
524
+ trim: true,
525
+ ...parseOptions
526
+ });
527
+ setData(parsed);
528
+ return parsed;
529
+ } catch (err) {
530
+ setError(err);
531
+ throw err;
532
+ } finally {
533
+ setIsLoading(false);
534
+ }
535
+ }, [url]);
536
+
537
+ return { data, isLoading, error, load };
538
+ }
539
+
540
+ // Example usage component
541
+ export function RemoteCsvLoader({ url }) {
542
+ const { data, isLoading, error, load } = useAsyncCsvLoader(url);
543
+
544
+ React.useEffect(() => {
545
+ load({ parseNumbers: true });
546
+ }, [load]);
547
+
548
+ if (isLoading) return <div>Loading CSV from {url}...</div>;
549
+ if (error) return <div>Error: {error.message}</div>;
550
+
551
+ return (
552
+ <div>
553
+ <h4>Loaded {data.length} rows</h4>
554
+ <pre>{JSON.stringify(data.slice(0, 5), null, 2)}</pre>
555
+ </div>
556
+ );
557
+ }
558
+
559
+ // =============================================================================
560
+ // Example 7: Complete App Example
561
+ // =============================================================================
562
+
563
+ export function CsvManagerApp() {
564
+ const [activeTab, setActiveTab] = useState('import');
565
+ const [data, setData] = useState([]);
566
+
567
+ return (
568
+ <div className="csv-manager-app">
569
+ <h1>CSV Manager</h1>
570
+
571
+ <div className="tabs">
572
+ <button
573
+ className={activeTab === 'import' ? 'active' : ''}
574
+ onClick={() => setActiveTab('import')}
575
+ >
576
+ Import
577
+ </button>
578
+ <button
579
+ className={activeTab === 'edit' ? 'active' : ''}
580
+ onClick={() => setActiveTab('edit')}
581
+ >
582
+ Edit
583
+ </button>
584
+ <button
585
+ className={activeTab === 'export' ? 'active' : ''}
586
+ onClick={() => setActiveTab('export')}
587
+ >
588
+ Export
589
+ </button>
590
+ <button
591
+ className={activeTab === 'convert' ? 'active' : ''}
592
+ onClick={() => setActiveTab('convert')}
593
+ >
594
+ Convert
595
+ </button>
596
+ </div>
597
+
598
+ <div className="tab-content">
599
+ {activeTab === 'import' && (
600
+ <CsvImporter
601
+ onImport={(imported) => {
602
+ setData(imported);
603
+ setActiveTab('edit');
604
+ }}
605
+ parseOptions={{ parseNumbers: true, parseBooleans: true }}
606
+ />
607
+ )}
608
+
609
+ {activeTab === 'edit' && (
610
+ <DataTableWithCsv
611
+ initialData={data}
612
+ editable={true}
613
+ onDataChange={setData}
614
+ />
615
+ )}
616
+
617
+ {activeTab === 'export' && (
618
+ <div>
619
+ <h3>Export Options</h3>
620
+ <CsvExporter
621
+ data={data}
622
+ filename="export.csv"
623
+ options={{ delimiter: ',' }}
624
+ />
625
+ </div>
626
+ )}
627
+
628
+ {activeTab === 'convert' && (
629
+ <CsvConverterTool />
630
+ )}
631
+ </div>
632
+ </div>
633
+ );
634
+ }
635
+
636
+ // Default export
637
+ export default CsvManagerApp;
@@ -5,11 +5,11 @@
5
5
  * data integrity during CSV/JSON conversions.
6
6
  */
7
7
 
8
- const {
8
+ const {
9
9
  jsonToCsv,
10
10
  csvToJson,
11
11
  ValidationError
12
- } = require('jtcsv');
12
+ } = await import("jtcsv");
13
13
 
14
14
  // =============================================================================
15
15
  // Example 1: Basic Schema Validation