jtcsv 2.2.7 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/README.md +31 -1
  2. package/bin/jtcsv.js +891 -821
  3. package/bin/jtcsv.ts +2534 -0
  4. package/csv-to-json.js +168 -145
  5. package/dist/jtcsv-core.cjs.js +1407 -0
  6. package/dist/jtcsv-core.cjs.js.map +1 -0
  7. package/dist/jtcsv-core.esm.js +1379 -0
  8. package/dist/jtcsv-core.esm.js.map +1 -0
  9. package/dist/jtcsv-core.umd.js +1413 -0
  10. package/dist/jtcsv-core.umd.js.map +1 -0
  11. package/dist/jtcsv-full.cjs.js +1912 -0
  12. package/dist/jtcsv-full.cjs.js.map +1 -0
  13. package/dist/jtcsv-full.esm.js +1880 -0
  14. package/dist/jtcsv-full.esm.js.map +1 -0
  15. package/dist/jtcsv-full.umd.js +1918 -0
  16. package/dist/jtcsv-full.umd.js.map +1 -0
  17. package/dist/jtcsv-workers.esm.js +759 -0
  18. package/dist/jtcsv-workers.esm.js.map +1 -0
  19. package/dist/jtcsv-workers.umd.js +773 -0
  20. package/dist/jtcsv-workers.umd.js.map +1 -0
  21. package/dist/jtcsv.cjs.js +61 -19
  22. package/dist/jtcsv.cjs.js.map +1 -1
  23. package/dist/jtcsv.esm.js +61 -19
  24. package/dist/jtcsv.esm.js.map +1 -1
  25. package/dist/jtcsv.umd.js +61 -19
  26. package/dist/jtcsv.umd.js.map +1 -1
  27. package/errors.js +188 -2
  28. package/examples/advanced/conditional-transformations.js +446 -0
  29. package/examples/advanced/conditional-transformations.ts +446 -0
  30. package/examples/advanced/csv-parser.worker.js +89 -0
  31. package/examples/advanced/csv-parser.worker.ts +89 -0
  32. package/examples/advanced/nested-objects-example.js +306 -0
  33. package/examples/advanced/nested-objects-example.ts +306 -0
  34. package/examples/advanced/performance-optimization.js +504 -0
  35. package/examples/advanced/performance-optimization.ts +504 -0
  36. package/examples/advanced/run-demo-server.js +116 -0
  37. package/examples/advanced/run-demo-server.ts +116 -0
  38. package/examples/advanced/web-worker-usage.html +874 -0
  39. package/examples/async-multithreaded-example.ts +335 -0
  40. package/examples/cli-advanced-usage.md +288 -0
  41. package/examples/cli-batch-processing.ts +38 -0
  42. package/examples/cli-tool.js +0 -3
  43. package/examples/cli-tool.ts +183 -0
  44. package/examples/error-handling.js +21 -7
  45. package/examples/error-handling.ts +356 -0
  46. package/examples/express-api.js +0 -3
  47. package/examples/express-api.ts +164 -0
  48. package/examples/large-dataset-example.js +0 -3
  49. package/examples/large-dataset-example.ts +204 -0
  50. package/examples/ndjson-processing.js +1 -1
  51. package/examples/ndjson-processing.ts +456 -0
  52. package/examples/plugin-excel-exporter.js +3 -4
  53. package/examples/plugin-excel-exporter.ts +406 -0
  54. package/examples/react-integration.tsx +637 -0
  55. package/examples/schema-validation.ts +640 -0
  56. package/examples/simple-usage.js +254 -254
  57. package/examples/simple-usage.ts +194 -0
  58. package/examples/streaming-example.js +4 -5
  59. package/examples/streaming-example.ts +419 -0
  60. package/examples/web-workers-advanced.ts +28 -0
  61. package/index.d.ts +1 -3
  62. package/index.js +15 -1
  63. package/json-save.js +9 -3
  64. package/json-to-csv.js +168 -21
  65. package/package.json +69 -10
  66. package/plugins/express-middleware/README.md +21 -2
  67. package/plugins/express-middleware/example.js +3 -4
  68. package/plugins/express-middleware/example.ts +135 -0
  69. package/plugins/express-middleware/index.d.ts +1 -1
  70. package/plugins/express-middleware/index.js +270 -118
  71. package/plugins/express-middleware/index.ts +557 -0
  72. package/plugins/fastify-plugin/index.js +2 -4
  73. package/plugins/fastify-plugin/index.ts +443 -0
  74. package/plugins/hono/index.ts +226 -0
  75. package/plugins/nestjs/index.ts +201 -0
  76. package/plugins/nextjs-api/examples/ConverterComponent.tsx +386 -0
  77. package/plugins/nextjs-api/examples/api-convert.js +0 -2
  78. package/plugins/nextjs-api/examples/api-convert.ts +67 -0
  79. package/plugins/nextjs-api/index.tsx +339 -0
  80. package/plugins/nextjs-api/route.js +2 -3
  81. package/plugins/nextjs-api/route.ts +370 -0
  82. package/plugins/nuxt/index.ts +94 -0
  83. package/plugins/nuxt/runtime/composables/useJtcsv.ts +100 -0
  84. package/plugins/nuxt/runtime/plugin.ts +71 -0
  85. package/plugins/remix/index.js +1 -1
  86. package/plugins/remix/index.ts +260 -0
  87. package/plugins/sveltekit/index.js +1 -1
  88. package/plugins/sveltekit/index.ts +301 -0
  89. package/plugins/trpc/index.ts +267 -0
  90. package/src/browser/browser-functions.ts +402 -0
  91. package/src/browser/core.js +92 -0
  92. package/src/browser/core.ts +152 -0
  93. package/src/browser/csv-to-json-browser.d.ts +3 -0
  94. package/src/browser/csv-to-json-browser.js +36 -14
  95. package/src/browser/csv-to-json-browser.ts +264 -0
  96. package/src/browser/errors-browser.ts +303 -0
  97. package/src/browser/extensions/plugins.js +92 -0
  98. package/src/browser/extensions/plugins.ts +93 -0
  99. package/src/browser/extensions/workers.js +39 -0
  100. package/src/browser/extensions/workers.ts +39 -0
  101. package/src/browser/globals.d.ts +5 -0
  102. package/src/browser/index.ts +192 -0
  103. package/src/browser/json-to-csv-browser.d.ts +3 -0
  104. package/src/browser/json-to-csv-browser.js +13 -3
  105. package/src/browser/json-to-csv-browser.ts +262 -0
  106. package/src/browser/streams.js +12 -2
  107. package/src/browser/streams.ts +336 -0
  108. package/src/browser/workers/csv-parser.worker.ts +377 -0
  109. package/src/browser/workers/worker-pool.ts +548 -0
  110. package/src/core/delimiter-cache.js +22 -8
  111. package/src/core/delimiter-cache.ts +310 -0
  112. package/src/core/node-optimizations.ts +449 -0
  113. package/src/core/plugin-system.js +29 -11
  114. package/src/core/plugin-system.ts +400 -0
  115. package/src/core/transform-hooks.ts +558 -0
  116. package/src/engines/fast-path-engine-new.ts +347 -0
  117. package/src/engines/fast-path-engine.ts +854 -0
  118. package/src/errors.ts +72 -0
  119. package/src/formats/ndjson-parser.ts +469 -0
  120. package/src/formats/tsv-parser.ts +334 -0
  121. package/src/index-with-plugins.js +16 -9
  122. package/src/index-with-plugins.ts +395 -0
  123. package/src/types/index.ts +255 -0
  124. package/src/utils/bom-utils.js +259 -0
  125. package/src/utils/bom-utils.ts +373 -0
  126. package/src/utils/encoding-support.js +124 -0
  127. package/src/utils/encoding-support.ts +155 -0
  128. package/src/utils/schema-validator.js +19 -19
  129. package/src/utils/schema-validator.ts +819 -0
  130. package/src/utils/transform-loader.js +1 -1
  131. package/src/utils/transform-loader.ts +389 -0
  132. package/src/utils/zod-adapter.js +170 -0
  133. package/src/utils/zod-adapter.ts +280 -0
  134. package/src/web-server/index.js +10 -10
  135. package/src/web-server/index.ts +683 -0
  136. package/src/workers/csv-multithreaded.ts +310 -0
  137. package/src/workers/csv-parser.worker.ts +227 -0
  138. package/src/workers/worker-pool.ts +409 -0
  139. package/stream-csv-to-json.js +26 -8
  140. package/stream-json-to-csv.js +1 -0
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Кэширование результатов авто-детектирования разделителя
3
+ * Использует WeakMap и LRU кэш для оптимизации производительности
4
+ *
5
+ * @version 1.0.0
6
+ * @date 2026-01-23
7
+ */
8
+
9
+ interface CacheStats {
10
+ hits: number;
11
+ misses: number;
12
+ evictions: number;
13
+ size: number;
14
+ }
15
+
16
+ interface CacheStatsWithRates extends CacheStats {
17
+ hitRate: number;
18
+ totalRequests: number;
19
+ }
20
+
21
+ export class DelimiterCache {
22
+ private weakMap: WeakMap<object, string>;
23
+ private lruCache: Map<string, string>;
24
+ private maxSize: number;
25
+ private stats: CacheStats;
26
+
27
+ constructor(maxSize: number = 100) {
28
+ this.weakMap = new WeakMap();
29
+ this.lruCache = new Map();
30
+ this.maxSize = maxSize;
31
+ this.stats = {
32
+ hits: 0,
33
+ misses: 0,
34
+ evictions: 0,
35
+ size: 0
36
+ };
37
+ }
38
+
39
+ /**
40
+ * Генерирует ключ кэша на основе строки и кандидатов
41
+ * @private
42
+ */
43
+ private _generateKey(csv: string, candidates: string[]): string {
44
+ // Используем хэш первых 1000 символов для производительности
45
+ const sample = csv.substring(0, Math.min(1000, csv.length));
46
+ const candidatesKey = candidates.join(',');
47
+ return `${this._hashString(sample)}:${candidatesKey}`;
48
+ }
49
+
50
+ /**
51
+ * Простая хэш-функция для строк
52
+ * @private
53
+ */
54
+ private _hashString(str: string): string {
55
+ let hash = 0;
56
+ for (let i = 0; i < str.length; i++) {
57
+ const char = str.charCodeAt(i);
58
+ hash = ((hash << 5) - hash) + char;
59
+ hash = hash & hash; // Convert to 32bit integer
60
+ }
61
+ return hash.toString(36);
62
+ }
63
+
64
+ /**
65
+ * Получает значение из кэша
66
+ * @param csv - CSV строка
67
+ * @param candidates - Кандидаты разделителей
68
+ * @returns Найденный разделитель или undefined
69
+ */
70
+ get(csv: string, candidates: string[]): string | null {
71
+ // Сначала проверяем WeakMap (для объектов)
72
+ if (typeof csv === 'object' && csv !== null) {
73
+ const result = this.weakMap.get(csv as any);
74
+ if (result !== undefined) {
75
+ this.stats.hits++;
76
+ return result;
77
+ }
78
+ }
79
+
80
+ // Проверяем LRU кэш
81
+ const key = this._generateKey(csv, candidates);
82
+ const result = this.lruCache.get(key);
83
+
84
+ if (result !== undefined) {
85
+ // Обновляем позицию в LRU (перемещаем в конец)
86
+ this.lruCache.delete(key);
87
+ this.lruCache.set(key, result);
88
+ this.stats.hits++;
89
+ return result;
90
+ }
91
+
92
+ this.stats.misses++;
93
+ return null;
94
+ }
95
+
96
+ /**
97
+ * Сохраняет значение в кэш
98
+ * @param csv - CSV строка
99
+ * @param candidates - Кандидаты разделителей
100
+ * @param delimiter - Найденный разделитель
101
+ */
102
+ set(csv: string, candidates: string[], delimiter: string): void {
103
+ // Сохраняем в WeakMap для объектов
104
+ if (typeof csv === 'object' && csv !== null) {
105
+ this.weakMap.set(csv as any, delimiter);
106
+ }
107
+
108
+ // Сохраняем в LRU кэш
109
+ const key = this._generateKey(csv, candidates);
110
+
111
+ // Проверяем размер кэша
112
+ if (this.lruCache.size >= this.maxSize) {
113
+ // Удаляем самый старый элемент (первый в Map)
114
+ const firstKey = this.lruCache.keys().next().value;
115
+ if (firstKey) {
116
+ this.lruCache.delete(firstKey);
117
+ this.stats.evictions++;
118
+ }
119
+ }
120
+
121
+ this.lruCache.set(key, delimiter);
122
+ this.stats.size = this.lruCache.size;
123
+ }
124
+
125
+ /**
126
+ * Очищает кэш
127
+ */
128
+ clear(): void {
129
+ this.weakMap = new WeakMap();
130
+ this.lruCache.clear();
131
+ this.stats = {
132
+ hits: 0,
133
+ misses: 0,
134
+ evictions: 0,
135
+ size: 0
136
+ };
137
+ }
138
+
139
+ /**
140
+ * Возвращает статистику кэша
141
+ */
142
+ getStats(): CacheStatsWithRates {
143
+ const totalRequests = this.stats.hits + this.stats.misses;
144
+ return {
145
+ ...this.stats,
146
+ totalRequests,
147
+ hitRate: totalRequests > 0 ? (this.stats.hits / totalRequests) * 100 : 0
148
+ };
149
+ }
150
+
151
+ /**
152
+ * Проверяет наличие значения в кэше
153
+ */
154
+ has(csv: string, candidates: string[]): boolean {
155
+ if (typeof csv === 'object' && csv !== null) {
156
+ return this.weakMap.has(csv as any);
157
+ }
158
+
159
+ const key = this._generateKey(csv, candidates);
160
+ return this.lruCache.has(key);
161
+ }
162
+
163
+ /**
164
+ * Удаляет значение из кэша
165
+ */
166
+ delete(csv: string, candidates: string[]): boolean {
167
+ let deleted = false;
168
+
169
+ if (typeof csv === 'object' && csv !== null) {
170
+ deleted = this.weakMap.delete(csv as any);
171
+ }
172
+
173
+ const key = this._generateKey(csv, candidates);
174
+ if (this.lruCache.delete(key)) {
175
+ deleted = true;
176
+ this.stats.size = this.lruCache.size;
177
+ }
178
+
179
+ return deleted;
180
+ }
181
+
182
+ /**
183
+ * Возвращает размер кэша
184
+ */
185
+ get size(): number {
186
+ return this.lruCache.size;
187
+ }
188
+
189
+ /**
190
+ * Асинхронная версия get
191
+ */
192
+ async getAsync(csv: string, candidates: string[]): Promise<string | null> {
193
+ return this.get(csv, candidates);
194
+ }
195
+
196
+ /**
197
+ * Асинхронная версия set
198
+ */
199
+ async setAsync(csv: string, candidates: string[], delimiter: string): Promise<void> {
200
+ return this.set(csv, candidates, delimiter);
201
+ }
202
+
203
+ /**
204
+ * Асинхронная версия clear
205
+ */
206
+ async clearAsync(): Promise<void> {
207
+ return this.clear();
208
+ }
209
+
210
+ /**
211
+ * Оптимизированная версия autoDetectDelimiter с кэшированием
212
+ */
213
+ static autoDetectDelimiter(
214
+ csv: string,
215
+ candidates: string[] = [';', ',', '\t', '|'],
216
+ cache: DelimiterCache | null = null
217
+ ): string {
218
+ if (!csv || typeof csv !== 'string') {
219
+ return ';';
220
+ }
221
+
222
+ // Проверяем кэш если он предоставлен
223
+ if (cache) {
224
+ const cached = cache.get(csv, candidates);
225
+ if (cached !== null) {
226
+ return cached;
227
+ }
228
+ }
229
+
230
+ const lines = csv.split('\n').filter(line => line.trim().length > 0);
231
+
232
+ if (lines.length === 0) {
233
+ return ';';
234
+ }
235
+
236
+ // Используем первую непустую строку для детектирования
237
+ const firstLine = lines[0];
238
+
239
+ // Быстрый подсчёт вхождений кандидатов за один проход
240
+ const counts: Record<string, number> = {};
241
+ const candidateSet = new Set(candidates);
242
+ for (let i = 0; i < firstLine.length; i++) {
243
+ const char = firstLine[i];
244
+ if (candidateSet.has(char)) {
245
+ counts[char] = (counts[char] || 0) + 1;
246
+ }
247
+ }
248
+
249
+ for (const delim of candidates) {
250
+ if (!(delim in counts)) {
251
+ counts[delim] = 0;
252
+ }
253
+ }
254
+
255
+ let maxCount = -1;
256
+ let detectedDelimiter = ';';
257
+ const maxDelimiters: string[] = [];
258
+
259
+ for (const [delim, count] of Object.entries(counts)) {
260
+ if (count > maxCount) {
261
+ maxCount = count;
262
+ maxDelimiters.length = 0;
263
+ maxDelimiters.push(delim);
264
+ } else if (count === maxCount) {
265
+ maxDelimiters.push(delim);
266
+ }
267
+ }
268
+
269
+ if (maxCount === 0 || maxDelimiters.length > 1) {
270
+ detectedDelimiter = ';';
271
+ } else {
272
+ detectedDelimiter = maxDelimiters[0];
273
+ }
274
+
275
+ if (cache) {
276
+ cache.set(csv, candidates, detectedDelimiter);
277
+ }
278
+
279
+ return detectedDelimiter;
280
+ }
281
+ }
282
+
283
+ // Создание глобального экземпляра кэша
284
+ let globalCache: DelimiterCache | null = null;
285
+
286
+ /**
287
+ * Возвращает глобальный экземпляр кэша разделителей
288
+ */
289
+ export function getGlobalDelimiterCache(maxSize: number = 100): DelimiterCache {
290
+ if (!globalCache) {
291
+ globalCache = new DelimiterCache(maxSize);
292
+ }
293
+ return globalCache;
294
+ }
295
+
296
+ /**
297
+ * Асинхронная версия getGlobalDelimiterCache
298
+ */
299
+ export async function getGlobalDelimiterCacheAsync(maxSize: number = 100): Promise<DelimiterCache> {
300
+ return getGlobalDelimiterCache(maxSize);
301
+ }
302
+
303
+ // Экспорт для CommonJS
304
+ if (typeof module !== 'undefined' && module.exports) {
305
+ module.exports = {
306
+ DelimiterCache,
307
+ getGlobalDelimiterCache,
308
+ getGlobalDelimiterCacheAsync
309
+ };
310
+ }