jtcsv 2.1.1 → 2.1.5
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 +64 -327
- package/bin/jtcsv.js +7 -24
- package/csv-to-json.js +35 -26
- package/dist/jtcsv.cjs.js +807 -133
- package/dist/jtcsv.cjs.js.map +1 -1
- package/dist/jtcsv.esm.js +800 -134
- package/dist/jtcsv.esm.js.map +1 -1
- package/dist/jtcsv.umd.js +807 -133
- package/dist/jtcsv.umd.js.map +1 -1
- package/errors.js +20 -0
- package/examples/browser-vanilla.html +37 -0
- package/examples/cli-batch-processing.js +38 -0
- package/examples/simple-usage.js +12 -10
- package/examples/web-workers-advanced.js +28 -0
- package/json-save.js +2 -1
- package/package.json +41 -21
- package/plugins/README.md +50 -332
- package/plugins/express-middleware/README.md +32 -274
- package/plugins/hono/README.md +28 -0
- package/plugins/hono/index.d.ts +12 -0
- package/plugins/hono/index.js +36 -0
- package/plugins/hono/package.json +35 -0
- package/plugins/nestjs/README.md +35 -0
- package/plugins/nestjs/index.d.ts +25 -0
- package/plugins/nestjs/index.js +77 -0
- package/plugins/nestjs/package.json +37 -0
- package/plugins/nextjs-api/README.md +28 -423
- package/plugins/nextjs-api/index.js +1 -2
- package/plugins/nextjs-api/route.js +1 -2
- package/plugins/nuxt/README.md +24 -0
- package/plugins/nuxt/index.js +21 -0
- package/plugins/nuxt/package.json +35 -0
- package/plugins/nuxt/runtime/composables/useJtcsv.js +6 -0
- package/plugins/nuxt/runtime/plugin.js +6 -0
- package/plugins/remix/README.md +26 -0
- package/plugins/remix/index.d.ts +16 -0
- package/plugins/remix/index.js +62 -0
- package/plugins/remix/package.json +35 -0
- package/plugins/sveltekit/README.md +28 -0
- package/plugins/sveltekit/index.d.ts +17 -0
- package/plugins/sveltekit/index.js +54 -0
- package/plugins/sveltekit/package.json +33 -0
- package/plugins/trpc/README.md +25 -0
- package/plugins/trpc/index.d.ts +7 -0
- package/plugins/trpc/index.js +32 -0
- package/plugins/trpc/package.json +34 -0
- package/src/browser/browser-functions.js +33 -3
- package/src/browser/csv-to-json-browser.js +269 -11
- package/src/browser/errors-browser.js +19 -1
- package/src/browser/index.js +39 -5
- package/src/browser/streams.js +393 -0
- package/src/browser/workers/csv-parser.worker.js +20 -2
- package/src/browser/workers/worker-pool.js +507 -447
- package/src/core/plugin-system.js +4 -0
- package/src/engines/fast-path-engine.js +31 -23
- package/src/formats/ndjson-parser.js +54 -5
- package/src/formats/tsv-parser.js +4 -1
- package/stream-csv-to-json.js +11 -2
- package/stream-json-to-csv.js +13 -1
- package/cli-tui.js +0 -1498
|
@@ -1,306 +1,64 @@
|
|
|
1
1
|
# @jtcsv/express-middleware
|
|
2
2
|
|
|
3
|
-
Express middleware
|
|
4
|
-
|
|
5
|
-
## 📦 Установка
|
|
3
|
+
Express middleware that converts CSV/JSON payloads and exposes the converted data on `req.converted`.
|
|
6
4
|
|
|
5
|
+
## Install
|
|
7
6
|
```bash
|
|
8
7
|
npm install @jtcsv/express-middleware express jtcsv
|
|
9
8
|
```
|
|
10
9
|
|
|
11
|
-
##
|
|
12
|
-
|
|
10
|
+
## Quick start
|
|
13
11
|
```javascript
|
|
14
12
|
const express = require('express');
|
|
15
|
-
const bodyParser = require('body-parser');
|
|
16
13
|
const { middleware } = require('@jtcsv/express-middleware');
|
|
17
14
|
|
|
18
15
|
const app = express();
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
app.use(bodyParser.json());
|
|
22
|
-
app.use(bodyParser.text({ type: 'text/csv' }));
|
|
23
|
-
|
|
24
|
-
// JTCSV middleware для автоматической конвертации
|
|
16
|
+
app.use(express.json());
|
|
17
|
+
app.use(express.text({ type: 'text/csv' }));
|
|
25
18
|
app.use(middleware());
|
|
26
19
|
|
|
27
|
-
// Роут, использующий автоматическую конвертацию
|
|
28
20
|
app.post('/api/convert', (req, res) => {
|
|
29
|
-
|
|
30
|
-
res.json({
|
|
31
|
-
success: true,
|
|
32
|
-
data: req.converted.data,
|
|
33
|
-
format: req.converted.format,
|
|
34
|
-
stats: req.converted.stats
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
app.listen(3000, () => {
|
|
39
|
-
console.log('Server started on port 3000');
|
|
21
|
+
res.json(req.converted);
|
|
40
22
|
});
|
|
41
23
|
```
|
|
42
24
|
|
|
43
|
-
##
|
|
44
|
-
|
|
45
|
-
### Основное middleware
|
|
25
|
+
## Options
|
|
26
|
+
The middleware detects input/output format based on `Content-Type`, `Accept`, and `?format=csv`.
|
|
46
27
|
|
|
47
28
|
```javascript
|
|
48
29
|
app.use(middleware({
|
|
49
|
-
|
|
50
|
-
autoDetect: true, // Автоматическое определение формата
|
|
51
|
-
delimiter: ',', // Разделитель CSV
|
|
52
|
-
enableFastPath: true, // Включить Fast-Path Engine
|
|
53
|
-
preventCsvInjection: true, // Защита от CSV инъекций
|
|
54
|
-
rfc4180Compliant: true // Соответствие RFC 4180
|
|
55
|
-
}));
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### Специфичные роуты
|
|
59
|
-
|
|
60
|
-
```javascript
|
|
61
|
-
const {
|
|
62
|
-
csvToJsonRoute,
|
|
63
|
-
jsonToCsvRoute,
|
|
64
|
-
uploadCsvRoute,
|
|
65
|
-
healthCheck
|
|
66
|
-
} = require('@jtcsv/express-middleware');
|
|
67
|
-
|
|
68
|
-
// Конвертация CSV в JSON
|
|
69
|
-
app.post('/api/csv-to-json', csvToJsonRoute({
|
|
30
|
+
autoDetect: true,
|
|
70
31
|
delimiter: ',',
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
includeHeaders: true,
|
|
79
|
-
preventCsvInjection: true
|
|
32
|
+
enableFastPath: true,
|
|
33
|
+
preventCsvInjection: true,
|
|
34
|
+
rfc4180Compliant: true,
|
|
35
|
+
conversionOptions: {
|
|
36
|
+
parseNumbers: true,
|
|
37
|
+
parseBooleans: true
|
|
38
|
+
}
|
|
80
39
|
}));
|
|
81
|
-
|
|
82
|
-
// Загрузка CSV файла (требуется multer)
|
|
83
|
-
const multer = require('multer');
|
|
84
|
-
const upload = multer({ dest: 'uploads/' });
|
|
85
|
-
app.post('/api/upload-csv', upload.single('file'), uploadCsvRoute());
|
|
86
|
-
|
|
87
|
-
// Health check
|
|
88
|
-
app.get('/api/health', healthCheck());
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## 🔧 Конфигурация
|
|
92
|
-
|
|
93
|
-
### Опции middleware
|
|
94
|
-
|
|
95
|
-
| Опция | Тип | По умолчанию | Описание |
|
|
96
|
-
|-------|-----|--------------|----------|
|
|
97
|
-
| `maxSize` | `string` | `'10mb'` | Максимальный размер тела запроса |
|
|
98
|
-
| `autoDetect` | `boolean` | `true` | Автоматическое определение формата |
|
|
99
|
-
| `delimiter` | `string` | `','` | Разделитель CSV |
|
|
100
|
-
| `enableFastPath` | `boolean` | `true` | Включить Fast-Path Engine |
|
|
101
|
-
| `preventCsvInjection` | `boolean` | `true` | Защита от CSV инъекций |
|
|
102
|
-
| `rfc4180Compliant` | `boolean` | `true` | Соответствие RFC 4180 |
|
|
103
|
-
| `conversionOptions` | `object` | `{}` | Дополнительные опции конвертации |
|
|
104
|
-
|
|
105
|
-
### Формат ответа
|
|
106
|
-
|
|
107
|
-
После обработки middleware, в объекте `req` появляется свойство `converted`:
|
|
108
|
-
|
|
109
|
-
```javascript
|
|
110
|
-
{
|
|
111
|
-
data: any, // Конвертированные данные
|
|
112
|
-
format: 'json' | 'csv', // Формат выходных данных
|
|
113
|
-
inputFormat: 'json' | 'csv' | 'unknown', // Формат входных данных
|
|
114
|
-
outputFormat: 'json' | 'csv', // Формат выходных данных
|
|
115
|
-
stats: { // Статистика конвертации
|
|
116
|
-
inputSize: number, // Размер входных данных (байты)
|
|
117
|
-
outputSize: number, // Размер выходных данных (байты)
|
|
118
|
-
processingTime: number, // Время обработки (мс)
|
|
119
|
-
conversion: string // Тип конвертации (например: "json→csv")
|
|
120
|
-
},
|
|
121
|
-
options: object // Использованные опции
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
## 🌐 Примеры запросов
|
|
126
|
-
|
|
127
|
-
### Конвертация JSON в CSV
|
|
128
|
-
|
|
129
|
-
```bash
|
|
130
|
-
curl -X POST http://localhost:3000/api/convert \
|
|
131
|
-
-H "Content-Type: application/json" \
|
|
132
|
-
-d '[{"name":"John","age":30},{"name":"Jane","age":25}]'
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
**Ответ:**
|
|
136
|
-
```csv
|
|
137
|
-
name,age
|
|
138
|
-
John,30
|
|
139
|
-
Jane,25
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### Конвертация CSV в JSON
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
curl -X POST http://localhost:3000/api/convert \
|
|
146
|
-
-H "Content-Type: text/csv" \
|
|
147
|
-
-d 'name,age\nJohn,30\nJane,25'
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
**Ответ:**
|
|
151
|
-
```json
|
|
152
|
-
[
|
|
153
|
-
{"name":"John","age":30},
|
|
154
|
-
{"name":"Jane","age":25}
|
|
155
|
-
]
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### Специфичный формат вывода
|
|
159
|
-
|
|
160
|
-
```bash
|
|
161
|
-
# Запросить CSV даже если отправляем JSON
|
|
162
|
-
curl -X POST http://localhost:3000/api/convert \
|
|
163
|
-
-H "Content-Type: application/json" \
|
|
164
|
-
-H "Accept: text/csv" \
|
|
165
|
-
-d '[{"name":"John","age":30}]'
|
|
166
|
-
|
|
167
|
-
# Или через query параметр
|
|
168
|
-
curl -X POST "http://localhost:3000/api/convert?format=csv" \
|
|
169
|
-
-H "Content-Type: application/json" \
|
|
170
|
-
-d '[{"name":"John","age":30}]'
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
## 🛡️ Безопасность
|
|
174
|
-
|
|
175
|
-
### CSV Injection Protection
|
|
176
|
-
|
|
177
|
-
Middleware автоматически защищает от CSV инъекций:
|
|
178
|
-
|
|
179
|
-
```javascript
|
|
180
|
-
// Входные данные с потенциальной инъекцией
|
|
181
|
-
const dangerousData = [
|
|
182
|
-
{ formula: '=1+1', command: '@echo hello' }
|
|
183
|
-
];
|
|
184
|
-
|
|
185
|
-
// Безопасный CSV
|
|
186
|
-
const safeCsv = jsonToCsv(dangerousData, {
|
|
187
|
-
preventCsvInjection: true
|
|
188
|
-
});
|
|
189
|
-
// Результат: "'=1+1','@echo hello"
|
|
190
40
|
```
|
|
191
41
|
|
|
192
|
-
|
|
42
|
+
Note: body size limits are controlled by your body parser (for example `express.json({ limit: '10mb' })`).
|
|
193
43
|
|
|
44
|
+
## Helpers
|
|
194
45
|
```javascript
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
### Health Check
|
|
203
|
-
|
|
204
|
-
```bash
|
|
205
|
-
curl http://localhost:3000/api/health
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
**Ответ:**
|
|
209
|
-
```json
|
|
210
|
-
{
|
|
211
|
-
"service": "jtcsv-express-middleware",
|
|
212
|
-
"status": "healthy",
|
|
213
|
-
"version": "1.0.0",
|
|
214
|
-
"timestamp": "2026-01-23T10:30:00.000Z",
|
|
215
|
-
"features": {
|
|
216
|
-
"csvToJson": true,
|
|
217
|
-
"jsonToCsv": true,
|
|
218
|
-
"fastPathEngine": true,
|
|
219
|
-
"csvInjectionProtection": true,
|
|
220
|
-
"streaming": true
|
|
221
|
-
}
|
|
222
|
-
}
|
|
46
|
+
const {
|
|
47
|
+
csvToJsonRoute,
|
|
48
|
+
jsonToCsvRoute,
|
|
49
|
+
uploadCsvRoute,
|
|
50
|
+
healthCheck
|
|
51
|
+
} = require('@jtcsv/express-middleware');
|
|
223
52
|
```
|
|
224
53
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
Каждый запрос включает статистику конвертации:
|
|
228
|
-
|
|
54
|
+
## req.converted shape
|
|
229
55
|
```json
|
|
230
56
|
{
|
|
231
|
-
"
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
57
|
+
"data": "...",
|
|
58
|
+
"format": "json",
|
|
59
|
+
"inputFormat": "csv",
|
|
60
|
+
"outputFormat": "json",
|
|
61
|
+
"stats": { "inputSize": 0, "outputSize": 0, "processingTime": 0, "conversion": "csv->json" },
|
|
62
|
+
"options": {}
|
|
237
63
|
}
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
## 🔌 Интеграция
|
|
241
|
-
|
|
242
|
-
### С TypeScript
|
|
243
|
-
|
|
244
|
-
```typescript
|
|
245
|
-
import { Request, Response } from 'express';
|
|
246
|
-
import { middleware, ConvertedData } from '@jtcsv/express-middleware';
|
|
247
|
-
|
|
248
|
-
app.use(middleware());
|
|
249
|
-
|
|
250
|
-
app.post('/api/convert', (req: Request, res: Response) => {
|
|
251
|
-
const converted = req.converted as ConvertedData;
|
|
252
|
-
// TypeScript знает тип converted
|
|
253
|
-
});
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
### С другими middleware
|
|
257
|
-
|
|
258
|
-
```javascript
|
|
259
|
-
const express = require('express');
|
|
260
|
-
const cors = require('cors');
|
|
261
|
-
const helmet = require('helmet');
|
|
262
|
-
const rateLimit = require('express-rate-limit');
|
|
263
|
-
const { middleware } = require('@jtcsv/express-middleware');
|
|
264
|
-
|
|
265
|
-
const app = express();
|
|
266
|
-
|
|
267
|
-
app.use(cors());
|
|
268
|
-
app.use(helmet());
|
|
269
|
-
app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));
|
|
270
|
-
app.use(express.json());
|
|
271
|
-
app.use(express.text({ type: 'text/csv' }));
|
|
272
|
-
app.use(middleware());
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
## 🧪 Тестирование
|
|
276
|
-
|
|
277
|
-
```bash
|
|
278
|
-
# Запуск примера
|
|
279
|
-
cd plugins/express-middleware
|
|
280
|
-
node example.js
|
|
281
|
-
|
|
282
|
-
# Тестовые запросы
|
|
283
|
-
curl -X POST http://localhost:3000/api/convert \
|
|
284
|
-
-H "Content-Type: application/json" \
|
|
285
|
-
-d '[{"test":"data"}]'
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
## 📄 Лицензия
|
|
289
|
-
|
|
290
|
-
MIT
|
|
291
|
-
|
|
292
|
-
## 🤝 Вклад в развитие
|
|
293
|
-
|
|
294
|
-
1. Форкните репозиторий
|
|
295
|
-
2. Создайте ветку для вашей функции (`git checkout -b feature/amazing-feature`)
|
|
296
|
-
3. Закоммитьте изменения (`git commit -m 'Add amazing feature'`)
|
|
297
|
-
4. Запушьте в ветку (`git push origin feature/amazing-feature`)
|
|
298
|
-
5. Откройте Pull Request
|
|
299
|
-
|
|
300
|
-
## 📞 Поддержка
|
|
301
|
-
|
|
302
|
-
- [Issues](https://github.com/Linol-Hamelton/jtcsv/issues)
|
|
303
|
-
- [Discussions](https://github.com/Linol-Hamelton/jtcsv/discussions)
|
|
304
|
-
- [Documentation](https://github.com/Linol-Hamelton/jtcsv#readme)
|
|
305
|
-
|
|
306
|
-
|
|
64
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# @jtcsv/hono
|
|
2
|
+
|
|
3
|
+
Minimal helpers for Hono routes.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
```bash
|
|
7
|
+
npm install @jtcsv/hono jtcsv
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
```javascript
|
|
12
|
+
import { Hono } from 'hono';
|
|
13
|
+
import { csvMiddleware, createCsvResponse } from '@jtcsv/hono';
|
|
14
|
+
|
|
15
|
+
const app = new Hono();
|
|
16
|
+
|
|
17
|
+
app.use('/upload', csvMiddleware());
|
|
18
|
+
app.post('/upload', (c) => c.json({ rows: c.get('csv') }));
|
|
19
|
+
|
|
20
|
+
app.get('/export', (c) => {
|
|
21
|
+
const { csv, headers } = createCsvResponse([{ id: 1 }], 'export.csv');
|
|
22
|
+
return c.text(csv, 200, headers);
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Exports
|
|
27
|
+
- csvMiddleware
|
|
28
|
+
- createCsvResponse
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CsvToJsonOptions, JsonToCsvOptions } from 'jtcsv';
|
|
2
|
+
import type { Context } from 'hono';
|
|
3
|
+
|
|
4
|
+
export function csvMiddleware(
|
|
5
|
+
options?: CsvToJsonOptions
|
|
6
|
+
): (c: Context, next: () => Promise<void>) => Promise<void>;
|
|
7
|
+
|
|
8
|
+
export function createCsvResponse(
|
|
9
|
+
data: unknown[] | Record<string, unknown>,
|
|
10
|
+
filename?: string,
|
|
11
|
+
options?: JsonToCsvOptions
|
|
12
|
+
): { csv: string; headers: Record<string, string> };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const jtcsv = require('jtcsv');
|
|
2
|
+
|
|
3
|
+
function normalizeFilename(filename) {
|
|
4
|
+
if (!filename || typeof filename !== 'string') {
|
|
5
|
+
return 'export.csv';
|
|
6
|
+
}
|
|
7
|
+
return filename.includes('.') ? filename : `${filename}.csv`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function csvMiddleware(options = {}) {
|
|
11
|
+
return async (c, next) => {
|
|
12
|
+
const csvText = await c.req.text();
|
|
13
|
+
const rows = jtcsv.csvToJson(csvText, options);
|
|
14
|
+
c.set('csv', rows);
|
|
15
|
+
await next();
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function createCsvResponse(data, filename = 'export.csv', options = {}) {
|
|
20
|
+
const safeName = normalizeFilename(filename);
|
|
21
|
+
const rows = Array.isArray(data) ? data : [data];
|
|
22
|
+
const csv = jtcsv.jsonToCsv(rows, options);
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
csv,
|
|
26
|
+
headers: {
|
|
27
|
+
'Content-Type': 'text/csv; charset=utf-8',
|
|
28
|
+
'Content-Disposition': `attachment; filename="${safeName}"`
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
csvMiddleware,
|
|
35
|
+
createCsvResponse
|
|
36
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jtcsv/hono",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Hono middleware for JTCSV",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"hono",
|
|
9
|
+
"csv",
|
|
10
|
+
"json",
|
|
11
|
+
"converter",
|
|
12
|
+
"jtcsv",
|
|
13
|
+
"middleware"
|
|
14
|
+
],
|
|
15
|
+
"author": "Ruslan Fomenko",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/Linol-Hamelton/jtcsv.git",
|
|
20
|
+
"directory": "plugins/hono"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/Linol-Hamelton/jtcsv/issues"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/Linol-Hamelton/jtcsv/tree/main/plugins/hono#readme",
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"hono": "^4.0.0",
|
|
28
|
+
"jtcsv": "^2.1.3"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"index.js",
|
|
32
|
+
"index.d.ts",
|
|
33
|
+
"README.md"
|
|
34
|
+
]
|
|
35
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# @jtcsv/nestjs
|
|
2
|
+
|
|
3
|
+
NestJS interceptors for parsing CSV payloads and returning CSV responses.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
```bash
|
|
7
|
+
npm install @jtcsv/nestjs jtcsv
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
```typescript
|
|
12
|
+
import { Controller, Post, Body, UseInterceptors } from '@nestjs/common';
|
|
13
|
+
import { CsvParserInterceptor, CsvDownloadDecorator } from '@jtcsv/nestjs';
|
|
14
|
+
|
|
15
|
+
@Controller('data')
|
|
16
|
+
export class DataController {
|
|
17
|
+
@Post('upload')
|
|
18
|
+
@CsvParserInterceptor({ delimiter: ',' })
|
|
19
|
+
upload(@Body() body: any[]) {
|
|
20
|
+
return { rows: body };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@Post('export')
|
|
24
|
+
@CsvDownloadDecorator({ filename: 'export.csv' })
|
|
25
|
+
exportCsv() {
|
|
26
|
+
return [{ id: 1, name: 'Jane' }];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Exports
|
|
32
|
+
- CsvParserInterceptor
|
|
33
|
+
- CsvDownloadDecorator
|
|
34
|
+
- createCsvParserInterceptor
|
|
35
|
+
- createCsvDownloadInterceptor
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Type } from '@nestjs/common';
|
|
2
|
+
import type { NestInterceptor } from '@nestjs/common';
|
|
3
|
+
import type { CsvToJsonOptions, JsonToCsvOptions } from 'jtcsv';
|
|
4
|
+
|
|
5
|
+
export interface CsvParserOptions extends CsvToJsonOptions {}
|
|
6
|
+
|
|
7
|
+
export interface CsvDownloadOptions extends JsonToCsvOptions {
|
|
8
|
+
filename?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createCsvParserInterceptor(
|
|
12
|
+
options?: CsvParserOptions
|
|
13
|
+
): Type<NestInterceptor>;
|
|
14
|
+
|
|
15
|
+
export function createCsvDownloadInterceptor(
|
|
16
|
+
options?: CsvDownloadOptions
|
|
17
|
+
): Type<NestInterceptor>;
|
|
18
|
+
|
|
19
|
+
export function CsvParserInterceptor(
|
|
20
|
+
options?: CsvParserOptions
|
|
21
|
+
): MethodDecorator & ClassDecorator;
|
|
22
|
+
|
|
23
|
+
export function CsvDownloadDecorator(
|
|
24
|
+
options?: CsvDownloadOptions
|
|
25
|
+
): MethodDecorator & ClassDecorator;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const { Injectable, UseInterceptors } = require('@nestjs/common');
|
|
2
|
+
const { map } = require('rxjs/operators');
|
|
3
|
+
const jtcsv = require('jtcsv');
|
|
4
|
+
|
|
5
|
+
function normalizeFilename(filename) {
|
|
6
|
+
if (!filename || typeof filename !== 'string') {
|
|
7
|
+
return 'export.csv';
|
|
8
|
+
}
|
|
9
|
+
return filename.includes('.') ? filename : `${filename}.csv`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function createCsvParserInterceptor(options = {}) {
|
|
13
|
+
class CsvParserInterceptorImpl {
|
|
14
|
+
intercept(context, next) {
|
|
15
|
+
const req = context.switchToHttp().getRequest();
|
|
16
|
+
const body = req && req.body;
|
|
17
|
+
|
|
18
|
+
if (typeof body === 'string' || Buffer.isBuffer(body)) {
|
|
19
|
+
const csv = Buffer.isBuffer(body) ? body.toString('utf8') : body;
|
|
20
|
+
req.body = jtcsv.csvToJson(csv, options);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return next.handle();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
Injectable()(CsvParserInterceptorImpl);
|
|
28
|
+
return CsvParserInterceptorImpl;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function createCsvDownloadInterceptor(options = {}) {
|
|
32
|
+
class CsvDownloadInterceptorImpl {
|
|
33
|
+
intercept(context, next) {
|
|
34
|
+
const res = context.switchToHttp().getResponse();
|
|
35
|
+
const filename = normalizeFilename(options.filename);
|
|
36
|
+
const csvOptions = { ...options };
|
|
37
|
+
delete csvOptions.filename;
|
|
38
|
+
|
|
39
|
+
return next.handle().pipe(
|
|
40
|
+
map(data => {
|
|
41
|
+
const rows = Array.isArray(data) ? data : [data];
|
|
42
|
+
const csv = jtcsv.jsonToCsv(rows, csvOptions);
|
|
43
|
+
|
|
44
|
+
if (res && typeof res.setHeader === 'function') {
|
|
45
|
+
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
|
|
46
|
+
res.setHeader(
|
|
47
|
+
'Content-Disposition',
|
|
48
|
+
`attachment; filename="${filename}"`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return csv;
|
|
53
|
+
})
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
Injectable()(CsvDownloadInterceptorImpl);
|
|
59
|
+
return CsvDownloadInterceptorImpl;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function CsvParserInterceptor(options = {}) {
|
|
63
|
+
const Interceptor = createCsvParserInterceptor(options);
|
|
64
|
+
return UseInterceptors(new Interceptor());
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function CsvDownloadDecorator(options = {}) {
|
|
68
|
+
const Interceptor = createCsvDownloadInterceptor(options);
|
|
69
|
+
return UseInterceptors(new Interceptor());
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = {
|
|
73
|
+
CsvParserInterceptor,
|
|
74
|
+
CsvDownloadDecorator,
|
|
75
|
+
createCsvParserInterceptor,
|
|
76
|
+
createCsvDownloadInterceptor
|
|
77
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jtcsv/nestjs",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "NestJS interceptors and decorators for JTCSV",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"nestjs",
|
|
9
|
+
"csv",
|
|
10
|
+
"json",
|
|
11
|
+
"converter",
|
|
12
|
+
"jtcsv",
|
|
13
|
+
"interceptor",
|
|
14
|
+
"decorator"
|
|
15
|
+
],
|
|
16
|
+
"author": "Ruslan Fomenko",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/Linol-Hamelton/jtcsv.git",
|
|
21
|
+
"directory": "plugins/nestjs"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/Linol-Hamelton/jtcsv/issues"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/Linol-Hamelton/jtcsv/tree/main/plugins/nestjs#readme",
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@nestjs/common": "^9.0.0 || ^10.0.0",
|
|
29
|
+
"jtcsv": "^2.1.3",
|
|
30
|
+
"rxjs": "^7.0.0"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"index.js",
|
|
34
|
+
"index.d.ts",
|
|
35
|
+
"README.md"
|
|
36
|
+
]
|
|
37
|
+
}
|