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.esm.js CHANGED
@@ -384,8 +384,18 @@ function jsonToCsv(data, options = {}) {
384
384
 
385
385
  // Защита от CSV инъекций
386
386
  let escapedValue = stringValue;
387
- if (preventCsvInjection && /^[=+\-@]/.test(stringValue)) {
388
- escapedValue = "'" + stringValue;
387
+ if (preventCsvInjection) {
388
+ // Dangerous prefixes: =, +, -, @, tab (\t), carriage return (\r)
389
+ if (/^[=+\-@\t\r]/.test(stringValue)) {
390
+ escapedValue = "'" + stringValue;
391
+ }
392
+ // Unicode Bidi override characters
393
+ const bidiChars = ['\u202A', '\u202B', '\u202C', '\u202D', '\u202E'];
394
+ for (const bidi of bidiChars) {
395
+ if (stringValue.includes(bidi)) {
396
+ escapedValue = escapedValue.replace(new RegExp(bidi, 'g'), '');
397
+ }
398
+ }
389
399
  }
390
400
 
391
401
  // Соответствие RFC 4180
@@ -698,10 +708,18 @@ function parseCsvValue(value, options) {
698
708
  }
699
709
 
700
710
  // Парсинг чисел
701
- if (parseNumbers && /^-?\d+(\.\d+)?$/.test(result)) {
702
- const num = parseFloat(result);
703
- if (!isNaN(num)) {
704
- return num;
711
+ if (parseNumbers) {
712
+ // Быстрая проверка числа: первый символ цифра, минус или точка
713
+ const trimmed = result.trim();
714
+ const firstChar = trimmed.charAt(0);
715
+ if (firstChar >= '0' && firstChar <= '9' || firstChar === '-' || firstChar === '.') {
716
+ const num = parseFloat(trimmed);
717
+ if (!isNaN(num) && isFinite(num)) {
718
+ // Убедимся, что строка полностью соответствует числу (без лишних символов)
719
+ if (String(num) === trimmed || trimmed.includes('.') && !isNaN(Number(trimmed))) {
720
+ return num;
721
+ }
722
+ }
705
723
  }
706
724
  }
707
725
 
@@ -813,28 +831,42 @@ function autoDetectDelimiter(csv, candidates = [';', ',', '\t', '|']) {
813
831
 
814
832
  // Использование первой непустой строки для определения
815
833
  const firstLine = lines[0];
834
+
835
+ // Быстрый подсчёт вхождений кандидатов за один проход
816
836
  const counts = {};
817
- candidates.forEach(delim => {
818
- const escapedDelim = delim.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
819
- const regex = new RegExp(escapedDelim, 'g');
820
- const matches = firstLine.match(regex);
821
- counts[delim] = matches ? matches.length : 0;
822
- });
837
+ const candidateSet = new Set(candidates);
838
+ for (let i = 0; i < firstLine.length; i++) {
839
+ const char = firstLine[i];
840
+ if (candidateSet.has(char)) {
841
+ counts[char] = (counts[char] || 0) + 1;
842
+ }
843
+ }
844
+ // Убедимся, что все кандидаты присутствуют в counts (даже с нулём)
845
+ for (const delim of candidates) {
846
+ if (!(delim in counts)) {
847
+ counts[delim] = 0;
848
+ }
849
+ }
823
850
 
824
851
  // Поиск разделителя с максимальным количеством
825
852
  let maxCount = -1;
826
853
  let detectedDelimiter = ';'; // значение по умолчанию
827
-
854
+ const maxDelimiters = [];
828
855
  for (const [delim, count] of Object.entries(counts)) {
829
856
  if (count > maxCount) {
830
857
  maxCount = count;
831
- detectedDelimiter = delim;
858
+ maxDelimiters.length = 0;
859
+ maxDelimiters.push(delim);
860
+ } else if (count === maxCount) {
861
+ maxDelimiters.push(delim);
832
862
  }
833
863
  }
834
864
 
835
- // Если разделитель не найден или ничья
836
- if (maxCount === 0) {
837
- return ';'; // значение по умолчанию
865
+ // Если разделитель не найден или есть ничья, возвращаем стандартный
866
+ if (maxCount === 0 || maxDelimiters.length > 1) {
867
+ detectedDelimiter = ';';
868
+ } else {
869
+ detectedDelimiter = maxDelimiters[0];
838
870
  }
839
871
  return detectedDelimiter;
840
872
  }
@@ -1333,8 +1365,18 @@ function escapeCsvValue(value, options) {
1333
1365
  }
1334
1366
  const stringValue = String(value);
1335
1367
  let escapedValue = stringValue;
1336
- if (preventCsvInjection && /^[=+\-@]/.test(stringValue)) {
1337
- escapedValue = "'" + stringValue;
1368
+ if (preventCsvInjection) {
1369
+ // Dangerous prefixes: =, +, -, @, tab (\t), carriage return (\r)
1370
+ if (/^[=+\-@\t\r]/.test(stringValue)) {
1371
+ escapedValue = "'" + stringValue;
1372
+ }
1373
+ // Unicode Bidi override characters
1374
+ const bidiChars = ['\u202A', '\u202B', '\u202C', '\u202D', '\u202E'];
1375
+ for (const bidi of bidiChars) {
1376
+ if (stringValue.includes(bidi)) {
1377
+ escapedValue = escapedValue.replace(new RegExp(bidi, 'g'), '');
1378
+ }
1379
+ }
1338
1380
  }
1339
1381
  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');
1340
1382
  if (needsQuoting) {