jtcsv 2.2.7 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/README.md +31 -1
  2. package/bin/jtcsv.js +891 -821
  3. package/bin/jtcsv.ts +2534 -0
  4. package/csv-to-json.js +168 -145
  5. package/dist/jtcsv-core.cjs.js +1407 -0
  6. package/dist/jtcsv-core.cjs.js.map +1 -0
  7. package/dist/jtcsv-core.esm.js +1379 -0
  8. package/dist/jtcsv-core.esm.js.map +1 -0
  9. package/dist/jtcsv-core.umd.js +1413 -0
  10. package/dist/jtcsv-core.umd.js.map +1 -0
  11. package/dist/jtcsv-full.cjs.js +1912 -0
  12. package/dist/jtcsv-full.cjs.js.map +1 -0
  13. package/dist/jtcsv-full.esm.js +1880 -0
  14. package/dist/jtcsv-full.esm.js.map +1 -0
  15. package/dist/jtcsv-full.umd.js +1918 -0
  16. package/dist/jtcsv-full.umd.js.map +1 -0
  17. package/dist/jtcsv-workers.esm.js +759 -0
  18. package/dist/jtcsv-workers.esm.js.map +1 -0
  19. package/dist/jtcsv-workers.umd.js +773 -0
  20. package/dist/jtcsv-workers.umd.js.map +1 -0
  21. package/dist/jtcsv.cjs.js +61 -19
  22. package/dist/jtcsv.cjs.js.map +1 -1
  23. package/dist/jtcsv.esm.js +61 -19
  24. package/dist/jtcsv.esm.js.map +1 -1
  25. package/dist/jtcsv.umd.js +61 -19
  26. package/dist/jtcsv.umd.js.map +1 -1
  27. package/errors.js +188 -2
  28. package/examples/advanced/conditional-transformations.js +446 -0
  29. package/examples/advanced/conditional-transformations.ts +446 -0
  30. package/examples/advanced/csv-parser.worker.js +89 -0
  31. package/examples/advanced/csv-parser.worker.ts +89 -0
  32. package/examples/advanced/nested-objects-example.js +306 -0
  33. package/examples/advanced/nested-objects-example.ts +306 -0
  34. package/examples/advanced/performance-optimization.js +504 -0
  35. package/examples/advanced/performance-optimization.ts +504 -0
  36. package/examples/advanced/run-demo-server.js +116 -0
  37. package/examples/advanced/run-demo-server.ts +116 -0
  38. package/examples/advanced/web-worker-usage.html +874 -0
  39. package/examples/async-multithreaded-example.ts +335 -0
  40. package/examples/cli-advanced-usage.md +288 -0
  41. package/examples/cli-batch-processing.ts +38 -0
  42. package/examples/cli-tool.js +0 -3
  43. package/examples/cli-tool.ts +183 -0
  44. package/examples/error-handling.js +21 -7
  45. package/examples/error-handling.ts +356 -0
  46. package/examples/express-api.js +0 -3
  47. package/examples/express-api.ts +164 -0
  48. package/examples/large-dataset-example.js +0 -3
  49. package/examples/large-dataset-example.ts +204 -0
  50. package/examples/ndjson-processing.js +1 -1
  51. package/examples/ndjson-processing.ts +456 -0
  52. package/examples/plugin-excel-exporter.js +3 -4
  53. package/examples/plugin-excel-exporter.ts +406 -0
  54. package/examples/react-integration.tsx +637 -0
  55. package/examples/schema-validation.ts +640 -0
  56. package/examples/simple-usage.js +254 -254
  57. package/examples/simple-usage.ts +194 -0
  58. package/examples/streaming-example.js +4 -5
  59. package/examples/streaming-example.ts +419 -0
  60. package/examples/web-workers-advanced.ts +28 -0
  61. package/index.d.ts +1 -3
  62. package/index.js +15 -1
  63. package/json-save.js +9 -3
  64. package/json-to-csv.js +168 -21
  65. package/package.json +69 -10
  66. package/plugins/express-middleware/README.md +21 -2
  67. package/plugins/express-middleware/example.js +3 -4
  68. package/plugins/express-middleware/example.ts +135 -0
  69. package/plugins/express-middleware/index.d.ts +1 -1
  70. package/plugins/express-middleware/index.js +270 -118
  71. package/plugins/express-middleware/index.ts +557 -0
  72. package/plugins/fastify-plugin/index.js +2 -4
  73. package/plugins/fastify-plugin/index.ts +443 -0
  74. package/plugins/hono/index.ts +226 -0
  75. package/plugins/nestjs/index.ts +201 -0
  76. package/plugins/nextjs-api/examples/ConverterComponent.tsx +386 -0
  77. package/plugins/nextjs-api/examples/api-convert.js +0 -2
  78. package/plugins/nextjs-api/examples/api-convert.ts +67 -0
  79. package/plugins/nextjs-api/index.tsx +339 -0
  80. package/plugins/nextjs-api/route.js +2 -3
  81. package/plugins/nextjs-api/route.ts +370 -0
  82. package/plugins/nuxt/index.ts +94 -0
  83. package/plugins/nuxt/runtime/composables/useJtcsv.ts +100 -0
  84. package/plugins/nuxt/runtime/plugin.ts +71 -0
  85. package/plugins/remix/index.js +1 -1
  86. package/plugins/remix/index.ts +260 -0
  87. package/plugins/sveltekit/index.js +1 -1
  88. package/plugins/sveltekit/index.ts +301 -0
  89. package/plugins/trpc/index.ts +267 -0
  90. package/src/browser/browser-functions.ts +402 -0
  91. package/src/browser/core.js +92 -0
  92. package/src/browser/core.ts +152 -0
  93. package/src/browser/csv-to-json-browser.d.ts +3 -0
  94. package/src/browser/csv-to-json-browser.js +36 -14
  95. package/src/browser/csv-to-json-browser.ts +264 -0
  96. package/src/browser/errors-browser.ts +303 -0
  97. package/src/browser/extensions/plugins.js +92 -0
  98. package/src/browser/extensions/plugins.ts +93 -0
  99. package/src/browser/extensions/workers.js +39 -0
  100. package/src/browser/extensions/workers.ts +39 -0
  101. package/src/browser/globals.d.ts +5 -0
  102. package/src/browser/index.ts +192 -0
  103. package/src/browser/json-to-csv-browser.d.ts +3 -0
  104. package/src/browser/json-to-csv-browser.js +13 -3
  105. package/src/browser/json-to-csv-browser.ts +262 -0
  106. package/src/browser/streams.js +12 -2
  107. package/src/browser/streams.ts +336 -0
  108. package/src/browser/workers/csv-parser.worker.ts +377 -0
  109. package/src/browser/workers/worker-pool.ts +548 -0
  110. package/src/core/delimiter-cache.js +22 -8
  111. package/src/core/delimiter-cache.ts +310 -0
  112. package/src/core/node-optimizations.ts +449 -0
  113. package/src/core/plugin-system.js +29 -11
  114. package/src/core/plugin-system.ts +400 -0
  115. package/src/core/transform-hooks.ts +558 -0
  116. package/src/engines/fast-path-engine-new.ts +347 -0
  117. package/src/engines/fast-path-engine.ts +854 -0
  118. package/src/errors.ts +72 -0
  119. package/src/formats/ndjson-parser.ts +469 -0
  120. package/src/formats/tsv-parser.ts +334 -0
  121. package/src/index-with-plugins.js +16 -9
  122. package/src/index-with-plugins.ts +395 -0
  123. package/src/types/index.ts +255 -0
  124. package/src/utils/bom-utils.js +259 -0
  125. package/src/utils/bom-utils.ts +373 -0
  126. package/src/utils/encoding-support.js +124 -0
  127. package/src/utils/encoding-support.ts +155 -0
  128. package/src/utils/schema-validator.js +19 -19
  129. package/src/utils/schema-validator.ts +819 -0
  130. package/src/utils/transform-loader.js +1 -1
  131. package/src/utils/transform-loader.ts +389 -0
  132. package/src/utils/zod-adapter.js +170 -0
  133. package/src/utils/zod-adapter.ts +280 -0
  134. package/src/web-server/index.js +10 -10
  135. package/src/web-server/index.ts +683 -0
  136. package/src/workers/csv-multithreaded.ts +310 -0
  137. package/src/workers/csv-parser.worker.ts +227 -0
  138. package/src/workers/worker-pool.ts +409 -0
  139. package/stream-csv-to-json.js +26 -8
  140. package/stream-json-to-csv.js +1 -0
@@ -0,0 +1,201 @@
1
+ /**
2
+ * NestJS plugin for jtcsv
3
+ * Provides interceptors and decorators for CSV parsing and downloading in NestJS applications
4
+ * @module plugins/nestjs
5
+ */
6
+
7
+ // Note: NestJS and RxJS types are optional - users need to install @nestjs/common and rxjs
8
+ // We use conditional imports to avoid breaking the build
9
+ type Injectable = any;
10
+ type UseInterceptors = any;
11
+ type ExecutionContext = any;
12
+ type CallHandler = any;
13
+ type NestInterceptor = any;
14
+ type Observable<T> = any;
15
+
16
+ import { csvToJson, jsonToCsv } from '../../index-core';
17
+ import type { CsvToJsonOptions, JsonToCsvOptions } from '../../src/types';
18
+
19
+ /**
20
+ * Options for CSV parser interceptor
21
+ */
22
+ export interface CsvParserOptions extends CsvToJsonOptions {
23
+ // Additional options specific to CSV parsing
24
+ }
25
+
26
+ /**
27
+ * Options for CSV download interceptor
28
+ */
29
+ export interface CsvDownloadOptions extends JsonToCsvOptions {
30
+ /** Filename for the downloaded CSV file */
31
+ filename?: string;
32
+ }
33
+
34
+ /**
35
+ * Normalize filename for CSV download
36
+ */
37
+ function normalizeFilename(filename?: string): string {
38
+ if (!filename || typeof filename !== 'string') {
39
+ return 'export.csv';
40
+ }
41
+ return filename.includes('.') ? filename : `${filename}.csv`;
42
+ }
43
+
44
+ /**
45
+ * Creates a CSV parser interceptor for NestJS
46
+ * Parses incoming CSV request bodies into JSON
47
+ */
48
+ export function createCsvParserInterceptor(options: CsvParserOptions = {}): any {
49
+ class CsvParserInterceptorImpl {
50
+ async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
51
+ const req = context.switchToHttp().getRequest();
52
+ const body = req && req.body;
53
+
54
+ if (typeof body === 'string' || Buffer.isBuffer(body)) {
55
+ const csv = Buffer.isBuffer(body) ? body.toString('utf8') : body;
56
+ req.body = await csvToJson(csv, options);
57
+ }
58
+
59
+ return next.handle();
60
+ }
61
+ }
62
+
63
+ return CsvParserInterceptorImpl;
64
+ }
65
+
66
+ /**
67
+ * Creates a CSV download interceptor for NestJS
68
+ * Converts JSON responses to CSV and sets appropriate headers
69
+ */
70
+ export function createCsvDownloadInterceptor(options: CsvDownloadOptions = {}): any {
71
+ class CsvDownloadInterceptorImpl {
72
+ intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
73
+ const res = context.switchToHttp().getResponse();
74
+ const filename = normalizeFilename(options.filename);
75
+ const csvOptions = { ...options } as JsonToCsvOptions;
76
+ delete (csvOptions as any).filename;
77
+
78
+ return next.handle().pipe(
79
+ (async (data: any) => {
80
+ const rows = Array.isArray(data) ? data : [data];
81
+ const csv = await jsonToCsv(rows, csvOptions);
82
+
83
+ if (res && typeof res.setHeader === 'function') {
84
+ res.setHeader('Content-Type', 'text/csv; charset=utf-8');
85
+ res.setHeader(
86
+ 'Content-Disposition',
87
+ `attachment; filename="${filename}"`
88
+ );
89
+ }
90
+
91
+ return csv;
92
+ }) as any
93
+ );
94
+ }
95
+ }
96
+
97
+ return CsvDownloadInterceptorImpl;
98
+ }
99
+
100
+ /**
101
+ * Decorator for CSV parsing interceptor
102
+ * Usage: @CsvParserInterceptor({ delimiter: ',' })
103
+ */
104
+ export function CsvParserInterceptor(options: CsvParserOptions = {}): any {
105
+ const Interceptor = createCsvParserInterceptor(options);
106
+ return Interceptor;
107
+ }
108
+
109
+ /**
110
+ * Decorator for CSV download interceptor
111
+ * Usage: @CsvDownloadDecorator({ filename: 'data.csv' })
112
+ */
113
+ export function CsvDownloadDecorator(options: CsvDownloadOptions = {}): any {
114
+ const Interceptor = createCsvDownloadInterceptor(options);
115
+ return Interceptor;
116
+ }
117
+
118
+ /**
119
+ * Async version of CSV parser interceptor
120
+ * Uses async/await for better performance with large files
121
+ */
122
+ export function createAsyncCsvParserInterceptor(options: CsvParserOptions = {}): any {
123
+ class AsyncCsvParserInterceptorImpl {
124
+ async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
125
+ const req = context.switchToHttp().getRequest();
126
+ const body = req && req.body;
127
+
128
+ if (typeof body === 'string' || Buffer.isBuffer(body)) {
129
+ const csv = Buffer.isBuffer(body) ? body.toString('utf8') : body;
130
+ req.body = await csvToJson(csv, options);
131
+ }
132
+
133
+ return next.handle();
134
+ }
135
+ }
136
+
137
+ return AsyncCsvParserInterceptorImpl;
138
+ }
139
+
140
+ /**
141
+ * Async version of CSV download interceptor
142
+ * Uses async/await for better performance with large files
143
+ */
144
+ export function createAsyncCsvDownloadInterceptor(options: CsvDownloadOptions = {}): any {
145
+ class AsyncCsvDownloadInterceptorImpl {
146
+ async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
147
+ const res = context.switchToHttp().getResponse();
148
+ const filename = normalizeFilename(options.filename);
149
+ const csvOptions = { ...options } as JsonToCsvOptions;
150
+ delete (csvOptions as any).filename;
151
+
152
+ return next.handle().pipe(
153
+ (async (data: any) => {
154
+ const rows = Array.isArray(data) ? data : [data];
155
+ const csv = await jsonToCsv(rows, csvOptions);
156
+
157
+ if (res && typeof res.setHeader === 'function') {
158
+ res.setHeader('Content-Type', 'text/csv; charset=utf-8');
159
+ res.setHeader(
160
+ 'Content-Disposition',
161
+ `attachment; filename="${filename}"`
162
+ );
163
+ }
164
+
165
+ return csv;
166
+ }) as any
167
+ );
168
+ }
169
+ }
170
+
171
+ return AsyncCsvDownloadInterceptorImpl;
172
+ }
173
+
174
+ /**
175
+ * Async decorator for CSV parsing interceptor
176
+ * Usage: @AsyncCsvParserInterceptor({ delimiter: ',' })
177
+ */
178
+ export function AsyncCsvParserInterceptor(options: CsvParserOptions = {}): any {
179
+ const Interceptor = createAsyncCsvParserInterceptor(options);
180
+ return Interceptor;
181
+ }
182
+
183
+ /**
184
+ * Async decorator for CSV download interceptor
185
+ * Usage: @AsyncCsvDownloadDecorator({ filename: 'data.csv' })
186
+ */
187
+ export function AsyncCsvDownloadDecorator(options: CsvDownloadOptions = {}): any {
188
+ const Interceptor = createAsyncCsvDownloadInterceptor(options);
189
+ return Interceptor;
190
+ }
191
+
192
+ export default {
193
+ CsvParserInterceptor,
194
+ CsvDownloadDecorator,
195
+ createCsvParserInterceptor,
196
+ createCsvDownloadInterceptor,
197
+ AsyncCsvParserInterceptor,
198
+ AsyncCsvDownloadDecorator,
199
+ createAsyncCsvParserInterceptor,
200
+ createAsyncCsvDownloadInterceptor,
201
+ };
@@ -0,0 +1,386 @@
1
+ /**
2
+ * Пример React компонента для конвертации CSV/JSON
3
+ * Использование в Next.js приложении
4
+ */
5
+
6
+ import React, { useState } from 'react';
7
+ import { useJtcsv, CsvFileUploader, downloadCsv } from '../index';
8
+
9
+ /**
10
+ * Компонент для конвертации CSV ↔ JSON
11
+ */
12
+ export default function ConverterComponent() {
13
+ const [input, setInput] = useState('');
14
+ const [output, setOutput] = useState('');
15
+ const [format, setFormat] = useState('csv'); // 'csv' или 'json'
16
+ const [delimiter, setDelimiter] = useState(',');
17
+
18
+ const {
19
+ convertCsvToJson,
20
+ convertJsonToCsv,
21
+ isLoading,
22
+ error,
23
+ stats
24
+ } = useJtcsv({
25
+ delimiter,
26
+ parseNumbers: true,
27
+ parseBooleans: true,
28
+ preventCsvInjection: true
29
+ });
30
+
31
+ const handleConvert = async () => {
32
+ if (!input.trim()) return;
33
+
34
+ try {
35
+ if (format === 'csv') {
36
+ // Конвертируем CSV в JSON
37
+ const result = await convertCsvToJson(input);
38
+ setOutput(JSON.stringify(result, null, 2));
39
+ } else {
40
+ // Конвертируем JSON в CSV
41
+ const json = JSON.parse(input);
42
+ const result = await convertJsonToCsv(json);
43
+ setOutput(result);
44
+ }
45
+ } catch (err) {
46
+ setOutput(`Error: ${err.message}`);
47
+ }
48
+ };
49
+
50
+ const handleFileUpload = (result, fileStats) => {
51
+ setInput(JSON.stringify(result, null, 2));
52
+ setFormat('json');
53
+
54
+ console.log('File converted:', fileStats);
55
+ };
56
+
57
+ const handleDownload = async () => {
58
+ if (!output) return;
59
+
60
+ try {
61
+ if (format === 'csv') {
62
+ // Скачиваем как CSV
63
+ const json = JSON.parse(input);
64
+ await downloadCsv(json, 'converted.csv', { delimiter });
65
+ } else {
66
+ // Скачиваем как JSON
67
+ const blob = new Blob([output], { type: 'application/json' });
68
+ const url = URL.createObjectURL(blob);
69
+ const link = document.createElement('a');
70
+ link.href = url;
71
+ link.download = 'converted.json';
72
+ link.click();
73
+ URL.revokeObjectURL(url);
74
+ }
75
+ } catch (err) {
76
+ console.error('Download error:', err);
77
+ }
78
+ };
79
+
80
+ const handleExample = () => {
81
+ if (format === 'csv') {
82
+ setInput('name,email,age\nJohn Doe,john@example.com,30\nJane Smith,jane@example.com,25');
83
+ } else {
84
+ setInput(JSON.stringify([
85
+ { name: 'John Doe', email: 'john@example.com', age: 30 },
86
+ { name: 'Jane Smith', email: 'jane@example.com', age: 25 }
87
+ ], null, 2));
88
+ }
89
+ };
90
+
91
+ const handleClear = () => {
92
+ setInput('');
93
+ setOutput('');
94
+ };
95
+
96
+ return (
97
+ <div style={styles.container}>
98
+ <h1 style={styles.title}>🔄 JTCSV Converter</h1>
99
+
100
+ <div style={styles.controls}>
101
+ <div style={styles.formatSelector}>
102
+ <label>
103
+ <input
104
+ type="radio"
105
+ value="csv"
106
+ checked={format === 'csv'}
107
+ onChange={(e) => setFormat(e.target.value)}
108
+ />
109
+ CSV → JSON
110
+ </label>
111
+ <label>
112
+ <input
113
+ type="radio"
114
+ value="json"
115
+ checked={format === 'json'}
116
+ onChange={(e) => setFormat(e.target.value)}
117
+ />
118
+ JSON → CSV
119
+ </label>
120
+ </div>
121
+
122
+ <div style={styles.delimiterSelector}>
123
+ <label>
124
+ Разделитель:
125
+ <select
126
+ value={delimiter}
127
+ onChange={(e) => setDelimiter(e.target.value)}
128
+ style={styles.select}
129
+ >
130
+ <option value=",">Запятая (,)</option>
131
+ <option value=";">Точка с запятой (;)</option>
132
+ <option value="\t">Табуляция (\t)</option>
133
+ <option value="|">Вертикальная черта (|)</option>
134
+ </select>
135
+ </label>
136
+ </div>
137
+ </div>
138
+
139
+ <div style={styles.inputSection}>
140
+ <div style={styles.inputHeader}>
141
+ <h3 style={styles.sectionTitle}>
142
+ {format === 'csv' ? 'CSV Input' : 'JSON Input'}
143
+ </h3>
144
+ <div style={styles.inputActions}>
145
+ <CsvFileUploader
146
+ onConvert={handleFileUpload}
147
+ options={{ delimiter }}
148
+ >
149
+ <button style={styles.buttonSecondary}>📁 Upload CSV</button>
150
+ </CsvFileUploader>
151
+ <button
152
+ onClick={handleExample}
153
+ style={styles.buttonSecondary}
154
+ >
155
+ 📋 Example
156
+ </button>
157
+ <button
158
+ onClick={handleClear}
159
+ style={styles.buttonSecondary}
160
+ >
161
+ 🗑️ Clear
162
+ </button>
163
+ </div>
164
+ </div>
165
+
166
+ <textarea
167
+ value={input}
168
+ onChange={(e) => setInput(e.target.value)}
169
+ placeholder={format === 'csv'
170
+ ? 'Введите CSV данные...\nПример:\nname,email,age\nJohn,john@example.com,30'
171
+ : 'Введите JSON данные...\nПример:\n[{"name":"John","age":30}]'
172
+ }
173
+ style={styles.textarea}
174
+ rows={10}
175
+ />
176
+ </div>
177
+
178
+ <div style={styles.convertButtonContainer}>
179
+ <button
180
+ onClick={handleConvert}
181
+ disabled={isLoading || !input.trim()}
182
+ style={{
183
+ ...styles.buttonPrimary,
184
+ opacity: isLoading || !input.trim() ? 0.6 : 1,
185
+ cursor: isLoading || !input.trim() ? 'not-allowed' : 'pointer'
186
+ }}
187
+ >
188
+ {isLoading ? '🔄 Converting...' : '🚀 Convert'}
189
+ </button>
190
+
191
+ {stats && (
192
+ <div style={styles.stats}>
193
+ <span>⏱️ {stats.processingTime}ms</span>
194
+ <span>📊 {stats.rows || stats.size} {stats.rows ? 'rows' : 'chars'}</span>
195
+ </div>
196
+ )}
197
+ </div>
198
+
199
+ {error && (
200
+ <div style={styles.error}>
201
+ ❌ Error: {error}
202
+ </div>
203
+ )}
204
+
205
+ <div style={styles.outputSection}>
206
+ <div style={styles.outputHeader}>
207
+ <h3 style={styles.sectionTitle}>
208
+ {format === 'csv' ? 'JSON Output' : 'CSV Output'}
209
+ </h3>
210
+ <div style={styles.outputActions}>
211
+ <button
212
+ onClick={handleDownload}
213
+ disabled={!output}
214
+ style={styles.buttonSecondary}
215
+ >
216
+ ⬇️ Download
217
+ </button>
218
+ <button
219
+ onClick={() => navigator.clipboard.writeText(output)}
220
+ disabled={!output}
221
+ style={styles.buttonSecondary}
222
+ >
223
+ 📋 Copy
224
+ </button>
225
+ </div>
226
+ </div>
227
+
228
+ <pre style={styles.output}>
229
+ {output || 'Результат появится здесь...'}
230
+ </pre>
231
+ </div>
232
+
233
+ <div style={styles.info}>
234
+ <p>💡 <strong>Подсказки:</strong></p>
235
+ <ul style={styles.tipsList}>
236
+ <li>Используйте кнопку "Example" для быстрого заполнения</li>
237
+ <li>Загружайте CSV файлы через кнопку "Upload CSV"</li>
238
+ <li>Выберите разделитель соответствующий вашим данным</li>
239
+ <li>Скачивайте результат в нужном формате</li>
240
+ </ul>
241
+ </div>
242
+ </div>
243
+ );
244
+ }
245
+
246
+ const styles = {
247
+ container: {
248
+ maxWidth: '1200px',
249
+ margin: '0 auto',
250
+ padding: '20px',
251
+ fontFamily: 'system-ui, -apple-system, sans-serif'
252
+ },
253
+ title: {
254
+ color: '#333',
255
+ textAlign: 'center',
256
+ marginBottom: '30px'
257
+ },
258
+ controls: {
259
+ display: 'flex',
260
+ justifyContent: 'space-between',
261
+ alignItems: 'center',
262
+ marginBottom: '20px',
263
+ flexWrap: 'wrap',
264
+ gap: '20px'
265
+ },
266
+ formatSelector: {
267
+ display: 'flex',
268
+ gap: '20px'
269
+ },
270
+ delimiterSelector: {
271
+ display: 'flex',
272
+ alignItems: 'center',
273
+ gap: '10px'
274
+ },
275
+ select: {
276
+ padding: '5px 10px',
277
+ marginLeft: '10px',
278
+ borderRadius: '4px',
279
+ border: '1px solid #ccc'
280
+ },
281
+ inputSection: {
282
+ marginBottom: '20px'
283
+ },
284
+ inputHeader: {
285
+ display: 'flex',
286
+ justifyContent: 'space-between',
287
+ alignItems: 'center',
288
+ marginBottom: '10px'
289
+ },
290
+ inputActions: {
291
+ display: 'flex',
292
+ gap: '10px'
293
+ },
294
+ sectionTitle: {
295
+ margin: '0',
296
+ color: '#555'
297
+ },
298
+ textarea: {
299
+ width: '100%',
300
+ padding: '15px',
301
+ border: '1px solid #ddd',
302
+ borderRadius: '8px',
303
+ fontFamily: 'monospace',
304
+ fontSize: '14px',
305
+ resize: 'vertical',
306
+ boxSizing: 'border-box'
307
+ },
308
+ convertButtonContainer: {
309
+ display: 'flex',
310
+ flexDirection: 'column',
311
+ alignItems: 'center',
312
+ gap: '10px',
313
+ margin: '20px 0'
314
+ },
315
+ buttonPrimary: {
316
+ padding: '12px 30px',
317
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
318
+ color: 'white',
319
+ border: 'none',
320
+ borderRadius: '25px',
321
+ fontSize: '16px',
322
+ fontWeight: 'bold',
323
+ cursor: 'pointer',
324
+ transition: 'transform 0.2s'
325
+ },
326
+ buttonSecondary: {
327
+ padding: '8px 16px',
328
+ background: '#f0f0f0',
329
+ color: '#333',
330
+ border: '1px solid #ddd',
331
+ borderRadius: '4px',
332
+ cursor: 'pointer',
333
+ transition: 'background 0.2s'
334
+ },
335
+ stats: {
336
+ display: 'flex',
337
+ gap: '20px',
338
+ color: '#666',
339
+ fontSize: '14px'
340
+ },
341
+ error: {
342
+ padding: '15px',
343
+ background: '#fee',
344
+ border: '1px solid #f99',
345
+ borderRadius: '8px',
346
+ color: '#c00',
347
+ marginBottom: '20px'
348
+ },
349
+ outputSection: {
350
+ marginTop: '20px'
351
+ },
352
+ outputHeader: {
353
+ display: 'flex',
354
+ justifyContent: 'space-between',
355
+ alignItems: 'center',
356
+ marginBottom: '10px'
357
+ },
358
+ outputActions: {
359
+ display: 'flex',
360
+ gap: '10px'
361
+ },
362
+ output: {
363
+ padding: '15px',
364
+ background: '#f8f8f8',
365
+ border: '1px solid #ddd',
366
+ borderRadius: '8px',
367
+ minHeight: '200px',
368
+ overflow: 'auto',
369
+ whiteSpace: 'pre-wrap',
370
+ wordBreak: 'break-all',
371
+ fontFamily: 'monospace',
372
+ fontSize: '14px'
373
+ },
374
+ info: {
375
+ marginTop: '30px',
376
+ padding: '20px',
377
+ background: '#f0f8ff',
378
+ border: '1px solid #cce5ff',
379
+ borderRadius: '8px'
380
+ },
381
+ tipsList: {
382
+ margin: '10px 0 0 20px',
383
+ color: '#555'
384
+ }
385
+ };
386
+
@@ -65,5 +65,3 @@ export const config = {
65
65
  * return jtcsvHandler(req, res);
66
66
  * }
67
67
  */
68
-
69
-
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Пример API route для Next.js
3
+ * Сохраните этот файл как pages/api/convert.js
4
+ *
5
+ * @example
6
+ * // Запросы к API:
7
+ * // POST /api/convert с JSON телом → получите CSV
8
+ * // POST /api/convert с CSV телом → получите JSON
9
+ * // GET /api/convert/health → проверка состояния
10
+ */
11
+
12
+ import {
13
+ handler as jtcsvHandler,
14
+ csvToJsonHandler,
15
+ jsonToCsvHandler,
16
+ healthCheckHandler
17
+ } from '../../route';
18
+
19
+ // Основной endpoint для автоматической конвертации
20
+ export default jtcsvHandler;
21
+
22
+ // Специализированные endpoints
23
+ export const config = {
24
+ api: {
25
+ bodyParser: {
26
+ sizeLimit: '50mb'
27
+ }
28
+ }
29
+ };
30
+
31
+ // Альтернативная реализация с отдельными путями
32
+ // export default async function handler(req, res) {
33
+ // const { path } = req.query;
34
+ //
35
+ // switch (path) {
36
+ // case 'csv-to-json':
37
+ // return csvToJsonHandler(req, res);
38
+ // case 'json-to-csv':
39
+ // return jsonToCsvHandler(req, res);
40
+ // case 'health':
41
+ // return healthCheckHandler(req, res);
42
+ // default:
43
+ // return jtcsvHandler(req, res);
44
+ // }
45
+ // }
46
+
47
+ /**
48
+ * Пример использования с кастомной логикой
49
+ *
50
+ * export default async function handler(req, res) {
51
+ * // Добавляем логирование
52
+ * console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
53
+ *
54
+ * // Проверяем аутентификацию
55
+ * const apiKey = req.headers['x-api-key'];
56
+ * if (!apiKey || apiKey !== process.env.API_KEY) {
57
+ * return res.status(401).json({ error: 'Unauthorized' });
58
+ * }
59
+ *
60
+ * // Ограничение по частоте запросов
61
+ * const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
62
+ * // ... rate limiting logic
63
+ *
64
+ * // Вызываем основной обработчик
65
+ * return jtcsvHandler(req, res);
66
+ * }
67
+ */