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,339 @@
1
+ /**
2
+ * Next.js API Route plugin for jtcsv
3
+ * Provides API routes for CSV/JSON conversion in Next.js applications
4
+ * @module plugins/nextjs-api
5
+ */
6
+
7
+ // Note: Next.js types are optional - users need to install @types/next
8
+ // We use conditional imports to avoid breaking the build
9
+ type NextApiRequest = any;
10
+ type NextApiResponse = any;
11
+
12
+ import type { CsvToJsonOptions, JsonToCsvOptions } from '../../src/types';
13
+ import { csvToJson, jsonToCsv } from '../../index-core';
14
+ import { JtcsvError } from '../../errors';
15
+
16
+ /**
17
+ * Configuration options for the Next.js API plugin
18
+ */
19
+ export interface NextJsApiOptions {
20
+ /** Maximum request body size in bytes (default: 10MB) */
21
+ maxBodySize?: number;
22
+ /** Allowed HTTP methods (default: ['POST']) */
23
+ allowedMethods?: string[];
24
+ /** Enable CORS headers (default: true) */
25
+ enableCors?: boolean;
26
+ /** Custom error handler */
27
+ onError?: (error: Error, req: NextApiRequest, res: NextApiResponse) => void;
28
+ }
29
+
30
+ /**
31
+ * Default configuration for the Next.js API plugin
32
+ */
33
+ const DEFAULT_OPTIONS: NextJsApiOptions = {
34
+ maxBodySize: 10 * 1024 * 1024, // 10MB
35
+ allowedMethods: ['POST'],
36
+ enableCors: true,
37
+ };
38
+
39
+ /**
40
+ * Creates a Next.js API route handler for CSV to JSON conversion
41
+ * @param options - Plugin configuration options
42
+ * @returns Next.js API route handler
43
+ */
44
+ export function createCsvToJsonApiHandler(options?: NextJsApiOptions) {
45
+ const config = { ...DEFAULT_OPTIONS, ...options };
46
+
47
+ return async function handler(req: NextApiRequest, res: NextApiResponse) {
48
+ try {
49
+ // Set CORS headers if enabled
50
+ if (config.enableCors) {
51
+ res.setHeader('Access-Control-Allow-Origin', '*');
52
+ res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
53
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
54
+ }
55
+
56
+ // Handle OPTIONS request for CORS preflight
57
+ if (req.method === 'OPTIONS') {
58
+ return res.status(200).end();
59
+ }
60
+
61
+ // Validate HTTP method
62
+ if (!config.allowedMethods?.includes(req.method || '')) {
63
+ return res.status(405).json({
64
+ error: 'Method Not Allowed',
65
+ message: `Only ${config.allowedMethods?.join(', ')} methods are allowed`,
66
+ });
67
+ }
68
+
69
+ // Validate content type
70
+ const contentType = req.headers['content-type'] || '';
71
+ if (!contentType.includes('text/csv') && !contentType.includes('application/csv')) {
72
+ return res.status(400).json({
73
+ error: 'Invalid Content-Type',
74
+ message: 'Content-Type must be text/csv or application/csv',
75
+ });
76
+ }
77
+
78
+ // Get request body
79
+ let csvData: string;
80
+ if (typeof req.body === 'string') {
81
+ csvData = req.body;
82
+ } else if (Buffer.isBuffer(req.body)) {
83
+ csvData = req.body.toString('utf-8');
84
+ } else {
85
+ return res.status(400).json({
86
+ error: 'Invalid Request Body',
87
+ message: 'Request body must be a CSV string or buffer',
88
+ });
89
+ }
90
+
91
+ // Validate body size
92
+ if (csvData.length > (config.maxBodySize || DEFAULT_OPTIONS.maxBodySize!)) {
93
+ return res.status(413).json({
94
+ error: 'Payload Too Large',
95
+ message: `Request body exceeds maximum size of ${config.maxBodySize} bytes`,
96
+ });
97
+ }
98
+
99
+ // Parse query parameters for CSV options
100
+ const csvOptions: CsvToJsonOptions = {
101
+ delimiter: req.query.delimiter as string || ',',
102
+ hasHeaders: req.query.hasHeaders !== 'false',
103
+ trim: req.query.trim === 'true',
104
+ maxRows: req.query.maxRows ? parseInt(req.query.maxRows as string, 10) : undefined,
105
+ parseNumbers: req.query.parseNumbers === 'true',
106
+ parseBooleans: req.query.parseBooleans === 'true',
107
+ useFastPath: req.query.useFastPath !== 'false',
108
+ preventCsvInjection: req.query.preventCsvInjection !== 'false',
109
+ rfc4180Compliant: req.query.rfc4180Compliant !== 'false',
110
+ };
111
+
112
+ // Convert CSV to JSON
113
+ const result = await csvToJson(csvData, csvOptions);
114
+
115
+ // Return successful response
116
+ return res.status(200).json({
117
+ success: true,
118
+ data: result,
119
+ metadata: {
120
+ rowCount: Array.isArray(result) ? result.length : 0,
121
+ convertedAt: new Date().toISOString(),
122
+ },
123
+ });
124
+
125
+ } catch (error) {
126
+ // Handle errors
127
+ if (config.onError) {
128
+ config.onError(error as Error, req, res);
129
+ }
130
+
131
+ if (error instanceof JtcsvError) {
132
+ return res.status(400).json({
133
+ error: error.name,
134
+ message: error.message,
135
+ code: error.code,
136
+ });
137
+ }
138
+
139
+ const err = error as Error;
140
+ return res.status(500).json({
141
+ error: 'Internal Server Error',
142
+ message: err.message || 'Unknown error occurred',
143
+ });
144
+ }
145
+ };
146
+ }
147
+
148
+ /**
149
+ * Creates a Next.js API route handler for JSON to CSV conversion
150
+ * @param options - Plugin configuration options
151
+ * @returns Next.js API route handler
152
+ */
153
+ export function createJsonToCsvApiHandler(options?: NextJsApiOptions) {
154
+ const config = { ...DEFAULT_OPTIONS, ...options };
155
+
156
+ return async function handler(req: NextApiRequest, res: NextApiResponse) {
157
+ try {
158
+ // Set CORS headers if enabled
159
+ if (config.enableCors) {
160
+ res.setHeader('Access-Control-Allow-Origin', '*');
161
+ res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
162
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
163
+ }
164
+
165
+ // Handle OPTIONS request for CORS preflight
166
+ if (req.method === 'OPTIONS') {
167
+ return res.status(200).end();
168
+ }
169
+
170
+ // Validate HTTP method
171
+ if (!config.allowedMethods?.includes(req.method || '')) {
172
+ return res.status(405).json({
173
+ error: 'Method Not Allowed',
174
+ message: `Only ${config.allowedMethods?.join(', ')} methods are allowed`,
175
+ });
176
+ }
177
+
178
+ // Validate content type
179
+ const contentType = req.headers['content-type'] || '';
180
+ if (!contentType.includes('application/json')) {
181
+ return res.status(400).json({
182
+ error: 'Invalid Content-Type',
183
+ message: 'Content-Type must be application/json',
184
+ });
185
+ }
186
+
187
+ // Get request body
188
+ let jsonData: any;
189
+ if (typeof req.body === 'string') {
190
+ try {
191
+ jsonData = JSON.parse(req.body);
192
+ } catch {
193
+ return res.status(400).json({
194
+ error: 'Invalid JSON',
195
+ message: 'Request body must be valid JSON',
196
+ });
197
+ }
198
+ } else if (typeof req.body === 'object' && req.body !== null) {
199
+ jsonData = req.body;
200
+ } else {
201
+ return res.status(400).json({
202
+ error: 'Invalid Request Body',
203
+ message: 'Request body must be JSON',
204
+ });
205
+ }
206
+
207
+ // Validate body size
208
+ const bodySize = JSON.stringify(jsonData).length;
209
+ if (bodySize > (config.maxBodySize || DEFAULT_OPTIONS.maxBodySize!)) {
210
+ return res.status(413).json({
211
+ error: 'Payload Too Large',
212
+ message: `Request body exceeds maximum size of ${config.maxBodySize} bytes`,
213
+ });
214
+ }
215
+
216
+ // Parse query parameters for CSV options
217
+ const csvOptions: JsonToCsvOptions = {
218
+ delimiter: req.query.delimiter as string || ',',
219
+ includeHeaders: req.query.includeHeaders !== 'false',
220
+ preventCsvInjection: req.query.preventCsvInjection !== 'false',
221
+ rfc4180Compliant: req.query.rfc4180Compliant !== 'false',
222
+ flatten: req.query.flatten === 'true',
223
+ flattenSeparator: req.query.flattenSeparator as string || '.',
224
+ flattenMaxDepth: req.query.flattenMaxDepth ? parseInt(req.query.flattenMaxDepth as string, 10) : undefined,
225
+ arrayHandling: req.query.arrayHandling as 'stringify' | 'join' | 'expand' || 'stringify',
226
+ };
227
+
228
+ // Convert JSON to CSV
229
+ const result = await jsonToCsv(jsonData, csvOptions);
230
+
231
+ // Return successful response
232
+ res.setHeader('Content-Type', 'text/csv');
233
+ res.setHeader('Content-Disposition', 'attachment; filename="converted.csv"');
234
+
235
+ return res.status(200).send(result);
236
+
237
+ } catch (error) {
238
+ // Handle errors
239
+ if (config.onError) {
240
+ config.onError(error as Error, req, res);
241
+ }
242
+
243
+ if (error instanceof JtcsvError) {
244
+ return res.status(400).json({
245
+ error: error.name,
246
+ message: error.message,
247
+ code: error.code,
248
+ });
249
+ }
250
+
251
+ const err = error as Error;
252
+ return res.status(500).json({
253
+ error: 'Internal Server Error',
254
+ message: err.message || 'Unknown error occurred',
255
+ });
256
+ }
257
+ };
258
+ }
259
+
260
+ /**
261
+ * Creates a Next.js API route handler for bidirectional conversion
262
+ * @param options - Plugin configuration options
263
+ * @returns Next.js API route handler that can handle both CSV→JSON and JSON→CSV
264
+ */
265
+ export function createBidirectionalApiHandler(options?: NextJsApiOptions) {
266
+ const config = { ...DEFAULT_OPTIONS, ...options };
267
+
268
+ return async function handler(req: NextApiRequest, res: NextApiResponse) {
269
+ try {
270
+ // Set CORS headers if enabled
271
+ if (config.enableCors) {
272
+ res.setHeader('Access-Control-Allow-Origin', '*');
273
+ res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
274
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
275
+ }
276
+
277
+ // Handle OPTIONS request for CORS preflight
278
+ if (req.method === 'OPTIONS') {
279
+ return res.status(200).end();
280
+ }
281
+
282
+ // Validate HTTP method
283
+ if (!config.allowedMethods?.includes(req.method || '')) {
284
+ return res.status(405).json({
285
+ error: 'Method Not Allowed',
286
+ message: `Only ${config.allowedMethods?.join(', ')} methods are allowed`,
287
+ });
288
+ }
289
+
290
+ // Determine conversion direction from query parameter or content type
291
+ const direction = req.query.direction as string ||
292
+ (req.headers['content-type']?.includes('text/csv') ? 'csvToJson' : 'jsonToCsv');
293
+
294
+ if (direction === 'csvToJson') {
295
+ // Use CSV to JSON handler
296
+ const csvHandler = createCsvToJsonApiHandler(config);
297
+ return csvHandler(req, res);
298
+ } else if (direction === 'jsonToCsv') {
299
+ // Use JSON to CSV handler
300
+ const jsonHandler = createJsonToCsvApiHandler(config);
301
+ return jsonHandler(req, res);
302
+ } else {
303
+ return res.status(400).json({
304
+ error: 'Invalid Direction',
305
+ message: 'Direction must be either "csvToJson" or "jsonToCsv"',
306
+ });
307
+ }
308
+
309
+ } catch (error) {
310
+ // Handle errors
311
+ if (config.onError) {
312
+ config.onError(error as Error, req, res);
313
+ }
314
+
315
+ return res.status(500).json({
316
+ error: 'Internal Server Error',
317
+ message: error instanceof Error ? error.message : 'Unknown error occurred',
318
+ });
319
+ }
320
+ };
321
+ }
322
+
323
+ /**
324
+ * Example Next.js API route implementation
325
+ * Usage in pages/api/convert.ts:
326
+ *
327
+ * import { createBidirectionalApiHandler } from '@jtcsv/nextjs-api';
328
+ *
329
+ * export default createBidirectionalApiHandler({
330
+ * maxBodySize: 5 * 1024 * 1024, // 5MB
331
+ * allowedMethods: ['POST', 'GET'],
332
+ * });
333
+ */
334
+
335
+ export default {
336
+ createCsvToJsonApiHandler,
337
+ createJsonToCsvApiHandler,
338
+ createBidirectionalApiHandler,
339
+ };
@@ -85,7 +85,7 @@ export default async function handler(req, res) {
85
85
  } = req.query;
86
86
 
87
87
  // Определяем желаемый формат вывода
88
- let outputFormat = format || (acceptHeader.includes('text/csv') ? 'csv' : 'json');
88
+ const outputFormat = format || (acceptHeader.includes('text/csv') ? 'csv' : 'json');
89
89
 
90
90
  // Определяем формат входных данных
91
91
  let inputFormat = 'unknown';
@@ -122,7 +122,7 @@ export default async function handler(req, res) {
122
122
  };
123
123
 
124
124
  let result;
125
- let stats = {
125
+ const stats = {
126
126
  inputSize: 0,
127
127
  outputSize: 0,
128
128
  processingTime: 0,
@@ -368,4 +368,3 @@ export function createJtcsvApiEndpoint(options = {}) {
368
368
  return handler(req, res);
369
369
  };
370
370
  }
371
-