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,350 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.jtcsvExpressMiddleware = jtcsvExpressMiddleware;
|
|
4
|
+
exports.jtcsvCsvToJsonRoute = jtcsvCsvToJsonRoute;
|
|
5
|
+
exports.jtcsvJsonToCsvRoute = jtcsvJsonToCsvRoute;
|
|
6
|
+
exports.jtcsvUploadCsvRoute = jtcsvUploadCsvRoute;
|
|
7
|
+
exports.jtcsvHealthCheck = jtcsvHealthCheck;
|
|
8
|
+
const index_core_1 = require("../../index-core");
|
|
9
|
+
const errors_1 = require("../../errors");
|
|
10
|
+
function parseSizeToBytes(sizeStr) {
|
|
11
|
+
if (typeof sizeStr === 'number') {
|
|
12
|
+
return sizeStr;
|
|
13
|
+
}
|
|
14
|
+
if (typeof sizeStr !== 'string') {
|
|
15
|
+
return 10 * 1024 * 1024;
|
|
16
|
+
}
|
|
17
|
+
const match = sizeStr.match(/^(\d+(?:\.\d+)?)\s*(B|KB|MB|GB|TB)$/i);
|
|
18
|
+
if (!match) {
|
|
19
|
+
return 10 * 1024 * 1024;
|
|
20
|
+
}
|
|
21
|
+
const value = parseFloat(match[1]);
|
|
22
|
+
const unit = match[2].toUpperCase();
|
|
23
|
+
switch (unit) {
|
|
24
|
+
case 'B': return value;
|
|
25
|
+
case 'KB': return value * 1024;
|
|
26
|
+
case 'MB': return value * 1024 * 1024;
|
|
27
|
+
case 'GB': return value * 1024 * 1024 * 1024;
|
|
28
|
+
case 'TB': return value * 1024 * 1024 * 1024 * 1024;
|
|
29
|
+
default: return value * 1024 * 1024;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function jtcsvExpressMiddleware(options = {}) {
|
|
33
|
+
const { maxSize = '10mb', maxFileSize = '500MB', maxFieldSize = 1024 * 1024, timeout = 300000, autoDetect = true, delimiter = ',', enableFastPath = true, preventCsvInjection = true, rfc4180Compliant = true, useAsync = true, useWorkers, workerCount, conversionOptions = {} } = options;
|
|
34
|
+
return async (req, res, next) => {
|
|
35
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
36
|
+
console.log('[jtcsv-middleware] Request received:', req.method, req.url, req.headers['content-type']);
|
|
37
|
+
}
|
|
38
|
+
req.startTime = Date.now();
|
|
39
|
+
if (!req.body || (typeof req.body !== 'string' && typeof req.body !== 'object')) {
|
|
40
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
41
|
+
console.log('[jtcsv-middleware] No body, skipping');
|
|
42
|
+
}
|
|
43
|
+
return next();
|
|
44
|
+
}
|
|
45
|
+
const contentLength = req.get('content-length');
|
|
46
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
47
|
+
console.log(`[jtcsv-middleware] Content-Length: "${contentLength}"`);
|
|
48
|
+
}
|
|
49
|
+
if (contentLength && contentLength.trim() !== '') {
|
|
50
|
+
const maxBytes = parseSizeToBytes(maxFileSize);
|
|
51
|
+
const contentLengthInt = parseInt(contentLength, 10);
|
|
52
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
53
|
+
console.log(`[jtcsv-middleware] maxBytes: ${maxBytes}, contentLengthInt: ${contentLengthInt}`);
|
|
54
|
+
}
|
|
55
|
+
if (contentLengthInt > maxBytes) {
|
|
56
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
57
|
+
console.log(`[jtcsv-middleware] File size limit exceeded: ${contentLengthInt} > ${maxBytes}`);
|
|
58
|
+
}
|
|
59
|
+
res.status(413).json({
|
|
60
|
+
success: false,
|
|
61
|
+
error: `File size exceeds limit of ${maxFileSize}`,
|
|
62
|
+
code: 'FILE_SIZE_LIMIT_EXCEEDED'
|
|
63
|
+
});
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const contentType = req.get('content-type') || '';
|
|
68
|
+
const acceptHeader = req.get('accept') || 'application/json';
|
|
69
|
+
let inputFormat = 'unknown';
|
|
70
|
+
const inputData = req.body;
|
|
71
|
+
if (autoDetect) {
|
|
72
|
+
if (contentType.includes('application/json') ||
|
|
73
|
+
(req.body !== null && typeof req.body === 'object' && !Array.isArray(req.body))) {
|
|
74
|
+
inputFormat = 'json';
|
|
75
|
+
}
|
|
76
|
+
else if (contentType.includes('text/csv') ||
|
|
77
|
+
contentType.includes('text/plain') ||
|
|
78
|
+
(typeof req.body === 'string' && req.body.includes(','))) {
|
|
79
|
+
inputFormat = 'csv';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
if (contentType.includes('application/json')) {
|
|
84
|
+
inputFormat = 'json';
|
|
85
|
+
}
|
|
86
|
+
else if (contentType.includes('text/csv')) {
|
|
87
|
+
inputFormat = 'csv';
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (inputFormat === 'unknown') {
|
|
91
|
+
return next();
|
|
92
|
+
}
|
|
93
|
+
let timeoutId;
|
|
94
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
95
|
+
timeoutId = setTimeout(() => {
|
|
96
|
+
reject(new Error(`Request processing timeout (${timeout}ms)`));
|
|
97
|
+
}, timeout);
|
|
98
|
+
});
|
|
99
|
+
try {
|
|
100
|
+
const processingPromise = (async () => {
|
|
101
|
+
let outputFormat = 'json';
|
|
102
|
+
if (acceptHeader.includes('text/csv')) {
|
|
103
|
+
outputFormat = 'csv';
|
|
104
|
+
}
|
|
105
|
+
else if (req.query.format === 'csv') {
|
|
106
|
+
outputFormat = 'csv';
|
|
107
|
+
}
|
|
108
|
+
else if (req.body?.format === 'csv') {
|
|
109
|
+
outputFormat = 'csv';
|
|
110
|
+
}
|
|
111
|
+
const mergedConversionOptions = {
|
|
112
|
+
delimiter,
|
|
113
|
+
preventCsvInjection,
|
|
114
|
+
rfc4180Compliant,
|
|
115
|
+
useFastPath: enableFastPath,
|
|
116
|
+
maxFieldSize,
|
|
117
|
+
useWorkers,
|
|
118
|
+
workerCount,
|
|
119
|
+
...req.query,
|
|
120
|
+
...conversionOptions
|
|
121
|
+
};
|
|
122
|
+
delete mergedConversionOptions.maxSize;
|
|
123
|
+
delete mergedConversionOptions.maxFileSize;
|
|
124
|
+
delete mergedConversionOptions.maxFieldSize;
|
|
125
|
+
delete mergedConversionOptions.timeout;
|
|
126
|
+
delete mergedConversionOptions.autoDetect;
|
|
127
|
+
delete mergedConversionOptions.enableFastPath;
|
|
128
|
+
delete mergedConversionOptions.useAsync;
|
|
129
|
+
delete mergedConversionOptions.useWorkers;
|
|
130
|
+
delete mergedConversionOptions.workerCount;
|
|
131
|
+
let result;
|
|
132
|
+
const stats = {
|
|
133
|
+
inputSize: 0,
|
|
134
|
+
outputSize: 0,
|
|
135
|
+
processingTime: 0,
|
|
136
|
+
conversion: `${inputFormat}→${outputFormat}`
|
|
137
|
+
};
|
|
138
|
+
const startTime = Date.now();
|
|
139
|
+
if (inputFormat === 'json' && outputFormat === 'csv') {
|
|
140
|
+
const jsonData = typeof inputData === 'string' ? JSON.parse(inputData) : inputData;
|
|
141
|
+
stats.inputSize = Buffer.byteLength(JSON.stringify(jsonData));
|
|
142
|
+
if (useAsync) {
|
|
143
|
+
result = await (0, index_core_1.jsonToCsvAsync)(jsonData, mergedConversionOptions);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
result = (0, index_core_1.jsonToCsv)(jsonData, mergedConversionOptions);
|
|
147
|
+
}
|
|
148
|
+
stats.outputSize = Buffer.byteLength(result);
|
|
149
|
+
}
|
|
150
|
+
else if (inputFormat === 'csv' && outputFormat === 'json') {
|
|
151
|
+
const csvData = typeof inputData === 'string' ? inputData : String(inputData);
|
|
152
|
+
stats.inputSize = Buffer.byteLength(csvData);
|
|
153
|
+
if (useAsync) {
|
|
154
|
+
result = await (0, index_core_1.csvToJsonAsync)(csvData, mergedConversionOptions);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
result = (0, index_core_1.csvToJson)(csvData, mergedConversionOptions);
|
|
158
|
+
}
|
|
159
|
+
stats.outputSize = Buffer.byteLength(JSON.stringify(result));
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
result = inputData;
|
|
163
|
+
stats.conversion = 'none';
|
|
164
|
+
}
|
|
165
|
+
stats.processingTime = Date.now() - startTime;
|
|
166
|
+
req.converted = {
|
|
167
|
+
data: result,
|
|
168
|
+
format: outputFormat,
|
|
169
|
+
inputFormat,
|
|
170
|
+
outputFormat,
|
|
171
|
+
stats,
|
|
172
|
+
options: mergedConversionOptions
|
|
173
|
+
};
|
|
174
|
+
if (outputFormat === 'csv') {
|
|
175
|
+
res.set('Content-Type', 'text/csv; charset=utf-8');
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
res.set('Content-Type', 'application/json; charset=utf-8');
|
|
179
|
+
}
|
|
180
|
+
next();
|
|
181
|
+
})();
|
|
182
|
+
await Promise.race([processingPromise, timeoutPromise]);
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
const err = error;
|
|
186
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
187
|
+
console.log('[jtcsv-middleware] Conversion error:', err.message, err.stack?.split('\n')[0]);
|
|
188
|
+
}
|
|
189
|
+
let statusCode = 400;
|
|
190
|
+
if (err.message.includes('timeout') || err.message.includes('Timeout')) {
|
|
191
|
+
statusCode = 408;
|
|
192
|
+
}
|
|
193
|
+
else if (err.message.includes('File size exceeds limit')) {
|
|
194
|
+
statusCode = 413;
|
|
195
|
+
}
|
|
196
|
+
else if (err instanceof errors_1.SecurityError) {
|
|
197
|
+
statusCode = 403;
|
|
198
|
+
}
|
|
199
|
+
else if (err instanceof errors_1.ValidationError) {
|
|
200
|
+
statusCode = 422;
|
|
201
|
+
}
|
|
202
|
+
else if (err instanceof errors_1.FileSystemError) {
|
|
203
|
+
statusCode = 500;
|
|
204
|
+
}
|
|
205
|
+
else if (err instanceof errors_1.JtcsvError) {
|
|
206
|
+
statusCode = 400;
|
|
207
|
+
}
|
|
208
|
+
const errorResponse = {
|
|
209
|
+
success: false,
|
|
210
|
+
error: err.message,
|
|
211
|
+
code: err.code || 'CONVERSION_ERROR',
|
|
212
|
+
timestamp: new Date().toISOString()
|
|
213
|
+
};
|
|
214
|
+
if (process.env.NODE_ENV === 'development') {
|
|
215
|
+
errorResponse.stack = err.stack;
|
|
216
|
+
errorResponse.details = {
|
|
217
|
+
contentType: req.get('content-type'),
|
|
218
|
+
contentLength: req.get('content-length'),
|
|
219
|
+
method: req.method,
|
|
220
|
+
url: req.url
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
res.status(statusCode).json(errorResponse);
|
|
224
|
+
}
|
|
225
|
+
finally {
|
|
226
|
+
if (timeoutId) {
|
|
227
|
+
clearTimeout(timeoutId);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function jtcsvCsvToJsonRoute(options = {}) {
|
|
233
|
+
return async (req, res) => {
|
|
234
|
+
try {
|
|
235
|
+
const csvData = req.body;
|
|
236
|
+
if (!csvData || (typeof csvData !== 'string' && !Buffer.isBuffer(csvData))) {
|
|
237
|
+
res.status(400).json({
|
|
238
|
+
success: false,
|
|
239
|
+
error: 'CSV data is required'
|
|
240
|
+
});
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const csvString = Buffer.isBuffer(csvData) ? csvData.toString() : csvData;
|
|
244
|
+
const result = await (0, index_core_1.csvToJsonAsync)(csvString, options);
|
|
245
|
+
res.json({
|
|
246
|
+
success: true,
|
|
247
|
+
data: result,
|
|
248
|
+
stats: {
|
|
249
|
+
rows: result.length,
|
|
250
|
+
processingTime: Date.now() - (req.startTime || Date.now())
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
const err = error;
|
|
256
|
+
res.status(400).json({
|
|
257
|
+
success: false,
|
|
258
|
+
error: err.message
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function jtcsvJsonToCsvRoute(options = {}) {
|
|
264
|
+
return async (req, res) => {
|
|
265
|
+
try {
|
|
266
|
+
const jsonData = req.body;
|
|
267
|
+
if (!jsonData || (typeof jsonData !== 'object' && typeof jsonData !== 'string')) {
|
|
268
|
+
res.status(400).json({
|
|
269
|
+
success: false,
|
|
270
|
+
error: 'JSON data is required'
|
|
271
|
+
});
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const data = typeof jsonData === 'string' ? JSON.parse(jsonData) : jsonData;
|
|
275
|
+
const result = await (0, index_core_1.jsonToCsvAsync)(data, options);
|
|
276
|
+
res.set('Content-Type', 'text/csv; charset=utf-8');
|
|
277
|
+
res.set('Content-Disposition', 'attachment; filename="data.csv"');
|
|
278
|
+
res.send(result);
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
const err = error;
|
|
282
|
+
res.status(400).json({
|
|
283
|
+
success: false,
|
|
284
|
+
error: err.message
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function jtcsvUploadCsvRoute(options = {}) {
|
|
290
|
+
return async (req, res) => {
|
|
291
|
+
try {
|
|
292
|
+
if (!req.file) {
|
|
293
|
+
res.status(400).json({
|
|
294
|
+
success: false,
|
|
295
|
+
error: 'CSV file is required'
|
|
296
|
+
});
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const fs = require('fs').promises;
|
|
300
|
+
const csvData = await fs.readFile(req.file.path, 'utf8');
|
|
301
|
+
const result = await (0, index_core_1.csvToJsonAsync)(csvData, options);
|
|
302
|
+
await fs.unlink(req.file.path);
|
|
303
|
+
res.json({
|
|
304
|
+
success: true,
|
|
305
|
+
data: result,
|
|
306
|
+
stats: {
|
|
307
|
+
rows: result.length,
|
|
308
|
+
fileSize: req.file.size,
|
|
309
|
+
processingTime: Date.now() - (req.startTime || Date.now())
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
const err = error;
|
|
315
|
+
res.status(400).json({
|
|
316
|
+
success: false,
|
|
317
|
+
error: err.message
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function jtcsvHealthCheck() {
|
|
323
|
+
return (req, res) => {
|
|
324
|
+
res.json({
|
|
325
|
+
service: 'jtcsv-express-middleware',
|
|
326
|
+
status: 'healthy',
|
|
327
|
+
version: '2.0.0',
|
|
328
|
+
timestamp: new Date().toISOString(),
|
|
329
|
+
features: {
|
|
330
|
+
csvToJson: true,
|
|
331
|
+
jsonToCsv: true,
|
|
332
|
+
fastPathEngine: true,
|
|
333
|
+
csvInjectionProtection: true,
|
|
334
|
+
streaming: true,
|
|
335
|
+
asyncProcessing: true,
|
|
336
|
+
workerPool: true
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
exports.default = {
|
|
342
|
+
middleware: jtcsvExpressMiddleware,
|
|
343
|
+
csvToJsonRoute: jtcsvCsvToJsonRoute,
|
|
344
|
+
jsonToCsvRoute: jtcsvJsonToCsvRoute,
|
|
345
|
+
uploadCsvRoute: jtcsvUploadCsvRoute,
|
|
346
|
+
healthCheck: jtcsvHealthCheck,
|
|
347
|
+
jtcsvMiddleware: jtcsvExpressMiddleware,
|
|
348
|
+
createMiddleware: jtcsvExpressMiddleware
|
|
349
|
+
};
|
|
350
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../plugins/express-middleware/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\r\n * Express middleware для JTCSV\r\n * Автоматическая конвертация CSV/JSON в HTTP запросах\r\n *\r\n * @version 2.0.0 - TypeScript версия с асинхронной поддержкой\r\n * @date 2026-01-29\r\n */\r\n\r\nimport { Request, Response, NextFunction } from 'express';\r\nimport { csvToJson, csvToJsonAsync, jsonToCsv, jsonToCsvAsync } from '../../index-core';\r\nimport { JtcsvError, ValidationError, SecurityError, FileSystemError } from '../../errors';\r\n\r\n/**\r\n * Интерфейс опций Express middleware\r\n */\r\nexport interface JtcsvExpressMiddlewareOptions {\r\n /** Максимальный размер тела запроса (default: '10mb') */\r\n maxSize?: string;\r\n /** Максимальный размер файла (например, '500MB', default: '500MB') */\r\n maxFileSize?: string;\r\n /** Максимальный размер поля в байтах (default: 1MB) */\r\n maxFieldSize?: number;\r\n /** Таймаут обработки в миллисекундах (default: 300000 = 5 минут) */\r\n timeout?: number;\r\n /** Автоматическое определение формата (default: true) */\r\n autoDetect?: boolean;\r\n /** Разделитель CSV (default: ',') */\r\n delimiter?: string;\r\n /** Включить Fast-Path Engine (default: true) */\r\n enableFastPath?: boolean;\r\n /** Защита от CSV инъекций (default: true) */\r\n preventCsvInjection?: boolean;\r\n /** Соблюдение RFC4180 (default: true) */\r\n rfc4180Compliant?: boolean;\r\n /** Дополнительные опции конвертации */\r\n conversionOptions?: Record<string, any>;\r\n /** Использовать асинхронные версии функций (default: true) */\r\n useAsync?: boolean;\r\n /** Использовать многопоточную обработку для больших данных (default: auto-detect) */\r\n useWorkers?: boolean;\r\n /** Количество worker'ов для многопоточной обработки (default: CPU cores - 1) */\r\n workerCount?: number;\r\n}\r\n\r\n/**\r\n * Интерфейс конвертированных данных в request object\r\n */\r\nexport interface ConvertedData {\r\n /** Конвертированные данные */\r\n data: any;\r\n /** Формат вывода */\r\n format: 'json' | 'csv';\r\n /** Формат ввода */\r\n inputFormat: 'json' | 'csv' | 'unknown';\r\n /** Формат вывода */\r\n outputFormat: 'json' | 'csv';\r\n /** Статистика обработки */\r\n stats: {\r\n inputSize: number;\r\n outputSize: number;\r\n processingTime: number;\r\n conversion: string;\r\n workerCount?: number;\r\n };\r\n /** Опции конвертации */\r\n options: Record<string, any>;\r\n}\r\n\r\n/**\r\n * Расширенный интерфейс Request с конвертированными данными\r\n */\r\ndeclare global {\r\n namespace Express {\r\n interface Request {\r\n /** Конвертированные данные */\r\n converted?: ConvertedData;\r\n /** Время начала обработки */\r\n startTime?: number;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Преобразует строку размера (например, '500MB') в байты\r\n * @param sizeStr - Строка размера (например, '10MB', '1GB', '500KB')\r\n * @returns Размер в байтах\r\n */\r\nfunction parseSizeToBytes(sizeStr: string | number): number {\r\n if (typeof sizeStr === 'number') {\r\n return sizeStr;\r\n }\r\n if (typeof sizeStr !== 'string') {\r\n return 10 * 1024 * 1024; // default 10MB\r\n }\r\n \r\n const match = sizeStr.match(/^(\\d+(?:\\.\\d+)?)\\s*(B|KB|MB|GB|TB)$/i);\r\n if (!match) {\r\n return 10 * 1024 * 1024;\r\n }\r\n \r\n const value = parseFloat(match[1]);\r\n const unit = match[2].toUpperCase();\r\n \r\n switch (unit) {\r\n case 'B': return value;\r\n case 'KB': return value * 1024;\r\n case 'MB': return value * 1024 * 1024;\r\n case 'GB': return value * 1024 * 1024 * 1024;\r\n case 'TB': return value * 1024 * 1024 * 1024 * 1024;\r\n default: return value * 1024 * 1024;\r\n }\r\n}\r\n\r\n/**\r\n * Express middleware для обработки CSV/JSON конвертации\r\n * \r\n * @param options - Опции middleware\r\n * @returns Express middleware\r\n * \r\n * @example\r\n * // Базовое использование\r\n * const app = express();\r\n * app.use(express.json());\r\n * app.use(express.text({ type: 'text/csv' }));\r\n * app.use(jtcsvExpressMiddleware());\r\n * \r\n * @example\r\n * // С кастомными опциями\r\n * app.use(jtcsvExpressMiddleware({\r\n * maxSize: '50mb',\r\n * maxFileSize: '1GB',\r\n * maxFieldSize: 5 * 1024 * 1024, // 5MB\r\n * timeout: 600000, // 10 минут\r\n * delimiter: ';',\r\n * enableFastPath: true,\r\n * useAsync: true, // Использовать асинхронные функции\r\n * useWorkers: true // Включить многопоточную обработку\r\n * }));\r\n */\r\nexport function jtcsvExpressMiddleware(options: JtcsvExpressMiddlewareOptions = {}): (req: Request, res: Response, next: NextFunction) => Promise<void> {\r\n const {\r\n maxSize = '10mb',\r\n maxFileSize = '500MB',\r\n maxFieldSize = 1024 * 1024, // 1MB\r\n timeout = 300000, // 5 minutes\r\n autoDetect = true,\r\n delimiter = ',',\r\n enableFastPath = true,\r\n preventCsvInjection = true,\r\n rfc4180Compliant = true,\r\n useAsync = true,\r\n useWorkers,\r\n workerCount,\r\n conversionOptions = {}\r\n } = options;\r\n\r\n return async (req: Request, res: Response, next: NextFunction): Promise<void> => {\r\n if (process.env.NODE_ENV !== 'test') {\r\n console.log('[jtcsv-middleware] Request received:', req.method, req.url, req.headers['content-type']);\r\n }\r\n \r\n // Устанавливаем время начала обработки\r\n req.startTime = Date.now();\r\n \r\n // Пропускаем запросы без тела\r\n if (!req.body || (typeof req.body !== 'string' && typeof req.body !== 'object')) {\r\n if (process.env.NODE_ENV !== 'test') {\r\n console.log('[jtcsv-middleware] No body, skipping');\r\n }\r\n return next();\r\n }\r\n\r\n // Проверка размера файла\r\n const contentLength = req.get('content-length');\r\n if (process.env.NODE_ENV !== 'test') {\r\n console.log(`[jtcsv-middleware] Content-Length: \"${contentLength}\"`);\r\n }\r\n if (contentLength && contentLength.trim() !== '') {\r\n const maxBytes = parseSizeToBytes(maxFileSize);\r\n const contentLengthInt = parseInt(contentLength, 10);\r\n if (process.env.NODE_ENV !== 'test') {\r\n console.log(`[jtcsv-middleware] maxBytes: ${maxBytes}, contentLengthInt: ${contentLengthInt}`);\r\n }\r\n if (contentLengthInt > maxBytes) {\r\n if (process.env.NODE_ENV !== 'test') {\r\n console.log(`[jtcsv-middleware] File size limit exceeded: ${contentLengthInt} > ${maxBytes}`);\r\n }\r\n res.status(413).json({\r\n success: false,\r\n error: `File size exceeds limit of ${maxFileSize}`,\r\n code: 'FILE_SIZE_LIMIT_EXCEEDED'\r\n });\r\n return;\r\n }\r\n }\r\n\r\n const contentType = req.get('content-type') || '';\r\n const acceptHeader = req.get('accept') || 'application/json';\r\n \r\n // Определяем формат входных данных заранее\r\n let inputFormat: 'json' | 'csv' | 'unknown' = 'unknown';\r\n const inputData = req.body;\r\n \r\n if (autoDetect) {\r\n if (contentType.includes('application/json') ||\r\n (req.body !== null && typeof req.body === 'object' && !Array.isArray(req.body))) {\r\n inputFormat = 'json';\r\n } else if (contentType.includes('text/csv') ||\r\n contentType.includes('text/plain') ||\r\n (typeof req.body === 'string' && req.body.includes(','))) {\r\n inputFormat = 'csv';\r\n }\r\n } else {\r\n // Ручное определение на основе content-type\r\n if (contentType.includes('application/json')) {\r\n inputFormat = 'json';\r\n } else if (contentType.includes('text/csv')) {\r\n inputFormat = 'csv';\r\n }\r\n }\r\n\r\n // Если формат не определен, пропускаем\r\n if (inputFormat === 'unknown') {\r\n return next();\r\n }\r\n \r\n // Установка таймаута\r\n let timeoutId: NodeJS.Timeout;\r\n const timeoutPromise = new Promise((_, reject) => {\r\n timeoutId = setTimeout(() => {\r\n reject(new Error(`Request processing timeout (${timeout}ms)`));\r\n }, timeout);\r\n });\r\n \r\n try {\r\n // Обернем основную логику в Promise.race с таймаутом\r\n const processingPromise = (async () => {\r\n // Определяем желаемый формат вывода на основе Accept header\r\n let outputFormat: 'json' | 'csv' = 'json';\r\n if (acceptHeader.includes('text/csv')) {\r\n outputFormat = 'csv';\r\n } else if (req.query.format === 'csv') {\r\n outputFormat = 'csv';\r\n } else if ((req.body as any)?.format === 'csv') {\r\n outputFormat = 'csv';\r\n }\r\n\r\n // Опции конвертации\r\n const mergedConversionOptions: Record<string, any> = {\r\n delimiter,\r\n preventCsvInjection,\r\n rfc4180Compliant,\r\n useFastPath: enableFastPath,\r\n maxFieldSize,\r\n useWorkers,\r\n workerCount,\r\n ...req.query,\r\n ...conversionOptions\r\n };\r\n\r\n // Удаляем параметры, которые не относятся к конвертации\r\n delete mergedConversionOptions.maxSize;\r\n delete mergedConversionOptions.maxFileSize;\r\n delete mergedConversionOptions.maxFieldSize;\r\n delete mergedConversionOptions.timeout;\r\n delete mergedConversionOptions.autoDetect;\r\n delete mergedConversionOptions.enableFastPath;\r\n delete mergedConversionOptions.useAsync;\r\n delete mergedConversionOptions.useWorkers;\r\n delete mergedConversionOptions.workerCount;\r\n\r\n let result: any;\r\n const stats: ConvertedData['stats'] = {\r\n inputSize: 0,\r\n outputSize: 0,\r\n processingTime: 0,\r\n conversion: `${inputFormat}→${outputFormat}`\r\n };\r\n\r\n const startTime = Date.now();\r\n\r\n // Выполняем конвертацию\r\n if (inputFormat === 'json' && outputFormat === 'csv') {\r\n const jsonData = typeof inputData === 'string' ? JSON.parse(inputData) : inputData;\r\n stats.inputSize = Buffer.byteLength(JSON.stringify(jsonData));\r\n \r\n if (useAsync) {\r\n result = await jsonToCsvAsync(jsonData, mergedConversionOptions);\r\n } else {\r\n result = jsonToCsv(jsonData, mergedConversionOptions);\r\n }\r\n stats.outputSize = Buffer.byteLength(result);\r\n \r\n } else if (inputFormat === 'csv' && outputFormat === 'json') {\r\n const csvData = typeof inputData === 'string' ? inputData : String(inputData);\r\n stats.inputSize = Buffer.byteLength(csvData);\r\n \r\n if (useAsync) {\r\n result = await csvToJsonAsync(csvData, mergedConversionOptions);\r\n } else {\r\n result = csvToJson(csvData, mergedConversionOptions);\r\n }\r\n stats.outputSize = Buffer.byteLength(JSON.stringify(result));\r\n \r\n } else {\r\n // Нет необходимости в конвертации\r\n result = inputData;\r\n stats.conversion = 'none';\r\n }\r\n\r\n stats.processingTime = Date.now() - startTime;\r\n\r\n // Сохраняем результат в request object\r\n req.converted = {\r\n data: result,\r\n format: outputFormat,\r\n inputFormat,\r\n outputFormat,\r\n stats,\r\n options: mergedConversionOptions\r\n };\r\n\r\n // Устанавливаем соответствующий Content-Type для ответа\r\n if (outputFormat === 'csv') {\r\n res.set('Content-Type', 'text/csv; charset=utf-8');\r\n } else {\r\n res.set('Content-Type', 'application/json; charset=utf-8');\r\n }\r\n\r\n next();\r\n })();\r\n\r\n // Ждем либо обработку, либо таймаут\r\n await Promise.race([processingPromise, timeoutPromise]);\r\n } catch (error) {\r\n // Обработка ошибок конвертации\r\n const err = error as Error & { code?: string };\r\n if (process.env.NODE_ENV !== 'test') {\r\n console.log('[jtcsv-middleware] Conversion error:', err.message, err.stack?.split('\\n')[0]);\r\n }\r\n \r\n // Определяем статус код на основе типа ошибки\r\n let statusCode = 400;\r\n if (err.message.includes('timeout') || err.message.includes('Timeout')) {\r\n statusCode = 408; // Request Timeout\r\n } else if (err.message.includes('File size exceeds limit')) {\r\n statusCode = 413; // Payload Too Large\r\n } else if (err instanceof SecurityError) {\r\n statusCode = 403; // Forbidden\r\n } else if (err instanceof ValidationError) {\r\n statusCode = 422; // Unprocessable Entity\r\n } else if (err instanceof FileSystemError) {\r\n statusCode = 500; // Internal Server Error\r\n } else if (err instanceof JtcsvError) {\r\n statusCode = 400; // Bad Request\r\n }\r\n \r\n const errorResponse: any = {\r\n success: false,\r\n error: err.message,\r\n code: err.code || 'CONVERSION_ERROR',\r\n timestamp: new Date().toISOString()\r\n };\r\n\r\n // Добавляем дополнительную информацию для отладки\r\n if (process.env.NODE_ENV === 'development') {\r\n errorResponse.stack = err.stack;\r\n errorResponse.details = {\r\n contentType: req.get('content-type'),\r\n contentLength: req.get('content-length'),\r\n method: req.method,\r\n url: req.url\r\n };\r\n }\r\n\r\n res.status(statusCode).json(errorResponse);\r\n } finally {\r\n // Очищаем таймаут\r\n if (timeoutId) {\r\n clearTimeout(timeoutId);\r\n }\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Express route для конвертации CSV в JSON\r\n * \r\n * @param options - Опции конвертации\r\n * @returns Express route handler\r\n * \r\n * @example\r\n * app.post('/api/csv-to-json', jtcsvCsvToJsonRoute());\r\n */\r\nexport function jtcsvCsvToJsonRoute(options: Record<string, any> = {}): (req: Request, res: Response) => Promise<void> {\r\n return async (req: Request, res: Response): Promise<void> => {\r\n try {\r\n const csvData = req.body;\r\n \r\n if (!csvData || (typeof csvData !== 'string' && !Buffer.isBuffer(csvData))) {\r\n res.status(400).json({\r\n success: false,\r\n error: 'CSV data is required'\r\n });\r\n return;\r\n }\r\n\r\n const csvString = Buffer.isBuffer(csvData) ? csvData.toString() : csvData;\r\n const result = await csvToJsonAsync(csvString, options);\r\n \r\n res.json({\r\n success: true,\r\n data: result,\r\n stats: {\r\n rows: result.length,\r\n processingTime: Date.now() - (req.startTime || Date.now())\r\n }\r\n });\r\n } catch (error) {\r\n const err = error as Error;\r\n res.status(400).json({\r\n success: false,\r\n error: err.message\r\n });\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Express route для конвертации JSON в CSV\r\n * \r\n * @param options - Опции конвертации\r\n * @returns Express route handler\r\n * \r\n * @example\r\n * app.post('/api/json-to-csv', jtcsvJsonToCsvRoute());\r\n */\r\nexport function jtcsvJsonToCsvRoute(options: Record<string, any> = {}): (req: Request, res: Response) => Promise<void> {\r\n return async (req: Request, res: Response): Promise<void> => {\r\n try {\r\n const jsonData = req.body;\r\n \r\n if (!jsonData || (typeof jsonData !== 'object' && typeof jsonData !== 'string')) {\r\n res.status(400).json({\r\n success: false,\r\n error: 'JSON data is required'\r\n });\r\n return;\r\n }\r\n\r\n const data = typeof jsonData === 'string' ? JSON.parse(jsonData) : jsonData;\r\n const result = await jsonToCsvAsync(data, options);\r\n \r\n res.set('Content-Type', 'text/csv; charset=utf-8');\r\n res.set('Content-Disposition', 'attachment; filename=\"data.csv\"');\r\n \r\n res.send(result);\r\n } catch (error) {\r\n const err = error as Error;\r\n res.status(400).json({\r\n success: false,\r\n error: err.message\r\n });\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Express route для загрузки CSV файла\r\n * \r\n * @param options - Опции конвертации\r\n * @returns Express route handler\r\n * \r\n * @example\r\n * const multer = require('multer');\r\n * const upload = multer({ dest: 'uploads/' });\r\n * app.post('/api/upload-csv', upload.single('file'), jtcsvUploadCsvRoute());\r\n */\r\nexport function jtcsvUploadCsvRoute(options: Record<string, any> = {}): (req: Request, res: Response) => Promise<void> {\r\n return async (req: Request, res: Response): Promise<void> => {\r\n try {\r\n if (!(req as any).file) {\r\n res.status(400).json({\r\n success: false,\r\n error: 'CSV file is required'\r\n });\r\n return;\r\n }\r\n\r\n const fs = require('fs').promises;\r\n const csvData = await fs.readFile((req as any).file.path, 'utf8');\r\n \r\n const result = await csvToJsonAsync(csvData, options);\r\n \r\n // Очищаем временный файл\r\n await fs.unlink((req as any).file.path);\r\n \r\n res.json({\r\n success: true,\r\n data: result,\r\n stats: {\r\n rows: result.length,\r\n fileSize: (req as any).file.size,\r\n processingTime: Date.now() - (req.startTime || Date.now())\r\n }\r\n });\r\n } catch (error) {\r\n const err = error as Error;\r\n res.status(400).json({\r\n success: false,\r\n error: err.message\r\n });\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Health check endpoint для JTCSV\r\n * \r\n * @returns Express route handler\r\n * \r\n * @example\r\n * app.get('/api/health', jtcsvHealthCheck());\r\n */\r\nexport function jtcsvHealthCheck(): (req: Request, res: Response) => void {\r\n return (req: Request, res: Response): void => {\r\n res.json({\r\n service: 'jtcsv-express-middleware',\r\n status: 'healthy',\r\n version: '2.0.0',\r\n timestamp: new Date().toISOString(),\r\n features: {\r\n csvToJson: true,\r\n jsonToCsv: true,\r\n fastPathEngine: true,\r\n csvInjectionProtection: true,\r\n streaming: true,\r\n asyncProcessing: true,\r\n workerPool: true\r\n }\r\n });\r\n };\r\n}\r\n\r\n// Экспорт всех функций\r\nexport default {\r\n middleware: jtcsvExpressMiddleware,\r\n csvToJsonRoute: jtcsvCsvToJsonRoute,\r\n jsonToCsvRoute: jtcsvJsonToCsvRoute,\r\n uploadCsvRoute: jtcsvUploadCsvRoute,\r\n healthCheck: jtcsvHealthCheck,\r\n \r\n // Aliases для удобства\r\n jtcsvMiddleware: jtcsvExpressMiddleware,\r\n createMiddleware: jtcsvExpressMiddleware\r\n};\r\n\r\n"]}
|