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,486 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript Examples for jtcsv
|
|
3
|
+
*
|
|
4
|
+
* This file demonstrates type-safe usage of jtcsv
|
|
5
|
+
* with full TypeScript support.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
// Core functions
|
|
10
|
+
csvToJson,
|
|
11
|
+
jsonToCsv,
|
|
12
|
+
saveAsCsv,
|
|
13
|
+
readCsvAsJson,
|
|
14
|
+
readCsvAsJsonSync,
|
|
15
|
+
|
|
16
|
+
// Streaming
|
|
17
|
+
createCsvToJsonStream,
|
|
18
|
+
createJsonToCsvStream,
|
|
19
|
+
streamCsvToJson,
|
|
20
|
+
streamJsonToCsv,
|
|
21
|
+
|
|
22
|
+
// NDJSON
|
|
23
|
+
jsonToNdjson,
|
|
24
|
+
ndjsonToJson,
|
|
25
|
+
parseNdjsonStream,
|
|
26
|
+
|
|
27
|
+
// TSV
|
|
28
|
+
jsonToTsv,
|
|
29
|
+
tsvToJson,
|
|
30
|
+
validateTsv,
|
|
31
|
+
|
|
32
|
+
// Types
|
|
33
|
+
JsonToCsvOptions,
|
|
34
|
+
CsvToJsonOptions,
|
|
35
|
+
NdjsonOptions,
|
|
36
|
+
TsvOptions,
|
|
37
|
+
TsvValidationResult,
|
|
38
|
+
|
|
39
|
+
// Error classes
|
|
40
|
+
JtcsvError,
|
|
41
|
+
ValidationError,
|
|
42
|
+
SecurityError,
|
|
43
|
+
ParsingError,
|
|
44
|
+
FileSystemError,
|
|
45
|
+
LimitError,
|
|
46
|
+
ConfigurationError
|
|
47
|
+
} from 'jtcsv';
|
|
48
|
+
|
|
49
|
+
import { Readable, Writable, Transform } from 'stream';
|
|
50
|
+
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// Type Definitions for Domain Objects
|
|
53
|
+
// =============================================================================
|
|
54
|
+
|
|
55
|
+
interface User {
|
|
56
|
+
id: number;
|
|
57
|
+
name: string;
|
|
58
|
+
email: string;
|
|
59
|
+
age: number;
|
|
60
|
+
isActive: boolean;
|
|
61
|
+
createdAt: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface Product {
|
|
65
|
+
sku: string;
|
|
66
|
+
name: string;
|
|
67
|
+
price: number;
|
|
68
|
+
category: string;
|
|
69
|
+
inStock: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface Order {
|
|
73
|
+
orderId: string;
|
|
74
|
+
customerId: number;
|
|
75
|
+
products: string[];
|
|
76
|
+
total: number;
|
|
77
|
+
status: 'pending' | 'shipped' | 'delivered';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// =============================================================================
|
|
81
|
+
// Example 1: Type-Safe JSON to CSV Conversion
|
|
82
|
+
// =============================================================================
|
|
83
|
+
|
|
84
|
+
function typedJsonToCsv(): void {
|
|
85
|
+
console.log('\n=== Type-Safe JSON to CSV ===\n');
|
|
86
|
+
|
|
87
|
+
const users: User[] = [
|
|
88
|
+
{ id: 1, name: 'John Doe', email: 'john@example.com', age: 30, isActive: true, createdAt: '2024-01-15' },
|
|
89
|
+
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', age: 25, isActive: false, createdAt: '2024-02-20' },
|
|
90
|
+
{ id: 3, name: 'Bob Wilson', email: 'bob@example.com', age: 35, isActive: true, createdAt: '2024-03-10' }
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
// Options with full type support
|
|
94
|
+
const options: JsonToCsvOptions = {
|
|
95
|
+
delimiter: ',',
|
|
96
|
+
includeHeaders: true,
|
|
97
|
+
preventCsvInjection: true,
|
|
98
|
+
rfc4180Compliant: true,
|
|
99
|
+
renameMap: {
|
|
100
|
+
id: 'User ID',
|
|
101
|
+
name: 'Full Name',
|
|
102
|
+
email: 'Email Address',
|
|
103
|
+
isActive: 'Active Status'
|
|
104
|
+
},
|
|
105
|
+
template: {
|
|
106
|
+
id: 0,
|
|
107
|
+
name: '',
|
|
108
|
+
email: '',
|
|
109
|
+
age: 0,
|
|
110
|
+
isActive: false,
|
|
111
|
+
createdAt: ''
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const csv = jsonToCsv<User>(users, options);
|
|
116
|
+
console.log('Generated CSV:');
|
|
117
|
+
console.log(csv);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// =============================================================================
|
|
121
|
+
// Example 2: Type-Safe CSV to JSON Conversion
|
|
122
|
+
// =============================================================================
|
|
123
|
+
|
|
124
|
+
function typedCsvToJson(): void {
|
|
125
|
+
console.log('\n=== Type-Safe CSV to JSON ===\n');
|
|
126
|
+
|
|
127
|
+
const csvData = `sku,name,price,category,inStock
|
|
128
|
+
PRD001,Laptop,999.99,Electronics,true
|
|
129
|
+
PRD002,Mouse,29.99,Electronics,true
|
|
130
|
+
PRD003,Desk Chair,199.99,Furniture,false`;
|
|
131
|
+
|
|
132
|
+
const options: CsvToJsonOptions = {
|
|
133
|
+
delimiter: ',',
|
|
134
|
+
hasHeaders: true,
|
|
135
|
+
parseNumbers: true,
|
|
136
|
+
parseBooleans: true,
|
|
137
|
+
trim: true
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// Parse as generic records first
|
|
141
|
+
const rawProducts = csvToJson(csvData, options);
|
|
142
|
+
|
|
143
|
+
// Type assertion after validation
|
|
144
|
+
const products: Product[] = rawProducts.map(row => ({
|
|
145
|
+
sku: String(row.sku),
|
|
146
|
+
name: String(row.name),
|
|
147
|
+
price: Number(row.price),
|
|
148
|
+
category: String(row.category),
|
|
149
|
+
inStock: Boolean(row.inStock)
|
|
150
|
+
}));
|
|
151
|
+
|
|
152
|
+
console.log('Parsed products:');
|
|
153
|
+
products.forEach(p => {
|
|
154
|
+
console.log(` ${p.sku}: ${p.name} - $${p.price.toFixed(2)} (${p.inStock ? 'In Stock' : 'Out of Stock'})`);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// =============================================================================
|
|
159
|
+
// Example 3: Generic Type Helper
|
|
160
|
+
// =============================================================================
|
|
161
|
+
|
|
162
|
+
// Type-safe parsing helper with validation
|
|
163
|
+
function parseTypedCsv<T extends Record<string, unknown>>(
|
|
164
|
+
csv: string,
|
|
165
|
+
options: CsvToJsonOptions,
|
|
166
|
+
validator: (row: Record<string, unknown>) => row is T
|
|
167
|
+
): T[] {
|
|
168
|
+
const rawData = csvToJson(csv, options);
|
|
169
|
+
const validData: T[] = [];
|
|
170
|
+
|
|
171
|
+
for (const row of rawData) {
|
|
172
|
+
if (validator(row)) {
|
|
173
|
+
validData.push(row);
|
|
174
|
+
} else {
|
|
175
|
+
console.warn('Invalid row skipped:', row);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return validData;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Type guard for User
|
|
183
|
+
function isUser(row: Record<string, unknown>): row is User {
|
|
184
|
+
return (
|
|
185
|
+
typeof row.id === 'number' &&
|
|
186
|
+
typeof row.name === 'string' &&
|
|
187
|
+
typeof row.email === 'string' &&
|
|
188
|
+
typeof row.age === 'number' &&
|
|
189
|
+
typeof row.isActive === 'boolean' &&
|
|
190
|
+
typeof row.createdAt === 'string'
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function genericTypeHelperExample(): void {
|
|
195
|
+
console.log('\n=== Generic Type Helper ===\n');
|
|
196
|
+
|
|
197
|
+
const csv = `id,name,email,age,isActive,createdAt
|
|
198
|
+
1,John,john@test.com,30,true,2024-01-01
|
|
199
|
+
2,Jane,jane@test.com,25,false,2024-02-01
|
|
200
|
+
invalid,Bob,bob@test.com,not-a-number,true,2024-03-01`;
|
|
201
|
+
|
|
202
|
+
const users = parseTypedCsv<User>(
|
|
203
|
+
csv,
|
|
204
|
+
{ parseNumbers: true, parseBooleans: true },
|
|
205
|
+
isUser
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
console.log('Valid users:', users.length);
|
|
209
|
+
users.forEach(u => console.log(` ${u.id}: ${u.name} (${u.age})`));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// =============================================================================
|
|
213
|
+
// Example 4: NDJSON with Types
|
|
214
|
+
// =============================================================================
|
|
215
|
+
|
|
216
|
+
async function typedNdjson(): Promise<void> {
|
|
217
|
+
console.log('\n=== Typed NDJSON ===\n');
|
|
218
|
+
|
|
219
|
+
const orders: Order[] = [
|
|
220
|
+
{ orderId: 'ORD001', customerId: 1, products: ['PRD001', 'PRD002'], total: 1029.98, status: 'shipped' },
|
|
221
|
+
{ orderId: 'ORD002', customerId: 2, products: ['PRD003'], total: 199.99, status: 'pending' },
|
|
222
|
+
{ orderId: 'ORD003', customerId: 1, products: ['PRD002'], total: 29.99, status: 'delivered' }
|
|
223
|
+
];
|
|
224
|
+
|
|
225
|
+
const ndjsonOptions: NdjsonOptions = {
|
|
226
|
+
transform: (obj, index) => ({
|
|
227
|
+
...obj,
|
|
228
|
+
lineNumber: index + 1
|
|
229
|
+
})
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// Convert to NDJSON
|
|
233
|
+
const ndjsonString = jsonToNdjson(orders, ndjsonOptions);
|
|
234
|
+
console.log('NDJSON output:');
|
|
235
|
+
console.log(ndjsonString);
|
|
236
|
+
|
|
237
|
+
// Parse back
|
|
238
|
+
const parsed = ndjsonToJson(ndjsonString) as (Order & { lineNumber: number })[];
|
|
239
|
+
console.log('\nParsed back:');
|
|
240
|
+
parsed.forEach(o => console.log(` Line ${o.lineNumber}: ${o.orderId} - ${o.status}`));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// =============================================================================
|
|
244
|
+
// Example 5: TSV with Validation
|
|
245
|
+
// =============================================================================
|
|
246
|
+
|
|
247
|
+
function typedTsv(): void {
|
|
248
|
+
console.log('\n=== Typed TSV with Validation ===\n');
|
|
249
|
+
|
|
250
|
+
const tsvData = `name\tage\tcity
|
|
251
|
+
Alice\t28\tNew York
|
|
252
|
+
Bob\t35\tLos Angeles
|
|
253
|
+
Charlie\t42\tChicago`;
|
|
254
|
+
|
|
255
|
+
// Validate TSV structure
|
|
256
|
+
const validation: TsvValidationResult = validateTsv(tsvData, {
|
|
257
|
+
requireConsistentColumns: true,
|
|
258
|
+
disallowEmptyFields: false
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
console.log('Validation result:');
|
|
262
|
+
console.log(' Valid:', validation.valid);
|
|
263
|
+
console.log(' Stats:', validation.stats);
|
|
264
|
+
|
|
265
|
+
if (validation.valid) {
|
|
266
|
+
const tsvOptions: TsvOptions = {
|
|
267
|
+
hasHeaders: true,
|
|
268
|
+
parseNumbers: true
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
interface Person {
|
|
272
|
+
name: string;
|
|
273
|
+
age: number;
|
|
274
|
+
city: string;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const people = tsvToJson(tsvData, tsvOptions) as Person[];
|
|
278
|
+
console.log('\nParsed people:');
|
|
279
|
+
people.forEach(p => console.log(` ${p.name}, ${p.age}, ${p.city}`));
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// =============================================================================
|
|
284
|
+
// Example 6: Error Handling with TypeScript
|
|
285
|
+
// =============================================================================
|
|
286
|
+
|
|
287
|
+
function typedErrorHandling(): void {
|
|
288
|
+
console.log('\n=== Typed Error Handling ===\n');
|
|
289
|
+
|
|
290
|
+
// Type-safe error handling
|
|
291
|
+
function processCsv(input: string): { success: true; data: Record<string, unknown>[] } | { success: false; error: JtcsvError } {
|
|
292
|
+
try {
|
|
293
|
+
const data = csvToJson(input);
|
|
294
|
+
return { success: true, data };
|
|
295
|
+
} catch (error) {
|
|
296
|
+
if (error instanceof JtcsvError) {
|
|
297
|
+
return { success: false, error };
|
|
298
|
+
}
|
|
299
|
+
throw error; // Re-throw non-jtcsv errors
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Test with various inputs
|
|
304
|
+
const testCases = [
|
|
305
|
+
'name,age\nJohn,30',
|
|
306
|
+
null as unknown as string,
|
|
307
|
+
'name,age\n"unclosed'
|
|
308
|
+
];
|
|
309
|
+
|
|
310
|
+
testCases.forEach((input, i) => {
|
|
311
|
+
const result = processCsv(input);
|
|
312
|
+
if (result.success) {
|
|
313
|
+
console.log(`Test ${i + 1}: Success - ${result.data.length} rows`);
|
|
314
|
+
} else {
|
|
315
|
+
console.log(`Test ${i + 1}: ${result.error.constructor.name} - ${result.error.message}`);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// =============================================================================
|
|
321
|
+
// Example 7: Discriminated Union for Error Types
|
|
322
|
+
// =============================================================================
|
|
323
|
+
|
|
324
|
+
type ParseResult<T> =
|
|
325
|
+
| { status: 'success'; data: T[] }
|
|
326
|
+
| { status: 'validation_error'; message: string }
|
|
327
|
+
| { status: 'parsing_error'; line: number | undefined; message: string }
|
|
328
|
+
| { status: 'security_error'; message: string }
|
|
329
|
+
| { status: 'unknown_error'; message: string };
|
|
330
|
+
|
|
331
|
+
function safeParseCsv<T>(csv: string, options?: CsvToJsonOptions): ParseResult<T> {
|
|
332
|
+
try {
|
|
333
|
+
const data = csvToJson(csv, options) as T[];
|
|
334
|
+
return { status: 'success', data };
|
|
335
|
+
} catch (error) {
|
|
336
|
+
if (error instanceof ValidationError) {
|
|
337
|
+
return { status: 'validation_error', message: error.message };
|
|
338
|
+
}
|
|
339
|
+
if (error instanceof ParsingError) {
|
|
340
|
+
return { status: 'parsing_error', line: error.lineNumber, message: error.message };
|
|
341
|
+
}
|
|
342
|
+
if (error instanceof SecurityError) {
|
|
343
|
+
return { status: 'security_error', message: error.message };
|
|
344
|
+
}
|
|
345
|
+
if (error instanceof Error) {
|
|
346
|
+
return { status: 'unknown_error', message: error.message };
|
|
347
|
+
}
|
|
348
|
+
return { status: 'unknown_error', message: 'Unknown error occurred' };
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function discriminatedUnionExample(): void {
|
|
353
|
+
console.log('\n=== Discriminated Union Errors ===\n');
|
|
354
|
+
|
|
355
|
+
const csv = 'a,b\n1,2';
|
|
356
|
+
const result = safeParseCsv<{ a: string; b: string }>(csv);
|
|
357
|
+
|
|
358
|
+
// TypeScript can narrow the type based on status
|
|
359
|
+
switch (result.status) {
|
|
360
|
+
case 'success':
|
|
361
|
+
console.log('Parsed data:', result.data);
|
|
362
|
+
break;
|
|
363
|
+
case 'validation_error':
|
|
364
|
+
console.log('Validation failed:', result.message);
|
|
365
|
+
break;
|
|
366
|
+
case 'parsing_error':
|
|
367
|
+
console.log(`Parse error at line ${result.line}:`, result.message);
|
|
368
|
+
break;
|
|
369
|
+
case 'security_error':
|
|
370
|
+
console.log('Security violation:', result.message);
|
|
371
|
+
break;
|
|
372
|
+
case 'unknown_error':
|
|
373
|
+
console.log('Unknown error:', result.message);
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// =============================================================================
|
|
379
|
+
// Example 8: Streaming with TypeScript
|
|
380
|
+
// =============================================================================
|
|
381
|
+
|
|
382
|
+
async function typedStreaming(): Promise<void> {
|
|
383
|
+
console.log('\n=== Typed Streaming ===\n');
|
|
384
|
+
|
|
385
|
+
// Create typed transform function
|
|
386
|
+
const transform = (row: Record<string, unknown>): Record<string, unknown> => ({
|
|
387
|
+
...row,
|
|
388
|
+
processed: true,
|
|
389
|
+
timestamp: new Date().toISOString()
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// Type-safe stream options
|
|
393
|
+
const streamOptions: JsonToCsvOptions & { transform: typeof transform } = {
|
|
394
|
+
delimiter: ',',
|
|
395
|
+
includeHeaders: true,
|
|
396
|
+
transform
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
// Note: In real usage, you'd pipe actual streams
|
|
400
|
+
console.log('Stream options configured:', Object.keys(streamOptions));
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// =============================================================================
|
|
404
|
+
// Example 9: Builder Pattern for Options
|
|
405
|
+
// =============================================================================
|
|
406
|
+
|
|
407
|
+
class CsvOptionsBuilder {
|
|
408
|
+
private options: CsvToJsonOptions = {};
|
|
409
|
+
|
|
410
|
+
withDelimiter(delimiter: string): this {
|
|
411
|
+
this.options.delimiter = delimiter;
|
|
412
|
+
return this;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
withHeaders(hasHeaders: boolean = true): this {
|
|
416
|
+
this.options.hasHeaders = hasHeaders;
|
|
417
|
+
return this;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
withNumberParsing(parse: boolean = true): this {
|
|
421
|
+
this.options.parseNumbers = parse;
|
|
422
|
+
return this;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
withBooleanParsing(parse: boolean = true): this {
|
|
426
|
+
this.options.parseBooleans = parse;
|
|
427
|
+
return this;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
withMaxRows(max: number): this {
|
|
431
|
+
this.options.maxRows = max;
|
|
432
|
+
return this;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
withFastPath(enabled: boolean = true): this {
|
|
436
|
+
this.options.useFastPath = enabled;
|
|
437
|
+
return this;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
build(): CsvToJsonOptions {
|
|
441
|
+
return { ...this.options };
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function builderPatternExample(): void {
|
|
446
|
+
console.log('\n=== Builder Pattern ===\n');
|
|
447
|
+
|
|
448
|
+
const options = new CsvOptionsBuilder()
|
|
449
|
+
.withDelimiter(',')
|
|
450
|
+
.withHeaders(true)
|
|
451
|
+
.withNumberParsing(true)
|
|
452
|
+
.withBooleanParsing(true)
|
|
453
|
+
.withMaxRows(1000)
|
|
454
|
+
.withFastPath(true)
|
|
455
|
+
.build();
|
|
456
|
+
|
|
457
|
+
console.log('Built options:', options);
|
|
458
|
+
|
|
459
|
+
const csv = 'id,name,active\n1,Test,true';
|
|
460
|
+
const data = csvToJson(csv, options);
|
|
461
|
+
console.log('Parsed data:', data);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// =============================================================================
|
|
465
|
+
// Main Execution
|
|
466
|
+
// =============================================================================
|
|
467
|
+
|
|
468
|
+
async function main(): Promise<void> {
|
|
469
|
+
console.log('jtcsv TypeScript Examples');
|
|
470
|
+
console.log('='.repeat(60));
|
|
471
|
+
|
|
472
|
+
typedJsonToCsv();
|
|
473
|
+
typedCsvToJson();
|
|
474
|
+
genericTypeHelperExample();
|
|
475
|
+
await typedNdjson();
|
|
476
|
+
typedTsv();
|
|
477
|
+
typedErrorHandling();
|
|
478
|
+
discriminatedUnionExample();
|
|
479
|
+
await typedStreaming();
|
|
480
|
+
builderPatternExample();
|
|
481
|
+
|
|
482
|
+
console.log('\n' + '='.repeat(60));
|
|
483
|
+
console.log('All TypeScript examples completed.');
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
main().catch(console.error);
|
package/index.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ declare module 'jtcsv' {
|
|
|
17
17
|
preventCsvInjection?: boolean;
|
|
18
18
|
/** Ensure RFC 4180 compliance (proper quoting, line endings) (default: true) */
|
|
19
19
|
rfc4180Compliant?: boolean;
|
|
20
|
+
/** JSON schema for data validation and formatting */
|
|
21
|
+
schema?: Record<string, any>;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
export interface SaveAsCsvOptions extends JsonToCsvOptions {
|