jtcsv 2.2.8 → 3.1.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 (246) hide show
  1. package/README.md +204 -115
  2. package/bin/jtcsv.ts +2612 -0
  3. package/browser.d.ts +142 -0
  4. package/dist/benchmark.js +446 -0
  5. package/dist/benchmark.js.map +1 -0
  6. package/dist/bin/jtcsv.js +1940 -0
  7. package/dist/bin/jtcsv.js.map +1 -0
  8. package/dist/csv-to-json.js +1262 -0
  9. package/dist/csv-to-json.js.map +1 -0
  10. package/dist/errors.js +291 -0
  11. package/dist/errors.js.map +1 -0
  12. package/dist/eslint.config.js +147 -0
  13. package/dist/eslint.config.js.map +1 -0
  14. package/dist/index-core.js +95 -0
  15. package/dist/index-core.js.map +1 -0
  16. package/dist/index.js +93 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/json-save.js +229 -0
  19. package/dist/json-save.js.map +1 -0
  20. package/dist/json-to-csv.js +576 -0
  21. package/dist/json-to-csv.js.map +1 -0
  22. package/dist/jtcsv-core.cjs.js +1736 -0
  23. package/dist/jtcsv-core.cjs.js.map +1 -0
  24. package/dist/jtcsv-core.esm.js +1708 -0
  25. package/dist/jtcsv-core.esm.js.map +1 -0
  26. package/dist/jtcsv-core.umd.js +1742 -0
  27. package/dist/jtcsv-core.umd.js.map +1 -0
  28. package/dist/jtcsv-full.cjs.js +2241 -0
  29. package/dist/jtcsv-full.cjs.js.map +1 -0
  30. package/dist/jtcsv-full.esm.js +2209 -0
  31. package/dist/jtcsv-full.esm.js.map +1 -0
  32. package/dist/jtcsv-full.umd.js +2247 -0
  33. package/dist/jtcsv-full.umd.js.map +1 -0
  34. package/dist/jtcsv-workers.esm.js +768 -0
  35. package/dist/jtcsv-workers.esm.js.map +1 -0
  36. package/dist/jtcsv-workers.umd.js +782 -0
  37. package/dist/jtcsv-workers.umd.js.map +1 -0
  38. package/dist/jtcsv.cjs.js +1996 -2048
  39. package/dist/jtcsv.cjs.js.map +1 -1
  40. package/dist/jtcsv.esm.js +1992 -2048
  41. package/dist/jtcsv.esm.js.map +1 -1
  42. package/dist/jtcsv.umd.js +2157 -2209
  43. package/dist/jtcsv.umd.js.map +1 -1
  44. package/dist/plugins/express-middleware/index.js +350 -0
  45. package/dist/plugins/express-middleware/index.js.map +1 -0
  46. package/dist/plugins/fastify-plugin/index.js +315 -0
  47. package/dist/plugins/fastify-plugin/index.js.map +1 -0
  48. package/dist/plugins/hono/index.js +111 -0
  49. package/dist/plugins/hono/index.js.map +1 -0
  50. package/dist/plugins/nestjs/index.js +112 -0
  51. package/dist/plugins/nestjs/index.js.map +1 -0
  52. package/dist/plugins/nuxt/index.js +53 -0
  53. package/dist/plugins/nuxt/index.js.map +1 -0
  54. package/dist/plugins/remix/index.js +133 -0
  55. package/dist/plugins/remix/index.js.map +1 -0
  56. package/dist/plugins/sveltekit/index.js +155 -0
  57. package/dist/plugins/sveltekit/index.js.map +1 -0
  58. package/dist/plugins/trpc/index.js +136 -0
  59. package/dist/plugins/trpc/index.js.map +1 -0
  60. package/dist/run-demo.js +49 -0
  61. package/dist/run-demo.js.map +1 -0
  62. package/dist/src/browser/browser-functions.js +193 -0
  63. package/dist/src/browser/browser-functions.js.map +1 -0
  64. package/dist/src/browser/core.js +123 -0
  65. package/dist/src/browser/core.js.map +1 -0
  66. package/dist/src/browser/csv-to-json-browser.js +353 -0
  67. package/dist/src/browser/csv-to-json-browser.js.map +1 -0
  68. package/dist/src/browser/errors-browser.js +219 -0
  69. package/dist/src/browser/errors-browser.js.map +1 -0
  70. package/dist/src/browser/extensions/plugins.js +106 -0
  71. package/dist/src/browser/extensions/plugins.js.map +1 -0
  72. package/dist/src/browser/extensions/workers.js +66 -0
  73. package/dist/src/browser/extensions/workers.js.map +1 -0
  74. package/dist/src/browser/index.js +140 -0
  75. package/dist/src/browser/index.js.map +1 -0
  76. package/dist/src/browser/json-to-csv-browser.js +225 -0
  77. package/dist/src/browser/json-to-csv-browser.js.map +1 -0
  78. package/dist/src/browser/streams.js +340 -0
  79. package/dist/src/browser/streams.js.map +1 -0
  80. package/dist/src/browser/workers/csv-parser.worker.js +264 -0
  81. package/dist/src/browser/workers/csv-parser.worker.js.map +1 -0
  82. package/dist/src/browser/workers/worker-pool.js +338 -0
  83. package/dist/src/browser/workers/worker-pool.js.map +1 -0
  84. package/dist/src/core/delimiter-cache.js +196 -0
  85. package/dist/src/core/delimiter-cache.js.map +1 -0
  86. package/dist/src/core/node-optimizations.js +279 -0
  87. package/dist/src/core/node-optimizations.js.map +1 -0
  88. package/dist/src/core/plugin-system.js +399 -0
  89. package/dist/src/core/plugin-system.js.map +1 -0
  90. package/dist/src/core/transform-hooks.js +348 -0
  91. package/dist/src/core/transform-hooks.js.map +1 -0
  92. package/dist/src/engines/fast-path-engine-new.js +262 -0
  93. package/dist/src/engines/fast-path-engine-new.js.map +1 -0
  94. package/dist/src/engines/fast-path-engine.js +671 -0
  95. package/dist/src/engines/fast-path-engine.js.map +1 -0
  96. package/dist/src/errors.js +18 -0
  97. package/dist/src/errors.js.map +1 -0
  98. package/dist/src/formats/ndjson-parser.js +332 -0
  99. package/dist/src/formats/ndjson-parser.js.map +1 -0
  100. package/dist/src/formats/tsv-parser.js +230 -0
  101. package/dist/src/formats/tsv-parser.js.map +1 -0
  102. package/dist/src/index-with-plugins.js +259 -0
  103. package/dist/src/index-with-plugins.js.map +1 -0
  104. package/dist/src/types/index.js +3 -0
  105. package/dist/src/types/index.js.map +1 -0
  106. package/dist/src/utils/bom-utils.js +267 -0
  107. package/dist/src/utils/bom-utils.js.map +1 -0
  108. package/dist/src/utils/encoding-support.js +77 -0
  109. package/dist/src/utils/encoding-support.js.map +1 -0
  110. package/dist/src/utils/schema-validator.js +609 -0
  111. package/dist/src/utils/schema-validator.js.map +1 -0
  112. package/dist/src/utils/transform-loader.js +281 -0
  113. package/dist/src/utils/transform-loader.js.map +1 -0
  114. package/dist/src/utils/validators.js +40 -0
  115. package/dist/src/utils/validators.js.map +1 -0
  116. package/dist/src/utils/zod-adapter.js +144 -0
  117. package/dist/src/utils/zod-adapter.js.map +1 -0
  118. package/dist/src/web-server/index.js +648 -0
  119. package/dist/src/web-server/index.js.map +1 -0
  120. package/dist/src/workers/csv-multithreaded.js +211 -0
  121. package/dist/src/workers/csv-multithreaded.js.map +1 -0
  122. package/dist/src/workers/csv-parser.worker.js +179 -0
  123. package/dist/src/workers/csv-parser.worker.js.map +1 -0
  124. package/dist/src/workers/worker-pool.js +228 -0
  125. package/dist/src/workers/worker-pool.js.map +1 -0
  126. package/dist/stream-csv-to-json.js +665 -0
  127. package/dist/stream-csv-to-json.js.map +1 -0
  128. package/dist/stream-json-to-csv.js +389 -0
  129. package/dist/stream-json-to-csv.js.map +1 -0
  130. package/examples/advanced/conditional-transformations.ts +446 -0
  131. package/examples/advanced/csv-parser.worker.ts +89 -0
  132. package/examples/advanced/nested-objects-example.ts +306 -0
  133. package/examples/advanced/performance-optimization.ts +504 -0
  134. package/examples/advanced/run-demo-server.ts +116 -0
  135. package/examples/advanced/web-worker-usage.html +874 -0
  136. package/examples/async-multithreaded-example.ts +335 -0
  137. package/examples/cli-advanced-usage.md +290 -0
  138. package/examples/{cli-batch-processing.js → cli-batch-processing.ts} +38 -38
  139. package/examples/{cli-tool.js → cli-tool.ts} +5 -8
  140. package/examples/{error-handling.js → error-handling.ts} +356 -324
  141. package/examples/{express-api.js → express-api.ts} +161 -164
  142. package/examples/{large-dataset-example.js → large-dataset-example.ts} +201 -182
  143. package/examples/{ndjson-processing.js → ndjson-processing.ts} +456 -434
  144. package/examples/{plugin-excel-exporter.js → plugin-excel-exporter.ts} +6 -7
  145. package/examples/react-integration.tsx +637 -0
  146. package/examples/{schema-validation.js → schema-validation.ts} +2 -2
  147. package/examples/simple-usage.ts +194 -0
  148. package/examples/{streaming-example.js → streaming-example.ts} +12 -12
  149. package/index.d.ts +187 -18
  150. package/package.json +75 -81
  151. package/plugins.d.ts +37 -0
  152. package/schema.d.ts +103 -0
  153. package/src/browser/browser-functions.ts +402 -0
  154. package/src/browser/core.ts +152 -0
  155. package/src/browser/csv-to-json-browser.d.ts +3 -0
  156. package/src/browser/csv-to-json-browser.ts +494 -0
  157. package/src/browser/{errors-browser.js → errors-browser.ts} +305 -197
  158. package/src/browser/extensions/plugins.ts +93 -0
  159. package/src/browser/extensions/workers.ts +39 -0
  160. package/src/browser/globals.d.ts +5 -0
  161. package/src/browser/index.ts +192 -0
  162. package/src/browser/json-to-csv-browser.d.ts +3 -0
  163. package/src/browser/json-to-csv-browser.ts +338 -0
  164. package/src/browser/streams.ts +403 -0
  165. package/src/browser/workers/{csv-parser.worker.js → csv-parser.worker.ts} +3 -3
  166. package/src/browser/workers/{worker-pool.js → worker-pool.ts} +51 -30
  167. package/src/core/delimiter-cache.ts +320 -0
  168. package/src/core/{node-optimizations.js → node-optimizations.ts} +448 -407
  169. package/src/core/plugin-system.ts +588 -0
  170. package/src/core/transform-hooks.ts +566 -0
  171. package/src/engines/{fast-path-engine-new.js → fast-path-engine-new.ts} +11 -2
  172. package/src/engines/{fast-path-engine.js → fast-path-engine.ts} +79 -53
  173. package/src/errors.ts +1 -0
  174. package/src/formats/{ndjson-parser.js → ndjson-parser.ts} +24 -16
  175. package/src/formats/{tsv-parser.js → tsv-parser.ts} +18 -17
  176. package/src/{index-with-plugins.js → index-with-plugins.ts} +381 -357
  177. package/src/types/index.ts +275 -0
  178. package/src/utils/bom-utils.ts +373 -0
  179. package/src/utils/encoding-support.ts +155 -0
  180. package/src/utils/{schema-validator.js → schema-validator.ts} +814 -589
  181. package/src/utils/transform-loader.ts +389 -0
  182. package/src/utils/validators.ts +35 -0
  183. package/src/utils/zod-adapter.ts +280 -0
  184. package/src/web-server/{index.js → index.ts} +19 -19
  185. package/src/workers/csv-multithreaded.ts +310 -0
  186. package/src/workers/csv-parser.worker.ts +227 -0
  187. package/src/workers/worker-pool.ts +409 -0
  188. package/bin/jtcsv.js +0 -2462
  189. package/csv-to-json.js +0 -688
  190. package/errors.js +0 -208
  191. package/examples/simple-usage.js +0 -282
  192. package/index.js +0 -68
  193. package/json-save.js +0 -254
  194. package/json-to-csv.js +0 -526
  195. package/plugins/README.md +0 -91
  196. package/plugins/express-middleware/README.md +0 -64
  197. package/plugins/express-middleware/example.js +0 -136
  198. package/plugins/express-middleware/index.d.ts +0 -114
  199. package/plugins/express-middleware/index.js +0 -360
  200. package/plugins/express-middleware/package.json +0 -52
  201. package/plugins/fastify-plugin/index.js +0 -406
  202. package/plugins/fastify-plugin/package.json +0 -55
  203. package/plugins/hono/README.md +0 -28
  204. package/plugins/hono/index.d.ts +0 -12
  205. package/plugins/hono/index.js +0 -36
  206. package/plugins/hono/package.json +0 -35
  207. package/plugins/nestjs/README.md +0 -35
  208. package/plugins/nestjs/index.d.ts +0 -25
  209. package/plugins/nestjs/index.js +0 -77
  210. package/plugins/nestjs/package.json +0 -37
  211. package/plugins/nextjs-api/README.md +0 -57
  212. package/plugins/nextjs-api/examples/ConverterComponent.jsx +0 -386
  213. package/plugins/nextjs-api/examples/api-convert.js +0 -69
  214. package/plugins/nextjs-api/index.js +0 -387
  215. package/plugins/nextjs-api/package.json +0 -63
  216. package/plugins/nextjs-api/route.js +0 -371
  217. package/plugins/nuxt/README.md +0 -24
  218. package/plugins/nuxt/index.js +0 -21
  219. package/plugins/nuxt/package.json +0 -35
  220. package/plugins/nuxt/runtime/composables/useJtcsv.js +0 -6
  221. package/plugins/nuxt/runtime/plugin.js +0 -6
  222. package/plugins/remix/README.md +0 -26
  223. package/plugins/remix/index.d.ts +0 -16
  224. package/plugins/remix/index.js +0 -62
  225. package/plugins/remix/package.json +0 -35
  226. package/plugins/sveltekit/README.md +0 -28
  227. package/plugins/sveltekit/index.d.ts +0 -17
  228. package/plugins/sveltekit/index.js +0 -54
  229. package/plugins/sveltekit/package.json +0 -33
  230. package/plugins/trpc/README.md +0 -25
  231. package/plugins/trpc/index.d.ts +0 -7
  232. package/plugins/trpc/index.js +0 -32
  233. package/plugins/trpc/package.json +0 -34
  234. package/src/browser/browser-functions.js +0 -219
  235. package/src/browser/csv-to-json-browser.js +0 -700
  236. package/src/browser/index.js +0 -113
  237. package/src/browser/json-to-csv-browser.js +0 -309
  238. package/src/browser/streams.js +0 -393
  239. package/src/core/delimiter-cache.js +0 -186
  240. package/src/core/plugin-system.js +0 -476
  241. package/src/core/transform-hooks.js +0 -350
  242. package/src/errors.js +0 -26
  243. package/src/utils/transform-loader.js +0 -205
  244. package/stream-csv-to-json.js +0 -542
  245. package/stream-json-to-csv.js +0 -464
  246. /package/examples/{web-workers-advanced.js → web-workers-advanced.ts} +0 -0
@@ -0,0 +1,2209 @@
1
+ // Система ошибок для браузерной версии jtcsv
2
+ // Адаптирована для работы без Node.js специфичных API
3
+ /**
4
+ * Базовый класс ошибки jtcsv
5
+ */
6
+ class JTCSVError extends Error {
7
+ constructor(message, code = 'JTCSV_ERROR', details = {}) {
8
+ super(message);
9
+ this.name = 'JTCSVError';
10
+ this.code = code;
11
+ this.details = details;
12
+ this.hint = details.hint;
13
+ this.docs = details.docs;
14
+ this.context = details.context;
15
+ // Сохранение stack trace
16
+ if (Error.captureStackTrace) {
17
+ Error.captureStackTrace(this, JTCSVError);
18
+ }
19
+ }
20
+ }
21
+ /**
22
+ * Ошибка валидации
23
+ */
24
+ class ValidationError extends JTCSVError {
25
+ constructor(message, details = {}) {
26
+ super(message, 'VALIDATION_ERROR', details);
27
+ this.name = 'ValidationError';
28
+ }
29
+ }
30
+ /**
31
+ * Ошибка безопасности
32
+ */
33
+ class SecurityError extends JTCSVError {
34
+ constructor(message, details = {}) {
35
+ super(message, 'SECURITY_ERROR', details);
36
+ this.name = 'SecurityError';
37
+ }
38
+ }
39
+ /**
40
+ * Ошибка файловой системы (адаптирована для браузера)
41
+ */
42
+ class FileSystemError extends JTCSVError {
43
+ constructor(message, originalError, details = {}) {
44
+ super(message, 'FILE_SYSTEM_ERROR', { ...details, originalError });
45
+ this.name = 'FileSystemError';
46
+ if (originalError && originalError.code) {
47
+ this.code = originalError.code;
48
+ }
49
+ }
50
+ }
51
+ /**
52
+ * Ошибка парсинга
53
+ */
54
+ class ParsingError extends JTCSVError {
55
+ constructor(message, lineNumber, details = {}) {
56
+ super(message, 'PARSING_ERROR', { ...details, lineNumber });
57
+ this.name = 'ParsingError';
58
+ this.lineNumber = lineNumber;
59
+ }
60
+ }
61
+ /**
62
+ * Ошибка превышения лимита
63
+ */
64
+ class LimitError extends JTCSVError {
65
+ constructor(message, limit, actual, details = {}) {
66
+ super(message, 'LIMIT_ERROR', { ...details, limit, actual });
67
+ this.name = 'LimitError';
68
+ this.limit = limit;
69
+ this.actual = actual;
70
+ }
71
+ }
72
+ /**
73
+ * Ошибка конфигурации
74
+ */
75
+ class ConfigurationError extends JTCSVError {
76
+ constructor(message, details = {}) {
77
+ super(message, 'CONFIGURATION_ERROR', details);
78
+ this.name = 'ConfigurationError';
79
+ }
80
+ }
81
+ /**
82
+ * Коды ошибок
83
+ */
84
+ const ERROR_CODES = {
85
+ JTCSV_ERROR: 'JTCSV_ERROR',
86
+ VALIDATION_ERROR: 'VALIDATION_ERROR',
87
+ SECURITY_ERROR: 'SECURITY_ERROR',
88
+ FILE_SYSTEM_ERROR: 'FILE_SYSTEM_ERROR',
89
+ PARSING_ERROR: 'PARSING_ERROR',
90
+ LIMIT_ERROR: 'LIMIT_ERROR',
91
+ CONFIGURATION_ERROR: 'CONFIGURATION_ERROR',
92
+ INVALID_INPUT: 'INVALID_INPUT',
93
+ SECURITY_VIOLATION: 'SECURITY_VIOLATION',
94
+ FILE_NOT_FOUND: 'FILE_NOT_FOUND',
95
+ PARSE_FAILED: 'PARSE_FAILED',
96
+ SIZE_LIMIT: 'SIZE_LIMIT',
97
+ INVALID_CONFIG: 'INVALID_CONFIG',
98
+ UNKNOWN_ERROR: 'UNKNOWN_ERROR'
99
+ };
100
+ /**
101
+ * Безопасное выполнение функции с обработкой ошибок
102
+ *
103
+ * @param fn - Функция для выполнения
104
+ * @param errorCode - Код ошибки по умолчанию
105
+ * @param errorDetails - Детали ошибки
106
+ * @returns Результат выполнения функции
107
+ */
108
+ function safeExecute(fn, errorCode = 'UNKNOWN_ERROR', errorDetails = {}) {
109
+ try {
110
+ if (typeof fn === 'function') {
111
+ return fn();
112
+ }
113
+ throw new ValidationError('Function expected');
114
+ }
115
+ catch (error) {
116
+ // Если ошибка уже является JTCSVError, перебросить её
117
+ if (error instanceof JTCSVError) {
118
+ throw error;
119
+ }
120
+ // Определить тип ошибки на основе сообщения или кода
121
+ let enhancedError;
122
+ const errorMessage = error.message || String(error);
123
+ if (errorMessage.includes('validation') || errorMessage.includes('Validation')) {
124
+ enhancedError = new ValidationError(errorMessage, { ...errorDetails, originalError: error });
125
+ }
126
+ else if (errorMessage.includes('security') || errorMessage.includes('Security')) {
127
+ enhancedError = new SecurityError(errorMessage, { ...errorDetails, originalError: error });
128
+ }
129
+ else if (errorMessage.includes('parsing') || errorMessage.includes('Parsing')) {
130
+ enhancedError = new ParsingError(errorMessage, undefined, { ...errorDetails, originalError: error });
131
+ }
132
+ else if (errorMessage.includes('limit') || errorMessage.includes('Limit')) {
133
+ enhancedError = new LimitError(errorMessage, null, null, { ...errorDetails, originalError: error });
134
+ }
135
+ else if (errorMessage.includes('configuration') || errorMessage.includes('Configuration')) {
136
+ enhancedError = new ConfigurationError(errorMessage, { ...errorDetails, originalError: error });
137
+ }
138
+ else if (errorMessage.includes('file') || errorMessage.includes('File')) {
139
+ enhancedError = new FileSystemError(errorMessage, error, errorDetails);
140
+ }
141
+ else {
142
+ // Общая ошибка
143
+ enhancedError = new JTCSVError(errorMessage, errorCode, { ...errorDetails, originalError: error });
144
+ }
145
+ // Сохранить оригинальный stack trace если возможно
146
+ if (error.stack) {
147
+ enhancedError.stack = error.stack;
148
+ }
149
+ throw enhancedError;
150
+ }
151
+ }
152
+ /**
153
+ * Асинхронная версия safeExecute
154
+ */
155
+ async function safeExecuteAsync(fn, errorCode = 'UNKNOWN_ERROR', errorDetails = {}) {
156
+ try {
157
+ if (typeof fn === 'function') {
158
+ return await fn();
159
+ }
160
+ throw new ValidationError('Function expected');
161
+ }
162
+ catch (error) {
163
+ // Если ошибка уже является JTCSVError, перебросить её
164
+ if (error instanceof JTCSVError) {
165
+ throw error;
166
+ }
167
+ // Определить тип ошибки
168
+ let enhancedError;
169
+ const errorMessage = error.message || String(error);
170
+ if (errorMessage.includes('validation') || errorMessage.includes('Validation')) {
171
+ enhancedError = new ValidationError(errorMessage, { ...errorDetails, originalError: error });
172
+ }
173
+ else if (errorMessage.includes('security') || errorMessage.includes('Security')) {
174
+ enhancedError = new SecurityError(errorMessage, { ...errorDetails, originalError: error });
175
+ }
176
+ else if (errorMessage.includes('parsing') || errorMessage.includes('Parsing')) {
177
+ enhancedError = new ParsingError(errorMessage, undefined, { ...errorDetails, originalError: error });
178
+ }
179
+ else if (errorMessage.includes('limit') || errorMessage.includes('Limit')) {
180
+ enhancedError = new LimitError(errorMessage, null, null, { ...errorDetails, originalError: error });
181
+ }
182
+ else if (errorMessage.includes('configuration') || errorMessage.includes('Configuration')) {
183
+ enhancedError = new ConfigurationError(errorMessage, { ...errorDetails, originalError: error });
184
+ }
185
+ else if (errorMessage.includes('file') || errorMessage.includes('File')) {
186
+ enhancedError = new FileSystemError(errorMessage, error, errorDetails);
187
+ }
188
+ else {
189
+ enhancedError = new JTCSVError(errorMessage, errorCode, { ...errorDetails, originalError: error });
190
+ }
191
+ if (error.stack) {
192
+ enhancedError.stack = error.stack;
193
+ }
194
+ throw enhancedError;
195
+ }
196
+ }
197
+ /**
198
+ * Создать сообщение об ошибке
199
+ */
200
+ function createErrorMessage(error, includeStack = false) {
201
+ let message = error.message || 'Unknown error';
202
+ if (error instanceof JTCSVError) {
203
+ message = `[${error.code}] ${message}`;
204
+ if (error instanceof ParsingError && error.lineNumber) {
205
+ message += ` (line ${error.lineNumber})`;
206
+ }
207
+ if (error instanceof LimitError && error.limit && error.actual) {
208
+ message += ` (limit: ${error.limit}, actual: ${error.actual})`;
209
+ }
210
+ if (error.hint) {
211
+ message += `\nHint: ${error.hint}`;
212
+ }
213
+ if (error.docs) {
214
+ message += `\nDocs: ${error.docs}`;
215
+ }
216
+ }
217
+ if (includeStack && error.stack) {
218
+ message += `\n${error.stack}`;
219
+ }
220
+ return message;
221
+ }
222
+ /**
223
+ * Обработка ошибки
224
+ */
225
+ function handleError(error, options = {}) {
226
+ const { log = true, throw: shouldThrow = false, format = true } = options;
227
+ const message = format ? createErrorMessage(error) : error.message;
228
+ if (log) {
229
+ console.error(`[jtcsv] ${message}`);
230
+ if (error instanceof JTCSVError && error.details) {
231
+ console.error('Error details:', error.details);
232
+ }
233
+ }
234
+ if (shouldThrow) {
235
+ throw error;
236
+ }
237
+ return message;
238
+ }
239
+ // Экспорт для Node.js совместимости
240
+ if (typeof module !== 'undefined' && module.exports) {
241
+ module.exports = {
242
+ JTCSVError,
243
+ ValidationError,
244
+ SecurityError,
245
+ FileSystemError,
246
+ ParsingError,
247
+ LimitError,
248
+ ConfigurationError,
249
+ ERROR_CODES,
250
+ safeExecute,
251
+ safeExecuteAsync,
252
+ createErrorMessage,
253
+ handleError
254
+ };
255
+ }
256
+
257
+ // Браузерная версия JSON to CSV конвертера
258
+ // Адаптирована для работы в браузере без Node.js API
259
+ /**
260
+ * Валидация входных данных и опций
261
+ * @private
262
+ */
263
+ function validateInput(data, options) {
264
+ // Validate data
265
+ if (!Array.isArray(data)) {
266
+ throw new ValidationError('Input data must be an array');
267
+ }
268
+ // Validate options
269
+ if (options && typeof options !== 'object') {
270
+ throw new ConfigurationError('Options must be an object');
271
+ }
272
+ // Validate delimiter
273
+ if (options?.delimiter && typeof options.delimiter !== 'string') {
274
+ throw new ConfigurationError('Delimiter must be a string');
275
+ }
276
+ if (options?.delimiter && options.delimiter.length !== 1) {
277
+ throw new ConfigurationError('Delimiter must be a single character');
278
+ }
279
+ // Validate renameMap
280
+ if (options?.renameMap && typeof options.renameMap !== 'object') {
281
+ throw new ConfigurationError('renameMap must be an object');
282
+ }
283
+ // Validate maxRecords
284
+ if (options && options.maxRecords !== undefined) {
285
+ if (typeof options.maxRecords !== 'number' || options.maxRecords <= 0) {
286
+ throw new ConfigurationError('maxRecords must be a positive number');
287
+ }
288
+ }
289
+ // Validate preventCsvInjection
290
+ if (options?.preventCsvInjection !== undefined && typeof options.preventCsvInjection !== 'boolean') {
291
+ throw new ConfigurationError('preventCsvInjection must be a boolean');
292
+ }
293
+ // Validate rfc4180Compliant
294
+ if (options?.rfc4180Compliant !== undefined && typeof options.rfc4180Compliant !== 'boolean') {
295
+ throw new ConfigurationError('rfc4180Compliant must be a boolean');
296
+ }
297
+ if (options?.normalizeQuotes !== undefined && typeof options.normalizeQuotes !== 'boolean') {
298
+ throw new ConfigurationError('normalizeQuotes must be a boolean');
299
+ }
300
+ return true;
301
+ }
302
+ /**
303
+ * Экранирование CSV значений для предотвращения инъекций
304
+ * @private
305
+ */
306
+ function escapeCsvValue(value, preventInjection = true) {
307
+ if (value === null || value === undefined) {
308
+ return '';
309
+ }
310
+ const str = String(value);
311
+ const isPotentialFormula = (input) => {
312
+ let idx = 0;
313
+ while (idx < input.length) {
314
+ const code = input.charCodeAt(idx);
315
+ if (code === 32 || code === 9 || code === 10 || code === 13 || code === 0xfeff) {
316
+ idx++;
317
+ continue;
318
+ }
319
+ break;
320
+ }
321
+ if (idx < input.length && (input[idx] === '"' || input[idx] === "'")) {
322
+ idx++;
323
+ while (idx < input.length) {
324
+ const code = input.charCodeAt(idx);
325
+ if (code === 32 || code === 9) {
326
+ idx++;
327
+ continue;
328
+ }
329
+ break;
330
+ }
331
+ }
332
+ if (idx >= input.length) {
333
+ return false;
334
+ }
335
+ const char = input[idx];
336
+ return char === '=' || char === '+' || char === '-' || char === '@';
337
+ };
338
+ // Экранирование формул для предотвращения CSV инъекций
339
+ if (preventInjection && isPotentialFormula(str)) {
340
+ return "'" + str;
341
+ }
342
+ // Экранирование кавычек и переносов строк
343
+ if (str.includes('"') || str.includes('\n') || str.includes('\r') || str.includes(',')) {
344
+ return '"' + str.replace(/"/g, '""') + '"';
345
+ }
346
+ return str;
347
+ }
348
+ function normalizeQuotesInField$2(value) {
349
+ // Не нормализуем кавычки в JSON-строках - это ломает структуру JSON
350
+ // Проверяем, выглядит ли значение как JSON (объект или массив)
351
+ if ((value.startsWith('{') && value.endsWith('}')) ||
352
+ (value.startsWith('[') && value.endsWith(']'))) {
353
+ return value; // Возвращаем как есть для JSON
354
+ }
355
+ let normalized = value.replace(/"{2,}/g, '"');
356
+ // Убираем правило, которое ломает JSON: не заменяем "," на ","
357
+ // normalized = normalized.replace(/"\s*,\s*"/g, ',');
358
+ normalized = normalized.replace(/"\n/g, '\n').replace(/\n"/g, '\n');
359
+ if (normalized.length >= 2 && normalized.startsWith('"') && normalized.endsWith('"')) {
360
+ normalized = normalized.slice(1, -1);
361
+ }
362
+ return normalized;
363
+ }
364
+ function normalizePhoneValue$2(value) {
365
+ const trimmed = value.trim();
366
+ if (trimmed === '') {
367
+ return trimmed;
368
+ }
369
+ return trimmed.replace(/["'\\]/g, '');
370
+ }
371
+ function normalizeValueForCsv$1(value, key, normalizeQuotes) {
372
+ if (!normalizeQuotes || typeof value !== 'string') {
373
+ return value;
374
+ }
375
+ const base = normalizeQuotesInField$2(value);
376
+ if (!key) {
377
+ return base;
378
+ }
379
+ const phoneKeys = new Set(['phone', 'phonenumber', 'phone_number', 'tel', 'telephone']);
380
+ if (phoneKeys.has(String(key).toLowerCase())) {
381
+ return normalizePhoneValue$2(base);
382
+ }
383
+ return base;
384
+ }
385
+ /**
386
+ * Извлечение всех уникальных ключей из массива объектов
387
+ * @private
388
+ */
389
+ function extractAllKeys(data) {
390
+ const keys = new Set();
391
+ for (const item of data) {
392
+ if (item && typeof item === 'object') {
393
+ Object.keys(item).forEach(key => keys.add(key));
394
+ }
395
+ }
396
+ return Array.from(keys);
397
+ }
398
+ /**
399
+ * Конвертация массива объектов в CSV строку
400
+ *
401
+ * @param data - Массив объектов для конвертации
402
+ * @param options - Опции конвертации
403
+ * @returns CSV строка
404
+ */
405
+ function jsonToCsv$1(data, options = {}) {
406
+ return safeExecute(() => {
407
+ validateInput(data, options);
408
+ if (data.length === 0) {
409
+ return '';
410
+ }
411
+ // Настройки по умолчанию
412
+ const delimiter = options.delimiter || ';';
413
+ const includeHeaders = options.includeHeaders !== false;
414
+ const maxRecords = options.maxRecords || data.length;
415
+ const preventInjection = options.preventCsvInjection !== false;
416
+ const rfc4180Compliant = options.rfc4180Compliant !== false;
417
+ const normalizeQuotes = options.normalizeQuotes !== false;
418
+ // Ограничение количества записей
419
+ const limitedData = data.slice(0, maxRecords);
420
+ // Извлечение всех ключей
421
+ const allKeys = extractAllKeys(limitedData);
422
+ // Применение renameMap если есть
423
+ const renameMap = options.renameMap || {};
424
+ const finalKeys = allKeys.map(key => renameMap[key] || key);
425
+ // Создание CSV строки
426
+ const lines = [];
427
+ // Заголовки
428
+ if (includeHeaders) {
429
+ const headerLine = finalKeys.map(key => escapeCsvValue(key, preventInjection)).join(delimiter);
430
+ lines.push(headerLine);
431
+ }
432
+ // Данные
433
+ for (const item of limitedData) {
434
+ const rowValues = allKeys.map(key => {
435
+ const value = item?.[key];
436
+ const normalized = normalizeValueForCsv$1(value, key, normalizeQuotes);
437
+ return escapeCsvValue(normalized, preventInjection);
438
+ });
439
+ lines.push(rowValues.join(delimiter));
440
+ }
441
+ // RFC 4180 compliance: CRLF line endings
442
+ if (rfc4180Compliant) {
443
+ return lines.join('\r\n');
444
+ }
445
+ return lines.join('\n');
446
+ });
447
+ }
448
+ /**
449
+ * Асинхронная версия jsonToCsv
450
+ */
451
+ async function jsonToCsvAsync$1(data, options = {}) {
452
+ return jsonToCsv$1(data, options);
453
+ }
454
+ /**
455
+ * Создает итератор для потоковой конвертации JSON в CSV
456
+ *
457
+ * @param data - Массив объектов или async итератор
458
+ * @param options - Опции конвертации
459
+ * @returns AsyncIterator с CSV чанками
460
+ */
461
+ async function* jsonToCsvIterator(data, options = {}) {
462
+ validateInput(Array.isArray(data) ? data : [], options);
463
+ const delimiter = options.delimiter || ';';
464
+ const includeHeaders = options.includeHeaders !== false;
465
+ const preventInjection = options.preventCsvInjection !== false;
466
+ const rfc4180Compliant = options.rfc4180Compliant !== false;
467
+ const normalizeQuotes = options.normalizeQuotes !== false;
468
+ let allKeys = [];
469
+ let renameMap = {};
470
+ // Если данные - массив, обрабатываем как массив
471
+ if (Array.isArray(data)) {
472
+ if (data.length === 0) {
473
+ return;
474
+ }
475
+ allKeys = extractAllKeys(data);
476
+ renameMap = options.renameMap || {};
477
+ const finalKeys = allKeys.map(key => renameMap[key] || key);
478
+ // Заголовки
479
+ if (includeHeaders) {
480
+ const headerLine = finalKeys.map(key => escapeCsvValue(key, preventInjection)).join(delimiter);
481
+ yield headerLine + (rfc4180Compliant ? '\r\n' : '\n');
482
+ }
483
+ // Данные
484
+ for (const item of data) {
485
+ const rowValues = allKeys.map(key => {
486
+ const value = item?.[key];
487
+ const normalized = normalizeValueForCsv$1(value, key, normalizeQuotes);
488
+ return escapeCsvValue(normalized, preventInjection);
489
+ });
490
+ yield rowValues.join(delimiter) + (rfc4180Compliant ? '\r\n' : '\n');
491
+ }
492
+ }
493
+ else {
494
+ // Для async итератора нужна другая логика
495
+ throw new ValidationError('Async iterators not yet implemented in browser version');
496
+ }
497
+ }
498
+ /**
499
+ * Асинхронная версия jsonToCsvIterator (псевдоним)
500
+ */
501
+ const jsonToCsvIteratorAsync = jsonToCsvIterator;
502
+ /**
503
+ * Безопасная конвертация с обработкой ошибок
504
+ *
505
+ * @param data - Массив объектов
506
+ * @param options - Опции конвертации
507
+ * @returns CSV строка или null при ошибке
508
+ */
509
+ function jsonToCsvSafe(data, options = {}) {
510
+ try {
511
+ return jsonToCsv$1(data, options);
512
+ }
513
+ catch (error) {
514
+ console.error('JSON to CSV conversion error:', error);
515
+ return null;
516
+ }
517
+ }
518
+ /**
519
+ * Асинхронная версия jsonToCsvSafe
520
+ */
521
+ async function jsonToCsvSafeAsync(data, options = {}) {
522
+ try {
523
+ return await jsonToCsvAsync$1(data, options);
524
+ }
525
+ catch (error) {
526
+ console.error('JSON to CSV conversion error:', error);
527
+ return null;
528
+ }
529
+ }
530
+ // Экспорт для Node.js совместимости
531
+ if (typeof module !== 'undefined' && module.exports) {
532
+ module.exports = {
533
+ jsonToCsv: jsonToCsv$1,
534
+ jsonToCsvAsync: jsonToCsvAsync$1,
535
+ jsonToCsvIterator,
536
+ jsonToCsvIteratorAsync,
537
+ jsonToCsvSafe,
538
+ jsonToCsvSafeAsync
539
+ };
540
+ }
541
+
542
+ var jsonToCsvBrowser = /*#__PURE__*/Object.freeze({
543
+ __proto__: null,
544
+ jsonToCsv: jsonToCsv$1,
545
+ jsonToCsvAsync: jsonToCsvAsync$1,
546
+ jsonToCsvIterator: jsonToCsvIterator,
547
+ jsonToCsvIteratorAsync: jsonToCsvIteratorAsync,
548
+ jsonToCsvSafe: jsonToCsvSafe,
549
+ jsonToCsvSafeAsync: jsonToCsvSafeAsync
550
+ });
551
+
552
+ // Браузерная версия CSV to JSON конвертера
553
+ // Адаптирована для работы в браузере без Node.js API
554
+ /**
555
+ * Валидация опций парсинга
556
+ * @private
557
+ */
558
+ function validateCsvOptions(options) {
559
+ // Validate options
560
+ if (options && typeof options !== 'object') {
561
+ throw new ConfigurationError('Options must be an object');
562
+ }
563
+ // Validate delimiter
564
+ if (options?.delimiter && typeof options.delimiter !== 'string') {
565
+ throw new ConfigurationError('Delimiter must be a string');
566
+ }
567
+ if (options?.delimiter && options.delimiter.length !== 1) {
568
+ throw new ConfigurationError('Delimiter must be a single character');
569
+ }
570
+ // Validate autoDetect
571
+ if (options?.autoDetect !== undefined && typeof options.autoDetect !== 'boolean') {
572
+ throw new ConfigurationError('autoDetect must be a boolean');
573
+ }
574
+ // Validate candidates
575
+ if (options?.candidates && !Array.isArray(options.candidates)) {
576
+ throw new ConfigurationError('candidates must be an array');
577
+ }
578
+ // Validate maxRows
579
+ if (options?.maxRows !== undefined && (typeof options.maxRows !== 'number' || options.maxRows <= 0)) {
580
+ throw new ConfigurationError('maxRows must be a positive number');
581
+ }
582
+ if (options?.warnExtraFields !== undefined && typeof options.warnExtraFields !== 'boolean') {
583
+ throw new ConfigurationError('warnExtraFields must be a boolean');
584
+ }
585
+ if (options?.repairRowShifts !== undefined && typeof options.repairRowShifts !== 'boolean') {
586
+ throw new ConfigurationError('repairRowShifts must be a boolean');
587
+ }
588
+ if (options?.normalizeQuotes !== undefined && typeof options.normalizeQuotes !== 'boolean') {
589
+ throw new ConfigurationError('normalizeQuotes must be a boolean');
590
+ }
591
+ return true;
592
+ }
593
+ /**
594
+ * Автоматическое определение разделителя
595
+ * @private
596
+ */
597
+ function autoDetectDelimiter$1(text, candidates = [',', ';', '\t', '|']) {
598
+ if (!text || typeof text !== 'string') {
599
+ return ',';
600
+ }
601
+ const firstLine = text.split('\n')[0];
602
+ if (!firstLine) {
603
+ return ',';
604
+ }
605
+ let bestCandidate = ',';
606
+ let bestCount = 0;
607
+ for (const candidate of candidates) {
608
+ const count = (firstLine.match(new RegExp(candidate.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')) || []).length;
609
+ if (count > bestCount) {
610
+ bestCount = count;
611
+ bestCandidate = candidate;
612
+ }
613
+ }
614
+ return bestCandidate;
615
+ }
616
+ function isEmptyValue(value) {
617
+ return value === undefined || value === null || value === '';
618
+ }
619
+ function hasOddQuotes(value) {
620
+ if (typeof value !== 'string') {
621
+ return false;
622
+ }
623
+ let count = 0;
624
+ for (let i = 0; i < value.length; i++) {
625
+ if (value[i] === '"') {
626
+ count++;
627
+ }
628
+ }
629
+ return count % 2 === 1;
630
+ }
631
+ function hasAnyQuotes(value) {
632
+ return typeof value === 'string' && value.includes('"');
633
+ }
634
+ function normalizeQuotesInField$1(value) {
635
+ if (typeof value !== 'string') {
636
+ return value;
637
+ }
638
+ // Не нормализуем кавычки в JSON-строках - это ломает структуру JSON
639
+ // Проверяем, выглядит ли значение как JSON (объект или массив)
640
+ if ((value.startsWith('{') && value.endsWith('}')) ||
641
+ (value.startsWith('[') && value.endsWith(']'))) {
642
+ return value; // Возвращаем как есть для JSON
643
+ }
644
+ let normalized = value.replace(/"{2,}/g, '"');
645
+ // Убираем правило, которое ломает JSON: не заменяем "," на ","
646
+ // normalized = normalized.replace(/"\s*,\s*"/g, ',');
647
+ normalized = normalized.replace(/"\n/g, '\n').replace(/\n"/g, '\n');
648
+ if (normalized.length >= 2 && normalized.startsWith('"') && normalized.endsWith('"')) {
649
+ normalized = normalized.slice(1, -1);
650
+ }
651
+ return normalized;
652
+ }
653
+ function normalizePhoneValue$1(value) {
654
+ if (typeof value !== 'string') {
655
+ return value;
656
+ }
657
+ const trimmed = value.trim();
658
+ if (trimmed === '') {
659
+ return trimmed;
660
+ }
661
+ return trimmed.replace(/["'\\]/g, '');
662
+ }
663
+ function normalizeRowQuotes(row, headers) {
664
+ const normalized = {};
665
+ const phoneKeys = new Set(['phone', 'phonenumber', 'phone_number', 'tel', 'telephone']);
666
+ for (const header of headers) {
667
+ const baseValue = normalizeQuotesInField$1(row[header]);
668
+ if (phoneKeys.has(String(header).toLowerCase())) {
669
+ normalized[header] = normalizePhoneValue$1(baseValue);
670
+ }
671
+ else {
672
+ normalized[header] = baseValue;
673
+ }
674
+ }
675
+ return normalized;
676
+ }
677
+ function looksLikeUserAgent(value) {
678
+ if (typeof value !== 'string') {
679
+ return false;
680
+ }
681
+ return /Mozilla\/|Opera\/|MSIE|AppleWebKit|Gecko|Safari|Chrome\//.test(value);
682
+ }
683
+ function isHexColor(value) {
684
+ return typeof value === 'string' && /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(value);
685
+ }
686
+ function repairShiftedRows(rows, headers, options = {}) {
687
+ if (!Array.isArray(rows) || rows.length === 0 || headers.length === 0) {
688
+ return rows;
689
+ }
690
+ const headerCount = headers.length;
691
+ const merged = [];
692
+ let index = 0;
693
+ while (index < rows.length) {
694
+ const row = rows[index];
695
+ if (!row || typeof row !== 'object') {
696
+ merged.push(row);
697
+ index++;
698
+ continue;
699
+ }
700
+ const values = headers.map((header) => row[header]);
701
+ let lastNonEmpty = -1;
702
+ for (let i = headerCount - 1; i >= 0; i--) {
703
+ if (!isEmptyValue(values[i])) {
704
+ lastNonEmpty = i;
705
+ break;
706
+ }
707
+ }
708
+ const missingCount = headerCount - 1 - lastNonEmpty;
709
+ if (lastNonEmpty >= 0 && missingCount > 0 && index + 1 < rows.length) {
710
+ const nextRow = rows[index + 1];
711
+ if (nextRow && typeof nextRow === 'object') {
712
+ const nextValues = headers.map((header) => nextRow[header]);
713
+ const nextTrailingEmpty = nextValues
714
+ .slice(headerCount - missingCount)
715
+ .every((value) => isEmptyValue(value));
716
+ const leadValues = nextValues
717
+ .slice(0, missingCount)
718
+ .filter((value) => !isEmptyValue(value));
719
+ const shouldMerge = nextTrailingEmpty
720
+ && leadValues.length > 0
721
+ && (hasOddQuotes(values[lastNonEmpty]) || hasAnyQuotes(values[lastNonEmpty]));
722
+ if (shouldMerge) {
723
+ const toAppend = leadValues.map((value) => String(value));
724
+ if (toAppend.length > 0) {
725
+ const base = isEmptyValue(values[lastNonEmpty]) ? '' : String(values[lastNonEmpty]);
726
+ values[lastNonEmpty] = base ? `${base}\n${toAppend.join('\n')}` : toAppend.join('\n');
727
+ }
728
+ for (let i = 0; i < missingCount; i++) {
729
+ values[lastNonEmpty + 1 + i] = nextValues[missingCount + i];
730
+ }
731
+ const mergedRow = {};
732
+ for (let i = 0; i < headerCount; i++) {
733
+ mergedRow[headers[i]] = values[i];
734
+ }
735
+ merged.push(mergedRow);
736
+ index += 2;
737
+ continue;
738
+ }
739
+ }
740
+ }
741
+ if (index + 1 < rows.length && headerCount >= 6) {
742
+ const nextRow = rows[index + 1];
743
+ if (nextRow && typeof nextRow === 'object') {
744
+ const nextHex = nextRow[headers[4]];
745
+ const nextUserAgentHead = nextRow[headers[2]];
746
+ const nextUserAgentTail = nextRow[headers[3]];
747
+ const shouldMergeUserAgent = isEmptyValue(values[4])
748
+ && isEmptyValue(values[5])
749
+ && isHexColor(nextHex)
750
+ && (looksLikeUserAgent(nextUserAgentHead) || looksLikeUserAgent(nextUserAgentTail));
751
+ if (shouldMergeUserAgent) {
752
+ const addressParts = [values[3], nextRow[headers[0]], nextRow[headers[1]]]
753
+ .filter((value) => !isEmptyValue(value))
754
+ .map((value) => String(value));
755
+ values[3] = addressParts.join('\n');
756
+ const uaHead = isEmptyValue(nextUserAgentHead) ? '' : String(nextUserAgentHead);
757
+ const uaTail = isEmptyValue(nextUserAgentTail) ? '' : String(nextUserAgentTail);
758
+ const joiner = uaHead && uaTail ? (uaTail.startsWith(' ') ? '' : ',') : '';
759
+ values[4] = uaHead + joiner + uaTail;
760
+ values[5] = String(nextHex);
761
+ const mergedRow = {};
762
+ for (let i = 0; i < headerCount; i++) {
763
+ mergedRow[headers[i]] = values[i];
764
+ }
765
+ merged.push(mergedRow);
766
+ index += 2;
767
+ continue;
768
+ }
769
+ }
770
+ }
771
+ merged.push(row);
772
+ index++;
773
+ }
774
+ if (options.normalizeQuotes) {
775
+ return merged.map((row) => normalizeRowQuotes(row, headers));
776
+ }
777
+ return merged;
778
+ }
779
+ /**
780
+ * Парсинг CSV строки в массив объектов
781
+ *
782
+ * @param csvText - CSV текст для парсинга
783
+ * @param options - Опции парсинга
784
+ * @returns Массив объектов
785
+ */
786
+ function csvToJson$1(csvText, options = {}) {
787
+ return safeExecute(() => {
788
+ validateCsvOptions(options);
789
+ if (typeof csvText !== 'string') {
790
+ throw new ValidationError('CSV text must be a string');
791
+ }
792
+ if (csvText.trim() === '') {
793
+ return [];
794
+ }
795
+ // Определение разделителя
796
+ const delimiter = options.delimiter ||
797
+ (options.autoDetect !== false ? autoDetectDelimiter$1(csvText, options.candidates) : ',');
798
+ // Разделение на строки
799
+ const lines = csvText.split('\n').filter(line => line.trim() !== '');
800
+ if (lines.length === 0) {
801
+ return [];
802
+ }
803
+ // Парсинг заголовков
804
+ const headers = lines[0].split(delimiter).map(h => h.trim());
805
+ const { repairRowShifts = true, normalizeQuotes = true } = options || {};
806
+ // Ограничение количества строк
807
+ const maxRows = options.maxRows || Infinity;
808
+ const dataRows = lines.slice(1, Math.min(lines.length, maxRows + 1));
809
+ // Парсинг данных
810
+ const result = [];
811
+ for (let i = 0; i < dataRows.length; i++) {
812
+ const line = dataRows[i];
813
+ const values = line.split(delimiter);
814
+ const row = {};
815
+ for (let j = 0; j < headers.length; j++) {
816
+ const header = headers[j];
817
+ const value = j < values.length ? values[j].trim() : '';
818
+ // Попытка парсинга чисел
819
+ if (/^-?\d+(\.\d+)?$/.test(value)) {
820
+ row[header] = parseFloat(value);
821
+ }
822
+ else if (value.toLowerCase() === 'true' || value.toLowerCase() === 'false') {
823
+ row[header] = value.toLowerCase() === 'true';
824
+ }
825
+ else {
826
+ row[header] = value;
827
+ }
828
+ }
829
+ result.push(row);
830
+ }
831
+ if (repairRowShifts) {
832
+ return repairShiftedRows(result, headers, { normalizeQuotes });
833
+ }
834
+ if (normalizeQuotes) {
835
+ return result.map((row) => normalizeRowQuotes(row, headers));
836
+ }
837
+ return result;
838
+ });
839
+ }
840
+ /**
841
+ * Асинхронная версия csvToJson
842
+ */
843
+ async function csvToJsonAsync$1(csvText, options = {}) {
844
+ return csvToJson$1(csvText, options);
845
+ }
846
+ /**
847
+ * Создает итератор для потокового парсинга CSV
848
+ *
849
+ * @param input - CSV текст, File или Blob
850
+ * @param options - Опции парсинга
851
+ * @returns AsyncGenerator
852
+ */
853
+ async function* csvToJsonIterator$1(input, options = {}) {
854
+ validateCsvOptions(options);
855
+ let csvText;
856
+ if (typeof input === 'string') {
857
+ csvText = input;
858
+ }
859
+ else if (input instanceof File || input instanceof Blob) {
860
+ csvText = await input.text();
861
+ }
862
+ else {
863
+ throw new ValidationError('Input must be string, File or Blob');
864
+ }
865
+ if (csvText.trim() === '') {
866
+ return;
867
+ }
868
+ // Определение разделителя
869
+ const delimiter = options.delimiter ||
870
+ (options.autoDetect !== false ? autoDetectDelimiter$1(csvText, options.candidates) : ',');
871
+ // Разделение на строки
872
+ const lines = csvText.split('\n').filter(line => line.trim() !== '');
873
+ if (lines.length === 0) {
874
+ return;
875
+ }
876
+ // Парсинг заголовков
877
+ const headers = lines[0].split(delimiter).map(h => h.trim());
878
+ const { repairRowShifts = true, normalizeQuotes = true } = options || {};
879
+ // Ограничение количества строк
880
+ const maxRows = options.maxRows || Infinity;
881
+ const dataRows = lines.slice(1, Math.min(lines.length, maxRows + 1));
882
+ // Возврат данных по одной строке
883
+ const parsedRows = [];
884
+ for (let i = 0; i < dataRows.length; i++) {
885
+ const line = dataRows[i];
886
+ const values = line.split(delimiter);
887
+ const row = {};
888
+ for (let j = 0; j < headers.length; j++) {
889
+ const header = headers[j];
890
+ const value = j < values.length ? values[j].trim() : '';
891
+ // Try parsing numbers
892
+ if (/^-?\d+(\.\d+)?$/.test(value)) {
893
+ row[header] = parseFloat(value);
894
+ }
895
+ else if (value.toLowerCase() === 'true' || value.toLowerCase() === 'false') {
896
+ row[header] = value.toLowerCase() === 'true';
897
+ }
898
+ else {
899
+ row[header] = value;
900
+ }
901
+ }
902
+ parsedRows.push(row);
903
+ }
904
+ const finalRows = repairRowShifts
905
+ ? repairShiftedRows(parsedRows, headers, { normalizeQuotes })
906
+ : (normalizeQuotes
907
+ ? parsedRows.map((row) => normalizeRowQuotes(row, headers))
908
+ : parsedRows);
909
+ for (const row of finalRows) {
910
+ yield row;
911
+ }
912
+ }
913
+ /**
914
+ * Асинхронная версия csvToJsonIterator (псевдоним)
915
+ */
916
+ const csvToJsonIteratorAsync = csvToJsonIterator$1;
917
+ /**
918
+ * Парсинг CSV с обработкой ошибок
919
+ *
920
+ * @param csvText - CSV текст
921
+ * @param options - Опции парсинга
922
+ * @returns Результат парсинга или null при ошибке
923
+ */
924
+ function parseCsvSafe(csvText, options = {}) {
925
+ try {
926
+ return csvToJson$1(csvText, options);
927
+ }
928
+ catch (error) {
929
+ console.error('CSV parsing error:', error);
930
+ return null;
931
+ }
932
+ }
933
+ /**
934
+ * Асинхронная версия parseCsvSafe
935
+ */
936
+ async function parseCsvSafeAsync(csvText, options = {}) {
937
+ try {
938
+ return await csvToJsonAsync$1(csvText, options);
939
+ }
940
+ catch (error) {
941
+ console.error('CSV parsing error:', error);
942
+ return null;
943
+ }
944
+ }
945
+ // Экспорт для Node.js совместимости
946
+ if (typeof module !== 'undefined' && module.exports) {
947
+ module.exports = {
948
+ csvToJson: csvToJson$1,
949
+ csvToJsonAsync: csvToJsonAsync$1,
950
+ csvToJsonIterator: csvToJsonIterator$1,
951
+ csvToJsonIteratorAsync,
952
+ parseCsvSafe,
953
+ parseCsvSafeAsync,
954
+ autoDetectDelimiter: autoDetectDelimiter$1
955
+ };
956
+ }
957
+
958
+ var csvToJsonBrowser = /*#__PURE__*/Object.freeze({
959
+ __proto__: null,
960
+ csvToJson: csvToJson$1,
961
+ csvToJsonAsync: csvToJsonAsync$1,
962
+ csvToJsonIterator: csvToJsonIterator$1,
963
+ csvToJsonIteratorAsync: csvToJsonIteratorAsync,
964
+ parseCsvSafe: parseCsvSafe,
965
+ parseCsvSafeAsync: parseCsvSafeAsync
966
+ });
967
+
968
+ const PHONE_KEYS = new Set(['phone', 'phonenumber', 'phone_number', 'tel', 'telephone']);
969
+ function isReadableStream(value) {
970
+ return value && typeof value.getReader === 'function';
971
+ }
972
+ function isAsyncIterable(value) {
973
+ return value && typeof value[Symbol.asyncIterator] === 'function';
974
+ }
975
+ function isIterable(value) {
976
+ return value && typeof value[Symbol.iterator] === 'function';
977
+ }
978
+ function createReadableStreamFromIterator(iterator) {
979
+ return new ReadableStream({
980
+ async pull(controller) {
981
+ try {
982
+ const { value, done } = await iterator.next();
983
+ if (done) {
984
+ controller.close();
985
+ return;
986
+ }
987
+ controller.enqueue(value);
988
+ }
989
+ catch (error) {
990
+ controller.error(error);
991
+ }
992
+ },
993
+ cancel() {
994
+ if (iterator.return) {
995
+ iterator.return();
996
+ }
997
+ }
998
+ });
999
+ }
1000
+ function detectInputFormat(input, options) {
1001
+ if (options && options.inputFormat) {
1002
+ return options.inputFormat;
1003
+ }
1004
+ if (typeof input === 'string') {
1005
+ const trimmed = input.trim();
1006
+ if (trimmed === '') {
1007
+ return 'unknown';
1008
+ }
1009
+ // Проверка на NDJSON (каждая строка - валидный JSON)
1010
+ if (trimmed.includes('\n')) {
1011
+ const lines = trimmed.split('\n').filter(line => line.trim() !== '');
1012
+ if (lines.length > 0) {
1013
+ try {
1014
+ JSON.parse(lines[0]);
1015
+ return 'ndjson';
1016
+ }
1017
+ catch {
1018
+ // Не NDJSON
1019
+ }
1020
+ }
1021
+ }
1022
+ // Проверка на JSON
1023
+ try {
1024
+ const parsed = JSON.parse(trimmed);
1025
+ if (Array.isArray(parsed) || (parsed && typeof parsed === 'object')) {
1026
+ return 'json';
1027
+ }
1028
+ }
1029
+ catch {
1030
+ // Не JSON
1031
+ }
1032
+ // Проверка на CSV
1033
+ if (trimmed.includes(',') || trimmed.includes(';') || trimmed.includes('\t')) {
1034
+ return 'csv';
1035
+ }
1036
+ }
1037
+ return 'unknown';
1038
+ }
1039
+ function normalizeQuotesInField(value) {
1040
+ // Не нормализуем кавычки в JSON-строках - это ломает структуру JSON
1041
+ // Проверяем, выглядит ли значение как JSON (объект или массив)
1042
+ if ((value.startsWith('{') && value.endsWith('}')) ||
1043
+ (value.startsWith('[') && value.endsWith(']'))) {
1044
+ return value; // Возвращаем как есть для JSON
1045
+ }
1046
+ let normalized = value.replace(/"{2,}/g, '"');
1047
+ // Убираем правило, которое ломает JSON: не заменяем "," на ","
1048
+ // normalized = normalized.replace(/"\s*,\s*"/g, ',');
1049
+ normalized = normalized.replace(/"\n/g, '\n').replace(/\n"/g, '\n');
1050
+ if (normalized.length >= 2 && normalized.startsWith('"') && normalized.endsWith('"')) {
1051
+ normalized = normalized.slice(1, -1);
1052
+ }
1053
+ return normalized;
1054
+ }
1055
+ function normalizePhoneValue(value) {
1056
+ const trimmed = value.trim();
1057
+ if (trimmed === '') {
1058
+ return trimmed;
1059
+ }
1060
+ return trimmed.replace(/["'\\]/g, '');
1061
+ }
1062
+ function normalizeValueForCsv(value, key, normalizeQuotes) {
1063
+ if (!normalizeQuotes || typeof value !== 'string') {
1064
+ return value;
1065
+ }
1066
+ const base = normalizeQuotesInField(value);
1067
+ if (key && PHONE_KEYS.has(String(key).toLowerCase())) {
1068
+ return normalizePhoneValue(base);
1069
+ }
1070
+ return base;
1071
+ }
1072
+ async function* jsonToCsvChunkIterator(input, options = {}) {
1073
+ const format = detectInputFormat(input, options);
1074
+ if (format === 'csv') {
1075
+ throw new ValidationError('Input appears to be CSV, not JSON');
1076
+ }
1077
+ // Вспомогательная функция для создания асинхронного итератора
1078
+ function toAsyncIterator(iterable) {
1079
+ if (isAsyncIterable(iterable)) {
1080
+ return iterable[Symbol.asyncIterator]();
1081
+ }
1082
+ if (isIterable(iterable)) {
1083
+ const syncIterator = iterable[Symbol.iterator]();
1084
+ return {
1085
+ next: () => Promise.resolve(syncIterator.next()),
1086
+ return: syncIterator.return ? () => Promise.resolve(syncIterator.return()) : undefined,
1087
+ throw: syncIterator.throw ? (error) => Promise.resolve(syncIterator.throw(error)) : undefined
1088
+ };
1089
+ }
1090
+ throw new ValidationError('Input is not iterable');
1091
+ }
1092
+ let iterator;
1093
+ if (isAsyncIterable(input) || isIterable(input)) {
1094
+ iterator = toAsyncIterator(input);
1095
+ }
1096
+ else if (typeof input === 'string') {
1097
+ const parsed = JSON.parse(input);
1098
+ if (Array.isArray(parsed)) {
1099
+ iterator = toAsyncIterator(parsed);
1100
+ }
1101
+ else {
1102
+ iterator = toAsyncIterator([parsed]);
1103
+ }
1104
+ }
1105
+ else if (Array.isArray(input)) {
1106
+ iterator = toAsyncIterator(input);
1107
+ }
1108
+ else {
1109
+ iterator = toAsyncIterator([input]);
1110
+ }
1111
+ const delimiter = options.delimiter || ';';
1112
+ const includeHeaders = options.includeHeaders !== false;
1113
+ const preventInjection = options.preventCsvInjection !== false;
1114
+ const normalizeQuotes = options.normalizeQuotes !== false;
1115
+ const isPotentialFormula = (input) => {
1116
+ let idx = 0;
1117
+ while (idx < input.length) {
1118
+ const code = input.charCodeAt(idx);
1119
+ if (code === 32 || code === 9 || code === 10 || code === 13 || code === 0xfeff) {
1120
+ idx++;
1121
+ continue;
1122
+ }
1123
+ break;
1124
+ }
1125
+ if (idx < input.length && (input[idx] === '"' || input[idx] === "'")) {
1126
+ idx++;
1127
+ while (idx < input.length) {
1128
+ const code = input.charCodeAt(idx);
1129
+ if (code === 32 || code === 9) {
1130
+ idx++;
1131
+ continue;
1132
+ }
1133
+ break;
1134
+ }
1135
+ }
1136
+ if (idx >= input.length) {
1137
+ return false;
1138
+ }
1139
+ const char = input[idx];
1140
+ return char === '=' || char === '+' || char === '-' || char === '@';
1141
+ };
1142
+ let isFirstChunk = true;
1143
+ let headers = [];
1144
+ while (true) {
1145
+ const { value, done } = await iterator.next();
1146
+ if (done)
1147
+ break;
1148
+ const item = value;
1149
+ if (isFirstChunk) {
1150
+ // Извлечение заголовков из первого элемента
1151
+ headers = Object.keys(item);
1152
+ if (includeHeaders) {
1153
+ const headerLine = headers.map(header => {
1154
+ const escaped = header.includes('"') ? `"${header.replace(/"/g, '""')}"` : header;
1155
+ return preventInjection && isPotentialFormula(escaped) ? `'${escaped}` : escaped;
1156
+ }).join(delimiter);
1157
+ yield headerLine + '\n';
1158
+ }
1159
+ isFirstChunk = false;
1160
+ }
1161
+ const row = headers.map(header => {
1162
+ const value = item[header];
1163
+ const normalized = normalizeValueForCsv(value, header, normalizeQuotes);
1164
+ const strValue = normalized === null || normalized === undefined ? '' : String(normalized);
1165
+ if (strValue.includes('"') || strValue.includes('\n') || strValue.includes('\r') || strValue.includes(delimiter)) {
1166
+ return `"${strValue.replace(/"/g, '""')}"`;
1167
+ }
1168
+ if (preventInjection && isPotentialFormula(strValue)) {
1169
+ return `'${strValue}`;
1170
+ }
1171
+ return strValue;
1172
+ }).join(delimiter);
1173
+ yield row + '\n';
1174
+ }
1175
+ }
1176
+ async function* jsonToNdjsonChunkIterator(input, options = {}) {
1177
+ const format = detectInputFormat(input, options);
1178
+ // Вспомогательная функция для создания асинхронного итератора
1179
+ function toAsyncIterator(iterable) {
1180
+ if (isAsyncIterable(iterable)) {
1181
+ return iterable[Symbol.asyncIterator]();
1182
+ }
1183
+ if (isIterable(iterable)) {
1184
+ const syncIterator = iterable[Symbol.iterator]();
1185
+ return {
1186
+ next: () => Promise.resolve(syncIterator.next()),
1187
+ return: syncIterator.return ? () => Promise.resolve(syncIterator.return()) : undefined,
1188
+ throw: syncIterator.throw ? (error) => Promise.resolve(syncIterator.throw(error)) : undefined
1189
+ };
1190
+ }
1191
+ throw new ValidationError('Input is not iterable');
1192
+ }
1193
+ let iterator;
1194
+ if (isAsyncIterable(input) || isIterable(input)) {
1195
+ iterator = toAsyncIterator(input);
1196
+ }
1197
+ else if (typeof input === 'string') {
1198
+ if (format === 'ndjson') {
1199
+ const lines = input.split('\n').filter(line => line.trim() !== '');
1200
+ iterator = toAsyncIterator(lines);
1201
+ }
1202
+ else {
1203
+ const parsed = JSON.parse(input);
1204
+ if (Array.isArray(parsed)) {
1205
+ iterator = toAsyncIterator(parsed);
1206
+ }
1207
+ else {
1208
+ iterator = toAsyncIterator([parsed]);
1209
+ }
1210
+ }
1211
+ }
1212
+ else if (Array.isArray(input)) {
1213
+ iterator = toAsyncIterator(input);
1214
+ }
1215
+ else {
1216
+ iterator = toAsyncIterator([input]);
1217
+ }
1218
+ while (true) {
1219
+ const { value, done } = await iterator.next();
1220
+ if (done)
1221
+ break;
1222
+ let jsonStr;
1223
+ if (typeof value === 'string') {
1224
+ try {
1225
+ // Проверяем, является ли строка валидным JSON
1226
+ JSON.parse(value);
1227
+ jsonStr = value;
1228
+ }
1229
+ catch {
1230
+ // Если нет, сериализуем как JSON
1231
+ jsonStr = JSON.stringify(value);
1232
+ }
1233
+ }
1234
+ else {
1235
+ jsonStr = JSON.stringify(value);
1236
+ }
1237
+ yield jsonStr + '\n';
1238
+ }
1239
+ }
1240
+ async function* csvToJsonChunkIterator(input, options = {}) {
1241
+ if (typeof input === 'string') {
1242
+ // Используем csvToJsonIterator из csv-to-json-browser
1243
+ yield* csvToJsonIterator$1(input, options);
1244
+ }
1245
+ else if (input instanceof File || input instanceof Blob) {
1246
+ const text = await input.text();
1247
+ yield* csvToJsonIterator$1(text, options);
1248
+ }
1249
+ else if (isReadableStream(input)) {
1250
+ const reader = input.getReader();
1251
+ const decoder = new TextDecoder();
1252
+ let buffer = '';
1253
+ try {
1254
+ while (true) {
1255
+ const { value, done } = await reader.read();
1256
+ if (done)
1257
+ break;
1258
+ buffer += decoder.decode(value, { stream: true });
1259
+ // Обработка буфера по строкам
1260
+ const lines = buffer.split('\n');
1261
+ buffer = lines.pop() || '';
1262
+ // TODO: Реализовать парсинг CSV из чанков
1263
+ // Пока просто возвращаем сырые строки
1264
+ for (const line of lines) {
1265
+ if (line.trim()) {
1266
+ yield { raw: line };
1267
+ }
1268
+ }
1269
+ }
1270
+ // Обработка остатка буфера
1271
+ if (buffer.trim()) {
1272
+ yield { raw: buffer };
1273
+ }
1274
+ }
1275
+ finally {
1276
+ reader.releaseLock();
1277
+ }
1278
+ }
1279
+ else {
1280
+ throw new ValidationError('Unsupported input type for CSV streaming');
1281
+ }
1282
+ }
1283
+ function jsonToCsvStream$1(input, options = {}) {
1284
+ const iterator = jsonToCsvChunkIterator(input, options);
1285
+ return createReadableStreamFromIterator(iterator);
1286
+ }
1287
+ function jsonToNdjsonStream$1(input, options = {}) {
1288
+ const iterator = jsonToNdjsonChunkIterator(input, options);
1289
+ return createReadableStreamFromIterator(iterator);
1290
+ }
1291
+ function csvToJsonStream$1(input, options = {}) {
1292
+ const iterator = csvToJsonChunkIterator(input, options);
1293
+ return createReadableStreamFromIterator(iterator);
1294
+ }
1295
+ /**
1296
+ * Асинхронная версия jsonToCsvStream
1297
+ */
1298
+ async function jsonToCsvStreamAsync(input, options = {}) {
1299
+ return jsonToCsvStream$1(input, options);
1300
+ }
1301
+ /**
1302
+ * Асинхронная версия jsonToNdjsonStream
1303
+ */
1304
+ async function jsonToNdjsonStreamAsync(input, options = {}) {
1305
+ return jsonToNdjsonStream$1(input, options);
1306
+ }
1307
+ /**
1308
+ * Асинхронная версия csvToJsonStream
1309
+ */
1310
+ async function csvToJsonStreamAsync(input, options = {}) {
1311
+ return csvToJsonStream$1(input, options);
1312
+ }
1313
+ // Экспорт для Node.js совместимости
1314
+ if (typeof module !== 'undefined' && module.exports) {
1315
+ module.exports = {
1316
+ jsonToCsvStream: jsonToCsvStream$1,
1317
+ jsonToCsvStreamAsync,
1318
+ jsonToNdjsonStream: jsonToNdjsonStream$1,
1319
+ jsonToNdjsonStreamAsync,
1320
+ csvToJsonStream: csvToJsonStream$1,
1321
+ csvToJsonStreamAsync,
1322
+ createReadableStreamFromIterator
1323
+ };
1324
+ }
1325
+
1326
+ // Браузерные специфичные функции для jtcsv
1327
+ // Функции, которые работают только в браузере
1328
+ /**
1329
+ * Скачивает JSON данные как CSV файл
1330
+ *
1331
+ * @param data - Массив объектов для конвертации
1332
+ * @param filename - Имя файла для скачивания (по умолчанию 'data.csv')
1333
+ * @param options - Опции для jsonToCsv
1334
+ *
1335
+ * @example
1336
+ * const data = [
1337
+ * { id: 1, name: 'John' },
1338
+ * { id: 2, name: 'Jane' }
1339
+ * ];
1340
+ * downloadAsCsv(data, 'users.csv', { delimiter: ',' });
1341
+ */
1342
+ function downloadAsCsv(data, filename = 'data.csv', options = {}) {
1343
+ // Проверка что мы в браузере
1344
+ if (typeof window === 'undefined') {
1345
+ throw new ValidationError('downloadAsCsv() работает только в браузере. Используйте saveAsCsv() в Node.js');
1346
+ }
1347
+ // Валидация имени файла
1348
+ if (typeof filename !== 'string' || filename.trim() === '') {
1349
+ throw new ValidationError('Filename must be a non-empty string');
1350
+ }
1351
+ // Добавление расширения .csv если его нет
1352
+ if (!filename.toLowerCase().endsWith('.csv')) {
1353
+ filename += '.csv';
1354
+ }
1355
+ // Конвертация в CSV
1356
+ const csv = jsonToCsv$1(data, options);
1357
+ // Создание Blob
1358
+ const blob = new Blob([csv], {
1359
+ type: 'text/csv;charset=utf-8;'
1360
+ });
1361
+ // Создание ссылки для скачивания
1362
+ const link = document.createElement('a');
1363
+ const url = URL.createObjectURL(blob);
1364
+ link.setAttribute('href', url);
1365
+ link.setAttribute('download', filename);
1366
+ link.style.visibility = 'hidden';
1367
+ document.body.appendChild(link);
1368
+ link.click();
1369
+ document.body.removeChild(link);
1370
+ // Освобождение URL
1371
+ setTimeout(() => URL.revokeObjectURL(url), 100);
1372
+ }
1373
+ /**
1374
+ * Асинхронная версия downloadAsCsv
1375
+ */
1376
+ async function downloadAsCsvAsync$1(data, filename = 'data.csv', options = {}) {
1377
+ return downloadAsCsv(data, filename, options);
1378
+ }
1379
+ /**
1380
+ * Парсит CSV файл из input[type="file"]
1381
+ *
1382
+ * @param file - File объект из input
1383
+ * @param options - Опции для csvToJson
1384
+ * @returns Promise с распарсенными данными
1385
+ */
1386
+ async function parseCsvFile(file, options = {}) {
1387
+ if (!(file instanceof File)) {
1388
+ throw new ValidationError('parseCsvFile() ожидает объект File');
1389
+ }
1390
+ // Чтение файла как текст
1391
+ const text = await file.text();
1392
+ // Парсинг CSV
1393
+ return csvToJson$1(text, options);
1394
+ }
1395
+ /**
1396
+ * Парсит CSV файл потоково
1397
+ *
1398
+ * @param file - File объект
1399
+ * @param options - Опции для потокового парсинга
1400
+ * @returns AsyncIterator с данными
1401
+ */
1402
+ function parseCsvFileStream(file, options = {}) {
1403
+ if (!(file instanceof File)) {
1404
+ throw new ValidationError('parseCsvFileStream() ожидает объект File');
1405
+ }
1406
+ // Используем csvToJsonIterator из импортированного модуля
1407
+ return csvToJsonIterator$1(file, options);
1408
+ }
1409
+ /**
1410
+ * Создает поток для конвертации JSON в CSV
1411
+ *
1412
+ * @param options - Опции для jsonToCsv
1413
+ * @returns ReadableStream
1414
+ */
1415
+ function jsonToCsvStream(options = {}) {
1416
+ return jsonToCsvStream$1(options);
1417
+ }
1418
+ /**
1419
+ * Создает поток для конвертации JSON в NDJSON
1420
+ *
1421
+ * @param options - Опции для конвертации
1422
+ * @returns ReadableStream
1423
+ */
1424
+ function jsonToNdjsonStream(options = {}) {
1425
+ return jsonToNdjsonStream$1(options);
1426
+ }
1427
+ /**
1428
+ * Создает поток для парсинга CSV в JSON
1429
+ *
1430
+ * @param options - Опции для csvToJson
1431
+ * @returns ReadableStream
1432
+ */
1433
+ function csvToJsonStream(options = {}) {
1434
+ return csvToJsonStream$1(options);
1435
+ }
1436
+ /**
1437
+ * Загружает CSV файл по URL
1438
+ *
1439
+ * @param url - URL CSV файла
1440
+ * @param options - Опции для csvToJson
1441
+ * @returns Promise с распарсенными данными
1442
+ */
1443
+ async function loadCsvFromUrl(url, options = {}) {
1444
+ if (typeof window === 'undefined') {
1445
+ throw new ValidationError('loadCsvFromUrl() работает только в браузере');
1446
+ }
1447
+ const response = await fetch(url);
1448
+ if (!response.ok) {
1449
+ throw new ValidationError(`Failed to load CSV from URL: ${response.status} ${response.statusText}`);
1450
+ }
1451
+ const text = await response.text();
1452
+ return csvToJson$1(text, options);
1453
+ }
1454
+ /**
1455
+ * Асинхронная версия loadCsvFromUrl
1456
+ */
1457
+ async function loadCsvFromUrlAsync(url, options = {}) {
1458
+ return loadCsvFromUrl(url, options);
1459
+ }
1460
+ /**
1461
+ * Экспортирует данные в CSV и открывает в новой вкладке
1462
+ *
1463
+ * @param data - Данные для экспорта
1464
+ * @param options - Опции для jsonToCsv
1465
+ */
1466
+ function openCsvInNewTab(data, options = {}) {
1467
+ if (typeof window === 'undefined') {
1468
+ throw new ValidationError('openCsvInNewTab() работает только в браузере');
1469
+ }
1470
+ const csv = jsonToCsv$1(data, options);
1471
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
1472
+ const url = URL.createObjectURL(blob);
1473
+ window.open(url, '_blank');
1474
+ // Освобождение URL через некоторое время
1475
+ setTimeout(() => URL.revokeObjectURL(url), 1000);
1476
+ }
1477
+ /**
1478
+ * Асинхронная версия openCsvInNewTab
1479
+ */
1480
+ async function openCsvInNewTabAsync(data, options = {}) {
1481
+ return openCsvInNewTab(data, options);
1482
+ }
1483
+ /**
1484
+ * Копирует CSV в буфер обмена
1485
+ *
1486
+ * @param data - Данные для копирования
1487
+ * @param options - Опции для jsonToCsv
1488
+ * @returns Promise с результатом копирования
1489
+ */
1490
+ async function copyCsvToClipboard(data, options = {}) {
1491
+ if (typeof window === 'undefined' || !navigator.clipboard) {
1492
+ throw new ValidationError('copyCsvToClipboard() требует поддержки Clipboard API');
1493
+ }
1494
+ const csv = jsonToCsv$1(data, options);
1495
+ try {
1496
+ await navigator.clipboard.writeText(csv);
1497
+ return true;
1498
+ }
1499
+ catch (error) {
1500
+ console.error('Failed to copy to clipboard:', error);
1501
+ return false;
1502
+ }
1503
+ }
1504
+ /**
1505
+ * Сохраняет CSV в localStorage
1506
+ *
1507
+ * @param key - Ключ для сохранения
1508
+ * @param data - Данные для сохранения
1509
+ * @param options - Опции для jsonToCsv
1510
+ */
1511
+ function saveCsvToLocalStorage(key, data, options = {}) {
1512
+ if (typeof window === 'undefined' || !localStorage) {
1513
+ throw new ValidationError('saveCsvToLocalStorage() требует localStorage');
1514
+ }
1515
+ const csv = jsonToCsv$1(data, options);
1516
+ localStorage.setItem(key, csv);
1517
+ }
1518
+ /**
1519
+ * Загружает CSV из localStorage
1520
+ *
1521
+ * @param key - Ключ для загрузки
1522
+ * @param options - Опции для csvToJson
1523
+ * @returns Распарсенные данные или null
1524
+ */
1525
+ function loadCsvFromLocalStorage(key, options = {}) {
1526
+ if (typeof window === 'undefined' || !localStorage) {
1527
+ throw new ValidationError('loadCsvFromLocalStorage() требует localStorage');
1528
+ }
1529
+ const csv = localStorage.getItem(key);
1530
+ if (!csv) {
1531
+ return null;
1532
+ }
1533
+ return csvToJson$1(csv, options);
1534
+ }
1535
+ /**
1536
+ * Асинхронная версия loadCsvFromLocalStorage
1537
+ */
1538
+ async function loadCsvFromLocalStorageAsync(key, options = {}) {
1539
+ return loadCsvFromLocalStorage(key, options);
1540
+ }
1541
+ /**
1542
+ * Создает CSV файл из JSON данных (альтернатива downloadAsCsv)
1543
+ * Возвращает Blob вместо автоматического скачивания
1544
+ *
1545
+ * @param data - Массив объектов
1546
+ * @param options - Опции для jsonToCsv
1547
+ * @returns CSV Blob
1548
+ */
1549
+ function createCsvBlob(data, options = {}) {
1550
+ const csv = jsonToCsv$1(data, options);
1551
+ return new Blob([csv], {
1552
+ type: 'text/csv;charset=utf-8;'
1553
+ });
1554
+ }
1555
+ /**
1556
+ * Асинхронная версия createCsvBlob
1557
+ */
1558
+ async function createCsvBlobAsync(data, options = {}) {
1559
+ return createCsvBlob(data, options);
1560
+ }
1561
+ /**
1562
+ * Парсит CSV строку из Blob
1563
+ *
1564
+ * @param blob - CSV Blob
1565
+ * @param options - Опции для csvToJson
1566
+ * @returns Promise с JSON данными
1567
+ */
1568
+ async function parseCsvBlob(blob, options = {}) {
1569
+ if (!(blob instanceof Blob)) {
1570
+ throw new ValidationError('Input must be a Blob object');
1571
+ }
1572
+ return new Promise((resolve, reject) => {
1573
+ const reader = new FileReader();
1574
+ reader.onload = function (event) {
1575
+ try {
1576
+ const csvText = event.target?.result;
1577
+ const json = csvToJson$1(csvText, options);
1578
+ resolve(json);
1579
+ }
1580
+ catch (error) {
1581
+ reject(error);
1582
+ }
1583
+ };
1584
+ reader.onerror = function () {
1585
+ reject(new ValidationError('Ошибка чтения Blob'));
1586
+ };
1587
+ reader.readAsText(blob, 'UTF-8');
1588
+ });
1589
+ }
1590
+ /**
1591
+ * Асинхронная версия parseCsvBlob
1592
+ */
1593
+ async function parseCsvBlobAsync(blob, options = {}) {
1594
+ return parseCsvBlob(blob, options);
1595
+ }
1596
+ // Экспорт для Node.js совместимости
1597
+ if (typeof module !== 'undefined' && module.exports) {
1598
+ module.exports = {
1599
+ downloadAsCsv,
1600
+ downloadAsCsvAsync: downloadAsCsvAsync$1,
1601
+ parseCsvFile,
1602
+ parseCsvFileStream,
1603
+ createCsvBlob,
1604
+ createCsvBlobAsync,
1605
+ parseCsvBlob,
1606
+ parseCsvBlobAsync,
1607
+ jsonToCsvStream,
1608
+ jsonToNdjsonStream,
1609
+ csvToJsonStream,
1610
+ loadCsvFromUrl,
1611
+ loadCsvFromUrlAsync,
1612
+ openCsvInNewTab,
1613
+ openCsvInNewTabAsync,
1614
+ copyCsvToClipboard,
1615
+ saveCsvToLocalStorage,
1616
+ loadCsvFromLocalStorage,
1617
+ loadCsvFromLocalStorageAsync
1618
+ };
1619
+ }
1620
+
1621
+ // Worker Pool для параллельной обработки CSV
1622
+ // Использует Comlink для простой коммуникации с Web Workers
1623
+ // Проверка поддержки Web Workers
1624
+ const WORKERS_SUPPORTED = typeof Worker !== 'undefined';
1625
+ function isTransferableBuffer(value) {
1626
+ if (!(value instanceof ArrayBuffer)) {
1627
+ return false;
1628
+ }
1629
+ if (typeof SharedArrayBuffer !== 'undefined' && value instanceof SharedArrayBuffer) {
1630
+ return false;
1631
+ }
1632
+ return true;
1633
+ }
1634
+ function collectTransferables(args) {
1635
+ const transferables = [];
1636
+ const collectFromValue = (value) => {
1637
+ if (!value) {
1638
+ return;
1639
+ }
1640
+ if (isTransferableBuffer(value)) {
1641
+ transferables.push(value);
1642
+ return;
1643
+ }
1644
+ if (ArrayBuffer.isView(value) && isTransferableBuffer(value.buffer)) {
1645
+ transferables.push(value.buffer);
1646
+ return;
1647
+ }
1648
+ if (Array.isArray(value)) {
1649
+ value.forEach(collectFromValue);
1650
+ }
1651
+ };
1652
+ args.forEach(collectFromValue);
1653
+ return transferables.length ? transferables : null;
1654
+ }
1655
+ /**
1656
+ * Опции для Worker Pool
1657
+ * @typedef {Object} WorkerPoolOptions
1658
+ * @property {number} [workerCount=4] - Количество workers в pool
1659
+ * @property {number} [maxQueueSize=100] - Максимальный размер очереди задач
1660
+ * @property {boolean} [autoScale=true] - Автоматическое масштабирование pool
1661
+ * @property {number} [idleTimeout=60000] - Таймаут простоя worker (мс)
1662
+ */
1663
+ /**
1664
+ * Статистика Worker Pool
1665
+ * @typedef {Object} WorkerPoolStats
1666
+ * @property {number} totalWorkers - Всего workers
1667
+ * @property {number} activeWorkers - Активные workers
1668
+ * @property {number} idleWorkers - Простаивающие workers
1669
+ * @property {number} queueSize - Размер очереди
1670
+ * @property {number} tasksCompleted - Завершенные задачи
1671
+ * @property {number} tasksFailed - Неудачные задачи
1672
+ */
1673
+ /**
1674
+ * Прогресс обработки задачи
1675
+ * @typedef {Object} TaskProgress
1676
+ * @property {number} processed - Обработано элементов
1677
+ * @property {number} total - Всего элементов
1678
+ * @property {number} percentage - Процент выполнения
1679
+ * @property {number} speed - Скорость обработки (элементов/сек)
1680
+ */
1681
+ /**
1682
+ * Worker Pool для параллельной обработки CSV
1683
+ */
1684
+ class WorkerPool {
1685
+ /**
1686
+ * Создает новый Worker Pool
1687
+ * @param {string} workerScript - URL скрипта worker
1688
+ * @param {WorkerPoolOptions} [options] - Опции pool
1689
+ */
1690
+ constructor(workerScript, options = {}) {
1691
+ if (!WORKERS_SUPPORTED) {
1692
+ throw new ValidationError('Web Workers не поддерживаются в этом браузере');
1693
+ }
1694
+ this.workerScript = workerScript;
1695
+ this.options = {
1696
+ workerCount: 4,
1697
+ maxQueueSize: 100,
1698
+ autoScale: true,
1699
+ idleTimeout: 60000,
1700
+ ...options
1701
+ };
1702
+ this.workers = [];
1703
+ this.taskQueue = [];
1704
+ this.activeTasks = new Map();
1705
+ this.stats = {
1706
+ totalWorkers: 0,
1707
+ activeWorkers: 0,
1708
+ idleWorkers: 0,
1709
+ queueSize: 0,
1710
+ tasksCompleted: 0,
1711
+ tasksFailed: 0
1712
+ };
1713
+ this.initializeWorkers();
1714
+ }
1715
+ /**
1716
+ * Инициализация workers
1717
+ * @private
1718
+ */
1719
+ initializeWorkers() {
1720
+ const { workerCount } = this.options;
1721
+ for (let i = 0; i < workerCount; i++) {
1722
+ this.createWorker();
1723
+ }
1724
+ this.updateStats();
1725
+ }
1726
+ /**
1727
+ * Создает нового worker
1728
+ * @private
1729
+ */
1730
+ createWorker() {
1731
+ try {
1732
+ const worker = new Worker(this.workerScript, { type: 'module' });
1733
+ worker.id = `worker-${this.workers.length}`;
1734
+ worker.status = 'idle';
1735
+ worker.lastUsed = Date.now();
1736
+ worker.taskId = null;
1737
+ // Обработчики событий
1738
+ worker.onmessage = (event) => this.handleWorkerMessage(worker, event);
1739
+ worker.onerror = (error) => this.handleWorkerError(worker, error);
1740
+ worker.onmessageerror = (error) => this.handleWorkerMessageError(worker, error);
1741
+ this.workers.push(worker);
1742
+ this.stats.totalWorkers++;
1743
+ this.stats.idleWorkers++;
1744
+ return worker;
1745
+ }
1746
+ catch (error) {
1747
+ throw new ConfigurationError(`Не удалось создать worker: ${error.message}`);
1748
+ }
1749
+ }
1750
+ /**
1751
+ * Обработка сообщений от worker
1752
+ * @private
1753
+ */
1754
+ handleWorkerMessage(worker, event) {
1755
+ const { data } = event;
1756
+ if (data.type === 'PROGRESS') {
1757
+ this.handleProgress(worker, data);
1758
+ }
1759
+ else if (data.type === 'RESULT') {
1760
+ this.handleResult(worker, data);
1761
+ }
1762
+ else if (data.type === 'ERROR') {
1763
+ this.handleWorkerTaskError(worker, data);
1764
+ }
1765
+ }
1766
+ /**
1767
+ * Обработка прогресса задачи
1768
+ * @private
1769
+ */
1770
+ handleProgress(worker, progressData) {
1771
+ const taskId = worker.taskId;
1772
+ if (taskId && this.activeTasks.has(taskId)) {
1773
+ const task = this.activeTasks.get(taskId);
1774
+ if (task.onProgress) {
1775
+ task.onProgress({
1776
+ processed: progressData.processed,
1777
+ total: progressData.total,
1778
+ percentage: (progressData.processed / progressData.total) * 100,
1779
+ speed: progressData.speed || 0
1780
+ });
1781
+ }
1782
+ }
1783
+ }
1784
+ /**
1785
+ * Обработка результата задачи
1786
+ * @private
1787
+ */
1788
+ handleResult(worker, resultData) {
1789
+ const taskId = worker.taskId;
1790
+ if (taskId && this.activeTasks.has(taskId)) {
1791
+ const task = this.activeTasks.get(taskId);
1792
+ // Освобождение worker
1793
+ worker.status = 'idle';
1794
+ worker.lastUsed = Date.now();
1795
+ worker.taskId = null;
1796
+ this.stats.activeWorkers--;
1797
+ this.stats.idleWorkers++;
1798
+ // Завершение задачи
1799
+ task.resolve(resultData.data);
1800
+ this.activeTasks.delete(taskId);
1801
+ this.stats.tasksCompleted++;
1802
+ // Обработка следующей задачи в очереди
1803
+ this.processQueue();
1804
+ this.updateStats();
1805
+ }
1806
+ }
1807
+ /**
1808
+ * Обработка ошибки задачи
1809
+ * @private
1810
+ */
1811
+ handleWorkerTaskError(worker, errorData) {
1812
+ const taskId = worker.taskId;
1813
+ if (taskId && this.activeTasks.has(taskId)) {
1814
+ const task = this.activeTasks.get(taskId);
1815
+ // Освобождение worker
1816
+ worker.status = 'idle';
1817
+ worker.lastUsed = Date.now();
1818
+ worker.taskId = null;
1819
+ this.stats.activeWorkers--;
1820
+ this.stats.idleWorkers++;
1821
+ // Завершение с ошибкой
1822
+ const workerError = new Error(errorData.message || 'Ошибка в worker');
1823
+ if (errorData.code) {
1824
+ workerError.code = errorData.code;
1825
+ }
1826
+ if (errorData.details) {
1827
+ workerError.details = errorData.details;
1828
+ }
1829
+ task.reject(workerError);
1830
+ this.activeTasks.delete(taskId);
1831
+ this.stats.tasksFailed++;
1832
+ // Обработка следующей задачи
1833
+ this.processQueue();
1834
+ this.updateStats();
1835
+ }
1836
+ }
1837
+ /**
1838
+ * Обработка ошибок worker
1839
+ * @private
1840
+ */
1841
+ handleWorkerError(worker, error) {
1842
+ console.error(`Worker ${worker.id} error:`, error);
1843
+ // Перезапуск worker
1844
+ this.restartWorker(worker);
1845
+ }
1846
+ /**
1847
+ * Обработка ошибок сообщений
1848
+ * @private
1849
+ */
1850
+ handleWorkerMessageError(worker, error) {
1851
+ console.error(`Worker ${worker.id} message error:`, error);
1852
+ }
1853
+ /**
1854
+ * Перезапуск worker
1855
+ * @private
1856
+ */
1857
+ restartWorker(worker) {
1858
+ const index = this.workers.indexOf(worker);
1859
+ if (index !== -1) {
1860
+ // Завершение старого worker
1861
+ worker.terminate();
1862
+ // Удаление из статистики
1863
+ if (worker.status === 'active') {
1864
+ this.stats.activeWorkers--;
1865
+ }
1866
+ else {
1867
+ this.stats.idleWorkers--;
1868
+ }
1869
+ this.stats.totalWorkers--;
1870
+ // Создание нового worker
1871
+ const newWorker = this.createWorker();
1872
+ this.workers[index] = newWorker;
1873
+ // Перезапуск задачи если была активна
1874
+ if (worker.taskId && this.activeTasks.has(worker.taskId)) {
1875
+ const task = this.activeTasks.get(worker.taskId);
1876
+ this.executeTask(newWorker, task);
1877
+ }
1878
+ }
1879
+ }
1880
+ /**
1881
+ * Выполнение задачи на worker
1882
+ * @private
1883
+ */
1884
+ executeTask(worker, task) {
1885
+ worker.status = 'active';
1886
+ worker.lastUsed = Date.now();
1887
+ worker.taskId = task.id;
1888
+ this.stats.idleWorkers--;
1889
+ this.stats.activeWorkers++;
1890
+ // Отправка задачи в worker
1891
+ const payload = {
1892
+ type: 'EXECUTE',
1893
+ taskId: task.id,
1894
+ method: task.method,
1895
+ args: task.args,
1896
+ options: task.options
1897
+ };
1898
+ if (task.transferList && task.transferList.length) {
1899
+ worker.postMessage(payload, task.transferList);
1900
+ }
1901
+ else {
1902
+ worker.postMessage(payload);
1903
+ }
1904
+ }
1905
+ /**
1906
+ * Обработка очереди задач
1907
+ * @private
1908
+ */
1909
+ processQueue() {
1910
+ if (this.taskQueue.length === 0) {
1911
+ return;
1912
+ }
1913
+ while (this.taskQueue.length > 0) {
1914
+ const idleWorker = this.workers.find(w => w.status === 'idle');
1915
+ if (!idleWorker) {
1916
+ if (this.options.autoScale && this.workers.length < this.options.maxQueueSize) {
1917
+ this.createWorker();
1918
+ continue;
1919
+ }
1920
+ break;
1921
+ }
1922
+ const task = this.taskQueue.shift();
1923
+ this.stats.queueSize--;
1924
+ this.executeTask(idleWorker, task);
1925
+ }
1926
+ this.updateStats();
1927
+ }
1928
+ /**
1929
+ * Обновление статистики
1930
+ * @private
1931
+ */
1932
+ updateStats() {
1933
+ this.stats.queueSize = this.taskQueue.length;
1934
+ }
1935
+ /**
1936
+ * Выполнение задачи через pool
1937
+ * @param {string} method - Метод для вызова в worker
1938
+ * @param {Array} args - Аргументы метода
1939
+ * @param {Object} [options] - Опции задачи
1940
+ * @param {Function} [onProgress] - Callback прогресса
1941
+ * @returns {Promise<unknown>} Результат выполнения
1942
+ */
1943
+ async exec(method, args = [], options = {}, onProgress = null) {
1944
+ return new Promise((resolve, reject) => {
1945
+ // Проверка размера очереди
1946
+ if (this.taskQueue.length >= this.options.maxQueueSize) {
1947
+ reject(new Error('Очередь задач переполнена'));
1948
+ return;
1949
+ }
1950
+ // Создание задачи
1951
+ const taskId = `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
1952
+ const { transfer, ...taskOptions } = options || {};
1953
+ const transferList = transfer || collectTransferables(args);
1954
+ const task = {
1955
+ id: taskId,
1956
+ method,
1957
+ args,
1958
+ options: taskOptions,
1959
+ transferList,
1960
+ onProgress,
1961
+ resolve,
1962
+ reject,
1963
+ createdAt: Date.now()
1964
+ };
1965
+ // Добавление в очередь
1966
+ this.taskQueue.push(task);
1967
+ this.stats.queueSize++;
1968
+ // Запуск обработки очереди
1969
+ this.processQueue();
1970
+ this.updateStats();
1971
+ });
1972
+ }
1973
+ /**
1974
+ * Получение статистики pool
1975
+ * @returns {WorkerPoolStats} Статистика
1976
+ */
1977
+ getStats() {
1978
+ return { ...this.stats };
1979
+ }
1980
+ /**
1981
+ * Очистка простаивающих workers
1982
+ */
1983
+ cleanupIdleWorkers() {
1984
+ const now = Date.now();
1985
+ const { idleTimeout } = this.options;
1986
+ for (let i = this.workers.length - 1; i >= 0; i--) {
1987
+ const worker = this.workers[i];
1988
+ if (worker.status === 'idle' && (now - worker.lastUsed) > idleTimeout) {
1989
+ // Сохранение минимального количества workers
1990
+ if (this.workers.length > 1) {
1991
+ worker.terminate();
1992
+ this.workers.splice(i, 1);
1993
+ this.stats.totalWorkers--;
1994
+ this.stats.idleWorkers--;
1995
+ }
1996
+ }
1997
+ }
1998
+ }
1999
+ /**
2000
+ * Завершение всех workers
2001
+ */
2002
+ terminate() {
2003
+ this.workers.forEach(worker => {
2004
+ worker.terminate();
2005
+ });
2006
+ this.workers = [];
2007
+ this.taskQueue = [];
2008
+ this.activeTasks.clear();
2009
+ // Сброс статистики
2010
+ this.stats = {
2011
+ totalWorkers: 0,
2012
+ activeWorkers: 0,
2013
+ idleWorkers: 0,
2014
+ queueSize: 0,
2015
+ tasksCompleted: 0,
2016
+ tasksFailed: 0
2017
+ };
2018
+ }
2019
+ }
2020
+ /**
2021
+ * Создает Worker Pool для обработки CSV
2022
+ * @param {WorkerPoolOptions} [options] - Опции pool
2023
+ * @returns {WorkerPool} Worker Pool
2024
+ */
2025
+ function createWorkerPool(options = {}) {
2026
+ // Используем встроенный worker скрипт
2027
+ const baseUrl = typeof document !== 'undefined'
2028
+ ? document.baseURI
2029
+ : (typeof self !== 'undefined' && self.location
2030
+ ? self.location.href
2031
+ : '');
2032
+ const workerScript = new URL('./csv-parser.worker.js', baseUrl).href;
2033
+ return new WorkerPool(workerScript, options);
2034
+ }
2035
+ /**
2036
+ * Парсит CSV с использованием Web Workers
2037
+ * @param {string|File} csvInput - CSV строка или File объект
2038
+ * @param {Object} [options] - Опции парсинга
2039
+ * @param {Function} [onProgress] - Callback прогресса
2040
+ * @returns {Promise<Array<Object>>} JSON данные
2041
+ */
2042
+ async function parseCSVWithWorker(csvInput, options = {}, onProgress = null) {
2043
+ // Создание pool если нужно
2044
+ const poolHolder = parseCSVWithWorker;
2045
+ if (!poolHolder.pool) {
2046
+ poolHolder.pool = createWorkerPool();
2047
+ }
2048
+ const pool = poolHolder.pool;
2049
+ // Подготовка CSV строки
2050
+ // ?????????? CSV ??????
2051
+ let csvPayload = csvInput;
2052
+ let transfer = null;
2053
+ if (csvInput instanceof File) {
2054
+ const buffer = await readFileAsArrayBuffer(csvInput);
2055
+ csvPayload = new Uint8Array(buffer);
2056
+ transfer = [buffer];
2057
+ }
2058
+ else if (csvInput instanceof ArrayBuffer) {
2059
+ csvPayload = csvInput;
2060
+ transfer = [csvInput];
2061
+ }
2062
+ else if (ArrayBuffer.isView(csvInput)) {
2063
+ csvPayload = csvInput;
2064
+ if (csvInput.buffer instanceof ArrayBuffer) {
2065
+ transfer = [csvInput.buffer];
2066
+ }
2067
+ }
2068
+ else if (typeof csvInput !== 'string') {
2069
+ throw new ValidationError('Input must be a CSV string, File, or ArrayBuffer');
2070
+ }
2071
+ // ????????? ?????? ????? pool
2072
+ const execOptions = transfer ? { transfer } : {};
2073
+ return pool.exec('parseCSV', [csvPayload, options], execOptions, onProgress);
2074
+ }
2075
+ /**
2076
+ * Чтение файла как текст
2077
+ * @private
2078
+ */
2079
+ async function readFileAsArrayBuffer(file) {
2080
+ return new Promise((resolve, reject) => {
2081
+ const reader = new FileReader();
2082
+ reader.onload = (event) => resolve(event.target.result);
2083
+ reader.onerror = (error) => reject(error);
2084
+ reader.readAsArrayBuffer(file);
2085
+ });
2086
+ }
2087
+ // Экспорт для Node.js совместимости
2088
+ if (typeof module !== 'undefined' && module.exports) {
2089
+ module.exports = {
2090
+ WorkerPool,
2091
+ createWorkerPool,
2092
+ parseCSVWithWorker
2093
+ };
2094
+ }
2095
+
2096
+ var workerPool = /*#__PURE__*/Object.freeze({
2097
+ __proto__: null,
2098
+ WorkerPool: WorkerPool,
2099
+ createWorkerPool: createWorkerPool,
2100
+ parseCSVWithWorker: parseCSVWithWorker
2101
+ });
2102
+
2103
+ // Браузерный entry point для jtcsv
2104
+ // Экспортирует все функции с поддержкой браузера
2105
+ const { jsonToCsv, preprocessData, deepUnwrap } = jsonToCsvBrowser;
2106
+ const { csvToJson, csvToJsonIterator, autoDetectDelimiter } = csvToJsonBrowser;
2107
+ /**
2108
+ * Ленивая инициализация Worker Pool
2109
+ */
2110
+ async function createWorkerPoolLazy(options = {}) {
2111
+ const mod = await Promise.resolve().then(function () { return workerPool; });
2112
+ return mod.createWorkerPool(options);
2113
+ }
2114
+ /**
2115
+ * Ленивый парсинг CSV с использованием Worker
2116
+ */
2117
+ async function parseCSVWithWorkerLazy(csvInput, options = {}, onProgress) {
2118
+ const mod = await Promise.resolve().then(function () { return workerPool; });
2119
+ return mod.parseCSVWithWorker(csvInput, options, onProgress);
2120
+ }
2121
+ /**
2122
+ * Асинхронная версия jsonToCsv
2123
+ */
2124
+ async function jsonToCsvAsync(data, options = {}) {
2125
+ return jsonToCsv(data, options);
2126
+ }
2127
+ /**
2128
+ * Асинхронная версия csvToJson
2129
+ */
2130
+ async function csvToJsonAsync(csv, options = {}) {
2131
+ return csvToJson(csv, options);
2132
+ }
2133
+ /**
2134
+ * Асинхронная версия parseCsvFile
2135
+ */
2136
+ async function parseCsvFileAsync(file, options = {}) {
2137
+ return parseCsvFile(file, options);
2138
+ }
2139
+ /**
2140
+ * Асинхронная версия autoDetectDelimiter
2141
+ */
2142
+ async function autoDetectDelimiterAsync(csv) {
2143
+ return autoDetectDelimiter(csv);
2144
+ }
2145
+ /**
2146
+ * Асинхронная версия downloadAsCsv
2147
+ */
2148
+ async function downloadAsCsvAsync(data, filename = 'export.csv', options = {}) {
2149
+ return downloadAsCsv(data, filename, options);
2150
+ }
2151
+ // Основной экспорт
2152
+ const jtcsv = {
2153
+ // JSON to CSV функции
2154
+ jsonToCsv,
2155
+ preprocessData,
2156
+ downloadAsCsv,
2157
+ deepUnwrap,
2158
+ // CSV to JSON функции
2159
+ csvToJson,
2160
+ csvToJsonIterator,
2161
+ parseCsvFile,
2162
+ parseCsvFileStream,
2163
+ jsonToCsvStream,
2164
+ jsonToNdjsonStream,
2165
+ csvToJsonStream,
2166
+ autoDetectDelimiter,
2167
+ // Web Workers функции
2168
+ createWorkerPool,
2169
+ parseCSVWithWorker,
2170
+ createWorkerPoolLazy,
2171
+ parseCSVWithWorkerLazy,
2172
+ // Асинхронные функции
2173
+ jsonToCsvAsync,
2174
+ csvToJsonAsync,
2175
+ parseCsvFileAsync,
2176
+ autoDetectDelimiterAsync,
2177
+ downloadAsCsvAsync,
2178
+ // Error classes
2179
+ ValidationError,
2180
+ SecurityError,
2181
+ FileSystemError,
2182
+ ParsingError,
2183
+ LimitError,
2184
+ ConfigurationError,
2185
+ ERROR_CODES,
2186
+ // Удобные алиасы
2187
+ parse: csvToJson,
2188
+ unparse: jsonToCsv,
2189
+ parseAsync: csvToJsonAsync,
2190
+ unparseAsync: jsonToCsvAsync,
2191
+ // Версия
2192
+ version: '2.0.0-browser'
2193
+ };
2194
+ // Экспорт для разных сред
2195
+ if (typeof module !== 'undefined' && module.exports) {
2196
+ // Node.js CommonJS
2197
+ module.exports = jtcsv;
2198
+ }
2199
+ else if (typeof define === 'function' && define.amd) {
2200
+ // AMD
2201
+ define([], () => jtcsv);
2202
+ }
2203
+ else if (typeof window !== 'undefined') {
2204
+ // Браузер (глобальная переменная)
2205
+ window.jtcsv = jtcsv;
2206
+ }
2207
+
2208
+ export { ConfigurationError, ERROR_CODES, FileSystemError, LimitError, ParsingError, SecurityError, ValidationError, autoDetectDelimiter, autoDetectDelimiterAsync, createWorkerPool, createWorkerPoolLazy, csvToJson, csvToJsonAsync, csvToJsonIterator, csvToJsonStream, deepUnwrap, jtcsv as default, downloadAsCsv, downloadAsCsvAsync, jsonToCsv, jsonToCsvAsync, jsonToCsvStream, jsonToNdjsonStream, parseCSVWithWorker, parseCSVWithWorkerLazy, parseCsvFile, parseCsvFileAsync, parseCsvFileStream, preprocessData };
2209
+ //# sourceMappingURL=jtcsv-full.esm.js.map