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,566 @@
1
+ /**
2
+ * Transform Hooks System
3
+ * Система хуков для трансформации данных перед/после конвертации
4
+ *
5
+ * @version 2.0.0
6
+ * @date 2026-01-29
7
+ */
8
+
9
+ import { ValidationError } from '../errors';
10
+
11
+ export interface HookContext {
12
+ [key: string]: any;
13
+ }
14
+
15
+ export type HookFunction<T = any, R = any> = (data: T, context?: HookContext) => R;
16
+ export type PerRowHookFunction<T = any, R = any> = (row: T, index: number, context?: HookContext) => R;
17
+ export type AsyncHookFunction<T = any, R = any> = (data: T, context?: HookContext) => Promise<R>;
18
+ export type AsyncPerRowHookFunction<T = any, R = any> = (row: T, index: number, context?: HookContext) => Promise<R>;
19
+
20
+ export interface TransformHooksOptions {
21
+ hooks?: {
22
+ beforeConvert?: Array<HookFunction | AsyncHookFunction>;
23
+ afterConvert?: Array<HookFunction | AsyncHookFunction>;
24
+ perRow?: Array<PerRowHookFunction | AsyncPerRowHookFunction>;
25
+ };
26
+ }
27
+
28
+ export class TransformHooks {
29
+ private hooks: {
30
+ beforeConvert: Array<HookFunction | AsyncHookFunction>;
31
+ afterConvert: Array<HookFunction | AsyncHookFunction>;
32
+ perRow: Array<PerRowHookFunction | AsyncPerRowHookFunction>;
33
+ };
34
+
35
+ constructor(options?: TransformHooksOptions) {
36
+ this.hooks = {
37
+ beforeConvert: options?.hooks?.beforeConvert || [],
38
+ afterConvert: options?.hooks?.afterConvert || [],
39
+ perRow: options?.hooks?.perRow || []
40
+ };
41
+ }
42
+
43
+ /**
44
+ * Регистрирует хук beforeConvert
45
+ * @param hook - Функция хука
46
+ * @returns this для цепочки вызовов
47
+ */
48
+ beforeConvert(hook: HookFunction | AsyncHookFunction): this {
49
+ if (typeof hook !== 'function') {
50
+ throw new ValidationError('beforeConvert hook must be a function');
51
+ }
52
+ this.hooks.beforeConvert.push(hook);
53
+ return this;
54
+ }
55
+
56
+ /**
57
+ * Регистрирует хук afterConvert
58
+ * @param hook - Функция хука
59
+ * @returns this для цепочки вызовов
60
+ */
61
+ afterConvert(hook: HookFunction | AsyncHookFunction): this {
62
+ if (typeof hook !== 'function') {
63
+ throw new ValidationError('afterConvert hook must be a function');
64
+ }
65
+ this.hooks.afterConvert.push(hook);
66
+ return this;
67
+ }
68
+
69
+ /**
70
+ * Регистрирует per-row хук
71
+ * @param hook - Функция хука
72
+ * @returns this для цепочки вызовов
73
+ */
74
+ perRow(hook: PerRowHookFunction | AsyncPerRowHookFunction): this {
75
+ if (typeof hook !== 'function') {
76
+ throw new ValidationError('perRow hook must be a function');
77
+ }
78
+ this.hooks.perRow.push(hook);
79
+ return this;
80
+ }
81
+
82
+ /**
83
+ * Применяет beforeConvert хуки
84
+ * @param data - Входные данные
85
+ * @param context - Контекст выполнения
86
+ * @returns Трансформированные данные
87
+ */
88
+ applyBeforeConvert<T = any, R = any>(data: T, context: HookContext = {}): R {
89
+ let result: any = data;
90
+ for (const hook of this.hooks.beforeConvert) {
91
+ result = hook(result, context);
92
+ }
93
+ return result as R;
94
+ }
95
+
96
+ /**
97
+ * Применяет beforeConvert хуки асинхронно
98
+ * @param data - Входные данные
99
+ * @param context - Контекст выполнения
100
+ * @returns Promise с трансформированными данными
101
+ */
102
+ async applyBeforeConvertAsync<T = any, R = any>(data: T, context: HookContext = {}): Promise<R> {
103
+ let result: any = data;
104
+ for (const hook of this.hooks.beforeConvert) {
105
+ if (this.isAsyncFunction(hook)) {
106
+ result = await (hook as AsyncHookFunction)(result, context);
107
+ } else {
108
+ result = (hook as HookFunction)(result, context);
109
+ }
110
+ }
111
+ return result as R;
112
+ }
113
+
114
+ /**
115
+ * Применяет afterConvert хуки
116
+ * @param data - Выходные данные
117
+ * @param context - Контекст выполнения
118
+ * @returns Трансформированные данные
119
+ */
120
+ applyAfterConvert<T = any, R = any>(data: T, context: HookContext = {}): R {
121
+ let result: any = data;
122
+ for (const hook of this.hooks.afterConvert) {
123
+ result = hook(result, context);
124
+ }
125
+ return result as R;
126
+ }
127
+
128
+ /**
129
+ * Применяет afterConvert хуки асинхронно
130
+ * @param data - Выходные данные
131
+ * @param context - Контекст выполнения
132
+ * @returns Promise с трансформированными данными
133
+ */
134
+ async applyAfterConvertAsync<T = any, R = any>(data: T, context: HookContext = {}): Promise<R> {
135
+ let result: any = data;
136
+ for (const hook of this.hooks.afterConvert) {
137
+ if (this.isAsyncFunction(hook)) {
138
+ result = await (hook as AsyncHookFunction)(result, context);
139
+ } else {
140
+ result = (hook as HookFunction)(result, context);
141
+ }
142
+ }
143
+ return result as R;
144
+ }
145
+
146
+ /**
147
+ * Применяет per-row хуки
148
+ * @param row - Строка данных
149
+ * @param index - Индекс строки
150
+ * @param context - Контекст выполнения
151
+ * @returns Трансформированная строка
152
+ */
153
+ applyPerRow<T = any, R = any>(row: T, index: number, context: HookContext = {}): R {
154
+ let result: any = row;
155
+ for (const hook of this.hooks.perRow) {
156
+ result = hook(result, index, context);
157
+ }
158
+ return result as R;
159
+ }
160
+
161
+ /**
162
+ * Применяет per-row хуки асинхронно
163
+ * @param row - Строка данных
164
+ * @param index - Индекс строки
165
+ * @param context - Контекст выполнения
166
+ * @returns Promise с трансформированной строкой
167
+ */
168
+ async applyPerRowAsync<T = any, R = any>(row: T, index: number, context: HookContext = {}): Promise<R> {
169
+ let result: any = row;
170
+ for (const hook of this.hooks.perRow) {
171
+ if (this.isAsyncFunction(hook)) {
172
+ result = await (hook as AsyncPerRowHookFunction)(result, index, context);
173
+ } else {
174
+ result = (hook as PerRowHookFunction)(result, index, context);
175
+ }
176
+ }
177
+ return result as R;
178
+ }
179
+
180
+ /**
181
+ * Применяет все хуки к массиву данных
182
+ * @param data - Массив данных
183
+ * @param context - Контекст выполнения
184
+ * @returns Трансформированный массив
185
+ */
186
+ applyAll<T = any, R = any>(data: T[], context: HookContext = {}): R[] {
187
+ if (!Array.isArray(data)) {
188
+ throw new ValidationError('Data must be an array for applyAll');
189
+ }
190
+
191
+ // Применяем beforeConvert хуки
192
+ let processedData: any[] = this.applyBeforeConvert(data, context);
193
+
194
+ // Применяем per-row хуки к каждой строке
195
+ if (this.hooks.perRow.length > 0) {
196
+ processedData = processedData.map((row: any, index: number) =>
197
+ this.applyPerRow(row, index, context)
198
+ );
199
+ }
200
+
201
+ // Применяем afterConvert хуки
202
+ return this.applyAfterConvert(processedData, context) as R[];
203
+ }
204
+
205
+ /**
206
+ * Применяет все хуки к массиву данных асинхронно
207
+ * @param data - Массив данных
208
+ * @param context - Контекст выполнения
209
+ * @returns Promise с трансформированным массивом
210
+ */
211
+ async applyAllAsync<T = any, R = any>(data: T[], context: HookContext = {}): Promise<R[]> {
212
+ if (!Array.isArray(data)) {
213
+ throw new ValidationError('Data must be an array for applyAll');
214
+ }
215
+
216
+ // Применяем beforeConvert хуки асинхронно
217
+ let processedData = await this.applyBeforeConvertAsync(data, context);
218
+
219
+ // Применяем per-row хуки асинхронно к каждой строке
220
+ if (this.hooks.perRow.length > 0) {
221
+ const processedRows: any[] = [];
222
+ for (let i = 0; i < processedData.length; i++) {
223
+ const processedRow = await this.applyPerRowAsync(processedData[i], i, context);
224
+ processedRows.push(processedRow);
225
+ }
226
+ processedData = processedRows;
227
+ }
228
+
229
+ // Применяем afterConvert хуки асинхронно
230
+ return await this.applyAfterConvertAsync(processedData, context) as R[];
231
+ }
232
+
233
+ /**
234
+ * Создает копию системы хуков
235
+ * @returns Новая копия
236
+ */
237
+ clone(): TransformHooks {
238
+ const cloned = new TransformHooks();
239
+ cloned.hooks = {
240
+ beforeConvert: [...this.hooks.beforeConvert],
241
+ afterConvert: [...this.hooks.afterConvert],
242
+ perRow: [...this.hooks.perRow]
243
+ };
244
+ return cloned;
245
+ }
246
+
247
+ /**
248
+ * Очищает все хуки
249
+ */
250
+ clear(): void {
251
+ this.hooks = {
252
+ beforeConvert: [],
253
+ afterConvert: [],
254
+ perRow: []
255
+ };
256
+ }
257
+
258
+ /**
259
+ * Возвращает статистику по хукам
260
+ * @returns Статистика
261
+ */
262
+ getStats(): { beforeConvert: number; afterConvert: number; perRow: number; total: number } {
263
+ return {
264
+ beforeConvert: this.hooks.beforeConvert.length,
265
+ afterConvert: this.hooks.afterConvert.length,
266
+ perRow: this.hooks.perRow.length,
267
+ total: this.hooks.beforeConvert.length +
268
+ this.hooks.afterConvert.length +
269
+ this.hooks.perRow.length
270
+ };
271
+ }
272
+
273
+ /**
274
+ * Проверяет, является ли функция асинхронной
275
+ * @private
276
+ */
277
+ private isAsyncFunction(fn: any): boolean {
278
+ return fn.constructor.name === 'AsyncFunction' ||
279
+ (typeof fn === 'function' && fn.constructor === (async () => {}).constructor);
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Предопределенные хуки
285
+ */
286
+ export const predefinedHooks = {
287
+ /**
288
+ * Хук для фильтрации данных
289
+ * @param predicate - Функция-предикат
290
+ * @returns Хук фильтрации
291
+ */
292
+ filter<T>(predicate: (item: T, index: number) => boolean): HookFunction<T[], T[]> {
293
+ return (data: T[]) => {
294
+ if (Array.isArray(data)) {
295
+ return data.filter(predicate);
296
+ }
297
+ return data;
298
+ };
299
+ },
300
+
301
+ /**
302
+ * Асинхронный хук для фильтрации данных
303
+ * @param predicate - Асинхронная функция-предикат
304
+ * @returns Асинхронный хук фильтрации
305
+ */
306
+ filterAsync<T>(predicate: (item: T, index: number) => Promise<boolean>): AsyncHookFunction<T[], T[]> {
307
+ return async (data: T[]) => {
308
+ if (Array.isArray(data)) {
309
+ const filtered: T[] = [];
310
+ for (let i = 0; i < data.length; i++) {
311
+ if (await predicate(data[i], i)) {
312
+ filtered.push(data[i]);
313
+ }
314
+ }
315
+ return filtered;
316
+ }
317
+ return data;
318
+ };
319
+ },
320
+
321
+ /**
322
+ * Хук для маппинга данных
323
+ * @param mapper - Функция-маппер
324
+ * @returns Хук маппинга
325
+ */
326
+ map<T, R>(mapper: (item: T, index: number) => R): HookFunction<T[], R[]> {
327
+ return (data: T[]) => {
328
+ if (Array.isArray(data)) {
329
+ return data.map(mapper);
330
+ }
331
+ return data as any;
332
+ };
333
+ },
334
+
335
+ /**
336
+ * Асинхронный хук для маппинга данных
337
+ * @param mapper - Асинхронная функция-маппер
338
+ * @returns Асинхронный хук маппинга
339
+ */
340
+ mapAsync<T, R>(mapper: (item: T, index: number) => Promise<R>): AsyncHookFunction<T[], R[]> {
341
+ return async (data: T[]) => {
342
+ if (Array.isArray(data)) {
343
+ const mapped: R[] = [];
344
+ for (let i = 0; i < data.length; i++) {
345
+ mapped.push(await mapper(data[i], i));
346
+ }
347
+ return mapped;
348
+ }
349
+ return data as any;
350
+ };
351
+ },
352
+
353
+ /**
354
+ * Хук для сортировки данных
355
+ * @param compareFn - Функция сравнения
356
+ * @returns Хук сортировки
357
+ */
358
+ sort<T>(compareFn?: (a: T, b: T) => number): HookFunction<T[], T[]> {
359
+ return (data: T[]) => {
360
+ if (Array.isArray(data)) {
361
+ return [...data].sort(compareFn);
362
+ }
363
+ return data;
364
+ };
365
+ },
366
+
367
+ /**
368
+ * Хук для ограничения количества записей
369
+ * @param limit - Максимальное количество записей
370
+ * @returns Хук ограничения
371
+ */
372
+ limit<T>(limit: number): HookFunction<T[], T[]> {
373
+ return (data: T[]) => {
374
+ if (Array.isArray(data)) {
375
+ return data.slice(0, limit);
376
+ }
377
+ return data;
378
+ };
379
+ },
380
+
381
+ /**
382
+ * Хук для добавления метаданных
383
+ * @param metadata - Метаданные для добавления
384
+ * @returns Хук добавления метаданных
385
+ */
386
+ addMetadata<T extends Record<string, any>>(metadata: Record<string, any>): HookFunction<T[], Array<T & { _metadata: any }>> {
387
+ return (data: T[], context?: HookContext) => {
388
+ if (Array.isArray(data)) {
389
+ return data.map(item => ({
390
+ ...item,
391
+ _metadata: {
392
+ ...metadata,
393
+ timestamp: new Date().toISOString(),
394
+ context
395
+ }
396
+ })) as Array<T & { _metadata: any }>;
397
+ }
398
+ return data as any;
399
+ };
400
+ },
401
+
402
+ /**
403
+ * Хук для преобразования ключей
404
+ * @param keyTransformer - Функция преобразования ключей
405
+ * @returns Хук преобразования ключей
406
+ */
407
+ transformKeys<T extends Record<string, any>>(keyTransformer: (key: string) => string): HookFunction<T[], Array<Record<string, any>>> {
408
+ return (data: T[]) => {
409
+ if (Array.isArray(data)) {
410
+ return data.map(item => {
411
+ const transformed: Record<string, any> = {};
412
+ for (const [key, value] of Object.entries(item)) {
413
+ transformed[keyTransformer(key)] = value;
414
+ }
415
+ return transformed;
416
+ });
417
+ }
418
+ return data;
419
+ };
420
+ },
421
+
422
+ /**
423
+ * Хук для преобразования значений
424
+ * @param valueTransformer - Функция преобразования значений
425
+ * @returns Хук преобразования значений
426
+ */
427
+ transformValues<T extends Record<string, any>>(valueTransformer: (value: any, key: string) => any): HookFunction<T[], Array<Record<string, any>>> {
428
+ return (data: T[]) => {
429
+ if (Array.isArray(data)) {
430
+ return data.map(item => {
431
+ const transformed: Record<string, any> = {};
432
+ for (const [key, value] of Object.entries(item)) {
433
+ transformed[key] = valueTransformer(value, key);
434
+ }
435
+ return transformed;
436
+ });
437
+ }
438
+ return data;
439
+ };
440
+ },
441
+
442
+ /**
443
+ * Хук для валидации данных
444
+ * @param validator - Функция валидации
445
+ * @param onError - Обработчик ошибки
446
+ * @returns Хук валидации
447
+ */
448
+ validate<T>(validator: (item: T, index: number) => boolean, onError: (errors: any[] | string, details?: any[]) => void = console.error): HookFunction<T[], T[]> {
449
+ return (data: T[]) => {
450
+ if (Array.isArray(data)) {
451
+ const validData: T[] = [];
452
+ const errors: any[] = [];
453
+
454
+ data.forEach((item, index) => {
455
+ try {
456
+ const isValid = validator(item, index);
457
+ if (isValid) {
458
+ validData.push(item);
459
+ } else {
460
+ errors.push({ index, item, reason: 'Validation failed' });
461
+ }
462
+ } catch (error: any) {
463
+ errors.push({ index, item, error: error.message });
464
+ }
465
+ });
466
+
467
+ if (errors.length > 0) {
468
+ if (onError.length >= 2) {
469
+ onError('Validation errors', errors);
470
+ } else {
471
+ onError(errors);
472
+ }
473
+ }
474
+
475
+ return validData;
476
+ }
477
+ return data;
478
+ };
479
+ },
480
+
481
+ /**
482
+ * Асинхронный хук для валидации данных
483
+ * @param validator - Асинхронная функция валидации
484
+ * @param onError - Обработчик ошибки
485
+ * @returns Асинхронный хук валидации
486
+ */
487
+ validateAsync<T>(validator: (item: T, index: number) => Promise<boolean>, onError: (errors: any[] | string, details?: any[]) => void = console.error): AsyncHookFunction<T[], T[]> {
488
+ return async (data: T[]) => {
489
+ if (Array.isArray(data)) {
490
+ const validData: T[] = [];
491
+ const errors: any[] = [];
492
+
493
+ for (let i = 0; i < data.length; i++) {
494
+ try {
495
+ const isValid = await validator(data[i], i);
496
+ if (isValid) {
497
+ validData.push(data[i]);
498
+ } else {
499
+ errors.push({ index: i, item: data[i], reason: 'Validation failed' });
500
+ }
501
+ } catch (error: any) {
502
+ errors.push({ index: i, item: data[i], error: error.message });
503
+ }
504
+ }
505
+
506
+ if (errors.length > 0) {
507
+ if (onError.length >= 2) {
508
+ onError('Validation errors', errors);
509
+ } else {
510
+ onError(errors);
511
+ }
512
+ }
513
+
514
+ return validData;
515
+ }
516
+ return data;
517
+ };
518
+ },
519
+
520
+ /**
521
+ * Хук для дедупликации данных
522
+ * @param keySelector - Функция выбора ключа
523
+ * @returns Хук дедупликации
524
+ */
525
+ deduplicate<T>(keySelector: (item: T) => string = JSON.stringify): HookFunction<T[], T[]> {
526
+ return (data: T[]) => {
527
+ if (Array.isArray(data)) {
528
+ const seen = new Set<string>();
529
+ return data.filter(item => {
530
+ const key = keySelector(item);
531
+ if (seen.has(key)) {
532
+ return false;
533
+ }
534
+ seen.add(key);
535
+ return true;
536
+ });
537
+ }
538
+ return data;
539
+ };
540
+ },
541
+
542
+ /**
543
+ * Асинхронный хук для дедупликации данных
544
+ * @param keySelector - Асинхронная функция выбора ключа
545
+ * @returns Асинхронный хук дедупликации
546
+ */
547
+ deduplicateAsync<T>(keySelector: (item: T) => Promise<string> = async (item) => JSON.stringify(item)): AsyncHookFunction<T[], T[]> {
548
+ return async (data: T[]) => {
549
+ if (Array.isArray(data)) {
550
+ const seen = new Set<string>();
551
+ const filtered: T[] = [];
552
+
553
+ for (const item of data) {
554
+ const key = await keySelector(item);
555
+ if (!seen.has(key)) {
556
+ seen.add(key);
557
+ filtered.push(item);
558
+ }
559
+ }
560
+
561
+ return filtered;
562
+ }
563
+ return data;
564
+ };
565
+ }
566
+ };
@@ -7,6 +7,15 @@
7
7
  */
8
8
 
9
9
  class FastPathEngine {
10
+ compilers: Map<any, any>;
11
+ stats: {
12
+ simpleParserCount: number;
13
+ quoteAwareParserCount: number;
14
+ standardParserCount: number;
15
+ cacheHits: number;
16
+ cacheMisses: number;
17
+ };
18
+
10
19
  constructor() {
11
20
  this.compilers = new Map();
12
21
  this.stats = {
@@ -21,7 +30,7 @@ class FastPathEngine {
21
30
  /**
22
31
  * Анализирует структуру CSV и определяет оптимальный парсер
23
32
  */
24
- analyzeStructure(sample, options = {}) {
33
+ analyzeStructure(sample, options: any = {}) {
25
34
  const delimiter = options.delimiter || this._detectDelimiter(sample);
26
35
  const lines = sample.split('\n').slice(0, 10);
27
36
 
@@ -335,4 +344,4 @@ class FastPathEngine {
335
344
  }
336
345
  }
337
346
 
338
- module.exports = FastPathEngine;
347
+ export default FastPathEngine;