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
package/dist/jtcsv.umd.js CHANGED
@@ -391,8 +391,18 @@
391
391
 
392
392
  // Защита от CSV инъекций
393
393
  let escapedValue = stringValue;
394
- if (preventCsvInjection && /^[=+\-@]/.test(stringValue)) {
395
- escapedValue = "'" + stringValue;
394
+ if (preventCsvInjection) {
395
+ // Dangerous prefixes: =, +, -, @, tab (\t), carriage return (\r)
396
+ if (/^[=+\-@\t\r]/.test(stringValue)) {
397
+ escapedValue = "'" + stringValue;
398
+ }
399
+ // Unicode Bidi override characters
400
+ const bidiChars = ['\u202A', '\u202B', '\u202C', '\u202D', '\u202E'];
401
+ for (const bidi of bidiChars) {
402
+ if (stringValue.includes(bidi)) {
403
+ escapedValue = escapedValue.replace(new RegExp(bidi, 'g'), '');
404
+ }
405
+ }
396
406
  }
397
407
 
398
408
  // Соответствие RFC 4180
@@ -705,10 +715,18 @@
705
715
  }
706
716
 
707
717
  // Парсинг чисел
708
- if (parseNumbers && /^-?\d+(\.\d+)?$/.test(result)) {
709
- const num = parseFloat(result);
710
- if (!isNaN(num)) {
711
- return num;
718
+ if (parseNumbers) {
719
+ // Быстрая проверка числа: первый символ цифра, минус или точка
720
+ const trimmed = result.trim();
721
+ const firstChar = trimmed.charAt(0);
722
+ if (firstChar >= '0' && firstChar <= '9' || firstChar === '-' || firstChar === '.') {
723
+ const num = parseFloat(trimmed);
724
+ if (!isNaN(num) && isFinite(num)) {
725
+ // Убедимся, что строка полностью соответствует числу (без лишних символов)
726
+ if (String(num) === trimmed || trimmed.includes('.') && !isNaN(Number(trimmed))) {
727
+ return num;
728
+ }
729
+ }
712
730
  }
713
731
  }
714
732
 
@@ -820,28 +838,42 @@
820
838
 
821
839
  // Использование первой непустой строки для определения
822
840
  const firstLine = lines[0];
841
+
842
+ // Быстрый подсчёт вхождений кандидатов за один проход
823
843
  const counts = {};
824
- candidates.forEach(delim => {
825
- const escapedDelim = delim.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
826
- const regex = new RegExp(escapedDelim, 'g');
827
- const matches = firstLine.match(regex);
828
- counts[delim] = matches ? matches.length : 0;
829
- });
844
+ const candidateSet = new Set(candidates);
845
+ for (let i = 0; i < firstLine.length; i++) {
846
+ const char = firstLine[i];
847
+ if (candidateSet.has(char)) {
848
+ counts[char] = (counts[char] || 0) + 1;
849
+ }
850
+ }
851
+ // Убедимся, что все кандидаты присутствуют в counts (даже с нулём)
852
+ for (const delim of candidates) {
853
+ if (!(delim in counts)) {
854
+ counts[delim] = 0;
855
+ }
856
+ }
830
857
 
831
858
  // Поиск разделителя с максимальным количеством
832
859
  let maxCount = -1;
833
860
  let detectedDelimiter = ';'; // значение по умолчанию
834
-
861
+ const maxDelimiters = [];
835
862
  for (const [delim, count] of Object.entries(counts)) {
836
863
  if (count > maxCount) {
837
864
  maxCount = count;
838
- detectedDelimiter = delim;
865
+ maxDelimiters.length = 0;
866
+ maxDelimiters.push(delim);
867
+ } else if (count === maxCount) {
868
+ maxDelimiters.push(delim);
839
869
  }
840
870
  }
841
871
 
842
- // Если разделитель не найден или ничья
843
- if (maxCount === 0) {
844
- return ';'; // значение по умолчанию
872
+ // Если разделитель не найден или есть ничья, возвращаем стандартный
873
+ if (maxCount === 0 || maxDelimiters.length > 1) {
874
+ detectedDelimiter = ';';
875
+ } else {
876
+ detectedDelimiter = maxDelimiters[0];
845
877
  }
846
878
  return detectedDelimiter;
847
879
  }
@@ -1340,8 +1372,18 @@
1340
1372
  }
1341
1373
  const stringValue = String(value);
1342
1374
  let escapedValue = stringValue;
1343
- if (preventCsvInjection && /^[=+\-@]/.test(stringValue)) {
1344
- escapedValue = "'" + stringValue;
1375
+ if (preventCsvInjection) {
1376
+ // Dangerous prefixes: =, +, -, @, tab (\t), carriage return (\r)
1377
+ if (/^[=+\-@\t\r]/.test(stringValue)) {
1378
+ escapedValue = "'" + stringValue;
1379
+ }
1380
+ // Unicode Bidi override characters
1381
+ const bidiChars = ['\u202A', '\u202B', '\u202C', '\u202D', '\u202E'];
1382
+ for (const bidi of bidiChars) {
1383
+ if (stringValue.includes(bidi)) {
1384
+ escapedValue = escapedValue.replace(new RegExp(bidi, 'g'), '');
1385
+ }
1386
+ }
1345
1387
  }
1346
1388
  const needsQuoting = rfc4180Compliant ? escapedValue.includes(delimiter) || escapedValue.includes('"') || escapedValue.includes('\n') || escapedValue.includes('\r') : escapedValue.includes(delimiter) || escapedValue.includes('"') || escapedValue.includes('\n') || escapedValue.includes('\r');
1347
1389
  if (needsQuoting) {