jtcsv 2.1.5 → 2.2.2
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/LICENSE +1 -1
- package/bin/jtcsv.js +2462 -1372
- package/examples/error-handling.js +324 -0
- package/examples/ndjson-processing.js +434 -0
- package/examples/react-integration.jsx +637 -0
- package/examples/schema-validation.js +640 -0
- package/examples/typescript-example.ts +486 -0
- package/index.d.ts +2 -0
- package/json-to-csv.js +171 -131
- package/package.json +21 -9
- package/src/errors.js +26 -0
- package/src/utils/schema-validator.js +594 -0
- package/src/utils/transform-loader.js +205 -0
- package/src/web-server/index.js +683 -0
- package/stream-csv-to-json.js +7 -87
- package/stream-json-to-csv.js +7 -87
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NDJSON Processing Examples for jtcsv
|
|
3
|
+
*
|
|
4
|
+
* NDJSON (Newline Delimited JSON) is a format where each line
|
|
5
|
+
* is a valid JSON object. It's ideal for streaming large datasets
|
|
6
|
+
* and log processing.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
jsonToNdjson,
|
|
11
|
+
ndjsonToJson,
|
|
12
|
+
parseNdjsonStream,
|
|
13
|
+
createNdjsonToCsvStream,
|
|
14
|
+
createCsvToNdjsonStream,
|
|
15
|
+
getNdjsonStats
|
|
16
|
+
} = require('jtcsv');
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const { Readable, PassThrough } = require('stream');
|
|
21
|
+
|
|
22
|
+
// =============================================================================
|
|
23
|
+
// Example 1: Basic NDJSON Conversion
|
|
24
|
+
// =============================================================================
|
|
25
|
+
|
|
26
|
+
function basicNdjsonConversion() {
|
|
27
|
+
console.log('\n=== Basic NDJSON Conversion ===\n');
|
|
28
|
+
|
|
29
|
+
const data = [
|
|
30
|
+
{ id: 1, event: 'login', user: 'john', timestamp: '2024-01-15T10:30:00Z' },
|
|
31
|
+
{ id: 2, event: 'click', user: 'john', timestamp: '2024-01-15T10:30:15Z' },
|
|
32
|
+
{ id: 3, event: 'purchase', user: 'john', timestamp: '2024-01-15T10:31:00Z' },
|
|
33
|
+
{ id: 4, event: 'logout', user: 'john', timestamp: '2024-01-15T10:35:00Z' }
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
// Convert to NDJSON
|
|
37
|
+
const ndjson = jsonToNdjson(data);
|
|
38
|
+
console.log('NDJSON output:');
|
|
39
|
+
console.log(ndjson);
|
|
40
|
+
|
|
41
|
+
// Parse back to JSON
|
|
42
|
+
const parsed = ndjsonToJson(ndjson);
|
|
43
|
+
console.log('\nParsed back:', parsed.length, 'records');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// =============================================================================
|
|
47
|
+
// Example 2: NDJSON with Transform and Filter
|
|
48
|
+
// =============================================================================
|
|
49
|
+
|
|
50
|
+
function ndjsonWithTransformAndFilter() {
|
|
51
|
+
console.log('\n=== NDJSON with Transform and Filter ===\n');
|
|
52
|
+
|
|
53
|
+
const logEntries = [
|
|
54
|
+
{ level: 'info', message: 'Application started', ts: 1705312200 },
|
|
55
|
+
{ level: 'debug', message: 'Connecting to database', ts: 1705312201 },
|
|
56
|
+
{ level: 'error', message: 'Connection failed', ts: 1705312202 },
|
|
57
|
+
{ level: 'info', message: 'Retry connection', ts: 1705312203 },
|
|
58
|
+
{ level: 'info', message: 'Connected successfully', ts: 1705312204 },
|
|
59
|
+
{ level: 'debug', message: 'Query executed', ts: 1705312205 }
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
// Convert with transform - add formatted timestamp
|
|
63
|
+
const ndjson = jsonToNdjson(logEntries, {
|
|
64
|
+
transform: (obj, index) => ({
|
|
65
|
+
...obj,
|
|
66
|
+
index,
|
|
67
|
+
formattedTime: new Date(obj.ts * 1000).toISOString()
|
|
68
|
+
})
|
|
69
|
+
});
|
|
70
|
+
console.log('Transformed NDJSON:');
|
|
71
|
+
console.log(ndjson);
|
|
72
|
+
|
|
73
|
+
// Parse back with filter - only errors and info
|
|
74
|
+
const filtered = ndjsonToJson(ndjson, {
|
|
75
|
+
filter: (obj) => obj.level === 'error' || obj.level === 'info'
|
|
76
|
+
});
|
|
77
|
+
console.log('\nFiltered entries (error + info only):', filtered.length);
|
|
78
|
+
filtered.forEach(e => console.log(` [${e.level}] ${e.message}`));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// =============================================================================
|
|
82
|
+
// Example 3: NDJSON Error Handling
|
|
83
|
+
// =============================================================================
|
|
84
|
+
|
|
85
|
+
function ndjsonErrorHandling() {
|
|
86
|
+
console.log('\n=== NDJSON Error Handling ===\n');
|
|
87
|
+
|
|
88
|
+
// NDJSON with some invalid lines
|
|
89
|
+
const mixedNdjson = `{"id": 1, "valid": true}
|
|
90
|
+
{"id": 2, "valid": true}
|
|
91
|
+
{invalid json here}
|
|
92
|
+
{"id": 3, "valid": true}
|
|
93
|
+
not json at all
|
|
94
|
+
{"id": 4, "valid": true}`;
|
|
95
|
+
|
|
96
|
+
const errors = [];
|
|
97
|
+
|
|
98
|
+
const result = ndjsonToJson(mixedNdjson, {
|
|
99
|
+
onError: (error, line, lineNumber) => {
|
|
100
|
+
errors.push({
|
|
101
|
+
lineNumber,
|
|
102
|
+
error: error.message,
|
|
103
|
+
content: line.substring(0, 30) + (line.length > 30 ? '...' : '')
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
console.log('Valid records:', result.length);
|
|
109
|
+
console.log('Errors encountered:', errors.length);
|
|
110
|
+
errors.forEach(e => {
|
|
111
|
+
console.log(` Line ${e.lineNumber}: ${e.error}`);
|
|
112
|
+
console.log(` Content: ${e.content}`);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// =============================================================================
|
|
117
|
+
// Example 4: Streaming NDJSON Processing
|
|
118
|
+
// =============================================================================
|
|
119
|
+
|
|
120
|
+
async function streamingNdjsonProcessing() {
|
|
121
|
+
console.log('\n=== Streaming NDJSON Processing ===\n');
|
|
122
|
+
|
|
123
|
+
// Create a stream from NDJSON string
|
|
124
|
+
const ndjsonContent = `{"type": "user", "name": "Alice", "age": 28}
|
|
125
|
+
{"type": "user", "name": "Bob", "age": 35}
|
|
126
|
+
{"type": "product", "name": "Laptop", "price": 999}
|
|
127
|
+
{"type": "user", "name": "Charlie", "age": 42}
|
|
128
|
+
{"type": "product", "name": "Phone", "price": 599}`;
|
|
129
|
+
|
|
130
|
+
// Create readable stream
|
|
131
|
+
const stream = Readable.from([ndjsonContent]);
|
|
132
|
+
|
|
133
|
+
// Process stream with async iterator
|
|
134
|
+
const users = [];
|
|
135
|
+
const products = [];
|
|
136
|
+
|
|
137
|
+
for await (const obj of parseNdjsonStream(stream)) {
|
|
138
|
+
if (obj.type === 'user') {
|
|
139
|
+
users.push(obj);
|
|
140
|
+
} else if (obj.type === 'product') {
|
|
141
|
+
products.push(obj);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
console.log('Users found:', users.length);
|
|
146
|
+
users.forEach(u => console.log(` - ${u.name}, ${u.age} years old`));
|
|
147
|
+
|
|
148
|
+
console.log('\nProducts found:', products.length);
|
|
149
|
+
products.forEach(p => console.log(` - ${p.name}: $${p.price}`));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// =============================================================================
|
|
153
|
+
// Example 5: NDJSON to CSV Conversion
|
|
154
|
+
// =============================================================================
|
|
155
|
+
|
|
156
|
+
async function ndjsonToCsvConversion() {
|
|
157
|
+
console.log('\n=== NDJSON to CSV Conversion ===\n');
|
|
158
|
+
|
|
159
|
+
const ndjson = `{"name": "Alice", "department": "Engineering", "salary": 85000}
|
|
160
|
+
{"name": "Bob", "department": "Marketing", "salary": 65000}
|
|
161
|
+
{"name": "Charlie", "department": "Engineering", "salary": 90000}
|
|
162
|
+
{"name": "Diana", "department": "Sales", "salary": 70000}`;
|
|
163
|
+
|
|
164
|
+
// Create NDJSON to CSV transform stream
|
|
165
|
+
const transformStream = createNdjsonToCsvStream({
|
|
166
|
+
delimiter: ',',
|
|
167
|
+
includeHeaders: true
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Collect CSV output
|
|
171
|
+
let csvOutput = '';
|
|
172
|
+
transformStream.writable.getWriter();
|
|
173
|
+
|
|
174
|
+
// Manual stream processing for demo
|
|
175
|
+
const lines = ndjson.split('\n');
|
|
176
|
+
const objects = lines.map(line => JSON.parse(line));
|
|
177
|
+
|
|
178
|
+
// Convert to CSV manually for this example
|
|
179
|
+
const { jsonToCsv } = require('jtcsv');
|
|
180
|
+
const csv = jsonToCsv(objects, { delimiter: ',' });
|
|
181
|
+
|
|
182
|
+
console.log('Converted CSV:');
|
|
183
|
+
console.log(csv);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// =============================================================================
|
|
187
|
+
// Example 6: CSV to NDJSON Conversion
|
|
188
|
+
// =============================================================================
|
|
189
|
+
|
|
190
|
+
function csvToNdjsonConversion() {
|
|
191
|
+
console.log('\n=== CSV to NDJSON Conversion ===\n');
|
|
192
|
+
|
|
193
|
+
const csv = `id,name,email,active
|
|
194
|
+
1,John Doe,john@example.com,true
|
|
195
|
+
2,Jane Smith,jane@example.com,false
|
|
196
|
+
3,Bob Wilson,bob@example.com,true`;
|
|
197
|
+
|
|
198
|
+
const { csvToJson } = require('jtcsv');
|
|
199
|
+
|
|
200
|
+
// Parse CSV first
|
|
201
|
+
const data = csvToJson(csv, {
|
|
202
|
+
parseNumbers: true,
|
|
203
|
+
parseBooleans: true
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Convert to NDJSON
|
|
207
|
+
const ndjson = jsonToNdjson(data);
|
|
208
|
+
console.log('NDJSON output:');
|
|
209
|
+
console.log(ndjson);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// =============================================================================
|
|
213
|
+
// Example 7: NDJSON Statistics
|
|
214
|
+
// =============================================================================
|
|
215
|
+
|
|
216
|
+
async function ndjsonStatistics() {
|
|
217
|
+
console.log('\n=== NDJSON Statistics ===\n');
|
|
218
|
+
|
|
219
|
+
const ndjson = `{"id": 1, "type": "event"}
|
|
220
|
+
{"id": 2, "type": "event"}
|
|
221
|
+
{invalid}
|
|
222
|
+
{"id": 3, "type": "event"}
|
|
223
|
+
not json
|
|
224
|
+
{"id": 4, "type": "event"}
|
|
225
|
+
{"id": 5, "type": "event"}`;
|
|
226
|
+
|
|
227
|
+
const stats = await getNdjsonStats(ndjson);
|
|
228
|
+
|
|
229
|
+
console.log('NDJSON Statistics:');
|
|
230
|
+
console.log(' Total lines:', stats.totalLines);
|
|
231
|
+
console.log(' Valid lines:', stats.validLines);
|
|
232
|
+
console.log(' Error lines:', stats.errorLines);
|
|
233
|
+
console.log(' Total bytes:', stats.totalBytes);
|
|
234
|
+
console.log(' Success rate:', (stats.successRate * 100).toFixed(1) + '%');
|
|
235
|
+
|
|
236
|
+
if (stats.errors.length > 0) {
|
|
237
|
+
console.log('\nErrors:');
|
|
238
|
+
stats.errors.forEach(e => {
|
|
239
|
+
console.log(` Line ${e.line}: ${e.error}`);
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// =============================================================================
|
|
245
|
+
// Example 8: Large NDJSON Processing with Memory Efficiency
|
|
246
|
+
// =============================================================================
|
|
247
|
+
|
|
248
|
+
async function largeNdjsonProcessing() {
|
|
249
|
+
console.log('\n=== Large NDJSON Processing ===\n');
|
|
250
|
+
|
|
251
|
+
// Simulate large NDJSON data
|
|
252
|
+
const generateLargeNdjson = (count) => {
|
|
253
|
+
const lines = [];
|
|
254
|
+
for (let i = 0; i < count; i++) {
|
|
255
|
+
lines.push(JSON.stringify({
|
|
256
|
+
id: i,
|
|
257
|
+
timestamp: Date.now() + i,
|
|
258
|
+
value: Math.random() * 100,
|
|
259
|
+
category: ['A', 'B', 'C'][i % 3]
|
|
260
|
+
}));
|
|
261
|
+
}
|
|
262
|
+
return lines.join('\n');
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const largeNdjson = generateLargeNdjson(10000);
|
|
266
|
+
console.log(`Generated ${largeNdjson.split('\n').length} NDJSON lines`);
|
|
267
|
+
|
|
268
|
+
// Process with aggregation
|
|
269
|
+
const stats = {
|
|
270
|
+
count: 0,
|
|
271
|
+
sum: 0,
|
|
272
|
+
categories: {}
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const startTime = Date.now();
|
|
276
|
+
|
|
277
|
+
for await (const obj of parseNdjsonStream(Readable.from([largeNdjson]))) {
|
|
278
|
+
stats.count++;
|
|
279
|
+
stats.sum += obj.value;
|
|
280
|
+
stats.categories[obj.category] = (stats.categories[obj.category] || 0) + 1;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const endTime = Date.now();
|
|
284
|
+
|
|
285
|
+
console.log('\nAggregation results:');
|
|
286
|
+
console.log(' Records processed:', stats.count);
|
|
287
|
+
console.log(' Average value:', (stats.sum / stats.count).toFixed(2));
|
|
288
|
+
console.log(' Categories:', stats.categories);
|
|
289
|
+
console.log(' Processing time:', endTime - startTime, 'ms');
|
|
290
|
+
console.log(' Throughput:', Math.round(stats.count / ((endTime - startTime) / 1000)), 'records/sec');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// =============================================================================
|
|
294
|
+
// Example 9: NDJSON Log Processing Pipeline
|
|
295
|
+
// =============================================================================
|
|
296
|
+
|
|
297
|
+
async function logProcessingPipeline() {
|
|
298
|
+
console.log('\n=== NDJSON Log Processing Pipeline ===\n');
|
|
299
|
+
|
|
300
|
+
// Simulated log entries
|
|
301
|
+
const logs = `{"ts": "2024-01-15T10:00:00Z", "level": "info", "service": "api", "msg": "Request received", "duration_ms": 50}
|
|
302
|
+
{"ts": "2024-01-15T10:00:01Z", "level": "debug", "service": "api", "msg": "Processing request", "duration_ms": 0}
|
|
303
|
+
{"ts": "2024-01-15T10:00:02Z", "level": "warn", "service": "db", "msg": "Slow query detected", "duration_ms": 1500}
|
|
304
|
+
{"ts": "2024-01-15T10:00:03Z", "level": "error", "service": "api", "msg": "Request failed", "duration_ms": 100, "error": "Timeout"}
|
|
305
|
+
{"ts": "2024-01-15T10:00:04Z", "level": "info", "service": "api", "msg": "Request completed", "duration_ms": 45}
|
|
306
|
+
{"ts": "2024-01-15T10:00:05Z", "level": "error", "service": "db", "msg": "Connection lost", "duration_ms": 0, "error": "Network error"}`;
|
|
307
|
+
|
|
308
|
+
// Pipeline stages
|
|
309
|
+
const pipeline = {
|
|
310
|
+
errors: [],
|
|
311
|
+
warnings: [],
|
|
312
|
+
slowRequests: [],
|
|
313
|
+
serviceStats: {}
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
// Process logs
|
|
317
|
+
for await (const log of parseNdjsonStream(Readable.from([logs]))) {
|
|
318
|
+
// Collect errors
|
|
319
|
+
if (log.level === 'error') {
|
|
320
|
+
pipeline.errors.push({
|
|
321
|
+
time: log.ts,
|
|
322
|
+
service: log.service,
|
|
323
|
+
message: log.msg,
|
|
324
|
+
error: log.error
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Collect warnings
|
|
329
|
+
if (log.level === 'warn') {
|
|
330
|
+
pipeline.warnings.push({
|
|
331
|
+
time: log.ts,
|
|
332
|
+
service: log.service,
|
|
333
|
+
message: log.msg
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Track slow requests (> 1000ms)
|
|
338
|
+
if (log.duration_ms > 1000) {
|
|
339
|
+
pipeline.slowRequests.push({
|
|
340
|
+
time: log.ts,
|
|
341
|
+
service: log.service,
|
|
342
|
+
duration: log.duration_ms
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Aggregate by service
|
|
347
|
+
if (!pipeline.serviceStats[log.service]) {
|
|
348
|
+
pipeline.serviceStats[log.service] = { count: 0, errors: 0 };
|
|
349
|
+
}
|
|
350
|
+
pipeline.serviceStats[log.service].count++;
|
|
351
|
+
if (log.level === 'error') {
|
|
352
|
+
pipeline.serviceStats[log.service].errors++;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Report
|
|
357
|
+
console.log('Log Analysis Report:');
|
|
358
|
+
console.log('\nErrors:', pipeline.errors.length);
|
|
359
|
+
pipeline.errors.forEach(e => {
|
|
360
|
+
console.log(` [${e.service}] ${e.message} - ${e.error}`);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
console.log('\nWarnings:', pipeline.warnings.length);
|
|
364
|
+
pipeline.warnings.forEach(w => {
|
|
365
|
+
console.log(` [${w.service}] ${w.message}`);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
console.log('\nSlow Requests (>1s):', pipeline.slowRequests.length);
|
|
369
|
+
pipeline.slowRequests.forEach(s => {
|
|
370
|
+
console.log(` [${s.service}] ${s.duration}ms`);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
console.log('\nService Statistics:');
|
|
374
|
+
Object.entries(pipeline.serviceStats).forEach(([service, stats]) => {
|
|
375
|
+
const errorRate = ((stats.errors / stats.count) * 100).toFixed(1);
|
|
376
|
+
console.log(` ${service}: ${stats.count} requests, ${stats.errors} errors (${errorRate}%)`);
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// =============================================================================
|
|
381
|
+
// Example 10: NDJSON Pretty Print and Compact
|
|
382
|
+
// =============================================================================
|
|
383
|
+
|
|
384
|
+
function ndjsonFormatting() {
|
|
385
|
+
console.log('\n=== NDJSON Formatting Options ===\n');
|
|
386
|
+
|
|
387
|
+
const data = [
|
|
388
|
+
{ name: 'Test', nested: { a: 1, b: 2 }, array: [1, 2, 3] }
|
|
389
|
+
];
|
|
390
|
+
|
|
391
|
+
// Compact (default)
|
|
392
|
+
const compact = jsonToNdjson(data);
|
|
393
|
+
console.log('Compact:');
|
|
394
|
+
console.log(compact);
|
|
395
|
+
|
|
396
|
+
// With custom replacer (filter out 'array' field)
|
|
397
|
+
const filtered = jsonToNdjson(data, {
|
|
398
|
+
replacer: (key, value) => key === 'array' ? undefined : value
|
|
399
|
+
});
|
|
400
|
+
console.log('\nFiltered (no array):');
|
|
401
|
+
console.log(filtered);
|
|
402
|
+
|
|
403
|
+
// With space for readability (not standard NDJSON but useful for debugging)
|
|
404
|
+
const pretty = jsonToNdjson(data, {
|
|
405
|
+
space: 0 // Standard NDJSON should have no space
|
|
406
|
+
});
|
|
407
|
+
console.log('\nStandard NDJSON:');
|
|
408
|
+
console.log(pretty);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// =============================================================================
|
|
412
|
+
// Run All Examples
|
|
413
|
+
// =============================================================================
|
|
414
|
+
|
|
415
|
+
async function main() {
|
|
416
|
+
console.log('jtcsv NDJSON Processing Examples');
|
|
417
|
+
console.log('='.repeat(60));
|
|
418
|
+
|
|
419
|
+
basicNdjsonConversion();
|
|
420
|
+
ndjsonWithTransformAndFilter();
|
|
421
|
+
ndjsonErrorHandling();
|
|
422
|
+
await streamingNdjsonProcessing();
|
|
423
|
+
await ndjsonToCsvConversion();
|
|
424
|
+
csvToNdjsonConversion();
|
|
425
|
+
await ndjsonStatistics();
|
|
426
|
+
await largeNdjsonProcessing();
|
|
427
|
+
await logProcessingPipeline();
|
|
428
|
+
ndjsonFormatting();
|
|
429
|
+
|
|
430
|
+
console.log('\n' + '='.repeat(60));
|
|
431
|
+
console.log('All NDJSON examples completed.');
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
main().catch(console.error);
|