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,403 @@
1
+ import {
2
+ ValidationError,
3
+ ConfigurationError,
4
+ LimitError
5
+ } from './errors-browser';
6
+ import { csvToJsonIterator } from './csv-to-json-browser';
7
+
8
+ import type { CsvToJsonOptions, JsonToCsvOptions } from '../types';
9
+
10
+ const DEFAULT_MAX_CHUNK_SIZE = 64 * 1024;
11
+ const PHONE_KEYS = new Set(['phone', 'phonenumber', 'phone_number', 'tel', 'telephone']);
12
+
13
+ function isReadableStream(value: any): value is ReadableStream {
14
+ return value && typeof value.getReader === 'function';
15
+ }
16
+
17
+ function isAsyncIterable(value: any): value is AsyncIterable<any> {
18
+ return value && typeof value[Symbol.asyncIterator] === 'function';
19
+ }
20
+
21
+ function isIterable(value: any): value is Iterable<any> {
22
+ return value && typeof value[Symbol.iterator] === 'function';
23
+ }
24
+
25
+ function createReadableStreamFromIterator<T>(iterator: AsyncIterator<T>): ReadableStream<T> {
26
+ return new ReadableStream({
27
+ async pull(controller) {
28
+ try {
29
+ const { value, done } = await iterator.next();
30
+ if (done) {
31
+ controller.close();
32
+ return;
33
+ }
34
+ controller.enqueue(value);
35
+ } catch (error) {
36
+ controller.error(error);
37
+ }
38
+ },
39
+ cancel() {
40
+ if (iterator.return) {
41
+ iterator.return();
42
+ }
43
+ }
44
+ });
45
+ }
46
+
47
+ function detectInputFormat(input: any, options: any): 'json' | 'ndjson' | 'csv' | 'unknown' {
48
+ if (options && options.inputFormat) {
49
+ return options.inputFormat;
50
+ }
51
+
52
+ if (typeof input === 'string') {
53
+ const trimmed = input.trim();
54
+ if (trimmed === '') {
55
+ return 'unknown';
56
+ }
57
+
58
+ // Проверка на NDJSON (каждая строка - валидный JSON)
59
+ if (trimmed.includes('\n')) {
60
+ const lines = trimmed.split('\n').filter(line => line.trim() !== '');
61
+ if (lines.length > 0) {
62
+ try {
63
+ JSON.parse(lines[0]);
64
+ return 'ndjson';
65
+ } catch {
66
+ // Не NDJSON
67
+ }
68
+ }
69
+ }
70
+
71
+ // Проверка на JSON
72
+ try {
73
+ const parsed = JSON.parse(trimmed);
74
+ if (Array.isArray(parsed) || (parsed && typeof parsed === 'object')) {
75
+ return 'json';
76
+ }
77
+ } catch {
78
+ // Не JSON
79
+ }
80
+
81
+ // Проверка на CSV
82
+ if (trimmed.includes(',') || trimmed.includes(';') || trimmed.includes('\t')) {
83
+ return 'csv';
84
+ }
85
+ }
86
+
87
+ return 'unknown';
88
+ }
89
+
90
+ function normalizeQuotesInField(value: string): string {
91
+ // Не нормализуем кавычки в JSON-строках - это ломает структуру JSON
92
+ // Проверяем, выглядит ли значение как JSON (объект или массив)
93
+ if ((value.startsWith('{') && value.endsWith('}')) ||
94
+ (value.startsWith('[') && value.endsWith(']'))) {
95
+ return value; // Возвращаем как есть для JSON
96
+ }
97
+
98
+ let normalized = value.replace(/"{2,}/g, '"');
99
+ // Убираем правило, которое ломает JSON: не заменяем "," на ","
100
+ // normalized = normalized.replace(/"\s*,\s*"/g, ',');
101
+ normalized = normalized.replace(/"\n/g, '\n').replace(/\n"/g, '\n');
102
+ if (normalized.length >= 2 && normalized.startsWith('"') && normalized.endsWith('"')) {
103
+ normalized = normalized.slice(1, -1);
104
+ }
105
+ return normalized;
106
+ }
107
+
108
+ function normalizePhoneValue(value: string): string {
109
+ const trimmed = value.trim();
110
+ if (trimmed === '') {
111
+ return trimmed;
112
+ }
113
+ return trimmed.replace(/["'\\]/g, '');
114
+ }
115
+
116
+ function normalizeValueForCsv(value: any, key: string | undefined, normalizeQuotes: boolean): any {
117
+ if (!normalizeQuotes || typeof value !== 'string') {
118
+ return value;
119
+ }
120
+ const base = normalizeQuotesInField(value);
121
+ if (key && PHONE_KEYS.has(String(key).toLowerCase())) {
122
+ return normalizePhoneValue(base);
123
+ }
124
+ return base;
125
+ }
126
+
127
+ async function* jsonToCsvChunkIterator(input: any, options: JsonToCsvOptions = {}): AsyncGenerator<string> {
128
+ const format = detectInputFormat(input, options);
129
+
130
+ if (format === 'csv') {
131
+ throw new ValidationError('Input appears to be CSV, not JSON');
132
+ }
133
+
134
+ // Вспомогательная функция для создания асинхронного итератора
135
+ function toAsyncIterator<T>(iterable: Iterable<T> | AsyncIterable<T>): AsyncIterator<T> {
136
+ if (isAsyncIterable(iterable)) {
137
+ return iterable[Symbol.asyncIterator]();
138
+ }
139
+
140
+ if (isIterable(iterable)) {
141
+ const syncIterator = iterable[Symbol.iterator]();
142
+ return {
143
+ next: () => Promise.resolve(syncIterator.next()),
144
+ return: syncIterator.return ? () => Promise.resolve(syncIterator.return!()) : undefined,
145
+ throw: syncIterator.throw ? (error: any) => Promise.resolve(syncIterator.throw!(error)) : undefined
146
+ };
147
+ }
148
+
149
+ throw new ValidationError('Input is not iterable');
150
+ }
151
+
152
+ let iterator: AsyncIterator<any>;
153
+
154
+ if (isAsyncIterable(input) || isIterable(input)) {
155
+ iterator = toAsyncIterator(input);
156
+ } else if (typeof input === 'string') {
157
+ const parsed = JSON.parse(input);
158
+ if (Array.isArray(parsed)) {
159
+ iterator = toAsyncIterator(parsed);
160
+ } else {
161
+ iterator = toAsyncIterator([parsed]);
162
+ }
163
+ } else if (Array.isArray(input)) {
164
+ iterator = toAsyncIterator(input);
165
+ } else {
166
+ iterator = toAsyncIterator([input]);
167
+ }
168
+
169
+ const delimiter = options.delimiter || ';';
170
+ const includeHeaders = options.includeHeaders !== false;
171
+ const preventInjection = options.preventCsvInjection !== false;
172
+ const normalizeQuotes = options.normalizeQuotes !== false;
173
+ const isPotentialFormula = (input: string): boolean => {
174
+ let idx = 0;
175
+ while (idx < input.length) {
176
+ const code = input.charCodeAt(idx);
177
+ if (code === 32 || code === 9 || code === 10 || code === 13 || code === 0xfeff) {
178
+ idx++;
179
+ continue;
180
+ }
181
+ break;
182
+ }
183
+ if (idx < input.length && (input[idx] === '"' || input[idx] === "'")) {
184
+ idx++;
185
+ while (idx < input.length) {
186
+ const code = input.charCodeAt(idx);
187
+ if (code === 32 || code === 9) {
188
+ idx++;
189
+ continue;
190
+ }
191
+ break;
192
+ }
193
+ }
194
+ if (idx >= input.length) {
195
+ return false;
196
+ }
197
+ const char = input[idx];
198
+ return char === '=' || char === '+' || char === '-' || char === '@';
199
+ };
200
+
201
+ let isFirstChunk = true;
202
+ let headers: string[] = [];
203
+
204
+ while (true) {
205
+ const { value, done } = await iterator.next();
206
+ if (done) break;
207
+
208
+ const item = value;
209
+
210
+ if (isFirstChunk) {
211
+ // Извлечение заголовков из первого элемента
212
+ headers = Object.keys(item);
213
+
214
+ if (includeHeaders) {
215
+ const headerLine = headers.map(header => {
216
+ const escaped = header.includes('"') ? `"${header.replace(/"/g, '""')}"` : header;
217
+ return preventInjection && isPotentialFormula(escaped) ? `'${escaped}` : escaped;
218
+ }).join(delimiter);
219
+
220
+ yield headerLine + '\n';
221
+ }
222
+
223
+ isFirstChunk = false;
224
+ }
225
+
226
+ const row = headers.map(header => {
227
+ const value = item[header];
228
+ const normalized = normalizeValueForCsv(value, header, normalizeQuotes);
229
+ const strValue = normalized === null || normalized === undefined ? '' : String(normalized);
230
+
231
+ if (strValue.includes('"') || strValue.includes('\n') || strValue.includes('\r') || strValue.includes(delimiter)) {
232
+ return `"${strValue.replace(/"/g, '""')}"`;
233
+ }
234
+
235
+ if (preventInjection && isPotentialFormula(strValue)) {
236
+ return `'${strValue}`;
237
+ }
238
+
239
+ return strValue;
240
+ }).join(delimiter);
241
+
242
+ yield row + '\n';
243
+ }
244
+ }
245
+
246
+ async function* jsonToNdjsonChunkIterator(input: any, options: any = {}): AsyncGenerator<string> {
247
+ const format = detectInputFormat(input, options);
248
+
249
+ // Вспомогательная функция для создания асинхронного итератора
250
+ function toAsyncIterator<T>(iterable: Iterable<T> | AsyncIterable<T>): AsyncIterator<T> {
251
+ if (isAsyncIterable(iterable)) {
252
+ return iterable[Symbol.asyncIterator]();
253
+ }
254
+
255
+ if (isIterable(iterable)) {
256
+ const syncIterator = iterable[Symbol.iterator]();
257
+ return {
258
+ next: () => Promise.resolve(syncIterator.next()),
259
+ return: syncIterator.return ? () => Promise.resolve(syncIterator.return!()) : undefined,
260
+ throw: syncIterator.throw ? (error: any) => Promise.resolve(syncIterator.throw!(error)) : undefined
261
+ };
262
+ }
263
+
264
+ throw new ValidationError('Input is not iterable');
265
+ }
266
+
267
+ let iterator: AsyncIterator<any>;
268
+
269
+ if (isAsyncIterable(input) || isIterable(input)) {
270
+ iterator = toAsyncIterator(input);
271
+ } else if (typeof input === 'string') {
272
+ if (format === 'ndjson') {
273
+ const lines = input.split('\n').filter(line => line.trim() !== '');
274
+ iterator = toAsyncIterator(lines);
275
+ } else {
276
+ const parsed = JSON.parse(input);
277
+ if (Array.isArray(parsed)) {
278
+ iterator = toAsyncIterator(parsed);
279
+ } else {
280
+ iterator = toAsyncIterator([parsed]);
281
+ }
282
+ }
283
+ } else if (Array.isArray(input)) {
284
+ iterator = toAsyncIterator(input);
285
+ } else {
286
+ iterator = toAsyncIterator([input]);
287
+ }
288
+
289
+ while (true) {
290
+ const { value, done } = await iterator.next();
291
+ if (done) break;
292
+
293
+ let jsonStr: string;
294
+
295
+ if (typeof value === 'string') {
296
+ try {
297
+ // Проверяем, является ли строка валидным JSON
298
+ JSON.parse(value);
299
+ jsonStr = value;
300
+ } catch {
301
+ // Если нет, сериализуем как JSON
302
+ jsonStr = JSON.stringify(value);
303
+ }
304
+ } else {
305
+ jsonStr = JSON.stringify(value);
306
+ }
307
+
308
+ yield jsonStr + '\n';
309
+ }
310
+ }
311
+
312
+ async function* csvToJsonChunkIterator(input: any, options: CsvToJsonOptions = {}): AsyncGenerator<any> {
313
+ if (typeof input === 'string') {
314
+ // Используем csvToJsonIterator из csv-to-json-browser
315
+ yield* csvToJsonIterator(input, options);
316
+ } else if (input instanceof File || input instanceof Blob) {
317
+ const text = await input.text();
318
+ yield* csvToJsonIterator(text, options);
319
+ } else if (isReadableStream(input)) {
320
+ const reader = input.getReader();
321
+ const decoder = new TextDecoder();
322
+ let buffer = '';
323
+
324
+ try {
325
+ while (true) {
326
+ const { value, done } = await reader.read();
327
+ if (done) break;
328
+
329
+ buffer += decoder.decode(value, { stream: true });
330
+
331
+ // Обработка буфера по строкам
332
+ const lines = buffer.split('\n');
333
+ buffer = lines.pop() || '';
334
+
335
+ // TODO: Реализовать парсинг CSV из чанков
336
+ // Пока просто возвращаем сырые строки
337
+ for (const line of lines) {
338
+ if (line.trim()) {
339
+ yield { raw: line };
340
+ }
341
+ }
342
+ }
343
+
344
+ // Обработка остатка буфера
345
+ if (buffer.trim()) {
346
+ yield { raw: buffer };
347
+ }
348
+ } finally {
349
+ reader.releaseLock();
350
+ }
351
+ } else {
352
+ throw new ValidationError('Unsupported input type for CSV streaming');
353
+ }
354
+ }
355
+
356
+ export function jsonToCsvStream(input: any, options: JsonToCsvOptions = {}): ReadableStream<string> {
357
+ const iterator = jsonToCsvChunkIterator(input, options);
358
+ return createReadableStreamFromIterator(iterator);
359
+ }
360
+
361
+ export function jsonToNdjsonStream(input: any, options: any = {}): ReadableStream<string> {
362
+ const iterator = jsonToNdjsonChunkIterator(input, options);
363
+ return createReadableStreamFromIterator(iterator);
364
+ }
365
+
366
+ export function csvToJsonStream(input: any, options: CsvToJsonOptions = {}): ReadableStream<any> {
367
+ const iterator = csvToJsonChunkIterator(input, options);
368
+ return createReadableStreamFromIterator(iterator);
369
+ }
370
+
371
+ /**
372
+ * Асинхронная версия jsonToCsvStream
373
+ */
374
+ export async function jsonToCsvStreamAsync(input: any, options: JsonToCsvOptions = {}): Promise<ReadableStream<string>> {
375
+ return jsonToCsvStream(input, options);
376
+ }
377
+
378
+ /**
379
+ * Асинхронная версия jsonToNdjsonStream
380
+ */
381
+ export async function jsonToNdjsonStreamAsync(input: any, options: any = {}): Promise<ReadableStream<string>> {
382
+ return jsonToNdjsonStream(input, options);
383
+ }
384
+
385
+ /**
386
+ * Асинхронная версия csvToJsonStream
387
+ */
388
+ export async function csvToJsonStreamAsync(input: any, options: CsvToJsonOptions = {}): Promise<ReadableStream<any>> {
389
+ return csvToJsonStream(input, options);
390
+ }
391
+
392
+ // Экспорт для Node.js совместимости
393
+ if (typeof module !== 'undefined' && module.exports) {
394
+ module.exports = {
395
+ jsonToCsvStream,
396
+ jsonToCsvStreamAsync,
397
+ jsonToNdjsonStream,
398
+ jsonToNdjsonStreamAsync,
399
+ csvToJsonStream,
400
+ csvToJsonStreamAsync,
401
+ createReadableStreamFromIterator
402
+ };
403
+ }
@@ -2,8 +2,8 @@
2
2
  // Работает в отдельном потоке, не блокируя основной
3
3
 
4
4
  // Импорт функций парсинга (они будут bundled вместе с worker)
5
- import { csvToJson } from '../csv-to-json-browser.js';
6
- import { jsonToCsv } from '../json-to-csv-browser.js';
5
+ import { csvToJson } from '../csv-to-json-browser';
6
+ import { jsonToCsv } from '../json-to-csv-browser';
7
7
 
8
8
  const textDecoder = new TextDecoder('utf-8');
9
9
 
@@ -374,4 +374,4 @@ if (typeof module !== 'undefined' && module.exports) {
374
374
  getStats,
375
375
  clearCache
376
376
  };
377
- }
377
+ }
@@ -1,7 +1,7 @@
1
1
  // Worker Pool для параллельной обработки CSV
2
2
  // Использует Comlink для простой коммуникации с Web Workers
3
3
 
4
- import { ValidationError, ConfigurationError } from '../errors-browser.js';
4
+ import { ValidationError, ConfigurationError } from '../errors-browser';
5
5
 
6
6
  // Проверка поддержки Web Workers
7
7
  const WORKERS_SUPPORTED = typeof Worker !== 'undefined';
@@ -37,8 +37,15 @@ function collectTransferables(args) {
37
37
  };
38
38
 
39
39
  args.forEach(collectFromValue);
40
- return transferables.length ? transferables : null;
41
- }
40
+ return transferables.length ? transferables : null;
41
+ }
42
+
43
+ type WorkerWithMeta = Worker & {
44
+ id: string;
45
+ status: 'idle' | 'busy' | 'error';
46
+ lastUsed: number;
47
+ taskId: string | null;
48
+ };
42
49
 
43
50
  /**
44
51
  * Опции для Worker Pool
@@ -72,8 +79,14 @@ function collectTransferables(args) {
72
79
  /**
73
80
  * Worker Pool для параллельной обработки CSV
74
81
  */
75
- export class WorkerPool {
76
- /**
82
+ export class WorkerPool {
83
+ workerScript: string;
84
+ options: any;
85
+ workers: WorkerWithMeta[];
86
+ taskQueue: any[];
87
+ activeTasks: Map<any, any>;
88
+ stats: any;
89
+ /**
77
90
  * Создает новый Worker Pool
78
91
  * @param {string} workerScript - URL скрипта worker
79
92
  * @param {WorkerPoolOptions} [options] - Опции pool
@@ -127,7 +140,7 @@ export class WorkerPool {
127
140
  */
128
141
  createWorker() {
129
142
  try {
130
- const worker = new Worker(this.workerScript, { type: 'module' });
143
+ const worker = new Worker(this.workerScript, { type: 'module' }) as WorkerWithMeta;
131
144
 
132
145
  worker.id = `worker-${this.workers.length}`;
133
146
  worker.status = 'idle';
@@ -229,11 +242,11 @@ export class WorkerPool {
229
242
 
230
243
  // Завершение с ошибкой
231
244
  const workerError = new Error(errorData.message || 'Ошибка в worker');
232
- if (errorData.code) {
233
- workerError.code = errorData.code;
245
+ if ((errorData as any).code) {
246
+ (workerError as any).code = (errorData as any).code;
234
247
  }
235
- if (errorData.details) {
236
- workerError.details = errorData.details;
248
+ if ((errorData as any).details) {
249
+ (workerError as any).details = (errorData as any).details;
237
250
  }
238
251
  task.reject(workerError);
239
252
  this.activeTasks.delete(taskId);
@@ -363,9 +376,9 @@ export class WorkerPool {
363
376
  * @param {Array} args - Аргументы метода
364
377
  * @param {Object} [options] - Опции задачи
365
378
  * @param {Function} [onProgress] - Callback прогресса
366
- * @returns {Promise<any>} Результат выполнения
379
+ * @returns {Promise<unknown>} Результат выполнения
367
380
  */
368
- async exec(method, args = [], options = {}, onProgress = null) {
381
+ async exec(method, args = [], options: any = {}, onProgress = null) {
369
382
  return new Promise((resolve, reject) => {
370
383
  // Проверка размера очереди
371
384
  if (this.taskQueue.length >= this.options.maxQueueSize) {
@@ -457,10 +470,16 @@ export class WorkerPool {
457
470
  * @param {WorkerPoolOptions} [options] - Опции pool
458
471
  * @returns {WorkerPool} Worker Pool
459
472
  */
460
- export function createWorkerPool(options = {}) {
473
+ export function createWorkerPool(options: any = {}): any {
461
474
  // Используем встроенный worker скрипт
462
- const workerScript = new URL('./csv-parser.worker.js', import.meta.url).href;
463
- return new WorkerPool(workerScript, options);
475
+ const baseUrl =
476
+ typeof document !== 'undefined'
477
+ ? document.baseURI
478
+ : (typeof self !== 'undefined' && (self as any).location
479
+ ? (self as any).location.href
480
+ : '');
481
+ const workerScript = new URL('./csv-parser.worker.js', baseUrl).href;
482
+ return new WorkerPool(workerScript, options);
464
483
  }
465
484
 
466
485
  /**
@@ -470,13 +489,14 @@ export function createWorkerPool(options = {}) {
470
489
  * @param {Function} [onProgress] - Callback прогресса
471
490
  * @returns {Promise<Array<Object>>} JSON данные
472
491
  */
473
- export async function parseCSVWithWorker(csvInput, options = {}, onProgress = null) {
492
+ export async function parseCSVWithWorker(csvInput, options: any = {}, onProgress = null) {
474
493
  // Создание pool если нужно
475
- if (!parseCSVWithWorker.pool) {
476
- parseCSVWithWorker.pool = createWorkerPool();
477
- }
478
-
479
- const pool = parseCSVWithWorker.pool;
494
+ const poolHolder = parseCSVWithWorker as any;
495
+ if (!poolHolder.pool) {
496
+ poolHolder.pool = createWorkerPool();
497
+ }
498
+
499
+ const pool = poolHolder.pool;
480
500
 
481
501
  // Подготовка CSV строки
482
502
  // ?????????? CSV ??????
@@ -508,20 +528,21 @@ export async function parseCSVWithWorker(csvInput, options = {}, onProgress = nu
508
528
  * Чтение файла как текст
509
529
  * @private
510
530
  */
511
- async function readFileAsArrayBuffer(file) {
531
+ async function readFileAsArrayBuffer(file: File): Promise<ArrayBuffer> {
512
532
  return new Promise((resolve, reject) => {
513
533
  const reader = new FileReader();
514
- reader.onload = (event) => resolve(event.target.result);
534
+ reader.onload = (event) =>
535
+ resolve((event.target as FileReader).result as ArrayBuffer);
515
536
  reader.onerror = (error) => reject(error);
516
537
  reader.readAsArrayBuffer(file);
517
538
  });
518
539
  }
519
540
 
520
541
  // Экспорт для Node.js совместимости
521
- if (typeof module !== 'undefined' && module.exports) {
522
- module.exports = {
523
- WorkerPool,
524
- createWorkerPool,
525
- parseCSVWithWorker
526
- };
527
- }
542
+ if (typeof module !== 'undefined' && module.exports) {
543
+ module.exports = {
544
+ WorkerPool,
545
+ createWorkerPool,
546
+ parseCSVWithWorker
547
+ };
548
+ }