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.
- package/README.md +204 -115
- package/bin/jtcsv.ts +2612 -0
- package/browser.d.ts +142 -0
- package/dist/benchmark.js +446 -0
- package/dist/benchmark.js.map +1 -0
- package/dist/bin/jtcsv.js +1940 -0
- package/dist/bin/jtcsv.js.map +1 -0
- package/dist/csv-to-json.js +1262 -0
- package/dist/csv-to-json.js.map +1 -0
- package/dist/errors.js +291 -0
- package/dist/errors.js.map +1 -0
- package/dist/eslint.config.js +147 -0
- package/dist/eslint.config.js.map +1 -0
- package/dist/index-core.js +95 -0
- package/dist/index-core.js.map +1 -0
- package/dist/index.js +93 -0
- package/dist/index.js.map +1 -0
- package/dist/json-save.js +229 -0
- package/dist/json-save.js.map +1 -0
- package/dist/json-to-csv.js +576 -0
- package/dist/json-to-csv.js.map +1 -0
- package/dist/jtcsv-core.cjs.js +1736 -0
- package/dist/jtcsv-core.cjs.js.map +1 -0
- package/dist/jtcsv-core.esm.js +1708 -0
- package/dist/jtcsv-core.esm.js.map +1 -0
- package/dist/jtcsv-core.umd.js +1742 -0
- package/dist/jtcsv-core.umd.js.map +1 -0
- package/dist/jtcsv-full.cjs.js +2241 -0
- package/dist/jtcsv-full.cjs.js.map +1 -0
- package/dist/jtcsv-full.esm.js +2209 -0
- package/dist/jtcsv-full.esm.js.map +1 -0
- package/dist/jtcsv-full.umd.js +2247 -0
- package/dist/jtcsv-full.umd.js.map +1 -0
- package/dist/jtcsv-workers.esm.js +768 -0
- package/dist/jtcsv-workers.esm.js.map +1 -0
- package/dist/jtcsv-workers.umd.js +782 -0
- package/dist/jtcsv-workers.umd.js.map +1 -0
- package/dist/jtcsv.cjs.js +1996 -2048
- package/dist/jtcsv.cjs.js.map +1 -1
- package/dist/jtcsv.esm.js +1992 -2048
- package/dist/jtcsv.esm.js.map +1 -1
- package/dist/jtcsv.umd.js +2157 -2209
- package/dist/jtcsv.umd.js.map +1 -1
- package/dist/plugins/express-middleware/index.js +350 -0
- package/dist/plugins/express-middleware/index.js.map +1 -0
- package/dist/plugins/fastify-plugin/index.js +315 -0
- package/dist/plugins/fastify-plugin/index.js.map +1 -0
- package/dist/plugins/hono/index.js +111 -0
- package/dist/plugins/hono/index.js.map +1 -0
- package/dist/plugins/nestjs/index.js +112 -0
- package/dist/plugins/nestjs/index.js.map +1 -0
- package/dist/plugins/nuxt/index.js +53 -0
- package/dist/plugins/nuxt/index.js.map +1 -0
- package/dist/plugins/remix/index.js +133 -0
- package/dist/plugins/remix/index.js.map +1 -0
- package/dist/plugins/sveltekit/index.js +155 -0
- package/dist/plugins/sveltekit/index.js.map +1 -0
- package/dist/plugins/trpc/index.js +136 -0
- package/dist/plugins/trpc/index.js.map +1 -0
- package/dist/run-demo.js +49 -0
- package/dist/run-demo.js.map +1 -0
- package/dist/src/browser/browser-functions.js +193 -0
- package/dist/src/browser/browser-functions.js.map +1 -0
- package/dist/src/browser/core.js +123 -0
- package/dist/src/browser/core.js.map +1 -0
- package/dist/src/browser/csv-to-json-browser.js +353 -0
- package/dist/src/browser/csv-to-json-browser.js.map +1 -0
- package/dist/src/browser/errors-browser.js +219 -0
- package/dist/src/browser/errors-browser.js.map +1 -0
- package/dist/src/browser/extensions/plugins.js +106 -0
- package/dist/src/browser/extensions/plugins.js.map +1 -0
- package/dist/src/browser/extensions/workers.js +66 -0
- package/dist/src/browser/extensions/workers.js.map +1 -0
- package/dist/src/browser/index.js +140 -0
- package/dist/src/browser/index.js.map +1 -0
- package/dist/src/browser/json-to-csv-browser.js +225 -0
- package/dist/src/browser/json-to-csv-browser.js.map +1 -0
- package/dist/src/browser/streams.js +340 -0
- package/dist/src/browser/streams.js.map +1 -0
- package/dist/src/browser/workers/csv-parser.worker.js +264 -0
- package/dist/src/browser/workers/csv-parser.worker.js.map +1 -0
- package/dist/src/browser/workers/worker-pool.js +338 -0
- package/dist/src/browser/workers/worker-pool.js.map +1 -0
- package/dist/src/core/delimiter-cache.js +196 -0
- package/dist/src/core/delimiter-cache.js.map +1 -0
- package/dist/src/core/node-optimizations.js +279 -0
- package/dist/src/core/node-optimizations.js.map +1 -0
- package/dist/src/core/plugin-system.js +399 -0
- package/dist/src/core/plugin-system.js.map +1 -0
- package/dist/src/core/transform-hooks.js +348 -0
- package/dist/src/core/transform-hooks.js.map +1 -0
- package/dist/src/engines/fast-path-engine-new.js +262 -0
- package/dist/src/engines/fast-path-engine-new.js.map +1 -0
- package/dist/src/engines/fast-path-engine.js +671 -0
- package/dist/src/engines/fast-path-engine.js.map +1 -0
- package/dist/src/errors.js +18 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/formats/ndjson-parser.js +332 -0
- package/dist/src/formats/ndjson-parser.js.map +1 -0
- package/dist/src/formats/tsv-parser.js +230 -0
- package/dist/src/formats/tsv-parser.js.map +1 -0
- package/dist/src/index-with-plugins.js +259 -0
- package/dist/src/index-with-plugins.js.map +1 -0
- package/dist/src/types/index.js +3 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/src/utils/bom-utils.js +267 -0
- package/dist/src/utils/bom-utils.js.map +1 -0
- package/dist/src/utils/encoding-support.js +77 -0
- package/dist/src/utils/encoding-support.js.map +1 -0
- package/dist/src/utils/schema-validator.js +609 -0
- package/dist/src/utils/schema-validator.js.map +1 -0
- package/dist/src/utils/transform-loader.js +281 -0
- package/dist/src/utils/transform-loader.js.map +1 -0
- package/dist/src/utils/validators.js +40 -0
- package/dist/src/utils/validators.js.map +1 -0
- package/dist/src/utils/zod-adapter.js +144 -0
- package/dist/src/utils/zod-adapter.js.map +1 -0
- package/dist/src/web-server/index.js +648 -0
- package/dist/src/web-server/index.js.map +1 -0
- package/dist/src/workers/csv-multithreaded.js +211 -0
- package/dist/src/workers/csv-multithreaded.js.map +1 -0
- package/dist/src/workers/csv-parser.worker.js +179 -0
- package/dist/src/workers/csv-parser.worker.js.map +1 -0
- package/dist/src/workers/worker-pool.js +228 -0
- package/dist/src/workers/worker-pool.js.map +1 -0
- package/dist/stream-csv-to-json.js +665 -0
- package/dist/stream-csv-to-json.js.map +1 -0
- package/dist/stream-json-to-csv.js +389 -0
- package/dist/stream-json-to-csv.js.map +1 -0
- package/examples/advanced/conditional-transformations.ts +446 -0
- package/examples/advanced/csv-parser.worker.ts +89 -0
- package/examples/advanced/nested-objects-example.ts +306 -0
- package/examples/advanced/performance-optimization.ts +504 -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 +290 -0
- package/examples/{cli-batch-processing.js → cli-batch-processing.ts} +38 -38
- package/examples/{cli-tool.js → cli-tool.ts} +5 -8
- package/examples/{error-handling.js → error-handling.ts} +356 -324
- package/examples/{express-api.js → express-api.ts} +161 -164
- package/examples/{large-dataset-example.js → large-dataset-example.ts} +201 -182
- package/examples/{ndjson-processing.js → ndjson-processing.ts} +456 -434
- package/examples/{plugin-excel-exporter.js → plugin-excel-exporter.ts} +6 -7
- package/examples/react-integration.tsx +637 -0
- package/examples/{schema-validation.js → schema-validation.ts} +2 -2
- package/examples/simple-usage.ts +194 -0
- package/examples/{streaming-example.js → streaming-example.ts} +12 -12
- package/index.d.ts +187 -18
- package/package.json +75 -81
- package/plugins.d.ts +37 -0
- package/schema.d.ts +103 -0
- package/src/browser/browser-functions.ts +402 -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.ts +494 -0
- package/src/browser/{errors-browser.js → errors-browser.ts} +305 -197
- package/src/browser/extensions/plugins.ts +93 -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.ts +338 -0
- package/src/browser/streams.ts +403 -0
- package/src/browser/workers/{csv-parser.worker.js → csv-parser.worker.ts} +3 -3
- package/src/browser/workers/{worker-pool.js → worker-pool.ts} +51 -30
- package/src/core/delimiter-cache.ts +320 -0
- package/src/core/{node-optimizations.js → node-optimizations.ts} +448 -407
- package/src/core/plugin-system.ts +588 -0
- package/src/core/transform-hooks.ts +566 -0
- package/src/engines/{fast-path-engine-new.js → fast-path-engine-new.ts} +11 -2
- package/src/engines/{fast-path-engine.js → fast-path-engine.ts} +79 -53
- package/src/errors.ts +1 -0
- package/src/formats/{ndjson-parser.js → ndjson-parser.ts} +24 -16
- package/src/formats/{tsv-parser.js → tsv-parser.ts} +18 -17
- package/src/{index-with-plugins.js → index-with-plugins.ts} +381 -357
- package/src/types/index.ts +275 -0
- package/src/utils/bom-utils.ts +373 -0
- package/src/utils/encoding-support.ts +155 -0
- package/src/utils/{schema-validator.js → schema-validator.ts} +814 -589
- package/src/utils/transform-loader.ts +389 -0
- package/src/utils/validators.ts +35 -0
- package/src/utils/zod-adapter.ts +280 -0
- package/src/web-server/{index.js → index.ts} +19 -19
- 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/bin/jtcsv.js +0 -2462
- package/csv-to-json.js +0 -688
- package/errors.js +0 -208
- package/examples/simple-usage.js +0 -282
- package/index.js +0 -68
- package/json-save.js +0 -254
- package/json-to-csv.js +0 -526
- package/plugins/README.md +0 -91
- package/plugins/express-middleware/README.md +0 -64
- package/plugins/express-middleware/example.js +0 -136
- package/plugins/express-middleware/index.d.ts +0 -114
- package/plugins/express-middleware/index.js +0 -360
- package/plugins/express-middleware/package.json +0 -52
- package/plugins/fastify-plugin/index.js +0 -406
- package/plugins/fastify-plugin/package.json +0 -55
- package/plugins/hono/README.md +0 -28
- package/plugins/hono/index.d.ts +0 -12
- package/plugins/hono/index.js +0 -36
- package/plugins/hono/package.json +0 -35
- package/plugins/nestjs/README.md +0 -35
- package/plugins/nestjs/index.d.ts +0 -25
- package/plugins/nestjs/index.js +0 -77
- package/plugins/nestjs/package.json +0 -37
- package/plugins/nextjs-api/README.md +0 -57
- package/plugins/nextjs-api/examples/ConverterComponent.jsx +0 -386
- package/plugins/nextjs-api/examples/api-convert.js +0 -69
- package/plugins/nextjs-api/index.js +0 -387
- package/plugins/nextjs-api/package.json +0 -63
- package/plugins/nextjs-api/route.js +0 -371
- package/plugins/nuxt/README.md +0 -24
- package/plugins/nuxt/index.js +0 -21
- package/plugins/nuxt/package.json +0 -35
- package/plugins/nuxt/runtime/composables/useJtcsv.js +0 -6
- package/plugins/nuxt/runtime/plugin.js +0 -6
- package/plugins/remix/README.md +0 -26
- package/plugins/remix/index.d.ts +0 -16
- package/plugins/remix/index.js +0 -62
- package/plugins/remix/package.json +0 -35
- package/plugins/sveltekit/README.md +0 -28
- package/plugins/sveltekit/index.d.ts +0 -17
- package/plugins/sveltekit/index.js +0 -54
- package/plugins/sveltekit/package.json +0 -33
- package/plugins/trpc/README.md +0 -25
- package/plugins/trpc/index.d.ts +0 -7
- package/plugins/trpc/index.js +0 -32
- package/plugins/trpc/package.json +0 -34
- package/src/browser/browser-functions.js +0 -219
- package/src/browser/csv-to-json-browser.js +0 -700
- package/src/browser/index.js +0 -113
- package/src/browser/json-to-csv-browser.js +0 -309
- package/src/browser/streams.js +0 -393
- package/src/core/delimiter-cache.js +0 -186
- package/src/core/plugin-system.js +0 -476
- package/src/core/transform-hooks.js +0 -350
- package/src/errors.js +0 -26
- package/src/utils/transform-loader.js +0 -205
- package/stream-csv-to-json.js +0 -542
- package/stream-json-to-csv.js +0 -464
- /package/examples/{web-workers-advanced.js → web-workers-advanced.ts} +0 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript интерфейсы для проекта jtcsv
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Базовые типы
|
|
6
|
+
export type AnyObject = Record<string, any>;
|
|
7
|
+
export type AnyArray = any[];
|
|
8
|
+
|
|
9
|
+
// JSON to CSV интерфейсы
|
|
10
|
+
export interface JsonToCsvOptions {
|
|
11
|
+
/** CSV delimiter (default: ';') */
|
|
12
|
+
delimiter?: string;
|
|
13
|
+
/** Include headers row (default: true) */
|
|
14
|
+
includeHeaders?: boolean;
|
|
15
|
+
/** Rename column headers { oldKey: newKey } */
|
|
16
|
+
renameMap?: Record<string, string>;
|
|
17
|
+
/** Template for guaranteed column order */
|
|
18
|
+
template?: Record<string, any>;
|
|
19
|
+
/** Maximum number of records to process (optional, no limit by default) */
|
|
20
|
+
maxRecords?: number;
|
|
21
|
+
/** Prevent CSV injection attacks by escaping formulas (default: true) */
|
|
22
|
+
preventCsvInjection?: boolean;
|
|
23
|
+
/** Ensure RFC 4180 compliance (proper quoting, line endings) (default: true) */
|
|
24
|
+
rfc4180Compliant?: boolean;
|
|
25
|
+
/** Normalize excessive quotes in JSON string values before CSV export (default: true) */
|
|
26
|
+
normalizeQuotes?: boolean;
|
|
27
|
+
/** JSON schema for data validation and formatting */
|
|
28
|
+
schema?: Record<string, any>;
|
|
29
|
+
/** Whether to flatten nested objects into dot notation keys */
|
|
30
|
+
flatten?: boolean;
|
|
31
|
+
/** Separator for flattened keys (e.g., 'user.name' with '.') */
|
|
32
|
+
flattenSeparator?: string;
|
|
33
|
+
/** Maximum depth for flattening nested objects */
|
|
34
|
+
flattenMaxDepth?: number;
|
|
35
|
+
/** How to handle arrays ('stringify', 'join', 'expand') */
|
|
36
|
+
arrayHandling?: 'stringify' | 'join' | 'expand';
|
|
37
|
+
/** Warn when record count exceeds this threshold (default: 1000000) */
|
|
38
|
+
memoryWarningThreshold?: number;
|
|
39
|
+
/** Safety limit for in-memory conversion (default: 5000000). Set to Infinity to disable. */
|
|
40
|
+
memoryLimit?: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface SaveAsCsvOptions extends JsonToCsvOptions {
|
|
44
|
+
/** Validate file path security (default: true) */
|
|
45
|
+
validatePath?: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// CSV to JSON интерфейсы
|
|
49
|
+
export interface CsvToJsonOptions {
|
|
50
|
+
/** CSV delimiter (default: auto-detected) */
|
|
51
|
+
delimiter?: string;
|
|
52
|
+
/** Auto-detect delimiter if not specified (default: true) */
|
|
53
|
+
autoDetect?: boolean;
|
|
54
|
+
/** Candidate delimiters for auto-detection (default: [';', ',', '\t', '|']) */
|
|
55
|
+
candidates?: string[];
|
|
56
|
+
/** Whether CSV has headers row (default: true) */
|
|
57
|
+
hasHeaders?: boolean;
|
|
58
|
+
/** Map for renaming column headers { newKey: oldKey } */
|
|
59
|
+
renameMap?: Record<string, string>;
|
|
60
|
+
/** Trim whitespace from values (default: true) */
|
|
61
|
+
trim?: boolean;
|
|
62
|
+
/** Parse numeric values (default: false) */
|
|
63
|
+
parseNumbers?: boolean;
|
|
64
|
+
/** Parse boolean values (default: false) */
|
|
65
|
+
parseBooleans?: boolean;
|
|
66
|
+
/** Maximum number of rows to process (optional, no limit by default) */
|
|
67
|
+
maxRows?: number;
|
|
68
|
+
/** Enable fast-path parsing (default: true) */
|
|
69
|
+
useFastPath?: boolean;
|
|
70
|
+
/** Fast-path output mode (default: 'objects') */
|
|
71
|
+
fastPathMode?: 'objects' | 'compact' | 'stream';
|
|
72
|
+
/** JSON schema for validation and formatting */
|
|
73
|
+
schema?: Record<string, any>;
|
|
74
|
+
/** Custom transform function for each row */
|
|
75
|
+
transform?: (row: Record<string, any>) => Record<string, any>;
|
|
76
|
+
/** Use delimiter cache for auto-detection (default: true) */
|
|
77
|
+
useCache?: boolean;
|
|
78
|
+
/** Custom delimiter cache instance */
|
|
79
|
+
cache?: any; // DelimiterCache type will be imported later
|
|
80
|
+
/** Hooks for custom processing */
|
|
81
|
+
hooks?: {
|
|
82
|
+
beforeConvert?: (csv: string, options: CsvToJsonOptions) => string;
|
|
83
|
+
perRow?: (row: Record<string, any>, index: number, context?: { options: CsvToJsonOptions }) => Record<string, any>;
|
|
84
|
+
afterConvert?: (result: any[], options: CsvToJsonOptions) => any[];
|
|
85
|
+
transformHooks?: import('../core/transform-hooks').TransformHooks;
|
|
86
|
+
onError?: (error: Error, csv: string, options: CsvToJsonOptions) => void;
|
|
87
|
+
};
|
|
88
|
+
/** Prevent CSV injection attacks by escaping formulas (default: true) */
|
|
89
|
+
preventCsvInjection?: boolean;
|
|
90
|
+
/** Ensure RFC 4180 compliance (proper quoting, line endings) (default: true) */
|
|
91
|
+
rfc4180Compliant?: boolean;
|
|
92
|
+
/** Warn about extra fields not in headers (default: false) */
|
|
93
|
+
warnExtraFields?: boolean;
|
|
94
|
+
/** Error recovery strategy for row-level errors (default: 'throw') */
|
|
95
|
+
onError?: 'skip' | 'warn' | 'throw';
|
|
96
|
+
/** Custom error handler for row-level errors */
|
|
97
|
+
errorHandler?: (error: Error, line: string, lineNumber: number) => void;
|
|
98
|
+
/** Warn when row count exceeds this threshold (default: 1000000) */
|
|
99
|
+
memoryWarningThreshold?: number;
|
|
100
|
+
/** Safety limit for in-memory conversion (default: 5000000). Set to Infinity to disable. */
|
|
101
|
+
memoryLimit?: number;
|
|
102
|
+
/** Attempt to repair shifted rows with trailing empty fields (default: true) */
|
|
103
|
+
repairRowShifts?: boolean;
|
|
104
|
+
/** Normalize excessive quotes in parsed fields (default: true) */
|
|
105
|
+
normalizeQuotes?: boolean;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// JSON save интерфейсы
|
|
109
|
+
export interface SaveAsJsonOptions {
|
|
110
|
+
/** Format JSON with indentation (default: false) */
|
|
111
|
+
prettyPrint?: boolean;
|
|
112
|
+
/** Maximum file size in bytes (default: 10MB = 10485760) */
|
|
113
|
+
maxSize?: number;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Streaming интерфейсы
|
|
117
|
+
export interface JsonToCsvStreamOptions extends JsonToCsvOptions {
|
|
118
|
+
/** Custom transform function for each row */
|
|
119
|
+
transform?: (row: Record<string, any>) => Record<string, any>;
|
|
120
|
+
/** JSON schema for validation and formatting */
|
|
121
|
+
schema?: Record<string, any>;
|
|
122
|
+
/** Add UTF-8 BOM for Excel compatibility (default: true) */
|
|
123
|
+
addBOM?: boolean;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface CsvToJsonStreamOptions extends CsvToJsonOptions {
|
|
127
|
+
/** Custom transform function for each row */
|
|
128
|
+
transform?: (row: Record<string, any>) => Record<string, any>;
|
|
129
|
+
/** JSON schema for validation and formatting */
|
|
130
|
+
schema?: Record<string, any>;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// NDJSON интерфейсы
|
|
134
|
+
export interface NdjsonOptions {
|
|
135
|
+
/** Buffer size for streaming (default: 64KB) */
|
|
136
|
+
bufferSize?: number;
|
|
137
|
+
/** Maximum line length (default: 10MB) */
|
|
138
|
+
maxLineLength?: number;
|
|
139
|
+
/** Error handler callback */
|
|
140
|
+
onError?: (error: Error, line: string, lineNumber: number) => void;
|
|
141
|
+
/** JSON stringify replacer function */
|
|
142
|
+
replacer?: (key: string, value: any) => any;
|
|
143
|
+
/** JSON stringify space (indentation) */
|
|
144
|
+
space?: number | string;
|
|
145
|
+
/** Filter function for rows */
|
|
146
|
+
filter?: (obj: Record<string, any>, index: number) => boolean;
|
|
147
|
+
/** Transform function for rows */
|
|
148
|
+
transform?: (obj: Record<string, any>, index: number) => any;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface NdjsonToCsvStreamOptions {
|
|
152
|
+
/** Delimiter for CSV output (default: ',') */
|
|
153
|
+
delimiter?: string;
|
|
154
|
+
/** Include headers row (default: true) */
|
|
155
|
+
includeHeaders?: boolean;
|
|
156
|
+
/** Rename column headers { oldKey: newKey } */
|
|
157
|
+
renameMap?: Record<string, string>;
|
|
158
|
+
/** Maximum number of records to process */
|
|
159
|
+
maxRecords?: number;
|
|
160
|
+
/** Prevent CSV injection attacks by escaping formulas (default: true) */
|
|
161
|
+
preventCsvInjection?: boolean;
|
|
162
|
+
/** Ensure RFC 4180 compliance (proper quoting, line endings) (default: true) */
|
|
163
|
+
rfc4180Compliant?: boolean;
|
|
164
|
+
/** JSON schema for validation and formatting */
|
|
165
|
+
schema?: Record<string, any>;
|
|
166
|
+
/** Custom transform function for each row */
|
|
167
|
+
transform?: (row: Record<string, any>) => Record<string, any>;
|
|
168
|
+
/** Add UTF-8 BOM for Excel compatibility (default: true) */
|
|
169
|
+
addBOM?: boolean;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// TSV интерфейсы
|
|
173
|
+
export interface TsvOptions {
|
|
174
|
+
/** Whether TSV has headers row (default: true) */
|
|
175
|
+
hasHeaders?: boolean;
|
|
176
|
+
/** Trim whitespace from values (default: true) */
|
|
177
|
+
trim?: boolean;
|
|
178
|
+
/** Parse numeric values (default: false) */
|
|
179
|
+
parseNumbers?: boolean;
|
|
180
|
+
/** Parse boolean values (default: false) */
|
|
181
|
+
parseBooleans?: boolean;
|
|
182
|
+
/** Maximum number of rows to process */
|
|
183
|
+
maxRows?: number;
|
|
184
|
+
/** JSON schema for validation and formatting */
|
|
185
|
+
schema?: Record<string, any>;
|
|
186
|
+
/** Custom transform function for each row */
|
|
187
|
+
transform?: (row: Record<string, any>) => Record<string, any>;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export interface TsvValidationResult {
|
|
191
|
+
/** Whether the content is valid TSV */
|
|
192
|
+
isValid: boolean;
|
|
193
|
+
/** Number of rows detected */
|
|
194
|
+
rowCount: number;
|
|
195
|
+
/** Number of columns (if consistent) */
|
|
196
|
+
columnCount?: number;
|
|
197
|
+
/** Error message if invalid */
|
|
198
|
+
error?: string;
|
|
199
|
+
/** Line number where error occurred */
|
|
200
|
+
errorLine?: number;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export interface ValidateTsvOptions {
|
|
204
|
+
/** Maximum number of rows to check */
|
|
205
|
+
maxRows?: number;
|
|
206
|
+
/** Whether to check for consistent column count */
|
|
207
|
+
checkConsistency?: boolean;
|
|
208
|
+
/** Whether to validate TSV format strictly */
|
|
209
|
+
strict?: boolean;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Worker интерфейсы для многопоточной обработки
|
|
213
|
+
export interface WorkerTask<T = any, R = any> {
|
|
214
|
+
id: string;
|
|
215
|
+
data: T;
|
|
216
|
+
type: string;
|
|
217
|
+
options?: Record<string, any>;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export interface WorkerResult<R = any> {
|
|
221
|
+
id: string;
|
|
222
|
+
result: R;
|
|
223
|
+
error?: Error;
|
|
224
|
+
duration: number;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export interface WorkerPoolStats {
|
|
228
|
+
totalWorkers: number;
|
|
229
|
+
activeWorkers: number;
|
|
230
|
+
idleWorkers: number;
|
|
231
|
+
totalTasks: number;
|
|
232
|
+
completedTasks: number;
|
|
233
|
+
failedTasks: number;
|
|
234
|
+
averageTaskDuration: number;
|
|
235
|
+
memoryUsage?: NodeJS.MemoryUsage;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Асинхронные интерфейсы
|
|
239
|
+
export interface AsyncJsonToCsvOptions extends JsonToCsvOptions {
|
|
240
|
+
/** Use worker threads for processing (default: auto-detect based on data size) */
|
|
241
|
+
useWorkers?: boolean;
|
|
242
|
+
/** Number of worker threads to use (default: CPU cores - 1) */
|
|
243
|
+
workerCount?: number;
|
|
244
|
+
/** Size of data chunks for parallel processing */
|
|
245
|
+
chunkSize?: number;
|
|
246
|
+
/** Progress callback function */
|
|
247
|
+
onProgress?: (progress: { processed: number; total: number; percentage: number }) => void;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export interface AsyncCsvToJsonOptions extends CsvToJsonOptions {
|
|
251
|
+
/** Use worker threads for processing (default: auto-detect based on data size) */
|
|
252
|
+
useWorkers?: boolean;
|
|
253
|
+
/** Number of worker threads to use (default: CPU cores - 1) */
|
|
254
|
+
workerCount?: number;
|
|
255
|
+
/** Size of data chunks for parallel processing */
|
|
256
|
+
chunkSize?: number;
|
|
257
|
+
/** Progress callback function */
|
|
258
|
+
onProgress?: (progress: { processed: number; total: number; percentage: number }) => void;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Утилитарные типы
|
|
262
|
+
export type PreprocessOptions = {
|
|
263
|
+
flatten?: boolean;
|
|
264
|
+
flattenSeparator?: string;
|
|
265
|
+
flattenMaxDepth?: number;
|
|
266
|
+
arrayHandling?: 'stringify' | 'join' | 'expand';
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
export type DeepUnwrapOptions = {
|
|
270
|
+
maxDepth?: number;
|
|
271
|
+
preserveArrays?: boolean;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// Экспорт всех интерфейсов
|
|
275
|
+
// Примечание: Типы ошибок экспортируются из errors.ts
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BOM (Byte Order Mark) Utilities for jtcsv
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to detect and strip BOM characters from UTF-8, UTF-16 LE/BE,
|
|
5
|
+
* and UTF-32 encoded strings/buffers.
|
|
6
|
+
*
|
|
7
|
+
* @module bom-utils
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Transform } from 'stream';
|
|
11
|
+
import * as fs from 'fs';
|
|
12
|
+
import * as fsPromises from 'fs/promises';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* BOM signatures for different encodings
|
|
16
|
+
*/
|
|
17
|
+
export const BOM_SIGNATURES = {
|
|
18
|
+
'utf-8': Buffer.from([0xEF, 0xBB, 0xBF]),
|
|
19
|
+
'utf-16le': Buffer.from([0xFF, 0xFE]),
|
|
20
|
+
'utf-16be': Buffer.from([0xFE, 0xFF]),
|
|
21
|
+
'utf-32le': Buffer.from([0xFF, 0xFE, 0x00, 0x00]),
|
|
22
|
+
'utf-32be': Buffer.from([0x00, 0x00, 0xFE, 0xFF])
|
|
23
|
+
} as const;
|
|
24
|
+
|
|
25
|
+
export type Encoding = keyof typeof BOM_SIGNATURES;
|
|
26
|
+
|
|
27
|
+
export interface BomDetectionResult {
|
|
28
|
+
encoding: Encoding;
|
|
29
|
+
bomLength: number;
|
|
30
|
+
hasBom: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ReadFileWithBomResult {
|
|
34
|
+
data: Buffer | string;
|
|
35
|
+
encoding: string;
|
|
36
|
+
hadBom: boolean;
|
|
37
|
+
bomInfo: BomDetectionResult | null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface NormalizeCsvInputOptions {
|
|
41
|
+
encoding?: string;
|
|
42
|
+
normalizeLineEndings?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Detects if a buffer or string starts with a BOM
|
|
47
|
+
*
|
|
48
|
+
* @param input - Input to check for BOM
|
|
49
|
+
* @returns Detection result or null if no BOM found
|
|
50
|
+
*/
|
|
51
|
+
export function detectBom(input: Buffer | string | null | undefined): BomDetectionResult | null {
|
|
52
|
+
if (!input) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let buffer: Buffer;
|
|
57
|
+
if (typeof input === 'string') {
|
|
58
|
+
buffer = Buffer.from(input, 'utf8');
|
|
59
|
+
} else if (Buffer.isBuffer(input)) {
|
|
60
|
+
buffer = input;
|
|
61
|
+
} else {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Check each BOM signature
|
|
66
|
+
for (const [encoding, signature] of Object.entries(BOM_SIGNATURES)) {
|
|
67
|
+
if (buffer.length >= signature.length) {
|
|
68
|
+
if (buffer.slice(0, signature.length).equals(signature)) {
|
|
69
|
+
return {
|
|
70
|
+
encoding: encoding as Encoding,
|
|
71
|
+
bomLength: signature.length,
|
|
72
|
+
hasBom: true
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Strips BOM from a buffer or string
|
|
83
|
+
*
|
|
84
|
+
* @param input - Input to strip BOM from
|
|
85
|
+
* @returns Input without BOM
|
|
86
|
+
*/
|
|
87
|
+
export function stripBom(input: Buffer | string | null | undefined): Buffer | string {
|
|
88
|
+
if (!input) {
|
|
89
|
+
return input as any;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const bomInfo = detectBom(input);
|
|
93
|
+
if (!bomInfo) {
|
|
94
|
+
return input;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (Buffer.isBuffer(input)) {
|
|
98
|
+
return input.slice(bomInfo.bomLength);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (typeof input === 'string') {
|
|
102
|
+
// Convert to buffer, strip BOM, then convert back to string
|
|
103
|
+
const buffer = Buffer.from(input, 'utf8');
|
|
104
|
+
const strippedBuffer = buffer.slice(bomInfo.bomLength);
|
|
105
|
+
|
|
106
|
+
// Determine correct encoding for conversion
|
|
107
|
+
let encoding: BufferEncoding = 'utf8';
|
|
108
|
+
if (bomInfo.encoding === 'utf-16le') {
|
|
109
|
+
encoding = 'utf16le';
|
|
110
|
+
} else if (bomInfo.encoding === 'utf-16be') {
|
|
111
|
+
encoding = 'utf16le'; // Node.js uses utf16le for both LE and BE, conversion handled by Buffer
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return strippedBuffer.toString(encoding);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return input;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Strips BOM from a string (optimized for strings)
|
|
122
|
+
*
|
|
123
|
+
* @param str - String to strip BOM from
|
|
124
|
+
* @returns String without BOM
|
|
125
|
+
*/
|
|
126
|
+
export function stripBomFromString(str: string): string {
|
|
127
|
+
if (typeof str !== 'string') {
|
|
128
|
+
return str as any;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Check for UTF-8 BOM (most common)
|
|
132
|
+
if (str.charCodeAt(0) === 0xFEFF) {
|
|
133
|
+
return str.slice(1);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Check for UTF-8 BOM bytes as characters
|
|
137
|
+
if (str.length >= 3 &&
|
|
138
|
+
str.charCodeAt(0) === 0xEF &&
|
|
139
|
+
str.charCodeAt(1) === 0xBB &&
|
|
140
|
+
str.charCodeAt(2) === 0xBF) {
|
|
141
|
+
return str.slice(3);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return str;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Creates a transform stream that strips BOM from incoming data
|
|
149
|
+
*
|
|
150
|
+
* @returns Transform stream
|
|
151
|
+
*/
|
|
152
|
+
export function createBomStripStream(): Transform {
|
|
153
|
+
let bomStripped = false;
|
|
154
|
+
|
|
155
|
+
return new Transform({
|
|
156
|
+
transform(chunk: Buffer, encoding: string, callback: (error?: Error | null, data?: Buffer) => void) {
|
|
157
|
+
if (!bomStripped) {
|
|
158
|
+
const bomInfo = detectBom(chunk);
|
|
159
|
+
if (bomInfo) {
|
|
160
|
+
// Strip BOM from first chunk
|
|
161
|
+
chunk = chunk.slice(bomInfo.bomLength);
|
|
162
|
+
bomStripped = true;
|
|
163
|
+
} else {
|
|
164
|
+
bomStripped = true; // No BOM found, but we've checked
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
this.push(chunk);
|
|
169
|
+
callback();
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Reads a file and automatically handles BOM
|
|
176
|
+
*
|
|
177
|
+
* @param filePath - Path to file
|
|
178
|
+
* @param options - Read options
|
|
179
|
+
* @returns Promise with file data and BOM info
|
|
180
|
+
*/
|
|
181
|
+
export async function readFileWithBomHandling(
|
|
182
|
+
filePath: string,
|
|
183
|
+
options: { encoding?: BufferEncoding } = {}
|
|
184
|
+
): Promise<ReadFileWithBomResult> {
|
|
185
|
+
const buffer = await fsPromises.readFile(filePath);
|
|
186
|
+
|
|
187
|
+
const bomInfo = detectBom(buffer);
|
|
188
|
+
const hadBom = !!bomInfo;
|
|
189
|
+
|
|
190
|
+
let data: Buffer | string;
|
|
191
|
+
let encoding = options.encoding || 'utf8';
|
|
192
|
+
|
|
193
|
+
if (bomInfo) {
|
|
194
|
+
// Strip BOM
|
|
195
|
+
data = buffer.slice(bomInfo.bomLength);
|
|
196
|
+
|
|
197
|
+
// Use detected encoding if not specified
|
|
198
|
+
if (!options.encoding) {
|
|
199
|
+
// Convert our encoding names to Node.js BufferEncoding
|
|
200
|
+
if (bomInfo.encoding === 'utf-8') {
|
|
201
|
+
encoding = 'utf8';
|
|
202
|
+
} else if (bomInfo.encoding === 'utf-16le') {
|
|
203
|
+
encoding = 'utf16le';
|
|
204
|
+
} else if (bomInfo.encoding === 'utf-16be') {
|
|
205
|
+
encoding = 'utf16le'; // Node.js uses utf16le for both
|
|
206
|
+
} else {
|
|
207
|
+
encoding = 'utf8'; // fallback
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
data = buffer;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Convert to string if encoding is specified
|
|
215
|
+
if (options.encoding || bomInfo) {
|
|
216
|
+
data = data.toString(encoding);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
data,
|
|
221
|
+
encoding,
|
|
222
|
+
hadBom,
|
|
223
|
+
bomInfo: bomInfo || null
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Checks if a file has BOM (synchronous)
|
|
229
|
+
*
|
|
230
|
+
* @param filePath - Path to file
|
|
231
|
+
* @returns BOM info or null
|
|
232
|
+
*/
|
|
233
|
+
export function fileHasBomSync(filePath: string): BomDetectionResult | null {
|
|
234
|
+
const fd = fs.openSync(filePath, 'r');
|
|
235
|
+
const buffer = Buffer.alloc(4);
|
|
236
|
+
const bytesRead = fs.readSync(fd, buffer, 0, 4, 0);
|
|
237
|
+
fs.closeSync(fd);
|
|
238
|
+
|
|
239
|
+
if (bytesRead < 2) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return detectBom(buffer.slice(0, bytesRead));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Normalizes CSV input by stripping BOM and ensuring proper encoding
|
|
248
|
+
*
|
|
249
|
+
* @param csvInput - CSV input
|
|
250
|
+
* @param options - Processing options
|
|
251
|
+
* @returns Normalized CSV string
|
|
252
|
+
*/
|
|
253
|
+
export function normalizeCsvInput(
|
|
254
|
+
csvInput: string | Buffer,
|
|
255
|
+
options: NormalizeCsvInputOptions = {}
|
|
256
|
+
): string {
|
|
257
|
+
if (!csvInput) {
|
|
258
|
+
return '';
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
let normalized: string;
|
|
262
|
+
|
|
263
|
+
if (Buffer.isBuffer(csvInput)) {
|
|
264
|
+
const bomInfo = detectBom(csvInput);
|
|
265
|
+
if (bomInfo) {
|
|
266
|
+
normalized = csvInput.slice(bomInfo.bomLength).toString(bomInfo.encoding as BufferEncoding);
|
|
267
|
+
} else {
|
|
268
|
+
normalized = csvInput.toString((options.encoding as BufferEncoding) || 'utf8');
|
|
269
|
+
}
|
|
270
|
+
} else if (typeof csvInput === 'string') {
|
|
271
|
+
normalized = stripBomFromString(csvInput);
|
|
272
|
+
} else {
|
|
273
|
+
throw new Error('CSV input must be a string or Buffer');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Ensure proper line endings
|
|
277
|
+
if (options.normalizeLineEndings !== false) {
|
|
278
|
+
normalized = normalized.replace(/\r\n|\r/g, '\n');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return normalized;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Async version of normalizeCsvInput that can handle large files
|
|
286
|
+
*
|
|
287
|
+
* @param csvInput - CSV input as string, Buffer, or file path
|
|
288
|
+
* @param options - Processing options
|
|
289
|
+
* @returns Promise with normalized CSV string
|
|
290
|
+
*/
|
|
291
|
+
export async function normalizeCsvInputAsync(
|
|
292
|
+
csvInput: string | Buffer | { filePath: string },
|
|
293
|
+
options: NormalizeCsvInputOptions = {}
|
|
294
|
+
): Promise<string> {
|
|
295
|
+
if (typeof csvInput === 'object' && 'filePath' in csvInput) {
|
|
296
|
+
// Read file asynchronously
|
|
297
|
+
const result = await readFileWithBomHandling(csvInput.filePath, {
|
|
298
|
+
encoding: options.encoding as BufferEncoding || 'utf8'
|
|
299
|
+
});
|
|
300
|
+
let normalized = typeof result.data === 'string' ? result.data : result.data.toString();
|
|
301
|
+
|
|
302
|
+
// Ensure proper line endings
|
|
303
|
+
if (options.normalizeLineEndings !== false) {
|
|
304
|
+
normalized = normalized.replace(/\r\n|\r/g, '\n');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return normalized;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Handle string or Buffer input
|
|
311
|
+
return normalizeCsvInput(csvInput as string | Buffer, options);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Creates an async iterator that strips BOM from a stream
|
|
316
|
+
*
|
|
317
|
+
* @param stream - Readable stream
|
|
318
|
+
* @returns Async iterator yielding chunks without BOM
|
|
319
|
+
*/
|
|
320
|
+
export async function* createBomStrippingIterator(
|
|
321
|
+
stream: NodeJS.ReadableStream
|
|
322
|
+
): AsyncIterableIterator<Buffer> {
|
|
323
|
+
let bomStripped = false;
|
|
324
|
+
|
|
325
|
+
for await (const chunk of stream) {
|
|
326
|
+
if (!bomStripped) {
|
|
327
|
+
const bomInfo = detectBom(chunk as Buffer);
|
|
328
|
+
if (bomInfo) {
|
|
329
|
+
// Strip BOM from first chunk
|
|
330
|
+
yield (chunk as Buffer).slice(bomInfo.bomLength);
|
|
331
|
+
bomStripped = true;
|
|
332
|
+
continue;
|
|
333
|
+
} else {
|
|
334
|
+
bomStripped = true;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
yield chunk as Buffer;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Detects BOM asynchronously for large files
|
|
344
|
+
*
|
|
345
|
+
* @param filePath - Path to file
|
|
346
|
+
* @returns Promise with BOM info or null
|
|
347
|
+
*/
|
|
348
|
+
export async function detectBomAsync(filePath: string): Promise<BomDetectionResult | null> {
|
|
349
|
+
const fd = await fsPromises.open(filePath, 'r');
|
|
350
|
+
const buffer = Buffer.alloc(4);
|
|
351
|
+
const { bytesRead } = await fd.read(buffer, 0, 4, 0);
|
|
352
|
+
await fd.close();
|
|
353
|
+
|
|
354
|
+
if (bytesRead < 2) {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return detectBom(buffer.slice(0, bytesRead));
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export default {
|
|
362
|
+
detectBom,
|
|
363
|
+
stripBom,
|
|
364
|
+
stripBomFromString,
|
|
365
|
+
createBomStripStream,
|
|
366
|
+
readFileWithBomHandling,
|
|
367
|
+
fileHasBomSync,
|
|
368
|
+
normalizeCsvInput,
|
|
369
|
+
normalizeCsvInputAsync,
|
|
370
|
+
createBomStrippingIterator,
|
|
371
|
+
detectBomAsync,
|
|
372
|
+
BOM_SIGNATURES
|
|
373
|
+
};
|