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,400 @@
1
+ /**
2
+ * Plugin System для JTCSV
3
+ * Middleware-like архитектура с поддержкой hooks и плагинов
4
+ *
5
+ * @version 1.0.0
6
+ * @date 2026-01-22
7
+ */
8
+
9
+ interface PluginStats {
10
+ pluginLoads: number;
11
+ hookExecutions: number;
12
+ middlewareExecutions: number;
13
+ }
14
+
15
+ interface Plugin {
16
+ name: string;
17
+ version?: string;
18
+ description?: string;
19
+ hooks?: Record<string, Function>;
20
+ middlewares?: Function[];
21
+ init?: (manager: PluginManager) => void;
22
+ destroy?: () => void;
23
+ }
24
+
25
+ type HookName =
26
+ | 'before:csvToJson'
27
+ | 'after:csvToJson'
28
+ | 'before:jsonToCsv'
29
+ | 'after:jsonToCsv'
30
+ | 'before:parse'
31
+ | 'after:parse'
32
+ | 'before:serialize'
33
+ | 'after:serialize'
34
+ | 'error'
35
+ | 'validation'
36
+ | 'transformation'
37
+ | string;
38
+
39
+ export class PluginManager {
40
+ private plugins: Map<string, Plugin>;
41
+ private hooks: Map<HookName, Function[]>;
42
+ private middlewares: Function[];
43
+ private context: Record<string, any>;
44
+ private stats: PluginStats;
45
+
46
+ constructor() {
47
+ this.plugins = new Map();
48
+ this.hooks = new Map();
49
+ this.middlewares = [];
50
+ this.context = {};
51
+ this.stats = {
52
+ pluginLoads: 0,
53
+ hookExecutions: 0,
54
+ middlewareExecutions: 0
55
+ };
56
+
57
+ // Регистрируем стандартные hooks
58
+ this._registerDefaultHooks();
59
+ }
60
+
61
+ /**
62
+ * Backwards-compatible alias for registerPlugin.
63
+ */
64
+ use(name: string, plugin: Plugin): void {
65
+ this.registerPlugin(name, plugin);
66
+ }
67
+
68
+ /**
69
+ * Регистрирует стандартные hooks
70
+ */
71
+ private _registerDefaultHooks(): void {
72
+ const defaultHooks: HookName[] = [
73
+ 'before:csvToJson',
74
+ 'after:csvToJson',
75
+ 'before:jsonToCsv',
76
+ 'after:jsonToCsv',
77
+ 'before:parse',
78
+ 'after:parse',
79
+ 'before:serialize',
80
+ 'after:serialize',
81
+ 'error',
82
+ 'validation',
83
+ 'transformation'
84
+ ];
85
+
86
+ defaultHooks.forEach(hook => {
87
+ this.hooks.set(hook, []);
88
+ });
89
+ }
90
+
91
+ /**
92
+ * Регистрирует плагин
93
+ * @param name - Уникальное имя плагина
94
+ * @param plugin - Объект плагина
95
+ */
96
+ registerPlugin(name: string, plugin: Plugin): void {
97
+ if (this.plugins.has(name)) {
98
+ throw new Error(`Plugin "${name}" already registered`);
99
+ }
100
+
101
+ // Добавляем плагин
102
+ this.plugins.set(name, plugin);
103
+ this.stats.pluginLoads++;
104
+
105
+ // Регистрируем hooks плагина
106
+ if (plugin.hooks) {
107
+ Object.entries(plugin.hooks).forEach(([hookName, handler]) => {
108
+ this.registerHook(hookName as HookName, handler);
109
+ });
110
+ }
111
+
112
+ // Регистрируем middleware плагина
113
+ if (plugin.middlewares) {
114
+ plugin.middlewares.forEach(middleware => {
115
+ this.registerMiddleware(middleware);
116
+ });
117
+ }
118
+
119
+ // Вызываем init если есть
120
+ if (plugin.init) {
121
+ plugin.init(this);
122
+ }
123
+
124
+ console.log(`✅ Plugin "${name}" registered successfully`);
125
+ }
126
+
127
+ /**
128
+ * Регистрирует hook
129
+ * @param hookName - Имя hook
130
+ * @param handler - Функция обработчик
131
+ */
132
+ registerHook(hookName: HookName, handler: Function): void {
133
+ if (!this.hooks.has(hookName)) {
134
+ this.hooks.set(hookName, []);
135
+ }
136
+
137
+ const handlers = this.hooks.get(hookName)!;
138
+ handlers.push(handler);
139
+ }
140
+
141
+ /**
142
+ * Регистрирует middleware
143
+ * @param middleware - Функция middleware
144
+ */
145
+ registerMiddleware(middleware: Function): void {
146
+ this.middlewares.push(middleware);
147
+ }
148
+
149
+ /**
150
+ * Выполняет hook
151
+ * @param hookName - Имя hook
152
+ * @param data - Данные для обработки
153
+ * @param context - Контекст выполнения
154
+ */
155
+ async executeHook(hookName: HookName, data: any, context: any = {}): Promise<any> {
156
+ const handlers = this.hooks.get(hookName);
157
+
158
+ if (!handlers || handlers.length === 0) {
159
+ return data;
160
+ }
161
+
162
+ this.stats.hookExecutions++;
163
+ let result = data;
164
+
165
+ // Выполняем все handlers последовательно
166
+ for (const handler of handlers) {
167
+ try {
168
+ result = await handler(result, { ...this.context, ...context, hookName });
169
+ } catch (error) {
170
+ console.error(`Error in hook "${hookName}":`, error);
171
+
172
+ // Выполняем error hook если есть
173
+ const errorHandlers = this.hooks.get('error');
174
+ if (errorHandlers && errorHandlers.length > 0) {
175
+ for (const errorHandler of errorHandlers) {
176
+ try {
177
+ errorHandler(error, { ...this.context, ...context, hookName, data: result });
178
+ } catch {
179
+ // Игнорируем ошибки в error handlers
180
+ }
181
+ }
182
+ }
183
+ }
184
+ }
185
+
186
+ return result;
187
+ }
188
+
189
+ /**
190
+ * Backwards-compatible alias for executeHook.
191
+ */
192
+ async executeHooks(hookName: HookName, data: any, context: any = {}): Promise<any> {
193
+ return this.executeHook(hookName, data, context);
194
+ }
195
+
196
+ /**
197
+ * Executes an operation with before/after hooks and middleware.
198
+ */
199
+ async executeWithPlugins(
200
+ operation: string,
201
+ input: any,
202
+ options: any,
203
+ handler: (input: any, options: any) => any | Promise<any>
204
+ ): Promise<any> {
205
+ const context = { operation, options, metadata: {} as Record<string, any> };
206
+ const beforeHook = `before:${operation}` as HookName;
207
+ const afterHook = `after:${operation}` as HookName;
208
+
209
+ const beforeInput = await this.executeHook(beforeHook, input, context);
210
+ const middlewareInput = { input: beforeInput, options, operation, metadata: context.metadata };
211
+ const middlewareResult = await this.executeMiddleware(middlewareInput, context);
212
+ const result = await handler(middlewareResult.input ?? beforeInput, options);
213
+ return this.executeHook(afterHook, result, context);
214
+ }
215
+
216
+ /**
217
+ * Returns registered plugin names.
218
+ */
219
+ listPlugins(): string[] {
220
+ return Array.from(this.plugins.keys());
221
+ }
222
+
223
+ /**
224
+ * Выполняет цепочку middleware
225
+ * @param input - Входные данные
226
+ * @param context - Контекст выполнения
227
+ */
228
+ async executeMiddleware(input: any, context: any = {}): Promise<any> {
229
+ if (this.middlewares.length === 0) {
230
+ return input;
231
+ }
232
+
233
+ this.stats.middlewareExecutions++;
234
+ let result = input;
235
+
236
+ // Выполняем middleware последовательно
237
+ for (const middleware of this.middlewares) {
238
+ try {
239
+ result = await middleware(result, { ...this.context, ...context });
240
+ } catch (error) {
241
+ console.error('Error in middleware:', error);
242
+
243
+ // Выполняем error hook если есть
244
+ const errorHandlers = this.hooks.get('error');
245
+ if (errorHandlers && errorHandlers.length > 0) {
246
+ for (const errorHandler of errorHandlers) {
247
+ try {
248
+ errorHandler(error, { ...this.context, ...context, data: result });
249
+ } catch {
250
+ // Игнорируем ошибки в error handlers
251
+ }
252
+ }
253
+ }
254
+
255
+ throw error;
256
+ }
257
+ }
258
+
259
+ return result;
260
+ }
261
+
262
+ /**
263
+ * Устанавливает контекст
264
+ * @param key - Ключ контекста
265
+ * @param value - Значение
266
+ */
267
+ setContext(key: string, value: any): void {
268
+ this.context[key] = value;
269
+ }
270
+
271
+ /**
272
+ * Получает контекст
273
+ * @param key - Ключ контекста (опционально)
274
+ */
275
+ getContext(key?: string): any {
276
+ if (key) {
277
+ return this.context[key];
278
+ }
279
+ return { ...this.context };
280
+ }
281
+
282
+ /**
283
+ * Возвращает статистику
284
+ */
285
+ getStats(): PluginStats {
286
+ return { ...this.stats };
287
+ }
288
+
289
+ /**
290
+ * Возвращает список зарегистрированных плагинов
291
+ */
292
+ getPlugins(): string[] {
293
+ return Array.from(this.plugins.keys());
294
+ }
295
+
296
+ /**
297
+ * Возвращает список зарегистрированных hooks
298
+ */
299
+ getHooks(): HookName[] {
300
+ return Array.from(this.hooks.keys());
301
+ }
302
+
303
+ /**
304
+ * Удаляет плагин
305
+ * @param name - Имя плагина
306
+ */
307
+ unregisterPlugin(name: string): boolean {
308
+ const plugin = this.plugins.get(name);
309
+
310
+ if (!plugin) {
311
+ return false;
312
+ }
313
+
314
+ // Вызываем destroy если есть
315
+ if (plugin.destroy) {
316
+ try {
317
+ plugin.destroy();
318
+ } catch (error) {
319
+ console.error(`Error destroying plugin "${name}":`, error);
320
+ }
321
+ }
322
+
323
+ // Удаляем плагин
324
+ this.plugins.delete(name);
325
+
326
+ // TODO: Удалить связанные hooks и middleware
327
+
328
+ console.log(`🗑️ Plugin "${name}" unregistered`);
329
+ return true;
330
+ }
331
+
332
+ /**
333
+ * Очищает все плагины и hooks
334
+ */
335
+ clear(): void {
336
+ // Вызываем destroy для всех плагинов
337
+ this.plugins.forEach((plugin, name) => {
338
+ if (plugin.destroy) {
339
+ try {
340
+ plugin.destroy();
341
+ } catch (error) {
342
+ console.error(`Error destroying plugin "${name}":`, error);
343
+ }
344
+ }
345
+ });
346
+
347
+ this.plugins.clear();
348
+ this.hooks.clear();
349
+ this.middlewares = [];
350
+ this.context = {};
351
+
352
+ // Регистрируем стандартные hooks заново
353
+ this._registerDefaultHooks();
354
+
355
+ console.log('🧹 Plugin system cleared');
356
+ }
357
+
358
+ /**
359
+ * Асинхронная версия executeHook
360
+ */
361
+ async executeHookAsync(hookName: HookName, data: any, context: any = {}): Promise<any> {
362
+ return this.executeHook(hookName, data, context);
363
+ }
364
+
365
+ /**
366
+ * Асинхронная версия executeMiddleware
367
+ */
368
+ async executeMiddlewareAsync(input: any, context: any = {}): Promise<any> {
369
+ return this.executeMiddleware(input, context);
370
+ }
371
+ }
372
+
373
+ // Создание глобального экземпляра PluginManager
374
+ let globalPluginManager: PluginManager | null = null;
375
+
376
+ /**
377
+ * Возвращает глобальный экземпляр PluginManager
378
+ */
379
+ export function getGlobalPluginManager(): PluginManager {
380
+ if (!globalPluginManager) {
381
+ globalPluginManager = new PluginManager();
382
+ }
383
+ return globalPluginManager;
384
+ }
385
+
386
+ /**
387
+ * Асинхронная версия getGlobalPluginManager
388
+ */
389
+ export async function getGlobalPluginManagerAsync(): Promise<PluginManager> {
390
+ return getGlobalPluginManager();
391
+ }
392
+
393
+ // Экспорт для CommonJS
394
+ if (typeof module !== 'undefined' && module.exports) {
395
+ module.exports = {
396
+ PluginManager,
397
+ getGlobalPluginManager,
398
+ getGlobalPluginManagerAsync
399
+ };
400
+ }