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,92 @@
1
+ // Ядро jtcsv - только базовые функции JSON<->CSV
2
+ // Минимальный размер, максимальная производительность
3
+
4
+ import { jsonToCsv, preprocessData, deepUnwrap } from './json-to-csv-browser.js';
5
+ import { csvToJson, csvToJsonIterator, autoDetectDelimiter } from './csv-to-json-browser.js';
6
+ import {
7
+ downloadAsCsv,
8
+ parseCsvFile,
9
+ parseCsvFileStream,
10
+ jsonToCsvStream,
11
+ jsonToNdjsonStream,
12
+ csvToJsonStream
13
+ } from './browser-functions.js';
14
+ import {
15
+ ValidationError,
16
+ SecurityError,
17
+ FileSystemError,
18
+ ParsingError,
19
+ LimitError,
20
+ ConfigurationError,
21
+ ERROR_CODES
22
+ } from './errors-browser.js';
23
+
24
+ // Основной экспорт ядра
25
+ const jtcsvCore = {
26
+ // JSON to CSV функции
27
+ jsonToCsv,
28
+ preprocessData,
29
+ downloadAsCsv,
30
+ deepUnwrap,
31
+
32
+ // CSV to JSON функции
33
+ csvToJson,
34
+ csvToJsonIterator,
35
+ parseCsvFile,
36
+ parseCsvFileStream,
37
+ jsonToCsvStream,
38
+ jsonToNdjsonStream,
39
+ csvToJsonStream,
40
+ autoDetectDelimiter,
41
+
42
+ // Error classes
43
+ ValidationError,
44
+ SecurityError,
45
+ FileSystemError,
46
+ ParsingError,
47
+ LimitError,
48
+ ConfigurationError,
49
+ ERROR_CODES,
50
+
51
+ // Удобные алиасы
52
+ parse: csvToJson,
53
+ unparse: jsonToCsv,
54
+
55
+ // Версия
56
+ version: '3.0.0-core'
57
+ };
58
+
59
+ // Экспорт для разных сред
60
+ if (typeof module !== 'undefined' && module.exports) {
61
+ // Node.js CommonJS
62
+ module.exports = jtcsvCore;
63
+ } else if (typeof define === 'function' && define.amd) {
64
+ // AMD
65
+ define([], () => jtcsvCore);
66
+ } else if (typeof window !== 'undefined') {
67
+ // Браузер (глобальная переменная)
68
+ window.jtcsv = jtcsvCore;
69
+ }
70
+
71
+ export default jtcsvCore;
72
+ export {
73
+ jsonToCsv,
74
+ preprocessData,
75
+ downloadAsCsv,
76
+ deepUnwrap,
77
+ csvToJson,
78
+ csvToJsonIterator,
79
+ parseCsvFile,
80
+ parseCsvFileStream,
81
+ jsonToCsvStream,
82
+ jsonToNdjsonStream,
83
+ csvToJsonStream,
84
+ autoDetectDelimiter,
85
+ ValidationError,
86
+ SecurityError,
87
+ FileSystemError,
88
+ ParsingError,
89
+ LimitError,
90
+ ConfigurationError,
91
+ ERROR_CODES
92
+ };
@@ -0,0 +1,152 @@
1
+ // Ядро jtcsv - только базовые функции JSON<->CSV
2
+ // Минимальный размер, максимальная производительность
3
+
4
+ import * as jsonToCsvBrowser from './json-to-csv-browser';
5
+ import * as csvToJsonBrowser from './csv-to-json-browser';
6
+ import {
7
+ downloadAsCsv,
8
+ parseCsvFile,
9
+ parseCsvFileStream,
10
+ jsonToCsvStream,
11
+ jsonToNdjsonStream,
12
+ csvToJsonStream
13
+ } from './browser-functions';
14
+ import {
15
+ ValidationError,
16
+ SecurityError,
17
+ FileSystemError,
18
+ ParsingError,
19
+ LimitError,
20
+ ConfigurationError,
21
+ ERROR_CODES
22
+ } from './errors-browser';
23
+
24
+ import type { JsonToCsvOptions, CsvToJsonOptions } from '../types';
25
+
26
+ const { jsonToCsv, preprocessData, deepUnwrap } = jsonToCsvBrowser as any;
27
+ const { csvToJson, csvToJsonIterator, autoDetectDelimiter } = csvToJsonBrowser as any;
28
+
29
+ /**
30
+ * Асинхронная версия jsonToCsv
31
+ */
32
+ async function jsonToCsvAsync(data: any, options: JsonToCsvOptions = {}): Promise<string> {
33
+ return jsonToCsv(data, options);
34
+ }
35
+
36
+ /**
37
+ * Асинхронная версия csvToJson
38
+ */
39
+ async function csvToJsonAsync(csv: string, options: CsvToJsonOptions = {}): Promise<any[]> {
40
+ return csvToJson(csv, options);
41
+ }
42
+
43
+ /**
44
+ * Асинхронная версия parseCsvFile
45
+ */
46
+ async function parseCsvFileAsync(file: File, options: CsvToJsonOptions = {}): Promise<any[]> {
47
+ return parseCsvFile(file, options);
48
+ }
49
+
50
+ /**
51
+ * Асинхронная версия autoDetectDelimiter
52
+ */
53
+ async function autoDetectDelimiterAsync(csv: string): Promise<string> {
54
+ return autoDetectDelimiter(csv);
55
+ }
56
+
57
+ /**
58
+ * Асинхронная версия downloadAsCsv
59
+ */
60
+ async function downloadAsCsvAsync(
61
+ data: any,
62
+ filename: string = 'export.csv',
63
+ options: JsonToCsvOptions = {}
64
+ ): Promise<void> {
65
+ return downloadAsCsv(data, filename, options);
66
+ }
67
+
68
+ // Основной экспорт ядра
69
+ const jtcsvCore = {
70
+ // JSON to CSV функции
71
+ jsonToCsv,
72
+ preprocessData,
73
+ downloadAsCsv,
74
+ deepUnwrap,
75
+
76
+ // CSV to JSON функции
77
+ csvToJson,
78
+ csvToJsonIterator,
79
+ parseCsvFile,
80
+ parseCsvFileStream,
81
+ jsonToCsvStream,
82
+ jsonToNdjsonStream,
83
+ csvToJsonStream,
84
+ autoDetectDelimiter,
85
+
86
+ // Асинхронные функции
87
+ jsonToCsvAsync,
88
+ csvToJsonAsync,
89
+ parseCsvFileAsync,
90
+ autoDetectDelimiterAsync,
91
+ downloadAsCsvAsync,
92
+
93
+ // Error classes
94
+ ValidationError,
95
+ SecurityError,
96
+ FileSystemError,
97
+ ParsingError,
98
+ LimitError,
99
+ ConfigurationError,
100
+ ERROR_CODES,
101
+
102
+ // Удобные алиасы
103
+ parse: csvToJson,
104
+ unparse: jsonToCsv,
105
+ parseAsync: csvToJsonAsync,
106
+ unparseAsync: jsonToCsvAsync,
107
+
108
+ // Версия
109
+ version: '3.0.0-core'
110
+ };
111
+
112
+ // Экспорт для разных сред
113
+ if (typeof module !== 'undefined' && module.exports) {
114
+ // Node.js CommonJS
115
+ module.exports = jtcsvCore;
116
+ } else if (typeof define === 'function' && (define as any).amd) {
117
+ // AMD
118
+ (define as any)([], () => jtcsvCore);
119
+ } else if (typeof window !== 'undefined') {
120
+ // Браузер (глобальная переменная)
121
+ (window as any).jtcsv = jtcsvCore;
122
+ }
123
+
124
+ export default jtcsvCore;
125
+ export {
126
+ jsonToCsv,
127
+ preprocessData,
128
+ downloadAsCsv,
129
+ deepUnwrap,
130
+ csvToJson,
131
+ csvToJsonIterator,
132
+ parseCsvFile,
133
+ parseCsvFileStream,
134
+ jsonToCsvStream,
135
+ jsonToNdjsonStream,
136
+ csvToJsonStream,
137
+ autoDetectDelimiter,
138
+ // Асинхронные функции
139
+ jsonToCsvAsync,
140
+ csvToJsonAsync,
141
+ parseCsvFileAsync,
142
+ autoDetectDelimiterAsync,
143
+ downloadAsCsvAsync,
144
+ // Error classes
145
+ ValidationError,
146
+ SecurityError,
147
+ FileSystemError,
148
+ ParsingError,
149
+ LimitError,
150
+ ConfigurationError,
151
+ ERROR_CODES
152
+ };
@@ -0,0 +1,3 @@
1
+ export function csvToJson(csv: string, options?: any): any[];
2
+ export function csvToJsonIterator(input: any, options?: any): AsyncGenerator<any, void, unknown>;
3
+ export function autoDetectDelimiter(csv: string, candidates?: string[]): string;
@@ -191,10 +191,18 @@ function parseCsvValue(value, options) {
191
191
  }
192
192
 
193
193
  // Парсинг чисел
194
- if (parseNumbers && /^-?\d+(\.\d+)?$/.test(result)) {
195
- const num = parseFloat(result);
196
- if (!isNaN(num)) {
197
- return num;
194
+ if (parseNumbers) {
195
+ // Быстрая проверка числа: первый символ цифра, минус или точка
196
+ const trimmed = result.trim();
197
+ const firstChar = trimmed.charAt(0);
198
+ if ((firstChar >= '0' && firstChar <= '9') || firstChar === '-' || firstChar === '.') {
199
+ const num = parseFloat(trimmed);
200
+ if (!isNaN(num) && isFinite(num)) {
201
+ // Убедимся, что строка полностью соответствует числу (без лишних символов)
202
+ if (String(num) === trimmed || (trimmed.includes('.') && !isNaN(Number(trimmed)))) {
203
+ return num;
204
+ }
205
+ }
198
206
  }
199
207
  }
200
208
 
@@ -327,28 +335,42 @@ export function autoDetectDelimiter(csv, candidates = [';', ',', '\t', '|']) {
327
335
  // Использование первой непустой строки для определения
328
336
  const firstLine = lines[0];
329
337
 
338
+ // Быстрый подсчёт вхождений кандидатов за один проход
330
339
  const counts = {};
331
- candidates.forEach(delim => {
332
- const escapedDelim = delim.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
333
- const regex = new RegExp(escapedDelim, 'g');
334
- const matches = firstLine.match(regex);
335
- counts[delim] = matches ? matches.length : 0;
336
- });
340
+ const candidateSet = new Set(candidates);
341
+ for (let i = 0; i < firstLine.length; i++) {
342
+ const char = firstLine[i];
343
+ if (candidateSet.has(char)) {
344
+ counts[char] = (counts[char] || 0) + 1;
345
+ }
346
+ }
347
+ // Убедимся, что все кандидаты присутствуют в counts (даже с нулём)
348
+ for (const delim of candidates) {
349
+ if (!(delim in counts)) {
350
+ counts[delim] = 0;
351
+ }
352
+ }
337
353
 
338
354
  // Поиск разделителя с максимальным количеством
339
355
  let maxCount = -1;
340
356
  let detectedDelimiter = ';'; // значение по умолчанию
357
+ const maxDelimiters = [];
341
358
 
342
359
  for (const [delim, count] of Object.entries(counts)) {
343
360
  if (count > maxCount) {
344
361
  maxCount = count;
345
- detectedDelimiter = delim;
362
+ maxDelimiters.length = 0;
363
+ maxDelimiters.push(delim);
364
+ } else if (count === maxCount) {
365
+ maxDelimiters.push(delim);
346
366
  }
347
367
  }
348
368
 
349
- // Если разделитель не найден или ничья
350
- if (maxCount === 0) {
351
- return ';'; // значение по умолчанию
369
+ // Если разделитель не найден или есть ничья, возвращаем стандартный
370
+ if (maxCount === 0 || maxDelimiters.length > 1) {
371
+ detectedDelimiter = ';';
372
+ } else {
373
+ detectedDelimiter = maxDelimiters[0];
352
374
  }
353
375
 
354
376
  return detectedDelimiter;
@@ -0,0 +1,264 @@
1
+ // Браузерная версия CSV to JSON конвертера
2
+ // Адаптирована для работы в браузере без Node.js API
3
+
4
+ import {
5
+ ValidationError,
6
+ ParsingError,
7
+ LimitError,
8
+ ConfigurationError,
9
+ safeExecute
10
+ } from './errors-browser';
11
+
12
+ import type { CsvToJsonOptions } from '../types';
13
+
14
+ /**
15
+ * Валидация опций парсинга
16
+ * @private
17
+ */
18
+ function validateCsvOptions(options: CsvToJsonOptions): boolean {
19
+ // Validate options
20
+ if (options && typeof options !== 'object') {
21
+ throw new ConfigurationError('Options must be an object');
22
+ }
23
+
24
+ // Validate delimiter
25
+ if (options?.delimiter && typeof options.delimiter !== 'string') {
26
+ throw new ConfigurationError('Delimiter must be a string');
27
+ }
28
+
29
+ if (options?.delimiter && options.delimiter.length !== 1) {
30
+ throw new ConfigurationError('Delimiter must be a single character');
31
+ }
32
+
33
+ // Validate autoDetect
34
+ if (options?.autoDetect !== undefined && typeof options.autoDetect !== 'boolean') {
35
+ throw new ConfigurationError('autoDetect must be a boolean');
36
+ }
37
+
38
+ // Validate candidates
39
+ if (options?.candidates && !Array.isArray(options.candidates)) {
40
+ throw new ConfigurationError('candidates must be an array');
41
+ }
42
+
43
+ // Validate maxRows
44
+ if (options?.maxRows !== undefined && (typeof options.maxRows !== 'number' || options.maxRows <= 0)) {
45
+ throw new ConfigurationError('maxRows must be a positive number');
46
+ }
47
+
48
+ if (options?.warnExtraFields !== undefined && typeof options.warnExtraFields !== 'boolean') {
49
+ throw new ConfigurationError('warnExtraFields must be a boolean');
50
+ }
51
+
52
+ return true;
53
+ }
54
+
55
+ /**
56
+ * Автоматическое определение разделителя
57
+ * @private
58
+ */
59
+ function autoDetectDelimiter(text: string, candidates: string[] = [',', ';', '\t', '|']): string {
60
+ if (!text || typeof text !== 'string') {
61
+ return ',';
62
+ }
63
+
64
+ const firstLine = text.split('\n')[0];
65
+ if (!firstLine) {
66
+ return ',';
67
+ }
68
+
69
+ let bestCandidate = ',';
70
+ let bestCount = 0;
71
+
72
+ for (const candidate of candidates) {
73
+ const count = (firstLine.match(new RegExp(candidate.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')) || []).length;
74
+ if (count > bestCount) {
75
+ bestCount = count;
76
+ bestCandidate = candidate;
77
+ }
78
+ }
79
+
80
+ return bestCandidate;
81
+ }
82
+
83
+ /**
84
+ * Парсинг CSV строки в массив объектов
85
+ *
86
+ * @param csvText - CSV текст для парсинга
87
+ * @param options - Опции парсинга
88
+ * @returns Массив объектов
89
+ */
90
+ export function csvToJson(csvText: string, options: CsvToJsonOptions = {}): any[] {
91
+ return safeExecute(() => {
92
+ validateCsvOptions(options);
93
+
94
+ if (typeof csvText !== 'string') {
95
+ throw new ValidationError('CSV text must be a string');
96
+ }
97
+
98
+ if (csvText.trim() === '') {
99
+ return [];
100
+ }
101
+
102
+ // Определение разделителя
103
+ const delimiter = options.delimiter ||
104
+ (options.autoDetect !== false ? autoDetectDelimiter(csvText, options.candidates) : ',');
105
+
106
+ // Разделение на строки
107
+ const lines = csvText.split('\n').filter(line => line.trim() !== '');
108
+ if (lines.length === 0) {
109
+ return [];
110
+ }
111
+
112
+ // Парсинг заголовков
113
+ const headers = lines[0].split(delimiter).map(h => h.trim());
114
+
115
+ // Ограничение количества строк
116
+ const maxRows = options.maxRows || Infinity;
117
+ const dataRows = lines.slice(1, Math.min(lines.length, maxRows + 1));
118
+
119
+ // Парсинг данных
120
+ const result = [];
121
+
122
+ for (let i = 0; i < dataRows.length; i++) {
123
+ const line = dataRows[i];
124
+ const values = line.split(delimiter);
125
+ const row: Record<string, any> = {};
126
+
127
+ for (let j = 0; j < headers.length; j++) {
128
+ const header = headers[j];
129
+ const value = j < values.length ? values[j].trim() : '';
130
+
131
+ // Попытка парсинга чисел
132
+ if (/^-?\d+(\.\d+)?$/.test(value)) {
133
+ row[header] = parseFloat(value);
134
+ } else if (value.toLowerCase() === 'true' || value.toLowerCase() === 'false') {
135
+ row[header] = value.toLowerCase() === 'true';
136
+ } else {
137
+ row[header] = value;
138
+ }
139
+ }
140
+
141
+ result.push(row);
142
+ }
143
+
144
+ return result;
145
+ });
146
+ }
147
+
148
+ /**
149
+ * Асинхронная версия csvToJson
150
+ */
151
+ export async function csvToJsonAsync(csvText: string, options: CsvToJsonOptions = {}): Promise<any[]> {
152
+ return csvToJson(csvText, options);
153
+ }
154
+
155
+ /**
156
+ * Создает итератор для потокового парсинга CSV
157
+ *
158
+ * @param input - CSV текст, File или Blob
159
+ * @param options - Опции парсинга
160
+ * @returns AsyncGenerator
161
+ */
162
+ export async function* csvToJsonIterator(input: string | File | Blob, options: CsvToJsonOptions = {}): AsyncGenerator<any> {
163
+ validateCsvOptions(options);
164
+
165
+ let csvText: string;
166
+
167
+ if (typeof input === 'string') {
168
+ csvText = input;
169
+ } else if (input instanceof File || input instanceof Blob) {
170
+ csvText = await input.text();
171
+ } else {
172
+ throw new ValidationError('Input must be string, File or Blob');
173
+ }
174
+
175
+ if (csvText.trim() === '') {
176
+ return;
177
+ }
178
+
179
+ // Определение разделителя
180
+ const delimiter = options.delimiter ||
181
+ (options.autoDetect !== false ? autoDetectDelimiter(csvText, options.candidates) : ',');
182
+
183
+ // Разделение на строки
184
+ const lines = csvText.split('\n').filter(line => line.trim() !== '');
185
+ if (lines.length === 0) {
186
+ return;
187
+ }
188
+
189
+ // Парсинг заголовков
190
+ const headers = lines[0].split(delimiter).map(h => h.trim());
191
+
192
+ // Ограничение количества строк
193
+ const maxRows = options.maxRows || Infinity;
194
+ const dataRows = lines.slice(1, Math.min(lines.length, maxRows + 1));
195
+
196
+ // Возврат данных по одной строке
197
+ for (let i = 0; i < dataRows.length; i++) {
198
+ const line = dataRows[i];
199
+ const values = line.split(delimiter);
200
+ const row: Record<string, any> = {};
201
+
202
+ for (let j = 0; j < headers.length; j++) {
203
+ const header = headers[j];
204
+ const value = j < values.length ? values[j].trim() : '';
205
+
206
+ // Попытка парсинга чисел
207
+ if (/^-?\d+(\.\d+)?$/.test(value)) {
208
+ row[header] = parseFloat(value);
209
+ } else if (value.toLowerCase() === 'true' || value.toLowerCase() === 'false') {
210
+ row[header] = value.toLowerCase() === 'true';
211
+ } else {
212
+ row[header] = value;
213
+ }
214
+ }
215
+
216
+ yield row;
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Асинхронная версия csvToJsonIterator (псевдоним)
222
+ */
223
+ export const csvToJsonIteratorAsync = csvToJsonIterator;
224
+
225
+ /**
226
+ * Парсинг CSV с обработкой ошибок
227
+ *
228
+ * @param csvText - CSV текст
229
+ * @param options - Опции парсинга
230
+ * @returns Результат парсинга или null при ошибке
231
+ */
232
+ export function parseCsvSafe(csvText: string, options: CsvToJsonOptions = {}): any[] | null {
233
+ try {
234
+ return csvToJson(csvText, options);
235
+ } catch (error) {
236
+ console.error('CSV parsing error:', error);
237
+ return null;
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Асинхронная версия parseCsvSafe
243
+ */
244
+ export async function parseCsvSafeAsync(csvText: string, options: CsvToJsonOptions = {}): Promise<any[] | null> {
245
+ try {
246
+ return await csvToJsonAsync(csvText, options);
247
+ } catch (error) {
248
+ console.error('CSV parsing error:', error);
249
+ return null;
250
+ }
251
+ }
252
+
253
+ // Экспорт для Node.js совместимости
254
+ if (typeof module !== 'undefined' && module.exports) {
255
+ module.exports = {
256
+ csvToJson,
257
+ csvToJsonAsync,
258
+ csvToJsonIterator,
259
+ csvToJsonIteratorAsync,
260
+ parseCsvSafe,
261
+ parseCsvSafeAsync,
262
+ autoDetectDelimiter
263
+ };
264
+ }