configforge 1.0.0-beta.5 → 1.0.0-beta.7
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 +342 -88
- package/dist/core/Converter.d.ts +3 -2
- package/dist/core/Converter.d.ts.map +1 -1
- package/dist/core/Converter.js +24 -9
- package/dist/core/Converter.js.map +1 -1
- package/dist/core/Mapper.d.ts +4 -0
- package/dist/core/Mapper.d.ts.map +1 -1
- package/dist/core/Mapper.js +64 -27
- package/dist/core/Mapper.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/validators/ValidatorRegistry.d.ts +55 -0
- package/dist/validators/ValidatorRegistry.d.ts.map +1 -0
- package/dist/validators/ValidatorRegistry.js +140 -0
- package/dist/validators/ValidatorRegistry.js.map +1 -0
- package/dist/validators/built-in.d.ts +93 -0
- package/dist/validators/built-in.d.ts.map +1 -0
- package/dist/validators/built-in.js +290 -0
- package/dist/validators/built-in.js.map +1 -0
- package/dist/validators/index.d.ts +4 -0
- package/dist/validators/index.d.ts.map +1 -0
- package/dist/validators/index.js +24 -0
- package/dist/validators/index.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -103,7 +103,26 @@ const converter = forge()
|
|
|
103
103
|
.end()
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
-
### 5.
|
|
106
|
+
### 5. Merge Multiple Fields
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
// Combine multiple source fields into one target field
|
|
110
|
+
.merge(['firstName', 'lastName'], 'fullName', (first, last) => `${first} ${last}`)
|
|
111
|
+
|
|
112
|
+
// Merge with transformation
|
|
113
|
+
.merge(['basePrice', 'tax'], 'totalPrice',
|
|
114
|
+
(price, tax) => price + tax,
|
|
115
|
+
total => `$${total.toFixed(2)}`
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
// Merge complex data
|
|
119
|
+
.merge(['user.profile', 'user.settings'], 'userInfo', (profile, settings) => ({
|
|
120
|
+
...profile,
|
|
121
|
+
...settings
|
|
122
|
+
}))
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 6. Set Default Values
|
|
107
126
|
|
|
108
127
|
```javascript
|
|
109
128
|
.defaults({
|
|
@@ -113,7 +132,34 @@ const converter = forge()
|
|
|
113
132
|
})
|
|
114
133
|
```
|
|
115
134
|
|
|
116
|
-
###
|
|
135
|
+
### 7. Add Validation
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
const { validators } = require('configforge');
|
|
139
|
+
|
|
140
|
+
// Add field validation
|
|
141
|
+
.validate('email', validators.email)
|
|
142
|
+
.validate('age', validators.all(
|
|
143
|
+
validators.required,
|
|
144
|
+
validators.type('number'),
|
|
145
|
+
validators.range(18, 120)
|
|
146
|
+
))
|
|
147
|
+
.validate('username', validators.all(
|
|
148
|
+
validators.required,
|
|
149
|
+
validators.minLength(3),
|
|
150
|
+
validators.pattern(/^[a-zA-Z0-9_]+$/, 'Only letters, numbers, and underscores allowed')
|
|
151
|
+
))
|
|
152
|
+
|
|
153
|
+
// Custom validation with context
|
|
154
|
+
.validate('password', (value, context) => {
|
|
155
|
+
if (context.source.confirmPassword !== value) {
|
|
156
|
+
return 'Passwords do not match';
|
|
157
|
+
}
|
|
158
|
+
return validators.minLength(8)(value, context);
|
|
159
|
+
})
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 8. Convert Data
|
|
117
163
|
|
|
118
164
|
```javascript
|
|
119
165
|
// Convert object data
|
|
@@ -247,69 +293,71 @@ console.log(result.data);
|
|
|
247
293
|
### Example 3: Array Processing with forEach()
|
|
248
294
|
|
|
249
295
|
```javascript
|
|
250
|
-
//
|
|
251
|
-
const
|
|
252
|
-
'
|
|
253
|
-
|
|
296
|
+
// Simple fruit inventory
|
|
297
|
+
const fruitData = {
|
|
298
|
+
storeId: 'STORE-001',
|
|
299
|
+
fruits: [
|
|
300
|
+
{
|
|
301
|
+
name: 'Apple',
|
|
302
|
+
color: 'red',
|
|
303
|
+
price: 1.5,
|
|
304
|
+
quantity: 100,
|
|
305
|
+
},
|
|
254
306
|
{
|
|
255
|
-
name: '
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
skintrait: { skinName: 'Nik_187t' },
|
|
260
|
-
commandtrait: { commands: [{ command: 'warp plots' }] },
|
|
261
|
-
},
|
|
307
|
+
name: 'Banana',
|
|
308
|
+
color: 'yellow',
|
|
309
|
+
price: 0.75,
|
|
310
|
+
quantity: 80,
|
|
262
311
|
},
|
|
263
312
|
{
|
|
264
|
-
name: '
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
skintrait: { skinName: 'FrostAMC' },
|
|
269
|
-
commandtrait: { commands: [{ command: 'knightrank' }] },
|
|
270
|
-
},
|
|
313
|
+
name: 'Orange',
|
|
314
|
+
color: 'orange',
|
|
315
|
+
price: 1.25,
|
|
316
|
+
quantity: 60,
|
|
271
317
|
},
|
|
272
318
|
],
|
|
273
319
|
};
|
|
274
320
|
|
|
275
321
|
const converter = forge()
|
|
276
|
-
.from('
|
|
277
|
-
.to('
|
|
278
|
-
.map('
|
|
279
|
-
.forEach('
|
|
322
|
+
.from('inventory')
|
|
323
|
+
.to('catalog')
|
|
324
|
+
.map('storeId', 'storeId')
|
|
325
|
+
.forEach('fruits', (fruit, index) => {
|
|
280
326
|
return {
|
|
281
|
-
id:
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
skin: npc.traits?.skintrait?.skinName,
|
|
289
|
-
commands:
|
|
290
|
-
npc.traits?.commandtrait?.commands?.map(cmd => cmd.command) || [],
|
|
327
|
+
id: index + 1,
|
|
328
|
+
fruitName: fruit.name,
|
|
329
|
+
displayColor: fruit.color,
|
|
330
|
+
pricePerItem: fruit.price,
|
|
331
|
+
inStock: fruit.quantity,
|
|
332
|
+
totalValue: fruit.price * fruit.quantity,
|
|
333
|
+
isExpensive: fruit.price > 1.0,
|
|
291
334
|
};
|
|
292
335
|
});
|
|
293
336
|
|
|
294
|
-
const result = await converter.convert(
|
|
337
|
+
const result = await converter.convert(fruitData);
|
|
295
338
|
console.log(result.data);
|
|
296
339
|
// {
|
|
297
|
-
//
|
|
298
|
-
//
|
|
340
|
+
// storeId: 'STORE-001',
|
|
341
|
+
// fruits: [
|
|
299
342
|
// {
|
|
300
|
-
// id:
|
|
301
|
-
//
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
//
|
|
343
|
+
// id: 1,
|
|
344
|
+
// fruitName: 'Apple',
|
|
345
|
+
// displayColor: 'red',
|
|
346
|
+
// pricePerItem: 1.50,
|
|
347
|
+
// inStock: 100,
|
|
348
|
+
// totalValue: 150,
|
|
349
|
+
// isExpensive: true
|
|
305
350
|
// },
|
|
306
351
|
// {
|
|
307
|
-
// id:
|
|
308
|
-
//
|
|
309
|
-
//
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
//
|
|
352
|
+
// id: 2,
|
|
353
|
+
// fruitName: 'Banana',
|
|
354
|
+
// displayColor: 'yellow',
|
|
355
|
+
// pricePerItem: 0.75,
|
|
356
|
+
// inStock: 80,
|
|
357
|
+
// totalValue: 60,
|
|
358
|
+
// isExpensive: false
|
|
359
|
+
// },
|
|
360
|
+
// // ... more fruits
|
|
313
361
|
// ]
|
|
314
362
|
// }
|
|
315
363
|
```
|
|
@@ -317,55 +365,255 @@ console.log(result.data);
|
|
|
317
365
|
### Example 4: Conditional Mapping with when()
|
|
318
366
|
|
|
319
367
|
```javascript
|
|
320
|
-
// Different
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
368
|
+
// Different types of pets need different mappings
|
|
369
|
+
const petData = {
|
|
370
|
+
type: 'dog',
|
|
371
|
+
name: 'Buddy',
|
|
372
|
+
age: 3,
|
|
373
|
+
dogInfo: {
|
|
374
|
+
breed: 'Golden Retriever',
|
|
375
|
+
tricks: ['sit', 'stay', 'fetch'],
|
|
376
|
+
},
|
|
377
|
+
catInfo: {
|
|
378
|
+
breed: 'Persian',
|
|
379
|
+
indoor: true,
|
|
380
|
+
},
|
|
381
|
+
birdInfo: {
|
|
382
|
+
species: 'Parrot',
|
|
383
|
+
canTalk: true,
|
|
331
384
|
},
|
|
332
385
|
};
|
|
333
386
|
|
|
334
387
|
const converter = forge()
|
|
335
|
-
.from('
|
|
336
|
-
.to('
|
|
337
|
-
.map('
|
|
338
|
-
.map('
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
.
|
|
343
|
-
.map('
|
|
388
|
+
.from('petData')
|
|
389
|
+
.to('petProfile')
|
|
390
|
+
.map('name', 'petName')
|
|
391
|
+
.map('age', 'petAge')
|
|
392
|
+
.map('type', 'animalType')
|
|
393
|
+
|
|
394
|
+
// Only map dog-specific info for dogs
|
|
395
|
+
.when(source => source.type === 'dog')
|
|
396
|
+
.map('dogInfo.breed', 'breed')
|
|
397
|
+
.map('dogInfo.tricks', 'knownTricks')
|
|
344
398
|
.end()
|
|
345
399
|
|
|
346
|
-
// Only map
|
|
347
|
-
.when(source => source.
|
|
348
|
-
.map('
|
|
349
|
-
.map('
|
|
400
|
+
// Only map cat-specific info for cats
|
|
401
|
+
.when(source => source.type === 'cat')
|
|
402
|
+
.map('catInfo.breed', 'breed')
|
|
403
|
+
.map('catInfo.indoor', 'isIndoorCat')
|
|
350
404
|
.end()
|
|
351
405
|
|
|
352
|
-
//
|
|
353
|
-
.when(
|
|
354
|
-
.map('
|
|
406
|
+
// Only map bird-specific info for birds
|
|
407
|
+
.when(source => source.type === 'bird')
|
|
408
|
+
.map('birdInfo.species', 'species')
|
|
409
|
+
.map('birdInfo.canTalk', 'canSpeak')
|
|
355
410
|
.end();
|
|
356
411
|
|
|
357
|
-
const result = await converter.convert(
|
|
412
|
+
const result = await converter.convert(petData);
|
|
358
413
|
console.log(result.data);
|
|
359
414
|
// {
|
|
360
|
-
//
|
|
361
|
-
//
|
|
362
|
-
//
|
|
363
|
-
//
|
|
364
|
-
//
|
|
415
|
+
// petName: 'Buddy',
|
|
416
|
+
// petAge: 3,
|
|
417
|
+
// animalType: 'dog',
|
|
418
|
+
// breed: 'Golden Retriever',
|
|
419
|
+
// knownTricks: ['sit', 'stay', 'fetch']
|
|
365
420
|
// }
|
|
366
421
|
```
|
|
367
422
|
|
|
368
|
-
### Example 5:
|
|
423
|
+
### Example 5: Merge Multiple Fields with merge()
|
|
424
|
+
|
|
425
|
+
```javascript
|
|
426
|
+
// Simple student report card
|
|
427
|
+
const studentData = {
|
|
428
|
+
student: {
|
|
429
|
+
firstName: 'Alice',
|
|
430
|
+
lastName: 'Smith',
|
|
431
|
+
},
|
|
432
|
+
grades: {
|
|
433
|
+
math: 85,
|
|
434
|
+
english: 92,
|
|
435
|
+
science: 78,
|
|
436
|
+
},
|
|
437
|
+
activities: ['soccer', 'chess', 'art'],
|
|
438
|
+
info: {
|
|
439
|
+
grade: '5th',
|
|
440
|
+
teacher: 'Ms. Johnson',
|
|
441
|
+
},
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
const converter = forge()
|
|
445
|
+
.from('report')
|
|
446
|
+
.to('summary')
|
|
447
|
+
|
|
448
|
+
// Merge student name fields
|
|
449
|
+
.merge(
|
|
450
|
+
['student.firstName', 'student.lastName'],
|
|
451
|
+
'studentName',
|
|
452
|
+
(first, last) => `${first} ${last}`
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
// Calculate total with transformation
|
|
456
|
+
.merge(
|
|
457
|
+
['order.basePrice', 'order.tax', 'order.shipping'],
|
|
458
|
+
'subtotal',
|
|
459
|
+
(base, tax, shipping) => base + tax + shipping
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
.merge(
|
|
463
|
+
['order.basePrice', 'order.tax', 'order.shipping', 'order.discount'],
|
|
464
|
+
'total',
|
|
465
|
+
(base, tax, shipping, discount) => base + tax + shipping - discount,
|
|
466
|
+
total => `$${total.toFixed(2)}`
|
|
467
|
+
) // Transform to currency format
|
|
468
|
+
|
|
469
|
+
// Merge arrays and objects
|
|
470
|
+
.merge(['items', 'metadata'], 'orderSummary', (items, meta) => ({
|
|
471
|
+
itemCount: items.length,
|
|
472
|
+
items: items.join(', '),
|
|
473
|
+
...meta,
|
|
474
|
+
}))
|
|
475
|
+
|
|
476
|
+
// Simple field mappings
|
|
477
|
+
.map('customer.email', 'billingEmail');
|
|
478
|
+
|
|
479
|
+
const result = await converter.convert(orderData);
|
|
480
|
+
console.log(result.data);
|
|
481
|
+
// {
|
|
482
|
+
// customerName: 'John Doe',
|
|
483
|
+
// subtotal: 121.49,
|
|
484
|
+
// total: '$106.49',
|
|
485
|
+
// orderSummary: {
|
|
486
|
+
// itemCount: 3,
|
|
487
|
+
// items: 'laptop, mouse, keyboard',
|
|
488
|
+
// orderDate: '2023-12-01',
|
|
489
|
+
// source: 'web'
|
|
490
|
+
// },
|
|
491
|
+
// billingEmail: 'john.doe@example.com'
|
|
492
|
+
// }
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### Example 6: Data Validation
|
|
496
|
+
|
|
497
|
+
```javascript
|
|
498
|
+
const { forge, validators } = require('configforge');
|
|
499
|
+
|
|
500
|
+
// User registration form data
|
|
501
|
+
const formData = {
|
|
502
|
+
personalInfo: {
|
|
503
|
+
firstName: 'John',
|
|
504
|
+
lastName: 'Doe',
|
|
505
|
+
email: 'john.doe@example.com',
|
|
506
|
+
age: 28,
|
|
507
|
+
},
|
|
508
|
+
accountInfo: {
|
|
509
|
+
username: 'johndoe123',
|
|
510
|
+
password: 'SecurePass123!',
|
|
511
|
+
confirmPassword: 'SecurePass123!',
|
|
512
|
+
},
|
|
513
|
+
preferences: {
|
|
514
|
+
newsletter: 'yes',
|
|
515
|
+
theme: 'dark',
|
|
516
|
+
language: 'en',
|
|
517
|
+
},
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
const converter = forge()
|
|
521
|
+
.from('form')
|
|
522
|
+
.to('user')
|
|
523
|
+
// Map fields
|
|
524
|
+
.merge(
|
|
525
|
+
['personalInfo.firstName', 'personalInfo.lastName'],
|
|
526
|
+
'fullName',
|
|
527
|
+
(first, last) => `${first} ${last}`
|
|
528
|
+
)
|
|
529
|
+
.map('personalInfo.email', 'email')
|
|
530
|
+
.map('personalInfo.age', 'age')
|
|
531
|
+
.map('accountInfo.username', 'username')
|
|
532
|
+
.map('accountInfo.password', 'password')
|
|
533
|
+
.map(
|
|
534
|
+
'preferences.newsletter',
|
|
535
|
+
'subscribeToNewsletter',
|
|
536
|
+
value => value === 'yes'
|
|
537
|
+
)
|
|
538
|
+
.map('preferences.theme', 'theme')
|
|
539
|
+
.map('preferences.language', 'language')
|
|
540
|
+
|
|
541
|
+
// Add validation rules
|
|
542
|
+
.validate(
|
|
543
|
+
'fullName',
|
|
544
|
+
validators.all(
|
|
545
|
+
validators.required,
|
|
546
|
+
validators.minLength(2),
|
|
547
|
+
validators.maxLength(100)
|
|
548
|
+
)
|
|
549
|
+
)
|
|
550
|
+
.validate('email', validators.all(validators.required, validators.email))
|
|
551
|
+
.validate(
|
|
552
|
+
'age',
|
|
553
|
+
validators.all(
|
|
554
|
+
validators.required,
|
|
555
|
+
validators.type('number'),
|
|
556
|
+
validators.range(13, 120)
|
|
557
|
+
)
|
|
558
|
+
)
|
|
559
|
+
.validate(
|
|
560
|
+
'username',
|
|
561
|
+
validators.all(
|
|
562
|
+
validators.required,
|
|
563
|
+
validators.minLength(3),
|
|
564
|
+
validators.maxLength(20),
|
|
565
|
+
validators.pattern(
|
|
566
|
+
/^[a-zA-Z0-9_]+$/,
|
|
567
|
+
'Username can only contain letters, numbers, and underscores'
|
|
568
|
+
)
|
|
569
|
+
)
|
|
570
|
+
)
|
|
571
|
+
.validate(
|
|
572
|
+
'password',
|
|
573
|
+
validators.all(
|
|
574
|
+
validators.required,
|
|
575
|
+
validators.minLength(8),
|
|
576
|
+
validators.pattern(
|
|
577
|
+
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/,
|
|
578
|
+
'Password must contain lowercase, uppercase, and number'
|
|
579
|
+
)
|
|
580
|
+
)
|
|
581
|
+
)
|
|
582
|
+
.validate('theme', validators.oneOf(['light', 'dark', 'auto']))
|
|
583
|
+
.validate('language', validators.oneOf(['en', 'es', 'fr', 'de']))
|
|
584
|
+
|
|
585
|
+
// Custom validation
|
|
586
|
+
.validate('password', (value, context) => {
|
|
587
|
+
if (context.source.accountInfo.confirmPassword !== value) {
|
|
588
|
+
return 'Passwords do not match';
|
|
589
|
+
}
|
|
590
|
+
return true;
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
const result = await converter.convert(formData);
|
|
594
|
+
|
|
595
|
+
if (result.errors.length > 0) {
|
|
596
|
+
console.log('Validation errors:');
|
|
597
|
+
result.errors.forEach(error => {
|
|
598
|
+
console.log(`- ${error.path}: ${error.message}`);
|
|
599
|
+
});
|
|
600
|
+
} else {
|
|
601
|
+
console.log('User data is valid!');
|
|
602
|
+
console.log(result.data);
|
|
603
|
+
// {
|
|
604
|
+
// fullName: 'John Doe',
|
|
605
|
+
// email: 'john.doe@example.com',
|
|
606
|
+
// age: 28,
|
|
607
|
+
// username: 'johndoe123',
|
|
608
|
+
// password: 'SecurePass123!',
|
|
609
|
+
// subscribeToNewsletter: true,
|
|
610
|
+
// theme: 'dark',
|
|
611
|
+
// language: 'en'
|
|
612
|
+
// }
|
|
613
|
+
}
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
### Example 7: File Conversion
|
|
369
617
|
|
|
370
618
|
```javascript
|
|
371
619
|
// Convert YAML file to JSON structure
|
|
@@ -391,6 +639,7 @@ await result.save('./config.json');
|
|
|
391
639
|
- ✅ **Array access**: Use bracket notation like `items[0]`
|
|
392
640
|
- ✅ **Transformations**: Transform values during mapping
|
|
393
641
|
- ✅ **Conditional mapping**: Use `when()` for conditional logic
|
|
642
|
+
- ✅ **Multi-field merging**: Use `merge()` to combine multiple sources into one target
|
|
394
643
|
- ✅ **Default values**: Set fallback values
|
|
395
644
|
- ✅ **File support**: Convert YAML, JSON files directly
|
|
396
645
|
- ✅ **Statistics**: Get detailed conversion reports
|
|
@@ -405,14 +654,15 @@ await result.save('./config.json');
|
|
|
405
654
|
- Value transformations
|
|
406
655
|
- Default values with `defaults()`
|
|
407
656
|
- **Array/object processing with `forEach()`**
|
|
408
|
-
- **Conditional mapping with `when()`**
|
|
657
|
+
- **Conditional mapping with `when()`**
|
|
658
|
+
- **Multi-field merging with `merge()`**
|
|
659
|
+
- **Field validation with `validate()`** ⭐ NEW!
|
|
409
660
|
- File parsing (YAML, JSON)
|
|
410
661
|
- Conversion statistics and reporting
|
|
411
662
|
- Before/after hooks
|
|
412
663
|
|
|
413
664
|
**🚧 Coming Soon:**
|
|
414
665
|
|
|
415
|
-
- Field validation
|
|
416
666
|
- CLI generation
|
|
417
667
|
- Plugin system
|
|
418
668
|
|
|
@@ -422,10 +672,14 @@ await result.save('./config.json');
|
|
|
422
672
|
2. **Use dot notation** for nested objects: `'user.profile.name'`
|
|
423
673
|
3. **Use bracket notation** for arrays: `'items[0]'`, `'items[1]'`
|
|
424
674
|
4. **Transform functions** get the value and context: `(value, ctx) => { ... }`
|
|
425
|
-
5. **Use conditional mapping** with `when()` for type-specific logic: `when(source => source.
|
|
426
|
-
6. **
|
|
427
|
-
7. **
|
|
428
|
-
8. **
|
|
675
|
+
5. **Use conditional mapping** with `when()` for type-specific logic: `when(source => source.accountType === 'premium')`
|
|
676
|
+
6. **Use merge()** to combine multiple fields: `merge(['field1', 'field2'], 'result', (a, b) => a + b)`
|
|
677
|
+
7. **Use validation** to ensure data quality: `validate('email', validators.email)`
|
|
678
|
+
8. **Combine validators** with `validators.all()` for multiple rules
|
|
679
|
+
9. **Always call `.end()`** after conditional mappings to return to the main converter
|
|
680
|
+
10. **Check `result.errors`** to see validation failures
|
|
681
|
+
11. **Check `result.unmapped`** to see which fields weren't mapped
|
|
682
|
+
12. **Use `result.print()`** for a nice conversion report
|
|
429
683
|
|
|
430
684
|
---
|
|
431
685
|
|
package/dist/core/Converter.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export declare class Converter<TSource = any, TTarget = any> {
|
|
|
10
10
|
private validators;
|
|
11
11
|
private hooks;
|
|
12
12
|
private options;
|
|
13
|
+
private sourceData?;
|
|
13
14
|
constructor(options?: ForgeOptions);
|
|
14
15
|
/**
|
|
15
16
|
* Define the source schema
|
|
@@ -48,9 +49,9 @@ export declare class Converter<TSource = any, TTarget = any> {
|
|
|
48
49
|
*/
|
|
49
50
|
forEach(source: string, mapFn: ForEachFn): this;
|
|
50
51
|
/**
|
|
51
|
-
* Merge multiple sources
|
|
52
|
+
* Merge multiple sources into a single target field
|
|
52
53
|
*/
|
|
53
|
-
merge(
|
|
54
|
+
merge(sources: string[], target: string, mergeFn: MergeFn, transform?: TransformFn): this;
|
|
54
55
|
/**
|
|
55
56
|
* Convert input data - supports objects, file paths, or arrays of file paths
|
|
56
57
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Converter.d.ts","sourceRoot":"","sources":["../../src/core/Converter.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,YAAY,EACZ,OAAO,EACP,WAAW,EACX,WAAW,EACX,MAAM,EACN,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,OAAO,EACP,SAAS,
|
|
1
|
+
{"version":3,"file":"Converter.d.ts","sourceRoot":"","sources":["../../src/core/Converter.ts"],"names":[],"mappings":"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"}
|
package/dist/core/Converter.js
CHANGED
|
@@ -126,11 +126,22 @@ class Converter {
|
|
|
126
126
|
return this;
|
|
127
127
|
}
|
|
128
128
|
/**
|
|
129
|
-
* Merge multiple sources
|
|
129
|
+
* Merge multiple sources into a single target field
|
|
130
130
|
*/
|
|
131
|
-
merge(
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
merge(sources, target, mergeFn, transform) {
|
|
132
|
+
const mapping = {
|
|
133
|
+
id: (0, uuid_1.v4)(),
|
|
134
|
+
source: sources.join(','), // Store sources as comma-separated string for identification
|
|
135
|
+
target,
|
|
136
|
+
type: 'merge',
|
|
137
|
+
...(transform && { transform }),
|
|
138
|
+
metadata: {
|
|
139
|
+
sources,
|
|
140
|
+
mergeFn,
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
this.mappings.push(mapping);
|
|
144
|
+
return this;
|
|
134
145
|
}
|
|
135
146
|
/**
|
|
136
147
|
* Convert input data - supports objects, file paths, or arrays of file paths
|
|
@@ -218,6 +229,8 @@ class Converter {
|
|
|
218
229
|
executeConversion(sourceData, startTime) {
|
|
219
230
|
const warnings = [];
|
|
220
231
|
const errors = [];
|
|
232
|
+
// Store source data for validation context
|
|
233
|
+
this.sourceData = sourceData;
|
|
221
234
|
// Run before hooks
|
|
222
235
|
let processedSource = sourceData;
|
|
223
236
|
for (const hook of this.hooks.before) {
|
|
@@ -307,20 +320,20 @@ class Converter {
|
|
|
307
320
|
try {
|
|
308
321
|
const value = this.getPath(targetData, field);
|
|
309
322
|
const result = validator(value, {
|
|
310
|
-
source: {},
|
|
323
|
+
source: this.sourceData || {}, // Store source data for validation context
|
|
311
324
|
target: targetData,
|
|
312
325
|
path: field,
|
|
313
326
|
converter: this,
|
|
314
|
-
allErrors: errors,
|
|
327
|
+
allErrors: errors.filter(e => e instanceof types_1.ValidationError),
|
|
315
328
|
});
|
|
316
329
|
if (result !== true) {
|
|
317
|
-
errors.push(new types_1.
|
|
330
|
+
errors.push(new types_1.ValidationError(typeof result === 'string'
|
|
318
331
|
? result
|
|
319
332
|
: `Validation failed for field: ${field}`, { path: field }));
|
|
320
333
|
}
|
|
321
334
|
}
|
|
322
335
|
catch (error) {
|
|
323
|
-
errors.push(new types_1.
|
|
336
|
+
errors.push(new types_1.ValidationError(`Validation error for field ${field}: ${error instanceof Error ? error.message : 'Unknown error'}`, { path: field }));
|
|
324
337
|
}
|
|
325
338
|
}
|
|
326
339
|
}
|
|
@@ -472,8 +485,10 @@ class ConditionalMapper {
|
|
|
472
485
|
source,
|
|
473
486
|
target,
|
|
474
487
|
type: 'conditional',
|
|
475
|
-
condition: this.condition,
|
|
476
488
|
...(transform && { transform }),
|
|
489
|
+
metadata: {
|
|
490
|
+
condition: this.condition,
|
|
491
|
+
},
|
|
477
492
|
};
|
|
478
493
|
// Add the conditional mapping to the converter
|
|
479
494
|
this.converter.addMapping(mapping);
|