jtcsv 1.2.0 → 2.1.1

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 (52) hide show
  1. package/README.md +272 -329
  2. package/bin/jtcsv.js +1092 -97
  3. package/cli-tui.js +0 -0
  4. package/csv-to-json.js +385 -311
  5. package/dist/jtcsv.cjs.js +1619 -0
  6. package/dist/jtcsv.cjs.js.map +1 -0
  7. package/dist/jtcsv.esm.js +1599 -0
  8. package/dist/jtcsv.esm.js.map +1 -0
  9. package/dist/jtcsv.umd.js +1625 -0
  10. package/dist/jtcsv.umd.js.map +1 -0
  11. package/examples/cli-tool.js +186 -0
  12. package/examples/express-api.js +167 -0
  13. package/examples/large-dataset-example.js +185 -0
  14. package/examples/plugin-excel-exporter.js +407 -0
  15. package/examples/simple-usage.js +280 -0
  16. package/examples/streaming-example.js +419 -0
  17. package/index.d.ts +288 -1
  18. package/index.js +23 -0
  19. package/json-save.js +1 -1
  20. package/json-to-csv.js +130 -89
  21. package/package.json +139 -13
  22. package/plugins/README.md +373 -0
  23. package/plugins/express-middleware/README.md +306 -0
  24. package/plugins/express-middleware/example.js +136 -0
  25. package/plugins/express-middleware/index.d.ts +114 -0
  26. package/plugins/express-middleware/index.js +360 -0
  27. package/plugins/express-middleware/package.json +52 -0
  28. package/plugins/fastify-plugin/index.js +406 -0
  29. package/plugins/fastify-plugin/package.json +55 -0
  30. package/plugins/nextjs-api/README.md +452 -0
  31. package/plugins/nextjs-api/examples/ConverterComponent.jsx +386 -0
  32. package/plugins/nextjs-api/examples/api-convert.js +69 -0
  33. package/plugins/nextjs-api/index.js +388 -0
  34. package/plugins/nextjs-api/package.json +63 -0
  35. package/plugins/nextjs-api/route.js +372 -0
  36. package/src/browser/browser-functions.js +189 -0
  37. package/src/browser/csv-to-json-browser.js +442 -0
  38. package/src/browser/errors-browser.js +194 -0
  39. package/src/browser/index.js +79 -0
  40. package/src/browser/json-to-csv-browser.js +309 -0
  41. package/src/browser/workers/csv-parser.worker.js +359 -0
  42. package/src/browser/workers/worker-pool.js +467 -0
  43. package/src/core/delimiter-cache.js +186 -0
  44. package/src/core/plugin-system.js +472 -0
  45. package/src/core/transform-hooks.js +350 -0
  46. package/src/engines/fast-path-engine-new.js +338 -0
  47. package/src/engines/fast-path-engine.js +836 -0
  48. package/src/formats/ndjson-parser.js +419 -0
  49. package/src/formats/tsv-parser.js +336 -0
  50. package/src/index-with-plugins.js +371 -0
  51. package/stream-csv-to-json.js +1 -1
  52. package/stream-json-to-csv.js +1 -1
@@ -0,0 +1,336 @@
1
+ /**
2
+ * TSV (Tab-Separated Values) парсер
3
+ * Специализированная поддержка TSV формата
4
+ *
5
+ * @version 1.0.0
6
+ * @date 2026-01-23
7
+ */
8
+
9
+ const { csvToJson } = require('../../csv-to-json');
10
+ const { jsonToCsv } = require('../../json-to-csv');
11
+ const { ValidationError, SecurityError, FileSystemError } = require('../../errors');
12
+ const path = require('path');
13
+
14
+ function validateTsvFilePath(filePath) {
15
+ if (typeof filePath !== 'string' || filePath.trim() === '') {
16
+ throw new ValidationError('File path must be a non-empty string');
17
+ }
18
+
19
+ if (!filePath.toLowerCase().endsWith('.tsv')) {
20
+ throw new ValidationError('File must have .tsv extension');
21
+ }
22
+
23
+ const normalizedPath = path.normalize(filePath);
24
+ if (normalizedPath.includes('..') ||
25
+ /\\\.\.\\|\/\.\.\//.test(filePath) ||
26
+ filePath.startsWith('..') ||
27
+ filePath.includes('/..')) {
28
+ throw new SecurityError('Directory traversal detected in file path');
29
+ }
30
+
31
+ return path.resolve(filePath);
32
+ }
33
+
34
+ class TsvParser {
35
+ /**
36
+ * Конвертирует массив объектов в TSV строку
37
+ * @param {Array} data - Массив объектов
38
+ * @param {Object} options - Опции форматирования
39
+ * @returns {string} TSV строка
40
+ *
41
+ * @example
42
+ * const data = [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }];
43
+ * const tsv = TsvParser.jsonToTsv(data);
44
+ * // Результат: "id\tname\n1\tJohn\n2\tJane"
45
+ */
46
+ static jsonToTsv(data, options = {}) {
47
+ const defaultOptions = {
48
+ delimiter: '\t',
49
+ includeHeaders: true,
50
+ ...options
51
+ };
52
+
53
+ return jsonToCsv(data, defaultOptions);
54
+ }
55
+
56
+ /**
57
+ * Конвертирует TSV строку в массив объектов
58
+ * @param {string} tsvString - TSV строка
59
+ * @param {Object} options - Опции парсинга
60
+ * @returns {Array} Массив объектов
61
+ *
62
+ * @example
63
+ * const tsv = "id\tname\n1\tJohn\n2\tJane";
64
+ * const data = TsvParser.tsvToJson(tsv);
65
+ * // Результат: [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }]
66
+ */
67
+ static tsvToJson(tsvString, options = {}) {
68
+ const defaultOptions = {
69
+ delimiter: '\t',
70
+ autoDetect: false,
71
+ hasHeaders: true,
72
+ ...options
73
+ };
74
+
75
+ return csvToJson(tsvString, defaultOptions);
76
+ }
77
+
78
+ /**
79
+ * Автоматически определяет является ли строка TSV
80
+ * @param {string} sample - Образец данных
81
+ * @returns {boolean} True если это TSV
82
+ */
83
+ static isTsv(sample) {
84
+ if (!sample || typeof sample !== 'string') {
85
+ return false;
86
+ }
87
+
88
+ const lines = sample.split('\n').slice(0, 10);
89
+ let tabCount = 0;
90
+ let commaCount = 0;
91
+ let semicolonCount = 0;
92
+
93
+ for (const line of lines) {
94
+ if (line.trim() === '') {
95
+ continue;
96
+ }
97
+
98
+ // Считаем разделители
99
+ tabCount += (line.match(/\t/g) || []).length;
100
+ commaCount += (line.match(/,/g) || []).length;
101
+ semicolonCount += (line.match(/;/g) || []).length;
102
+ }
103
+
104
+ // Если табуляций больше чем других разделителей, считаем это TSV
105
+ return tabCount > commaCount && tabCount > semicolonCount;
106
+ }
107
+
108
+ /**
109
+ * Создает TransformStream для конвертации JSON в TSV
110
+ * @param {Object} options - Опции конвертации
111
+ * @returns {TransformStream} Transform stream
112
+ */
113
+ static createJsonToTsvStream(options = {}) {
114
+ const { createJsonToCsvStream } = require('../../stream-json-to-csv');
115
+
116
+ return createJsonToCsvStream({
117
+ delimiter: '\t',
118
+ ...options
119
+ });
120
+ }
121
+
122
+ /**
123
+ * Создает TransformStream для конвертации TSV в JSON
124
+ * @param {Object} options - Опции конвертации
125
+ * @returns {TransformStream} Transform stream
126
+ */
127
+ static createTsvToJsonStream(options = {}) {
128
+ const { createCsvToJsonStream } = require('../../stream-csv-to-json');
129
+
130
+ return createCsvToJsonStream({
131
+ delimiter: '\t',
132
+ autoDetect: false,
133
+ ...options
134
+ });
135
+ }
136
+
137
+ /**
138
+ * Читает TSV файл и конвертирует в JSON
139
+ * @param {string} filePath - Путь к TSV файлу
140
+ * @param {Object} options - Опции парсинга
141
+ * @returns {Promise<Array>} Promise с массивом объектов
142
+ */
143
+ static async readTsvAsJson(filePath, options = {}) {
144
+ const fs = require('fs').promises;
145
+ const safePath = validateTsvFilePath(filePath);
146
+
147
+ try {
148
+ const tsvContent = await fs.readFile(safePath, 'utf8');
149
+ return csvToJson(tsvContent, {
150
+ delimiter: '\t',
151
+ autoDetect: false,
152
+ ...options
153
+ });
154
+ } catch (error) {
155
+ if (error instanceof ValidationError || error instanceof SecurityError) {
156
+ throw error;
157
+ }
158
+ if (error.code === 'ENOENT') {
159
+ throw new FileSystemError(`File not found: ${safePath}`, error);
160
+ }
161
+ if (error.code === 'EACCES') {
162
+ throw new FileSystemError(`Permission denied: ${safePath}`, error);
163
+ }
164
+ if (error.code === 'EISDIR') {
165
+ throw new FileSystemError(`Path is a directory: ${safePath}`, error);
166
+ }
167
+ throw new FileSystemError(`Failed to read TSV file: ${error.message}`, error);
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Синхронно читает TSV файл и конвертирует в JSON
173
+ * @param {string} filePath - Путь к TSV файлу
174
+ * @param {Object} options - Опции парсинга
175
+ * @returns {Array} Массив объектов
176
+ */
177
+ static readTsvAsJsonSync(filePath, options = {}) {
178
+ const fs = require('fs');
179
+ const safePath = validateTsvFilePath(filePath);
180
+
181
+ try {
182
+ const tsvContent = fs.readFileSync(safePath, 'utf8');
183
+ return csvToJson(tsvContent, {
184
+ delimiter: '\t',
185
+ autoDetect: false,
186
+ ...options
187
+ });
188
+ } catch (error) {
189
+ if (error instanceof ValidationError || error instanceof SecurityError) {
190
+ throw error;
191
+ }
192
+ if (error.code === 'ENOENT') {
193
+ throw new FileSystemError(`File not found: ${safePath}`, error);
194
+ }
195
+ if (error.code === 'EACCES') {
196
+ throw new FileSystemError(`Permission denied: ${safePath}`, error);
197
+ }
198
+ if (error.code === 'EISDIR') {
199
+ throw new FileSystemError(`Path is a directory: ${safePath}`, error);
200
+ }
201
+ throw new FileSystemError(`Failed to read TSV file: ${error.message}`, error);
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Сохраняет массив объектов как TSV файл
207
+ * @param {Array} data - Массив объектов
208
+ * @param {string} filePath - Путь для сохранения
209
+ * @param {Object} options - Опции сохранения
210
+ * @returns {Promise<void>}
211
+ */
212
+ static async saveAsTsv(data, filePath, options = {}) {
213
+ const fs = require('fs').promises;
214
+ const safePath = validateTsvFilePath(filePath);
215
+ const tsvContent = this.jsonToTsv(data, options);
216
+ const dir = path.dirname(safePath);
217
+
218
+ try {
219
+ await fs.mkdir(dir, { recursive: true });
220
+ await fs.writeFile(safePath, tsvContent, 'utf8');
221
+ return safePath;
222
+ } catch (error) {
223
+ if (error.code === 'ENOENT') {
224
+ throw new FileSystemError(`Directory does not exist: ${dir}`, error);
225
+ }
226
+ if (error.code === 'EACCES') {
227
+ throw new FileSystemError(`Permission denied: ${safePath}`, error);
228
+ }
229
+ if (error.code === 'ENOSPC') {
230
+ throw new FileSystemError(`No space left on device: ${safePath}`, error);
231
+ }
232
+ throw new FileSystemError(`Failed to save TSV file: ${error.message}`, error);
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Синхронно сохраняет массив объектов как TSV файл
238
+ * @param {Array} data - Массив объектов
239
+ * @param {string} filePath - Путь для сохранения
240
+ * @param {Object} options - Опции сохранения
241
+ */
242
+ static saveAsTsvSync(data, filePath, options = {}) {
243
+ const fs = require('fs');
244
+ const safePath = validateTsvFilePath(filePath);
245
+ const tsvContent = this.jsonToTsv(data, options);
246
+
247
+ fs.mkdirSync(path.dirname(safePath), { recursive: true });
248
+ fs.writeFileSync(safePath, tsvContent, 'utf8');
249
+ }
250
+
251
+ /**
252
+ * Валидирует TSV строку
253
+ * @param {string} tsvString - TSV строка для валидации
254
+ * @param {Object} options - Опции валидации
255
+ * @returns {Object} Результат валидации
256
+ */
257
+ static validateTsv(tsvString, options = {}) {
258
+ const { requireConsistentColumns = true } = options;
259
+
260
+ if (!tsvString || typeof tsvString !== 'string') {
261
+ return {
262
+ valid: false,
263
+ error: 'Input must be a non-empty string',
264
+ details: { inputType: typeof tsvString }
265
+ };
266
+ }
267
+
268
+ const lines = tsvString.split('\n').filter(line => line.trim() !== '');
269
+
270
+ if (lines.length === 0) {
271
+ return {
272
+ valid: false,
273
+ error: 'No data found in TSV',
274
+ details: { lineCount: 0 }
275
+ };
276
+ }
277
+
278
+ const columnCounts = [];
279
+ const errors = [];
280
+
281
+ for (let i = 0; i < lines.length; i++) {
282
+ const line = lines[i];
283
+ const columns = line.split('\t');
284
+ columnCounts.push(columns.length);
285
+
286
+ // Проверяем наличие пустых полей (если требуется)
287
+ if (options.disallowEmptyFields) {
288
+ const emptyFields = columns.filter(field => field.trim() === '');
289
+ if (emptyFields.length > 0) {
290
+ errors.push({
291
+ line: i + 1,
292
+ error: `Found ${emptyFields.length} empty field(s)`,
293
+ fields: emptyFields.map((_, idx) => idx + 1)
294
+ });
295
+ }
296
+ }
297
+ }
298
+
299
+ // Проверяем консистентность колонки
300
+ if (requireConsistentColumns && columnCounts.length > 1) {
301
+ const firstCount = columnCounts[0];
302
+ const inconsistentLines = [];
303
+
304
+ for (let i = 1; i < columnCounts.length; i++) {
305
+ if (columnCounts[i] !== firstCount) {
306
+ inconsistentLines.push({
307
+ line: i + 1,
308
+ expected: firstCount,
309
+ actual: columnCounts[i]
310
+ });
311
+ }
312
+ }
313
+
314
+ if (inconsistentLines.length > 0) {
315
+ errors.push({
316
+ error: 'Inconsistent column count',
317
+ details: inconsistentLines
318
+ });
319
+ }
320
+ }
321
+
322
+ return {
323
+ valid: errors.length === 0,
324
+ stats: {
325
+ totalLines: lines.length,
326
+ totalColumns: columnCounts[0] || 0,
327
+ minColumns: Math.min(...columnCounts),
328
+ maxColumns: Math.max(...columnCounts),
329
+ consistentColumns: new Set(columnCounts).size === 1
330
+ },
331
+ errors: errors.length > 0 ? errors : undefined
332
+ };
333
+ }
334
+ }
335
+
336
+ module.exports = TsvParser;
@@ -0,0 +1,371 @@
1
+ /**
2
+ * JTCSV с поддержкой плагинов
3
+ * Расширяемая версия основного API с plugin system
4
+ *
5
+ * @version 1.0.0
6
+ * @date 2026-01-22
7
+ */
8
+
9
+ const PluginManager = require('./core/plugin-system');
10
+ const FastPathEngine = require('./engines/fast-path-engine');
11
+ const NdjsonParser = require('./formats/ndjson-parser');
12
+
13
+ // Импортируем основные функции
14
+ const coreJsonToCsv = require('../json-to-csv').jsonToCsv;
15
+ const coreCsvToJson = require('../csv-to-json').csvToJson;
16
+ const coreCsvToJsonIterator = require('../csv-to-json').csvToJsonIterator;
17
+ const coreSaveAsCsv = require('../json-to-csv').saveAsCsv;
18
+ const coreReadCsvAsJson = require('../csv-to-json').readCsvAsJson;
19
+
20
+ class JtcsvWithPlugins {
21
+ constructor(options = {}) {
22
+ this.pluginManager = new PluginManager();
23
+ this.fastPathEngine = new FastPathEngine();
24
+ this.options = {
25
+ enableFastPath: true,
26
+ enablePlugins: true,
27
+ ...options
28
+ };
29
+
30
+ // Регистрируем встроенные плагины
31
+ this._registerBuiltinPlugins();
32
+ }
33
+
34
+ /**
35
+ * Регистрирует встроенные плагины
36
+ */
37
+ _registerBuiltinPlugins() {
38
+ // Fast Path Engine плагин
39
+ this.pluginManager.use('fast-path-engine', {
40
+ name: 'Fast Path Engine',
41
+ version: '1.0.0',
42
+ description: 'Оптимизированный парсер CSV с автоматическим выбором стратегии',
43
+ hooks: {
44
+ 'before:csvToJson': (csv, context) => {
45
+ if (this.options.enableFastPath && context.options?.useFastPath !== false) {
46
+ // Используем fast path engine для анализа
47
+ const sample = csv.substring(0, Math.min(1000, csv.length));
48
+ const structure = this.fastPathEngine.analyzeStructure(sample, context.options);
49
+
50
+ context.metadata.fastPathStructure = structure;
51
+ console.log(`🚀 Используется ${structure.recommendedEngine} парсер`);
52
+ }
53
+ return csv;
54
+ },
55
+ 'after:csvToJson': (result, context) => {
56
+ if (context.metadata?.fastPathStructure) {
57
+ context.metadata.fastPathStats = this.fastPathEngine.getStats();
58
+ }
59
+ return result;
60
+ }
61
+ }
62
+ });
63
+
64
+ // NDJSON плагин
65
+ this.pluginManager.use('ndjson-support', {
66
+ name: 'NDJSON Support',
67
+ version: '1.0.0',
68
+ description: 'Поддержка Newline Delimited JSON формата',
69
+ hooks: {
70
+ 'before:parse': (input, context) => {
71
+ if (context.options?.format === 'ndjson') {
72
+ // Парсим NDJSON
73
+ return NdjsonParser.fromNdjson(input, context.options);
74
+ }
75
+ return input;
76
+ },
77
+ 'after:serialize': (output, context) => {
78
+ if (context.options?.format === 'ndjson') {
79
+ // Сериализуем в NDJSON
80
+ return NdjsonParser.toNdjson(output, context.options);
81
+ }
82
+ return output;
83
+ }
84
+ }
85
+ });
86
+
87
+ // Валидация данных плагин
88
+ this.pluginManager.use('data-validation', {
89
+ name: 'Data Validation',
90
+ version: '1.0.0',
91
+ description: 'Валидация входных и выходных данных',
92
+ hooks: {
93
+ 'validation': (data, context) => {
94
+ if (!data) {
95
+ throw new Error('Данные не могут быть пустыми');
96
+ }
97
+
98
+ if (context.operation === 'jsonToCsv' && !Array.isArray(data)) {
99
+ throw new Error('Для конвертации в CSV данные должны быть массивом');
100
+ }
101
+
102
+ return data;
103
+ }
104
+ },
105
+ middlewares: [
106
+ async (ctx, next) => {
107
+ // Валидация перед выполнением
108
+ await this.pluginManager.executeHooks('validation', ctx.input, ctx);
109
+ await next();
110
+ // Валидация после выполнения
111
+ await this.pluginManager.executeHooks('validation', ctx.result, ctx);
112
+ }
113
+ ]
114
+ });
115
+
116
+ // Логирование плагин
117
+ this.pluginManager.use('logging', {
118
+ name: 'Logging',
119
+ version: '1.0.0',
120
+ description: 'Логирование операций',
121
+ hooks: {
122
+ 'before:csvToJson': (csv, context) => {
123
+ console.log(`📥 Начало csvToJson, размер: ${csv.length} байт`);
124
+ return csv;
125
+ },
126
+ 'after:csvToJson': (result, context) => {
127
+ console.log(`📤 Завершение csvToJson, результат: ${result.length} записей`);
128
+ return result;
129
+ },
130
+ 'before:jsonToCsv': (json, context) => {
131
+ console.log(`📥 Начало jsonToCsv, записей: ${json.length}`);
132
+ return json;
133
+ },
134
+ 'after:jsonToCsv': (csv, context) => {
135
+ console.log(`📤 Завершение jsonToCsv, размер: ${csv.length} байт`);
136
+ return csv;
137
+ }
138
+ }
139
+ });
140
+ }
141
+
142
+ /**
143
+ * Конвертирует CSV в JSON с поддержкой плагинов
144
+ * @param {string} csv - CSV данные
145
+ * @param {Object} options - Опции парсинга
146
+ * @returns {Promise<Array>} JSON данные
147
+ */
148
+ async csvToJson(csv, options = {}) {
149
+ if (!this.options.enablePlugins) {
150
+ return coreCsvToJson(csv, options);
151
+ }
152
+
153
+ return this.pluginManager.executeWithPlugins(
154
+ 'csvToJson',
155
+ csv,
156
+ options,
157
+ (input, opts) => {
158
+ if (this.options.enableFastPath && opts?.useFastPath !== false) {
159
+ return coreCsvToJson(input, { ...opts, useFastPath: true });
160
+ }
161
+
162
+ return coreCsvToJson(input, opts);
163
+ }
164
+ );
165
+ }
166
+
167
+ /**
168
+ * Convert CSV to JSON rows as async iterator with plugin hooks.
169
+ * @param {string} csv - CSV input
170
+ * @param {Object} options - Conversion options
171
+ * @returns {AsyncGenerator} Async iterator of rows
172
+ */
173
+ async *csvToJsonIterator(csv, options = {}) {
174
+ if (!this.options.enablePlugins) {
175
+ for await (const row of coreCsvToJsonIterator(csv, options)) {
176
+ yield row;
177
+ }
178
+ return;
179
+ }
180
+
181
+ const iterator = await this.pluginManager.executeWithPlugins(
182
+ 'csvToJson',
183
+ csv,
184
+ options,
185
+ (input, opts) => {
186
+ if (this.options.enableFastPath && opts?.useFastPath !== false) {
187
+ return coreCsvToJsonIterator(input, { ...opts, useFastPath: true });
188
+ }
189
+
190
+ return coreCsvToJsonIterator(input, opts);
191
+ }
192
+ );
193
+
194
+ for await (const row of iterator) {
195
+ yield row;
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Конвертирует JSON в CSV с поддержкой плагинов
201
+ * @param {Array} json - JSON данные
202
+ * @param {Object} options - Опции сериализации
203
+ * @returns {Promise<string>} CSV данные
204
+ */
205
+ async jsonToCsv(json, options = {}) {
206
+ if (!this.options.enablePlugins) {
207
+ return coreJsonToCsv(json, options);
208
+ }
209
+
210
+ return this.pluginManager.executeWithPlugins(
211
+ 'jsonToCsv',
212
+ json,
213
+ options,
214
+ coreJsonToCsv
215
+ );
216
+ }
217
+
218
+ /**
219
+ * Сохраняет JSON как CSV файл
220
+ * @param {Array} data - JSON данные
221
+ * @param {string} filePath - Путь к файлу
222
+ * @param {Object} options - Опции
223
+ * @returns {Promise<void>}
224
+ */
225
+ async saveAsCsv(data, filePath, options = {}) {
226
+ if (!this.options.enablePlugins) {
227
+ return coreSaveAsCsv(data, filePath, options);
228
+ }
229
+
230
+ const csv = await this.jsonToCsv(data, options);
231
+
232
+ // Используем плагины для сохранения
233
+ return this.pluginManager.executeWithPlugins(
234
+ 'saveAsCsv',
235
+ { data: csv, filePath },
236
+ options,
237
+ async (input) => {
238
+ const fs = require('fs').promises;
239
+ await fs.writeFile(input.filePath, input.data, 'utf8');
240
+ return input.filePath;
241
+ }
242
+ );
243
+ }
244
+
245
+ /**
246
+ * Читает CSV файл и конвертирует в JSON
247
+ * @param {string} filePath - Путь к файлу
248
+ * @param {Object} options - Опции
249
+ * @returns {Promise<Array>} JSON данные
250
+ */
251
+ async readCsvAsJson(filePath, options = {}) {
252
+ if (!this.options.enablePlugins) {
253
+ return coreReadCsvAsJson(filePath, options);
254
+ }
255
+
256
+ // Читаем файл
257
+ const fs = require('fs').promises;
258
+ const csv = await fs.readFile(filePath, 'utf8');
259
+
260
+ // Конвертируем с использованием плагинов
261
+ return this.csvToJson(csv, options);
262
+ }
263
+
264
+ /**
265
+ * Парсит NDJSON данные
266
+ * @param {string|ReadableStream} input - NDJSON данные
267
+ * @param {Object} options - Опции
268
+ * @returns {Promise<Array>} JSON данные
269
+ */
270
+ async parseNdjson(input, options = {}) {
271
+ if (typeof input === 'string') {
272
+ return NdjsonParser.fromNdjson(input, options);
273
+ }
274
+
275
+ // Для потоков
276
+ const result = [];
277
+ for await (const obj of NdjsonParser.parseStream(input, options)) {
278
+ result.push(obj);
279
+ }
280
+ return result;
281
+ }
282
+
283
+ /**
284
+ * Конвертирует JSON в NDJSON
285
+ * @param {Array} data - JSON данные
286
+ * @param {Object} options - Опции
287
+ * @returns {string} NDJSON строка
288
+ */
289
+ toNdjson(data, options = {}) {
290
+ return NdjsonParser.toNdjson(data, options);
291
+ }
292
+
293
+ /**
294
+ * Регистрирует плагин
295
+ * @param {string} name - Имя плагина
296
+ * @param {Object} plugin - Конфигурация плагина
297
+ * @returns {JtcsvWithPlugins} this для chaining
298
+ */
299
+ use(name, plugin) {
300
+ this.pluginManager.use(name, plugin);
301
+ return this;
302
+ }
303
+
304
+ /**
305
+ * Возвращает менеджер плагинов
306
+ * @returns {PluginManager}
307
+ */
308
+ getPluginManager() {
309
+ return this.pluginManager;
310
+ }
311
+
312
+ /**
313
+ * Возвращает fast path engine
314
+ * @returns {FastPathEngine}
315
+ */
316
+ getFastPathEngine() {
317
+ return this.fastPathEngine;
318
+ }
319
+
320
+ /**
321
+ * Возвращает список плагинов
322
+ * @returns {Array}
323
+ */
324
+ listPlugins() {
325
+ return this.pluginManager.listPlugins();
326
+ }
327
+
328
+ /**
329
+ * Возвращает статистику
330
+ * @returns {Object}
331
+ */
332
+ getStats() {
333
+ return {
334
+ plugins: this.pluginManager.getStats(),
335
+ fastPath: this.fastPathEngine.getStats(),
336
+ options: this.options
337
+ };
338
+ }
339
+
340
+ /**
341
+ * Настраивает опции
342
+ * @param {Object} newOptions - Новые опции
343
+ */
344
+ configure(newOptions) {
345
+ this.options = { ...this.options, ...newOptions };
346
+ return this;
347
+ }
348
+
349
+ /**
350
+ * Создает экземпляр с настройками по умолчанию
351
+ * @param {Object} options - Опции
352
+ * @returns {JtcsvWithPlugins}
353
+ */
354
+ static create(options = {}) {
355
+ return new JtcsvWithPlugins(options);
356
+ }
357
+ }
358
+
359
+ // Экспортируем основной класс
360
+ module.exports = JtcsvWithPlugins;
361
+
362
+ // Экспортируем утилиты
363
+ module.exports.PluginManager = PluginManager;
364
+ module.exports.FastPathEngine = FastPathEngine;
365
+ module.exports.NdjsonParser = NdjsonParser;
366
+
367
+ // Экспортируем фабричный метод
368
+ module.exports.create = JtcsvWithPlugins.create;
369
+
370
+
371
+
@@ -610,4 +610,4 @@ module.exports = {
610
610
  // For ES6 module compatibility
611
611
  if (typeof module !== 'undefined' && module.exports) {
612
612
  module.exports.default = createCsvToJsonStream;
613
- }
613
+ }