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,773 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.jtcsvWorkers = {}));
5
+ })(this, (function (exports) { 'use strict';
6
+
7
+ // Система ошибок для браузерной версии jtcsv
8
+ // Адаптирована для работы без Node.js специфичных API
9
+ /**
10
+ * Базовый класс ошибки jtcsv
11
+ */
12
+ class JTCSVError extends Error {
13
+ constructor(message, code = 'JTCSV_ERROR', details = {}) {
14
+ super(message);
15
+ this.name = 'JTCSVError';
16
+ this.code = code;
17
+ this.details = details;
18
+ // Сохранение stack trace
19
+ if (Error.captureStackTrace) {
20
+ Error.captureStackTrace(this, JTCSVError);
21
+ }
22
+ }
23
+ }
24
+ /**
25
+ * Ошибка валидации
26
+ */
27
+ class ValidationError extends JTCSVError {
28
+ constructor(message, details = {}) {
29
+ super(message, 'VALIDATION_ERROR', details);
30
+ this.name = 'ValidationError';
31
+ }
32
+ }
33
+ /**
34
+ * Ошибка безопасности
35
+ */
36
+ class SecurityError extends JTCSVError {
37
+ constructor(message, details = {}) {
38
+ super(message, 'SECURITY_ERROR', details);
39
+ this.name = 'SecurityError';
40
+ }
41
+ }
42
+ /**
43
+ * Ошибка файловой системы (адаптирована для браузера)
44
+ */
45
+ class FileSystemError extends JTCSVError {
46
+ constructor(message, originalError, details = {}) {
47
+ super(message, 'FILE_SYSTEM_ERROR', { ...details, originalError });
48
+ this.name = 'FileSystemError';
49
+ if (originalError && originalError.code) {
50
+ this.code = originalError.code;
51
+ }
52
+ }
53
+ }
54
+ /**
55
+ * Ошибка парсинга
56
+ */
57
+ class ParsingError extends JTCSVError {
58
+ constructor(message, lineNumber, details = {}) {
59
+ super(message, 'PARSING_ERROR', { ...details, lineNumber });
60
+ this.name = 'ParsingError';
61
+ this.lineNumber = lineNumber;
62
+ }
63
+ }
64
+ /**
65
+ * Ошибка превышения лимита
66
+ */
67
+ class LimitError extends JTCSVError {
68
+ constructor(message, limit, actual, details = {}) {
69
+ super(message, 'LIMIT_ERROR', { ...details, limit, actual });
70
+ this.name = 'LimitError';
71
+ this.limit = limit;
72
+ this.actual = actual;
73
+ }
74
+ }
75
+ /**
76
+ * Ошибка конфигурации
77
+ */
78
+ class ConfigurationError extends JTCSVError {
79
+ constructor(message, details = {}) {
80
+ super(message, 'CONFIGURATION_ERROR', details);
81
+ this.name = 'ConfigurationError';
82
+ }
83
+ }
84
+ /**
85
+ * Коды ошибок
86
+ */
87
+ const ERROR_CODES = {
88
+ JTCSV_ERROR: 'JTCSV_ERROR',
89
+ VALIDATION_ERROR: 'VALIDATION_ERROR',
90
+ SECURITY_ERROR: 'SECURITY_ERROR',
91
+ FILE_SYSTEM_ERROR: 'FILE_SYSTEM_ERROR',
92
+ PARSING_ERROR: 'PARSING_ERROR',
93
+ LIMIT_ERROR: 'LIMIT_ERROR',
94
+ CONFIGURATION_ERROR: 'CONFIGURATION_ERROR',
95
+ INVALID_INPUT: 'INVALID_INPUT',
96
+ SECURITY_VIOLATION: 'SECURITY_VIOLATION',
97
+ FILE_NOT_FOUND: 'FILE_NOT_FOUND',
98
+ PARSE_FAILED: 'PARSE_FAILED',
99
+ SIZE_LIMIT: 'SIZE_LIMIT',
100
+ INVALID_CONFIG: 'INVALID_CONFIG',
101
+ UNKNOWN_ERROR: 'UNKNOWN_ERROR'
102
+ };
103
+ /**
104
+ * Безопасное выполнение функции с обработкой ошибок
105
+ *
106
+ * @param fn - Функция для выполнения
107
+ * @param errorCode - Код ошибки по умолчанию
108
+ * @param errorDetails - Детали ошибки
109
+ * @returns Результат выполнения функции
110
+ */
111
+ function safeExecute(fn, errorCode = 'UNKNOWN_ERROR', errorDetails = {}) {
112
+ try {
113
+ if (typeof fn === 'function') {
114
+ return fn();
115
+ }
116
+ throw new ValidationError('Function expected');
117
+ }
118
+ catch (error) {
119
+ // Если ошибка уже является JTCSVError, перебросить её
120
+ if (error instanceof JTCSVError) {
121
+ throw error;
122
+ }
123
+ // Определить тип ошибки на основе сообщения или кода
124
+ let enhancedError;
125
+ const errorMessage = error.message || String(error);
126
+ if (errorMessage.includes('validation') || errorMessage.includes('Validation')) {
127
+ enhancedError = new ValidationError(errorMessage, { ...errorDetails, originalError: error });
128
+ }
129
+ else if (errorMessage.includes('security') || errorMessage.includes('Security')) {
130
+ enhancedError = new SecurityError(errorMessage, { ...errorDetails, originalError: error });
131
+ }
132
+ else if (errorMessage.includes('parsing') || errorMessage.includes('Parsing')) {
133
+ enhancedError = new ParsingError(errorMessage, undefined, { ...errorDetails, originalError: error });
134
+ }
135
+ else if (errorMessage.includes('limit') || errorMessage.includes('Limit')) {
136
+ enhancedError = new LimitError(errorMessage, null, null, { ...errorDetails, originalError: error });
137
+ }
138
+ else if (errorMessage.includes('configuration') || errorMessage.includes('Configuration')) {
139
+ enhancedError = new ConfigurationError(errorMessage, { ...errorDetails, originalError: error });
140
+ }
141
+ else if (errorMessage.includes('file') || errorMessage.includes('File')) {
142
+ enhancedError = new FileSystemError(errorMessage, error, errorDetails);
143
+ }
144
+ else {
145
+ // Общая ошибка
146
+ enhancedError = new JTCSVError(errorMessage, errorCode, { ...errorDetails, originalError: error });
147
+ }
148
+ // Сохранить оригинальный stack trace если возможно
149
+ if (error.stack) {
150
+ enhancedError.stack = error.stack;
151
+ }
152
+ throw enhancedError;
153
+ }
154
+ }
155
+ /**
156
+ * Асинхронная версия safeExecute
157
+ */
158
+ async function safeExecuteAsync(fn, errorCode = 'UNKNOWN_ERROR', errorDetails = {}) {
159
+ try {
160
+ if (typeof fn === 'function') {
161
+ return await fn();
162
+ }
163
+ throw new ValidationError('Function expected');
164
+ }
165
+ catch (error) {
166
+ // Если ошибка уже является JTCSVError, перебросить её
167
+ if (error instanceof JTCSVError) {
168
+ throw error;
169
+ }
170
+ // Определить тип ошибки
171
+ let enhancedError;
172
+ const errorMessage = error.message || String(error);
173
+ if (errorMessage.includes('validation') || errorMessage.includes('Validation')) {
174
+ enhancedError = new ValidationError(errorMessage, { ...errorDetails, originalError: error });
175
+ }
176
+ else if (errorMessage.includes('security') || errorMessage.includes('Security')) {
177
+ enhancedError = new SecurityError(errorMessage, { ...errorDetails, originalError: error });
178
+ }
179
+ else if (errorMessage.includes('parsing') || errorMessage.includes('Parsing')) {
180
+ enhancedError = new ParsingError(errorMessage, undefined, { ...errorDetails, originalError: error });
181
+ }
182
+ else if (errorMessage.includes('limit') || errorMessage.includes('Limit')) {
183
+ enhancedError = new LimitError(errorMessage, null, null, { ...errorDetails, originalError: error });
184
+ }
185
+ else if (errorMessage.includes('configuration') || errorMessage.includes('Configuration')) {
186
+ enhancedError = new ConfigurationError(errorMessage, { ...errorDetails, originalError: error });
187
+ }
188
+ else if (errorMessage.includes('file') || errorMessage.includes('File')) {
189
+ enhancedError = new FileSystemError(errorMessage, error, errorDetails);
190
+ }
191
+ else {
192
+ enhancedError = new JTCSVError(errorMessage, errorCode, { ...errorDetails, originalError: error });
193
+ }
194
+ if (error.stack) {
195
+ enhancedError.stack = error.stack;
196
+ }
197
+ throw enhancedError;
198
+ }
199
+ }
200
+ /**
201
+ * Создать сообщение об ошибке
202
+ */
203
+ function createErrorMessage(error, includeStack = false) {
204
+ let message = error.message || 'Unknown error';
205
+ if (error instanceof JTCSVError) {
206
+ message = `[${error.code}] ${message}`;
207
+ if (error instanceof ParsingError && error.lineNumber) {
208
+ message += ` (line ${error.lineNumber})`;
209
+ }
210
+ if (error instanceof LimitError && error.limit && error.actual) {
211
+ message += ` (limit: ${error.limit}, actual: ${error.actual})`;
212
+ }
213
+ }
214
+ if (includeStack && error.stack) {
215
+ message += `\n${error.stack}`;
216
+ }
217
+ return message;
218
+ }
219
+ /**
220
+ * Обработка ошибки
221
+ */
222
+ function handleError(error, options = {}) {
223
+ const { log = true, throw: shouldThrow = false, format = true } = options;
224
+ const message = format ? createErrorMessage(error) : error.message;
225
+ if (log) {
226
+ console.error(`[jtcsv] ${message}`);
227
+ if (error instanceof JTCSVError && error.details) {
228
+ console.error('Error details:', error.details);
229
+ }
230
+ }
231
+ if (shouldThrow) {
232
+ throw error;
233
+ }
234
+ return message;
235
+ }
236
+ // Экспорт для Node.js совместимости
237
+ if (typeof module !== 'undefined' && module.exports) {
238
+ module.exports = {
239
+ JTCSVError,
240
+ ValidationError,
241
+ SecurityError,
242
+ FileSystemError,
243
+ ParsingError,
244
+ LimitError,
245
+ ConfigurationError,
246
+ ERROR_CODES,
247
+ safeExecute,
248
+ safeExecuteAsync,
249
+ createErrorMessage,
250
+ handleError
251
+ };
252
+ }
253
+
254
+ // Worker Pool для параллельной обработки CSV
255
+ // Использует Comlink для простой коммуникации с Web Workers
256
+ // Проверка поддержки Web Workers
257
+ const WORKERS_SUPPORTED = typeof Worker !== 'undefined';
258
+ function isTransferableBuffer(value) {
259
+ if (!(value instanceof ArrayBuffer)) {
260
+ return false;
261
+ }
262
+ if (typeof SharedArrayBuffer !== 'undefined' && value instanceof SharedArrayBuffer) {
263
+ return false;
264
+ }
265
+ return true;
266
+ }
267
+ function collectTransferables(args) {
268
+ const transferables = [];
269
+ const collectFromValue = (value) => {
270
+ if (!value) {
271
+ return;
272
+ }
273
+ if (isTransferableBuffer(value)) {
274
+ transferables.push(value);
275
+ return;
276
+ }
277
+ if (ArrayBuffer.isView(value) && isTransferableBuffer(value.buffer)) {
278
+ transferables.push(value.buffer);
279
+ return;
280
+ }
281
+ if (Array.isArray(value)) {
282
+ value.forEach(collectFromValue);
283
+ }
284
+ };
285
+ args.forEach(collectFromValue);
286
+ return transferables.length ? transferables : null;
287
+ }
288
+ /**
289
+ * Опции для Worker Pool
290
+ * @typedef {Object} WorkerPoolOptions
291
+ * @property {number} [workerCount=4] - Количество workers в pool
292
+ * @property {number} [maxQueueSize=100] - Максимальный размер очереди задач
293
+ * @property {boolean} [autoScale=true] - Автоматическое масштабирование pool
294
+ * @property {number} [idleTimeout=60000] - Таймаут простоя worker (мс)
295
+ */
296
+ /**
297
+ * Статистика Worker Pool
298
+ * @typedef {Object} WorkerPoolStats
299
+ * @property {number} totalWorkers - Всего workers
300
+ * @property {number} activeWorkers - Активные workers
301
+ * @property {number} idleWorkers - Простаивающие workers
302
+ * @property {number} queueSize - Размер очереди
303
+ * @property {number} tasksCompleted - Завершенные задачи
304
+ * @property {number} tasksFailed - Неудачные задачи
305
+ */
306
+ /**
307
+ * Прогресс обработки задачи
308
+ * @typedef {Object} TaskProgress
309
+ * @property {number} processed - Обработано элементов
310
+ * @property {number} total - Всего элементов
311
+ * @property {number} percentage - Процент выполнения
312
+ * @property {number} speed - Скорость обработки (элементов/сек)
313
+ */
314
+ /**
315
+ * Worker Pool для параллельной обработки CSV
316
+ */
317
+ class WorkerPool {
318
+ /**
319
+ * Создает новый Worker Pool
320
+ * @param {string} workerScript - URL скрипта worker
321
+ * @param {WorkerPoolOptions} [options] - Опции pool
322
+ */
323
+ constructor(workerScript, options = {}) {
324
+ if (!WORKERS_SUPPORTED) {
325
+ throw new ValidationError('Web Workers не поддерживаются в этом браузере');
326
+ }
327
+ this.workerScript = workerScript;
328
+ this.options = {
329
+ workerCount: 4,
330
+ maxQueueSize: 100,
331
+ autoScale: true,
332
+ idleTimeout: 60000,
333
+ ...options
334
+ };
335
+ this.workers = [];
336
+ this.taskQueue = [];
337
+ this.activeTasks = new Map();
338
+ this.stats = {
339
+ totalWorkers: 0,
340
+ activeWorkers: 0,
341
+ idleWorkers: 0,
342
+ queueSize: 0,
343
+ tasksCompleted: 0,
344
+ tasksFailed: 0
345
+ };
346
+ this.initializeWorkers();
347
+ }
348
+ /**
349
+ * Инициализация workers
350
+ * @private
351
+ */
352
+ initializeWorkers() {
353
+ const { workerCount } = this.options;
354
+ for (let i = 0; i < workerCount; i++) {
355
+ this.createWorker();
356
+ }
357
+ this.updateStats();
358
+ }
359
+ /**
360
+ * Создает нового worker
361
+ * @private
362
+ */
363
+ createWorker() {
364
+ try {
365
+ const worker = new Worker(this.workerScript, { type: 'module' });
366
+ worker.id = `worker-${this.workers.length}`;
367
+ worker.status = 'idle';
368
+ worker.lastUsed = Date.now();
369
+ worker.taskId = null;
370
+ // Обработчики событий
371
+ worker.onmessage = (event) => this.handleWorkerMessage(worker, event);
372
+ worker.onerror = (error) => this.handleWorkerError(worker, error);
373
+ worker.onmessageerror = (error) => this.handleWorkerMessageError(worker, error);
374
+ this.workers.push(worker);
375
+ this.stats.totalWorkers++;
376
+ this.stats.idleWorkers++;
377
+ return worker;
378
+ }
379
+ catch (error) {
380
+ throw new ConfigurationError(`Не удалось создать worker: ${error.message}`);
381
+ }
382
+ }
383
+ /**
384
+ * Обработка сообщений от worker
385
+ * @private
386
+ */
387
+ handleWorkerMessage(worker, event) {
388
+ const { data } = event;
389
+ if (data.type === 'PROGRESS') {
390
+ this.handleProgress(worker, data);
391
+ }
392
+ else if (data.type === 'RESULT') {
393
+ this.handleResult(worker, data);
394
+ }
395
+ else if (data.type === 'ERROR') {
396
+ this.handleWorkerTaskError(worker, data);
397
+ }
398
+ }
399
+ /**
400
+ * Обработка прогресса задачи
401
+ * @private
402
+ */
403
+ handleProgress(worker, progressData) {
404
+ const taskId = worker.taskId;
405
+ if (taskId && this.activeTasks.has(taskId)) {
406
+ const task = this.activeTasks.get(taskId);
407
+ if (task.onProgress) {
408
+ task.onProgress({
409
+ processed: progressData.processed,
410
+ total: progressData.total,
411
+ percentage: (progressData.processed / progressData.total) * 100,
412
+ speed: progressData.speed || 0
413
+ });
414
+ }
415
+ }
416
+ }
417
+ /**
418
+ * Обработка результата задачи
419
+ * @private
420
+ */
421
+ handleResult(worker, resultData) {
422
+ const taskId = worker.taskId;
423
+ if (taskId && this.activeTasks.has(taskId)) {
424
+ const task = this.activeTasks.get(taskId);
425
+ // Освобождение worker
426
+ worker.status = 'idle';
427
+ worker.lastUsed = Date.now();
428
+ worker.taskId = null;
429
+ this.stats.activeWorkers--;
430
+ this.stats.idleWorkers++;
431
+ // Завершение задачи
432
+ task.resolve(resultData.data);
433
+ this.activeTasks.delete(taskId);
434
+ this.stats.tasksCompleted++;
435
+ // Обработка следующей задачи в очереди
436
+ this.processQueue();
437
+ this.updateStats();
438
+ }
439
+ }
440
+ /**
441
+ * Обработка ошибки задачи
442
+ * @private
443
+ */
444
+ handleWorkerTaskError(worker, errorData) {
445
+ const taskId = worker.taskId;
446
+ if (taskId && this.activeTasks.has(taskId)) {
447
+ const task = this.activeTasks.get(taskId);
448
+ // Освобождение worker
449
+ worker.status = 'idle';
450
+ worker.lastUsed = Date.now();
451
+ worker.taskId = null;
452
+ this.stats.activeWorkers--;
453
+ this.stats.idleWorkers++;
454
+ // Завершение с ошибкой
455
+ const workerError = new Error(errorData.message || 'Ошибка в worker');
456
+ if (errorData.code) {
457
+ workerError.code = errorData.code;
458
+ }
459
+ if (errorData.details) {
460
+ workerError.details = errorData.details;
461
+ }
462
+ task.reject(workerError);
463
+ this.activeTasks.delete(taskId);
464
+ this.stats.tasksFailed++;
465
+ // Обработка следующей задачи
466
+ this.processQueue();
467
+ this.updateStats();
468
+ }
469
+ }
470
+ /**
471
+ * Обработка ошибок worker
472
+ * @private
473
+ */
474
+ handleWorkerError(worker, error) {
475
+ console.error(`Worker ${worker.id} error:`, error);
476
+ // Перезапуск worker
477
+ this.restartWorker(worker);
478
+ }
479
+ /**
480
+ * Обработка ошибок сообщений
481
+ * @private
482
+ */
483
+ handleWorkerMessageError(worker, error) {
484
+ console.error(`Worker ${worker.id} message error:`, error);
485
+ }
486
+ /**
487
+ * Перезапуск worker
488
+ * @private
489
+ */
490
+ restartWorker(worker) {
491
+ const index = this.workers.indexOf(worker);
492
+ if (index !== -1) {
493
+ // Завершение старого worker
494
+ worker.terminate();
495
+ // Удаление из статистики
496
+ if (worker.status === 'active') {
497
+ this.stats.activeWorkers--;
498
+ }
499
+ else {
500
+ this.stats.idleWorkers--;
501
+ }
502
+ this.stats.totalWorkers--;
503
+ // Создание нового worker
504
+ const newWorker = this.createWorker();
505
+ this.workers[index] = newWorker;
506
+ // Перезапуск задачи если была активна
507
+ if (worker.taskId && this.activeTasks.has(worker.taskId)) {
508
+ const task = this.activeTasks.get(worker.taskId);
509
+ this.executeTask(newWorker, task);
510
+ }
511
+ }
512
+ }
513
+ /**
514
+ * Выполнение задачи на worker
515
+ * @private
516
+ */
517
+ executeTask(worker, task) {
518
+ worker.status = 'active';
519
+ worker.lastUsed = Date.now();
520
+ worker.taskId = task.id;
521
+ this.stats.idleWorkers--;
522
+ this.stats.activeWorkers++;
523
+ // Отправка задачи в worker
524
+ const payload = {
525
+ type: 'EXECUTE',
526
+ taskId: task.id,
527
+ method: task.method,
528
+ args: task.args,
529
+ options: task.options
530
+ };
531
+ if (task.transferList && task.transferList.length) {
532
+ worker.postMessage(payload, task.transferList);
533
+ }
534
+ else {
535
+ worker.postMessage(payload);
536
+ }
537
+ }
538
+ /**
539
+ * Обработка очереди задач
540
+ * @private
541
+ */
542
+ processQueue() {
543
+ if (this.taskQueue.length === 0) {
544
+ return;
545
+ }
546
+ while (this.taskQueue.length > 0) {
547
+ const idleWorker = this.workers.find(w => w.status === 'idle');
548
+ if (!idleWorker) {
549
+ if (this.options.autoScale && this.workers.length < this.options.maxQueueSize) {
550
+ this.createWorker();
551
+ continue;
552
+ }
553
+ break;
554
+ }
555
+ const task = this.taskQueue.shift();
556
+ this.stats.queueSize--;
557
+ this.executeTask(idleWorker, task);
558
+ }
559
+ this.updateStats();
560
+ }
561
+ /**
562
+ * Обновление статистики
563
+ * @private
564
+ */
565
+ updateStats() {
566
+ this.stats.queueSize = this.taskQueue.length;
567
+ }
568
+ /**
569
+ * Выполнение задачи через pool
570
+ * @param {string} method - Метод для вызова в worker
571
+ * @param {Array} args - Аргументы метода
572
+ * @param {Object} [options] - Опции задачи
573
+ * @param {Function} [onProgress] - Callback прогресса
574
+ * @returns {Promise<unknown>} Результат выполнения
575
+ */
576
+ async exec(method, args = [], options = {}, onProgress = null) {
577
+ return new Promise((resolve, reject) => {
578
+ // Проверка размера очереди
579
+ if (this.taskQueue.length >= this.options.maxQueueSize) {
580
+ reject(new Error('Очередь задач переполнена'));
581
+ return;
582
+ }
583
+ // Создание задачи
584
+ const taskId = `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
585
+ const { transfer, ...taskOptions } = options || {};
586
+ const transferList = transfer || collectTransferables(args);
587
+ const task = {
588
+ id: taskId,
589
+ method,
590
+ args,
591
+ options: taskOptions,
592
+ transferList,
593
+ onProgress,
594
+ resolve,
595
+ reject,
596
+ createdAt: Date.now()
597
+ };
598
+ // Добавление в очередь
599
+ this.taskQueue.push(task);
600
+ this.stats.queueSize++;
601
+ // Запуск обработки очереди
602
+ this.processQueue();
603
+ this.updateStats();
604
+ });
605
+ }
606
+ /**
607
+ * Получение статистики pool
608
+ * @returns {WorkerPoolStats} Статистика
609
+ */
610
+ getStats() {
611
+ return { ...this.stats };
612
+ }
613
+ /**
614
+ * Очистка простаивающих workers
615
+ */
616
+ cleanupIdleWorkers() {
617
+ const now = Date.now();
618
+ const { idleTimeout } = this.options;
619
+ for (let i = this.workers.length - 1; i >= 0; i--) {
620
+ const worker = this.workers[i];
621
+ if (worker.status === 'idle' && (now - worker.lastUsed) > idleTimeout) {
622
+ // Сохранение минимального количества workers
623
+ if (this.workers.length > 1) {
624
+ worker.terminate();
625
+ this.workers.splice(i, 1);
626
+ this.stats.totalWorkers--;
627
+ this.stats.idleWorkers--;
628
+ }
629
+ }
630
+ }
631
+ }
632
+ /**
633
+ * Завершение всех workers
634
+ */
635
+ terminate() {
636
+ this.workers.forEach(worker => {
637
+ worker.terminate();
638
+ });
639
+ this.workers = [];
640
+ this.taskQueue = [];
641
+ this.activeTasks.clear();
642
+ // Сброс статистики
643
+ this.stats = {
644
+ totalWorkers: 0,
645
+ activeWorkers: 0,
646
+ idleWorkers: 0,
647
+ queueSize: 0,
648
+ tasksCompleted: 0,
649
+ tasksFailed: 0
650
+ };
651
+ }
652
+ }
653
+ /**
654
+ * Создает Worker Pool для обработки CSV
655
+ * @param {WorkerPoolOptions} [options] - Опции pool
656
+ * @returns {WorkerPool} Worker Pool
657
+ */
658
+ function createWorkerPool(options = {}) {
659
+ // Используем встроенный worker скрипт
660
+ const baseUrl = typeof document !== 'undefined'
661
+ ? document.baseURI
662
+ : (typeof self !== 'undefined' && self.location
663
+ ? self.location.href
664
+ : '');
665
+ const workerScript = new URL('./csv-parser.worker.js', baseUrl).href;
666
+ return new WorkerPool(workerScript, options);
667
+ }
668
+ /**
669
+ * Парсит CSV с использованием Web Workers
670
+ * @param {string|File} csvInput - CSV строка или File объект
671
+ * @param {Object} [options] - Опции парсинга
672
+ * @param {Function} [onProgress] - Callback прогресса
673
+ * @returns {Promise<Array<Object>>} JSON данные
674
+ */
675
+ async function parseCSVWithWorker(csvInput, options = {}, onProgress = null) {
676
+ // Создание pool если нужно
677
+ const poolHolder = parseCSVWithWorker;
678
+ if (!poolHolder.pool) {
679
+ poolHolder.pool = createWorkerPool();
680
+ }
681
+ const pool = poolHolder.pool;
682
+ // Подготовка CSV строки
683
+ // ?????????? CSV ??????
684
+ let csvPayload = csvInput;
685
+ let transfer = null;
686
+ if (csvInput instanceof File) {
687
+ const buffer = await readFileAsArrayBuffer(csvInput);
688
+ csvPayload = new Uint8Array(buffer);
689
+ transfer = [buffer];
690
+ }
691
+ else if (csvInput instanceof ArrayBuffer) {
692
+ csvPayload = csvInput;
693
+ transfer = [csvInput];
694
+ }
695
+ else if (ArrayBuffer.isView(csvInput)) {
696
+ csvPayload = csvInput;
697
+ if (csvInput.buffer instanceof ArrayBuffer) {
698
+ transfer = [csvInput.buffer];
699
+ }
700
+ }
701
+ else if (typeof csvInput !== 'string') {
702
+ throw new ValidationError('Input must be a CSV string, File, or ArrayBuffer');
703
+ }
704
+ // ????????? ?????? ????? pool
705
+ const execOptions = transfer ? { transfer } : {};
706
+ return pool.exec('parseCSV', [csvPayload, options], execOptions, onProgress);
707
+ }
708
+ /**
709
+ * Чтение файла как текст
710
+ * @private
711
+ */
712
+ async function readFileAsArrayBuffer(file) {
713
+ return new Promise((resolve, reject) => {
714
+ const reader = new FileReader();
715
+ reader.onload = (event) => resolve(event.target.result);
716
+ reader.onerror = (error) => reject(error);
717
+ reader.readAsArrayBuffer(file);
718
+ });
719
+ }
720
+ // Экспорт для Node.js совместимости
721
+ if (typeof module !== 'undefined' && module.exports) {
722
+ module.exports = {
723
+ WorkerPool,
724
+ createWorkerPool,
725
+ parseCSVWithWorker
726
+ };
727
+ }
728
+
729
+ var workerPool = /*#__PURE__*/Object.freeze({
730
+ __proto__: null,
731
+ WorkerPool: WorkerPool,
732
+ createWorkerPool: createWorkerPool,
733
+ parseCSVWithWorker: parseCSVWithWorker
734
+ });
735
+
736
+ // Расширение Web Workers для jtcsv
737
+ // Дополнительный модуль для параллельной обработки больших CSV
738
+ async function createWorkerPoolLazy(options = {}) {
739
+ const mod = await Promise.resolve().then(function () { return workerPool; });
740
+ return mod.createWorkerPool(options);
741
+ }
742
+ async function parseCSVWithWorkerLazy(csvInput, options = {}, onProgress = null) {
743
+ const mod = await Promise.resolve().then(function () { return workerPool; });
744
+ return mod.parseCSVWithWorker(csvInput, options, onProgress);
745
+ }
746
+ const jtcsvWorkers = {
747
+ createWorkerPool,
748
+ parseCSVWithWorker,
749
+ createWorkerPoolLazy,
750
+ parseCSVWithWorkerLazy
751
+ };
752
+ // Экспорт
753
+ if (typeof module !== 'undefined' && module.exports) {
754
+ module.exports = jtcsvWorkers;
755
+ }
756
+ else if (typeof define === 'function' && define.amd) {
757
+ define([], () => jtcsvWorkers);
758
+ }
759
+ else if (typeof window !== 'undefined' && window.jtcsv) {
760
+ // Расширяем глобальный jtcsv, если он существует
761
+ Object.assign(window.jtcsv, jtcsvWorkers);
762
+ }
763
+
764
+ exports.createWorkerPool = createWorkerPool;
765
+ exports.createWorkerPoolLazy = createWorkerPoolLazy;
766
+ exports.default = jtcsvWorkers;
767
+ exports.parseCSVWithWorker = parseCSVWithWorker;
768
+ exports.parseCSVWithWorkerLazy = parseCSVWithWorkerLazy;
769
+
770
+ Object.defineProperty(exports, '__esModule', { value: true });
771
+
772
+ }));
773
+ //# sourceMappingURL=jtcsv-workers.umd.js.map