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.
- package/README.md +31 -1
- package/bin/jtcsv.js +891 -821
- package/bin/jtcsv.ts +2534 -0
- package/csv-to-json.js +168 -145
- package/dist/jtcsv-core.cjs.js +1407 -0
- package/dist/jtcsv-core.cjs.js.map +1 -0
- package/dist/jtcsv-core.esm.js +1379 -0
- package/dist/jtcsv-core.esm.js.map +1 -0
- package/dist/jtcsv-core.umd.js +1413 -0
- package/dist/jtcsv-core.umd.js.map +1 -0
- package/dist/jtcsv-full.cjs.js +1912 -0
- package/dist/jtcsv-full.cjs.js.map +1 -0
- package/dist/jtcsv-full.esm.js +1880 -0
- package/dist/jtcsv-full.esm.js.map +1 -0
- package/dist/jtcsv-full.umd.js +1918 -0
- package/dist/jtcsv-full.umd.js.map +1 -0
- package/dist/jtcsv-workers.esm.js +759 -0
- package/dist/jtcsv-workers.esm.js.map +1 -0
- package/dist/jtcsv-workers.umd.js +773 -0
- package/dist/jtcsv-workers.umd.js.map +1 -0
- package/dist/jtcsv.cjs.js +61 -19
- package/dist/jtcsv.cjs.js.map +1 -1
- package/dist/jtcsv.esm.js +61 -19
- package/dist/jtcsv.esm.js.map +1 -1
- package/dist/jtcsv.umd.js +61 -19
- package/dist/jtcsv.umd.js.map +1 -1
- package/errors.js +188 -2
- package/examples/advanced/conditional-transformations.js +446 -0
- package/examples/advanced/conditional-transformations.ts +446 -0
- package/examples/advanced/csv-parser.worker.js +89 -0
- package/examples/advanced/csv-parser.worker.ts +89 -0
- package/examples/advanced/nested-objects-example.js +306 -0
- package/examples/advanced/nested-objects-example.ts +306 -0
- package/examples/advanced/performance-optimization.js +504 -0
- package/examples/advanced/performance-optimization.ts +504 -0
- package/examples/advanced/run-demo-server.js +116 -0
- package/examples/advanced/run-demo-server.ts +116 -0
- package/examples/advanced/web-worker-usage.html +874 -0
- package/examples/async-multithreaded-example.ts +335 -0
- package/examples/cli-advanced-usage.md +288 -0
- package/examples/cli-batch-processing.ts +38 -0
- package/examples/cli-tool.js +0 -3
- package/examples/cli-tool.ts +183 -0
- package/examples/error-handling.js +21 -7
- package/examples/error-handling.ts +356 -0
- package/examples/express-api.js +0 -3
- package/examples/express-api.ts +164 -0
- package/examples/large-dataset-example.js +0 -3
- package/examples/large-dataset-example.ts +204 -0
- package/examples/ndjson-processing.js +1 -1
- package/examples/ndjson-processing.ts +456 -0
- package/examples/plugin-excel-exporter.js +3 -4
- package/examples/plugin-excel-exporter.ts +406 -0
- package/examples/react-integration.tsx +637 -0
- package/examples/schema-validation.ts +640 -0
- package/examples/simple-usage.js +254 -254
- package/examples/simple-usage.ts +194 -0
- package/examples/streaming-example.js +4 -5
- package/examples/streaming-example.ts +419 -0
- package/examples/web-workers-advanced.ts +28 -0
- package/index.d.ts +1 -3
- package/index.js +15 -1
- package/json-save.js +9 -3
- package/json-to-csv.js +168 -21
- package/package.json +69 -10
- package/plugins/express-middleware/README.md +21 -2
- package/plugins/express-middleware/example.js +3 -4
- package/plugins/express-middleware/example.ts +135 -0
- package/plugins/express-middleware/index.d.ts +1 -1
- package/plugins/express-middleware/index.js +270 -118
- package/plugins/express-middleware/index.ts +557 -0
- package/plugins/fastify-plugin/index.js +2 -4
- package/plugins/fastify-plugin/index.ts +443 -0
- package/plugins/hono/index.ts +226 -0
- package/plugins/nestjs/index.ts +201 -0
- package/plugins/nextjs-api/examples/ConverterComponent.tsx +386 -0
- package/plugins/nextjs-api/examples/api-convert.js +0 -2
- package/plugins/nextjs-api/examples/api-convert.ts +67 -0
- package/plugins/nextjs-api/index.tsx +339 -0
- package/plugins/nextjs-api/route.js +2 -3
- package/plugins/nextjs-api/route.ts +370 -0
- package/plugins/nuxt/index.ts +94 -0
- package/plugins/nuxt/runtime/composables/useJtcsv.ts +100 -0
- package/plugins/nuxt/runtime/plugin.ts +71 -0
- package/plugins/remix/index.js +1 -1
- package/plugins/remix/index.ts +260 -0
- package/plugins/sveltekit/index.js +1 -1
- package/plugins/sveltekit/index.ts +301 -0
- package/plugins/trpc/index.ts +267 -0
- package/src/browser/browser-functions.ts +402 -0
- package/src/browser/core.js +92 -0
- package/src/browser/core.ts +152 -0
- package/src/browser/csv-to-json-browser.d.ts +3 -0
- package/src/browser/csv-to-json-browser.js +36 -14
- package/src/browser/csv-to-json-browser.ts +264 -0
- package/src/browser/errors-browser.ts +303 -0
- package/src/browser/extensions/plugins.js +92 -0
- package/src/browser/extensions/plugins.ts +93 -0
- package/src/browser/extensions/workers.js +39 -0
- package/src/browser/extensions/workers.ts +39 -0
- package/src/browser/globals.d.ts +5 -0
- package/src/browser/index.ts +192 -0
- package/src/browser/json-to-csv-browser.d.ts +3 -0
- package/src/browser/json-to-csv-browser.js +13 -3
- package/src/browser/json-to-csv-browser.ts +262 -0
- package/src/browser/streams.js +12 -2
- package/src/browser/streams.ts +336 -0
- package/src/browser/workers/csv-parser.worker.ts +377 -0
- package/src/browser/workers/worker-pool.ts +548 -0
- package/src/core/delimiter-cache.js +22 -8
- package/src/core/delimiter-cache.ts +310 -0
- package/src/core/node-optimizations.ts +449 -0
- package/src/core/plugin-system.js +29 -11
- package/src/core/plugin-system.ts +400 -0
- package/src/core/transform-hooks.ts +558 -0
- package/src/engines/fast-path-engine-new.ts +347 -0
- package/src/engines/fast-path-engine.ts +854 -0
- package/src/errors.ts +72 -0
- package/src/formats/ndjson-parser.ts +469 -0
- package/src/formats/tsv-parser.ts +334 -0
- package/src/index-with-plugins.js +16 -9
- package/src/index-with-plugins.ts +395 -0
- package/src/types/index.ts +255 -0
- package/src/utils/bom-utils.js +259 -0
- package/src/utils/bom-utils.ts +373 -0
- package/src/utils/encoding-support.js +124 -0
- package/src/utils/encoding-support.ts +155 -0
- package/src/utils/schema-validator.js +19 -19
- package/src/utils/schema-validator.ts +819 -0
- package/src/utils/transform-loader.js +1 -1
- package/src/utils/transform-loader.ts +389 -0
- package/src/utils/zod-adapter.js +170 -0
- package/src/utils/zod-adapter.ts +280 -0
- package/src/web-server/index.js +10 -10
- package/src/web-server/index.ts +683 -0
- package/src/workers/csv-multithreaded.ts +310 -0
- package/src/workers/csv-parser.worker.ts +227 -0
- package/src/workers/worker-pool.ts +409 -0
- package/stream-csv-to-json.js +26 -8
- package/stream-json-to-csv.js +1 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js Runtime Optimizations
|
|
3
|
+
*
|
|
4
|
+
* Detects Node.js version and provides optimized implementations
|
|
5
|
+
* for modern runtimes while maintaining backward compatibility.
|
|
6
|
+
*
|
|
7
|
+
* Optimized for: Node 20, 22, 24
|
|
8
|
+
* Compatible with: Node 12+
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Parse Node.js version
|
|
12
|
+
const nodeVersionStr = process.versions?.node || '12.0.0';
|
|
13
|
+
const [major, minor] = nodeVersionStr.split('.').map(Number);
|
|
14
|
+
|
|
15
|
+
// Feature detection flags
|
|
16
|
+
export const features = {
|
|
17
|
+
// Node 14.17+ / 16+
|
|
18
|
+
hasAbortController: typeof AbortController !== 'undefined',
|
|
19
|
+
|
|
20
|
+
// Node 15+
|
|
21
|
+
hasPromiseAny: typeof (Promise as any).any === 'function',
|
|
22
|
+
|
|
23
|
+
// Node 16+
|
|
24
|
+
hasArrayAt: typeof Array.prototype.at === 'function',
|
|
25
|
+
hasObjectHasOwn: typeof (Object as any).hasOwn === 'function',
|
|
26
|
+
|
|
27
|
+
// Node 17+
|
|
28
|
+
hasStructuredClone: typeof globalThis.structuredClone === 'function',
|
|
29
|
+
|
|
30
|
+
// Node 18+
|
|
31
|
+
hasFetch: typeof globalThis.fetch === 'function',
|
|
32
|
+
|
|
33
|
+
// Node 20+
|
|
34
|
+
hasWebStreams: typeof globalThis.ReadableStream !== 'undefined' && major >= 20,
|
|
35
|
+
hasArrayGroup: typeof (Array.prototype as any).group === 'function',
|
|
36
|
+
|
|
37
|
+
// Node 21+
|
|
38
|
+
hasSetMethods: typeof (Set.prototype as any).union === 'function',
|
|
39
|
+
|
|
40
|
+
// Node 22+
|
|
41
|
+
hasImportMeta: major >= 22,
|
|
42
|
+
hasExplicitResourceManagement: major >= 22,
|
|
43
|
+
|
|
44
|
+
// Version checks
|
|
45
|
+
isNode20Plus: major >= 20,
|
|
46
|
+
isNode22Plus: major >= 22,
|
|
47
|
+
isNode24Plus: major >= 24
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Optimized Object.hasOwn polyfill for older Node versions
|
|
52
|
+
*/
|
|
53
|
+
export const hasOwn = features.hasObjectHasOwn
|
|
54
|
+
? (Object as any).hasOwn
|
|
55
|
+
: (obj: object, prop: string | number | symbol): boolean =>
|
|
56
|
+
Object.prototype.hasOwnProperty.call(obj, prop);
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Optimized deep clone function
|
|
60
|
+
* Uses structuredClone on Node 17+ for best performance
|
|
61
|
+
*/
|
|
62
|
+
export const deepClone = features.hasStructuredClone
|
|
63
|
+
? <T>(obj: T): T => structuredClone(obj)
|
|
64
|
+
: <T>(obj: T): T => JSON.parse(JSON.stringify(obj));
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Optimized array access with at() method
|
|
68
|
+
*/
|
|
69
|
+
export const arrayAt = features.hasArrayAt
|
|
70
|
+
? <T>(arr: T[], index: number): T | undefined => arr.at(index)
|
|
71
|
+
: <T>(arr: T[], index: number): T | undefined => {
|
|
72
|
+
const len = arr.length;
|
|
73
|
+
const normalizedIndex = index < 0 ? len + index : index;
|
|
74
|
+
return normalizedIndex >= 0 && normalizedIndex < len ? arr[normalizedIndex] : undefined;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* High-performance string builder for large CSV generation
|
|
79
|
+
* Uses different strategies based on Node version
|
|
80
|
+
*/
|
|
81
|
+
export class StringBuilderOptimized {
|
|
82
|
+
private parts: string[];
|
|
83
|
+
private length: number;
|
|
84
|
+
private initialCapacity: number;
|
|
85
|
+
private chunkSize: number;
|
|
86
|
+
|
|
87
|
+
constructor(initialCapacity = 1024) {
|
|
88
|
+
this.parts = [];
|
|
89
|
+
this.length = 0;
|
|
90
|
+
this.initialCapacity = initialCapacity;
|
|
91
|
+
|
|
92
|
+
// Node 20+ uses more aggressive chunking
|
|
93
|
+
this.chunkSize = features.isNode20Plus ? 65536 : 16384;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
append(str: string): this {
|
|
97
|
+
if (str) {
|
|
98
|
+
this.parts.push(str);
|
|
99
|
+
this.length += str.length;
|
|
100
|
+
}
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
toString(): string {
|
|
105
|
+
return this.parts.join('');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
clear(): void {
|
|
109
|
+
this.parts = [];
|
|
110
|
+
this.length = 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
getLength(): number {
|
|
114
|
+
return this.length;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Optimized row buffer for streaming CSV parsing
|
|
120
|
+
* Minimizes allocations on modern Node versions
|
|
121
|
+
*/
|
|
122
|
+
export class RowBuffer<T = any> {
|
|
123
|
+
private rows: T[][];
|
|
124
|
+
private currentRow: T[];
|
|
125
|
+
private rowCount: number;
|
|
126
|
+
|
|
127
|
+
constructor(initialSize = 100) {
|
|
128
|
+
this.rows = [];
|
|
129
|
+
this.currentRow = [];
|
|
130
|
+
this.rowCount = 0;
|
|
131
|
+
|
|
132
|
+
// Pre-allocate on Node 20+
|
|
133
|
+
if (features.isNode20Plus) {
|
|
134
|
+
this.rows = new Array(initialSize);
|
|
135
|
+
this.rows.length = 0;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
addField(field: T): void {
|
|
140
|
+
this.currentRow.push(field);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
commitRow(): void {
|
|
144
|
+
if (this.currentRow.length > 0) {
|
|
145
|
+
this.rows.push(this.currentRow);
|
|
146
|
+
this.rowCount++;
|
|
147
|
+
this.currentRow = [];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
getRows(): T[][] {
|
|
152
|
+
return this.rows;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
clear(): void {
|
|
156
|
+
this.rows = features.isNode20Plus ? new Array(100) : [];
|
|
157
|
+
this.rows.length = 0;
|
|
158
|
+
this.currentRow = [];
|
|
159
|
+
this.rowCount = 0;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
getRowCount(): number {
|
|
163
|
+
return this.rowCount;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Optimized field parser with char code comparisons
|
|
169
|
+
* Faster than string comparisons on all Node versions
|
|
170
|
+
*/
|
|
171
|
+
export const CHAR_CODES = {
|
|
172
|
+
QUOTE: 34, // "
|
|
173
|
+
COMMA: 44, // ,
|
|
174
|
+
SEMICOLON: 59, // ;
|
|
175
|
+
TAB: 9, // \t
|
|
176
|
+
PIPE: 124, // |
|
|
177
|
+
NEWLINE: 10, // \n
|
|
178
|
+
CARRIAGE: 13, // \r
|
|
179
|
+
SPACE: 32, // space
|
|
180
|
+
EQUALS: 61, // =
|
|
181
|
+
PLUS: 43, // +
|
|
182
|
+
MINUS: 45, // -
|
|
183
|
+
AT: 64, // @
|
|
184
|
+
BACKSLASH: 92, // \
|
|
185
|
+
APOSTROPHE: 39 // '
|
|
186
|
+
} as const;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Fast delimiter detection using char codes
|
|
190
|
+
*/
|
|
191
|
+
export function fastDetectDelimiter(sample: string, candidates = [';', ',', '\t', '|']): string {
|
|
192
|
+
const firstLineEnd = sample.indexOf('\n');
|
|
193
|
+
const firstLine = firstLineEnd > -1 ? sample.slice(0, firstLineEnd) : sample;
|
|
194
|
+
|
|
195
|
+
const candidateCodes = candidates.map(c => c.charCodeAt(0));
|
|
196
|
+
const counts = new Array(candidateCodes.length).fill(0);
|
|
197
|
+
|
|
198
|
+
// Use fast char code iteration on Node 20+
|
|
199
|
+
const len = Math.min(firstLine.length, 10000);
|
|
200
|
+
|
|
201
|
+
for (let i = 0; i < len; i++) {
|
|
202
|
+
const code = firstLine.charCodeAt(i);
|
|
203
|
+
for (let j = 0; j < candidateCodes.length; j++) {
|
|
204
|
+
if (code === candidateCodes[j]) {
|
|
205
|
+
counts[j]++;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
let maxCount = 0;
|
|
211
|
+
let maxIndex = 0;
|
|
212
|
+
|
|
213
|
+
for (let i = 0; i < counts.length; i++) {
|
|
214
|
+
if (counts[i] > maxCount) {
|
|
215
|
+
maxCount = counts[i];
|
|
216
|
+
maxIndex = i;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return candidates[maxIndex];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Optimized batch processor for large datasets
|
|
225
|
+
* Uses different chunk sizes based on Node version
|
|
226
|
+
*/
|
|
227
|
+
export function createBatchProcessor<T, R>(
|
|
228
|
+
processor: (batch: T[]) => Promise<R[]> | R[],
|
|
229
|
+
options: { batchSize?: number; parallelism?: number } = {}
|
|
230
|
+
): (items: T[]) => AsyncGenerator<R> {
|
|
231
|
+
const batchSize = options.batchSize || (features.isNode20Plus ? 10000 : 5000);
|
|
232
|
+
const parallelism = options.parallelism || (features.isNode22Plus ? 4 : 2);
|
|
233
|
+
|
|
234
|
+
return async function* processBatches(items: T[]): AsyncGenerator<R> {
|
|
235
|
+
const batches: T[][] = [];
|
|
236
|
+
|
|
237
|
+
for (let i = 0; i < items.length; i += batchSize) {
|
|
238
|
+
batches.push(items.slice(i, i + batchSize));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Process batches with limited parallelism
|
|
242
|
+
for (let i = 0; i < batches.length; i += parallelism) {
|
|
243
|
+
const chunk = batches.slice(i, i + parallelism);
|
|
244
|
+
const results = await Promise.all(chunk.map(batch => processor(batch)));
|
|
245
|
+
|
|
246
|
+
for (const result of results) {
|
|
247
|
+
yield* result;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Memory-efficient object pool for row objects
|
|
255
|
+
* Reduces GC pressure on large CSV files
|
|
256
|
+
*/
|
|
257
|
+
export class ObjectPool<T> {
|
|
258
|
+
private factory: () => T;
|
|
259
|
+
private pool: T[];
|
|
260
|
+
private inUse: number;
|
|
261
|
+
|
|
262
|
+
constructor(factory: () => T, initialSize = 100) {
|
|
263
|
+
this.factory = factory;
|
|
264
|
+
this.pool = [];
|
|
265
|
+
this.inUse = 0;
|
|
266
|
+
|
|
267
|
+
// Pre-warm pool on Node 20+
|
|
268
|
+
if (features.isNode20Plus) {
|
|
269
|
+
for (let i = 0; i < initialSize; i++) {
|
|
270
|
+
this.pool.push(factory());
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
acquire(): T {
|
|
276
|
+
this.inUse++;
|
|
277
|
+
if (this.pool.length > 0) {
|
|
278
|
+
return this.pool.pop()!;
|
|
279
|
+
}
|
|
280
|
+
return this.factory();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
release(obj: T): void {
|
|
284
|
+
this.inUse--;
|
|
285
|
+
// Clear object properties before returning to pool
|
|
286
|
+
for (const key in obj) {
|
|
287
|
+
if (hasOwn(obj, key)) {
|
|
288
|
+
delete (obj as any)[key];
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
this.pool.push(obj);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
getStats(): { poolSize: number; inUse: number } {
|
|
295
|
+
return {
|
|
296
|
+
poolSize: this.pool.length,
|
|
297
|
+
inUse: this.inUse
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Fast string escape for CSV values
|
|
304
|
+
* Uses pre-computed regex on all versions
|
|
305
|
+
*/
|
|
306
|
+
const QUOTE_REGEX = /"/g;
|
|
307
|
+
|
|
308
|
+
export function fastEscapeValue(value: any, delimiterCode: number): string {
|
|
309
|
+
if (value === null || value === undefined || value === '') {
|
|
310
|
+
return '';
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const str = typeof value === 'string' ? value : String(value);
|
|
314
|
+
const len = str.length;
|
|
315
|
+
|
|
316
|
+
// Quick scan for special characters using char codes
|
|
317
|
+
let needsQuoting = false;
|
|
318
|
+
let hasQuote = false;
|
|
319
|
+
|
|
320
|
+
for (let i = 0; i < len; i++) {
|
|
321
|
+
const code = str.charCodeAt(i);
|
|
322
|
+
if (code === CHAR_CODES.QUOTE) {
|
|
323
|
+
hasQuote = true;
|
|
324
|
+
needsQuoting = true;
|
|
325
|
+
} else if (code === delimiterCode || code === CHAR_CODES.NEWLINE || code === CHAR_CODES.CARRIAGE) {
|
|
326
|
+
needsQuoting = true;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (!needsQuoting) {
|
|
331
|
+
return str;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const escaped = hasQuote ? str.replace(QUOTE_REGEX, '""') : str;
|
|
335
|
+
return `"${escaped}"`;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Async iterator utilities for streaming
|
|
340
|
+
*/
|
|
341
|
+
export const asyncIterUtils = {
|
|
342
|
+
/**
|
|
343
|
+
* Map over async iterator with concurrency control (Node 20+)
|
|
344
|
+
*/
|
|
345
|
+
async *mapConcurrent<T, R>(
|
|
346
|
+
iterator: AsyncIterable<T>,
|
|
347
|
+
mapper: (item: T) => Promise<R> | R,
|
|
348
|
+
concurrency = 4
|
|
349
|
+
): AsyncGenerator<R> {
|
|
350
|
+
const pending: Promise<R>[] = [];
|
|
351
|
+
|
|
352
|
+
for await (const item of iterator) {
|
|
353
|
+
pending.push(Promise.resolve(mapper(item)));
|
|
354
|
+
|
|
355
|
+
if (pending.length >= concurrency) {
|
|
356
|
+
const results = await Promise.all(pending.splice(0, concurrency));
|
|
357
|
+
for (const result of results) {
|
|
358
|
+
yield result;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (pending.length > 0) {
|
|
364
|
+
const results = await Promise.all(pending);
|
|
365
|
+
for (const result of results) {
|
|
366
|
+
yield result;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Batch items from async iterator
|
|
373
|
+
*/
|
|
374
|
+
async *batch<T>(iterator: AsyncIterable<T>, size = 1000): AsyncGenerator<T[]> {
|
|
375
|
+
let batch: T[] = [];
|
|
376
|
+
|
|
377
|
+
for await (const item of iterator) {
|
|
378
|
+
batch.push(item);
|
|
379
|
+
|
|
380
|
+
if (batch.length >= size) {
|
|
381
|
+
yield batch;
|
|
382
|
+
batch = [];
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (batch.length > 0) {
|
|
387
|
+
yield batch;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Get runtime optimization hints
|
|
394
|
+
*/
|
|
395
|
+
export function getOptimizationHints(): {
|
|
396
|
+
nodeVersion: string;
|
|
397
|
+
features: typeof features;
|
|
398
|
+
recommendations: {
|
|
399
|
+
useWebStreams: boolean;
|
|
400
|
+
useStructuredClone: boolean;
|
|
401
|
+
useLargerBatches: boolean;
|
|
402
|
+
useHigherParallelism: boolean;
|
|
403
|
+
preferredChunkSize: number;
|
|
404
|
+
};
|
|
405
|
+
} {
|
|
406
|
+
return {
|
|
407
|
+
nodeVersion: `${major}.${minor}`,
|
|
408
|
+
features,
|
|
409
|
+
recommendations: {
|
|
410
|
+
useWebStreams: features.hasWebStreams,
|
|
411
|
+
useStructuredClone: features.hasStructuredClone,
|
|
412
|
+
useLargerBatches: features.isNode20Plus,
|
|
413
|
+
useHigherParallelism: features.isNode22Plus,
|
|
414
|
+
preferredChunkSize: features.isNode24Plus ? 131072 : (features.isNode20Plus ? 65536 : 16384)
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export const nodeVersionInfo = { major, minor };
|
|
420
|
+
|
|
421
|
+
export default {
|
|
422
|
+
// Feature detection
|
|
423
|
+
features,
|
|
424
|
+
nodeVersion: { major, minor },
|
|
425
|
+
|
|
426
|
+
// Polyfills and optimized functions
|
|
427
|
+
hasOwn,
|
|
428
|
+
deepClone,
|
|
429
|
+
arrayAt,
|
|
430
|
+
|
|
431
|
+
// Classes
|
|
432
|
+
StringBuilderOptimized,
|
|
433
|
+
RowBuffer,
|
|
434
|
+
ObjectPool,
|
|
435
|
+
|
|
436
|
+
// Constants
|
|
437
|
+
CHAR_CODES,
|
|
438
|
+
|
|
439
|
+
// Functions
|
|
440
|
+
fastDetectDelimiter,
|
|
441
|
+
fastEscapeValue,
|
|
442
|
+
createBatchProcessor,
|
|
443
|
+
|
|
444
|
+
// Async utilities
|
|
445
|
+
asyncIterUtils,
|
|
446
|
+
|
|
447
|
+
// Diagnostics
|
|
448
|
+
getOptimizationHints
|
|
449
|
+
};
|
|
@@ -97,7 +97,9 @@ class PluginManager {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
this.stats.pluginLoads++;
|
|
100
|
-
|
|
100
|
+
if (process.env.NODE_ENV === 'development') {
|
|
101
|
+
console.log(`✅ Plugin "${name}" зарегистрирован`);
|
|
102
|
+
}
|
|
101
103
|
return this;
|
|
102
104
|
}
|
|
103
105
|
|
|
@@ -163,7 +165,9 @@ class PluginManager {
|
|
|
163
165
|
registeredAt: new Date()
|
|
164
166
|
});
|
|
165
167
|
|
|
166
|
-
|
|
168
|
+
if (process.env.NODE_ENV === 'development') {
|
|
169
|
+
console.log(`📌 Hook "${hookName}" зарегистрирован${pluginName ? ` для плагина "${pluginName}"` : ''}`);
|
|
170
|
+
}
|
|
167
171
|
}
|
|
168
172
|
|
|
169
173
|
/**
|
|
@@ -178,7 +182,9 @@ class PluginManager {
|
|
|
178
182
|
registeredAt: new Date()
|
|
179
183
|
});
|
|
180
184
|
|
|
181
|
-
|
|
185
|
+
if (process.env.NODE_ENV === 'development') {
|
|
186
|
+
console.log(`🔄 Middleware "${name || 'anonymous'}" зарегистрирован`);
|
|
187
|
+
}
|
|
182
188
|
}
|
|
183
189
|
|
|
184
190
|
/**
|
|
@@ -195,7 +201,9 @@ class PluginManager {
|
|
|
195
201
|
return data;
|
|
196
202
|
}
|
|
197
203
|
|
|
198
|
-
|
|
204
|
+
if (process.env.NODE_ENV === 'development') {
|
|
205
|
+
console.log(`⚡ Выполнение hook "${hookName}" с ${handlers.length} обработчиками`);
|
|
206
|
+
}
|
|
199
207
|
|
|
200
208
|
let result = data;
|
|
201
209
|
|
|
@@ -238,7 +246,9 @@ class PluginManager {
|
|
|
238
246
|
return ctx;
|
|
239
247
|
}
|
|
240
248
|
|
|
241
|
-
|
|
249
|
+
if (process.env.NODE_ENV === 'development') {
|
|
250
|
+
console.log(`🚀 Запуск middleware pipeline с ${this.middlewares.length} middleware`);
|
|
251
|
+
}
|
|
242
252
|
|
|
243
253
|
let index = -1;
|
|
244
254
|
const middlewares = this.middlewares.map(m => m.middleware);
|
|
@@ -336,8 +346,10 @@ class PluginManager {
|
|
|
336
346
|
// Записываем время выполнения
|
|
337
347
|
ctx.duration = Date.now() - ctx.startTime;
|
|
338
348
|
|
|
339
|
-
// Логируем успешное выполнение
|
|
340
|
-
|
|
349
|
+
// Логируем успешное выполнение только в development
|
|
350
|
+
if (process.env.NODE_ENV === 'development') {
|
|
351
|
+
console.log(`✅ Операция "${operation}" выполнена за ${ctx.duration}ms`);
|
|
352
|
+
}
|
|
341
353
|
|
|
342
354
|
return ctx.result;
|
|
343
355
|
} catch (error) {
|
|
@@ -407,7 +419,9 @@ class PluginManager {
|
|
|
407
419
|
|
|
408
420
|
plugin.enabled = enabled;
|
|
409
421
|
/* istanbul ignore next */
|
|
410
|
-
|
|
422
|
+
if (process.env.NODE_ENV === 'development') {
|
|
423
|
+
console.log(`🔧 Plugin "${pluginName}" ${enabled ? 'включен' : 'выключен'}`);
|
|
424
|
+
}
|
|
411
425
|
}
|
|
412
426
|
|
|
413
427
|
/**
|
|
@@ -431,7 +445,9 @@ class PluginManager {
|
|
|
431
445
|
// Удаляем плагин
|
|
432
446
|
this.plugins.delete(pluginName);
|
|
433
447
|
|
|
434
|
-
|
|
448
|
+
if (process.env.NODE_ENV === 'development') {
|
|
449
|
+
console.log(`🗑️ Plugin "${pluginName}" удален`);
|
|
450
|
+
}
|
|
435
451
|
}
|
|
436
452
|
|
|
437
453
|
/**
|
|
@@ -469,8 +485,10 @@ class PluginManager {
|
|
|
469
485
|
this.resetStats();
|
|
470
486
|
this._registerDefaultHooks();
|
|
471
487
|
|
|
472
|
-
|
|
488
|
+
if (process.env.NODE_ENV === 'development') {
|
|
489
|
+
console.log('🧹 Все плагины и hooks очищены');
|
|
490
|
+
}
|
|
473
491
|
}
|
|
474
492
|
}
|
|
475
493
|
|
|
476
|
-
module.exports = PluginManager;
|
|
494
|
+
module.exports = PluginManager;
|