configforge 1.0.0-beta.6 → 1.0.0-beta.8
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 +424 -109
- package/dist/core/Converter.d.ts +5 -0
- package/dist/core/Converter.d.ts.map +1 -1
- package/dist/core/Converter.js +98 -10
- package/dist/core/Converter.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/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
|
@@ -125,14 +125,86 @@ const converter = forge()
|
|
|
125
125
|
### 6. Set Default Values
|
|
126
126
|
|
|
127
127
|
```javascript
|
|
128
|
+
// Set static default values
|
|
128
129
|
.defaults({
|
|
129
130
|
version: '1.0.0',
|
|
130
131
|
enabled: true,
|
|
131
132
|
environment: 'production'
|
|
132
133
|
})
|
|
134
|
+
|
|
135
|
+
// Set dynamic default values using functions
|
|
136
|
+
.defaults({
|
|
137
|
+
timestamp: () => new Date().toISOString(),
|
|
138
|
+
id: () => `user_${Date.now()}`,
|
|
139
|
+
sessionId: () => Math.random().toString(36).substr(2, 9)
|
|
140
|
+
})
|
|
133
141
|
```
|
|
134
142
|
|
|
135
|
-
### 7.
|
|
143
|
+
### 7. Add Lifecycle Hooks
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
// Before hooks - run before conversion starts
|
|
147
|
+
.before((sourceData) => {
|
|
148
|
+
console.log('Starting conversion...');
|
|
149
|
+
// Modify source data if needed
|
|
150
|
+
return {
|
|
151
|
+
...sourceData,
|
|
152
|
+
processed: true
|
|
153
|
+
};
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
// After hooks - run after conversion completes
|
|
157
|
+
.after((targetData) => {
|
|
158
|
+
console.log('Conversion completed!');
|
|
159
|
+
// Add computed fields or modify target data
|
|
160
|
+
return {
|
|
161
|
+
...targetData,
|
|
162
|
+
processedAt: new Date().toISOString(),
|
|
163
|
+
checksum: calculateChecksum(targetData)
|
|
164
|
+
};
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
// Multiple hooks execute in order
|
|
168
|
+
.before(validateInput)
|
|
169
|
+
.before(preprocessData)
|
|
170
|
+
.after(addMetadata)
|
|
171
|
+
.after(logResults)
|
|
172
|
+
|
|
173
|
+
// Async hooks are supported
|
|
174
|
+
.before(async (data) => {
|
|
175
|
+
const enrichedData = await fetchAdditionalData(data.userId);
|
|
176
|
+
return { ...data, ...enrichedData };
|
|
177
|
+
})
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### 8. Add Validation
|
|
181
|
+
|
|
182
|
+
```javascript
|
|
183
|
+
const { validators } = require('configforge');
|
|
184
|
+
|
|
185
|
+
// Add field validation
|
|
186
|
+
.validate('email', validators.email)
|
|
187
|
+
.validate('age', validators.all(
|
|
188
|
+
validators.required,
|
|
189
|
+
validators.type('number'),
|
|
190
|
+
validators.range(18, 120)
|
|
191
|
+
))
|
|
192
|
+
.validate('username', validators.all(
|
|
193
|
+
validators.required,
|
|
194
|
+
validators.minLength(3),
|
|
195
|
+
validators.pattern(/^[a-zA-Z0-9_]+$/, 'Only letters, numbers, and underscores allowed')
|
|
196
|
+
))
|
|
197
|
+
|
|
198
|
+
// Custom validation with context
|
|
199
|
+
.validate('password', (value, context) => {
|
|
200
|
+
if (context.source.confirmPassword !== value) {
|
|
201
|
+
return 'Passwords do not match';
|
|
202
|
+
}
|
|
203
|
+
return validators.minLength(8)(value, context);
|
|
204
|
+
})
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 9. Convert Data
|
|
136
208
|
|
|
137
209
|
```javascript
|
|
138
210
|
// Convert object data
|
|
@@ -266,69 +338,71 @@ console.log(result.data);
|
|
|
266
338
|
### Example 3: Array Processing with forEach()
|
|
267
339
|
|
|
268
340
|
```javascript
|
|
269
|
-
//
|
|
270
|
-
const
|
|
271
|
-
'
|
|
272
|
-
|
|
341
|
+
// Simple fruit inventory
|
|
342
|
+
const fruitData = {
|
|
343
|
+
storeId: 'STORE-001',
|
|
344
|
+
fruits: [
|
|
345
|
+
{
|
|
346
|
+
name: 'Apple',
|
|
347
|
+
color: 'red',
|
|
348
|
+
price: 1.5,
|
|
349
|
+
quantity: 100,
|
|
350
|
+
},
|
|
273
351
|
{
|
|
274
|
-
name: '
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
skintrait: { skinName: 'Nik_187t' },
|
|
279
|
-
commandtrait: { commands: [{ command: 'warp plots' }] },
|
|
280
|
-
},
|
|
352
|
+
name: 'Banana',
|
|
353
|
+
color: 'yellow',
|
|
354
|
+
price: 0.75,
|
|
355
|
+
quantity: 80,
|
|
281
356
|
},
|
|
282
357
|
{
|
|
283
|
-
name: '
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
skintrait: { skinName: 'FrostAMC' },
|
|
288
|
-
commandtrait: { commands: [{ command: 'knightrank' }] },
|
|
289
|
-
},
|
|
358
|
+
name: 'Orange',
|
|
359
|
+
color: 'orange',
|
|
360
|
+
price: 1.25,
|
|
361
|
+
quantity: 60,
|
|
290
362
|
},
|
|
291
363
|
],
|
|
292
364
|
};
|
|
293
365
|
|
|
294
366
|
const converter = forge()
|
|
295
|
-
.from('
|
|
296
|
-
.to('
|
|
297
|
-
.map('
|
|
298
|
-
.forEach('
|
|
367
|
+
.from('inventory')
|
|
368
|
+
.to('catalog')
|
|
369
|
+
.map('storeId', 'storeId')
|
|
370
|
+
.forEach('fruits', (fruit, index) => {
|
|
299
371
|
return {
|
|
300
|
-
id:
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
skin: npc.traits?.skintrait?.skinName,
|
|
308
|
-
commands:
|
|
309
|
-
npc.traits?.commandtrait?.commands?.map(cmd => cmd.command) || [],
|
|
372
|
+
id: index + 1,
|
|
373
|
+
fruitName: fruit.name,
|
|
374
|
+
displayColor: fruit.color,
|
|
375
|
+
pricePerItem: fruit.price,
|
|
376
|
+
inStock: fruit.quantity,
|
|
377
|
+
totalValue: fruit.price * fruit.quantity,
|
|
378
|
+
isExpensive: fruit.price > 1.0,
|
|
310
379
|
};
|
|
311
380
|
});
|
|
312
381
|
|
|
313
|
-
const result = await converter.convert(
|
|
382
|
+
const result = await converter.convert(fruitData);
|
|
314
383
|
console.log(result.data);
|
|
315
384
|
// {
|
|
316
|
-
//
|
|
317
|
-
//
|
|
385
|
+
// storeId: 'STORE-001',
|
|
386
|
+
// fruits: [
|
|
318
387
|
// {
|
|
319
|
-
// id:
|
|
320
|
-
//
|
|
321
|
-
//
|
|
322
|
-
//
|
|
323
|
-
//
|
|
388
|
+
// id: 1,
|
|
389
|
+
// fruitName: 'Apple',
|
|
390
|
+
// displayColor: 'red',
|
|
391
|
+
// pricePerItem: 1.50,
|
|
392
|
+
// inStock: 100,
|
|
393
|
+
// totalValue: 150,
|
|
394
|
+
// isExpensive: true
|
|
324
395
|
// },
|
|
325
396
|
// {
|
|
326
|
-
// id:
|
|
327
|
-
//
|
|
328
|
-
//
|
|
329
|
-
//
|
|
330
|
-
//
|
|
331
|
-
//
|
|
397
|
+
// id: 2,
|
|
398
|
+
// fruitName: 'Banana',
|
|
399
|
+
// displayColor: 'yellow',
|
|
400
|
+
// pricePerItem: 0.75,
|
|
401
|
+
// inStock: 80,
|
|
402
|
+
// totalValue: 60,
|
|
403
|
+
// isExpensive: false
|
|
404
|
+
// },
|
|
405
|
+
// // ... more fruits
|
|
332
406
|
// ]
|
|
333
407
|
// }
|
|
334
408
|
```
|
|
@@ -336,85 +410,90 @@ console.log(result.data);
|
|
|
336
410
|
### Example 4: Conditional Mapping with when()
|
|
337
411
|
|
|
338
412
|
```javascript
|
|
339
|
-
// Different
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
413
|
+
// Different types of pets need different mappings
|
|
414
|
+
const petData = {
|
|
415
|
+
type: 'dog',
|
|
416
|
+
name: 'Buddy',
|
|
417
|
+
age: 3,
|
|
418
|
+
dogInfo: {
|
|
419
|
+
breed: 'Golden Retriever',
|
|
420
|
+
tricks: ['sit', 'stay', 'fetch'],
|
|
421
|
+
},
|
|
422
|
+
catInfo: {
|
|
423
|
+
breed: 'Persian',
|
|
424
|
+
indoor: true,
|
|
425
|
+
},
|
|
426
|
+
birdInfo: {
|
|
427
|
+
species: 'Parrot',
|
|
428
|
+
canTalk: true,
|
|
350
429
|
},
|
|
351
430
|
};
|
|
352
431
|
|
|
353
432
|
const converter = forge()
|
|
354
|
-
.from('
|
|
355
|
-
.to('
|
|
356
|
-
.map('
|
|
357
|
-
.map('
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
.
|
|
362
|
-
.map('
|
|
433
|
+
.from('petData')
|
|
434
|
+
.to('petProfile')
|
|
435
|
+
.map('name', 'petName')
|
|
436
|
+
.map('age', 'petAge')
|
|
437
|
+
.map('type', 'animalType')
|
|
438
|
+
|
|
439
|
+
// Only map dog-specific info for dogs
|
|
440
|
+
.when(source => source.type === 'dog')
|
|
441
|
+
.map('dogInfo.breed', 'breed')
|
|
442
|
+
.map('dogInfo.tricks', 'knownTricks')
|
|
363
443
|
.end()
|
|
364
444
|
|
|
365
|
-
// Only map
|
|
366
|
-
.when(source => source.
|
|
367
|
-
.map('
|
|
368
|
-
.map('
|
|
445
|
+
// Only map cat-specific info for cats
|
|
446
|
+
.when(source => source.type === 'cat')
|
|
447
|
+
.map('catInfo.breed', 'breed')
|
|
448
|
+
.map('catInfo.indoor', 'isIndoorCat')
|
|
369
449
|
.end()
|
|
370
450
|
|
|
371
|
-
//
|
|
372
|
-
.when(
|
|
373
|
-
.map('
|
|
451
|
+
// Only map bird-specific info for birds
|
|
452
|
+
.when(source => source.type === 'bird')
|
|
453
|
+
.map('birdInfo.species', 'species')
|
|
454
|
+
.map('birdInfo.canTalk', 'canSpeak')
|
|
374
455
|
.end();
|
|
375
456
|
|
|
376
|
-
const result = await converter.convert(
|
|
457
|
+
const result = await converter.convert(petData);
|
|
377
458
|
console.log(result.data);
|
|
378
459
|
// {
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
//
|
|
383
|
-
//
|
|
460
|
+
// petName: 'Buddy',
|
|
461
|
+
// petAge: 3,
|
|
462
|
+
// animalType: 'dog',
|
|
463
|
+
// breed: 'Golden Retriever',
|
|
464
|
+
// knownTricks: ['sit', 'stay', 'fetch']
|
|
384
465
|
// }
|
|
385
466
|
```
|
|
386
467
|
|
|
387
468
|
### Example 5: Merge Multiple Fields with merge()
|
|
388
469
|
|
|
389
470
|
```javascript
|
|
390
|
-
//
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
firstName: '
|
|
394
|
-
lastName: '
|
|
395
|
-
email: 'john.doe@example.com',
|
|
471
|
+
// Simple student report card
|
|
472
|
+
const studentData = {
|
|
473
|
+
student: {
|
|
474
|
+
firstName: 'Alice',
|
|
475
|
+
lastName: 'Smith',
|
|
396
476
|
},
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
discount: 15.0,
|
|
477
|
+
grades: {
|
|
478
|
+
math: 85,
|
|
479
|
+
english: 92,
|
|
480
|
+
science: 78,
|
|
402
481
|
},
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
482
|
+
activities: ['soccer', 'chess', 'art'],
|
|
483
|
+
info: {
|
|
484
|
+
grade: '5th',
|
|
485
|
+
teacher: 'Ms. Johnson',
|
|
407
486
|
},
|
|
408
487
|
};
|
|
409
488
|
|
|
410
489
|
const converter = forge()
|
|
411
|
-
.from('
|
|
412
|
-
.to('
|
|
490
|
+
.from('report')
|
|
491
|
+
.to('summary')
|
|
413
492
|
|
|
414
|
-
// Merge
|
|
493
|
+
// Merge student name fields
|
|
415
494
|
.merge(
|
|
416
|
-
['
|
|
417
|
-
'
|
|
495
|
+
['student.firstName', 'student.lastName'],
|
|
496
|
+
'studentName',
|
|
418
497
|
(first, last) => `${first} ${last}`
|
|
419
498
|
)
|
|
420
499
|
|
|
@@ -458,7 +537,233 @@ console.log(result.data);
|
|
|
458
537
|
// }
|
|
459
538
|
```
|
|
460
539
|
|
|
461
|
-
### Example 6:
|
|
540
|
+
### Example 6: Data Validation
|
|
541
|
+
|
|
542
|
+
```javascript
|
|
543
|
+
const { forge, validators } = require('configforge');
|
|
544
|
+
|
|
545
|
+
// User registration form data
|
|
546
|
+
const formData = {
|
|
547
|
+
personalInfo: {
|
|
548
|
+
firstName: 'John',
|
|
549
|
+
lastName: 'Doe',
|
|
550
|
+
email: 'john.doe@example.com',
|
|
551
|
+
age: 28,
|
|
552
|
+
},
|
|
553
|
+
accountInfo: {
|
|
554
|
+
username: 'johndoe123',
|
|
555
|
+
password: 'SecurePass123!',
|
|
556
|
+
confirmPassword: 'SecurePass123!',
|
|
557
|
+
},
|
|
558
|
+
preferences: {
|
|
559
|
+
newsletter: 'yes',
|
|
560
|
+
theme: 'dark',
|
|
561
|
+
language: 'en',
|
|
562
|
+
},
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
const converter = forge()
|
|
566
|
+
.from('form')
|
|
567
|
+
.to('user')
|
|
568
|
+
// Map fields
|
|
569
|
+
.merge(
|
|
570
|
+
['personalInfo.firstName', 'personalInfo.lastName'],
|
|
571
|
+
'fullName',
|
|
572
|
+
(first, last) => `${first} ${last}`
|
|
573
|
+
)
|
|
574
|
+
.map('personalInfo.email', 'email')
|
|
575
|
+
.map('personalInfo.age', 'age')
|
|
576
|
+
.map('accountInfo.username', 'username')
|
|
577
|
+
.map('accountInfo.password', 'password')
|
|
578
|
+
.map(
|
|
579
|
+
'preferences.newsletter',
|
|
580
|
+
'subscribeToNewsletter',
|
|
581
|
+
value => value === 'yes'
|
|
582
|
+
)
|
|
583
|
+
.map('preferences.theme', 'theme')
|
|
584
|
+
.map('preferences.language', 'language')
|
|
585
|
+
|
|
586
|
+
// Add validation rules
|
|
587
|
+
.validate(
|
|
588
|
+
'fullName',
|
|
589
|
+
validators.all(
|
|
590
|
+
validators.required,
|
|
591
|
+
validators.minLength(2),
|
|
592
|
+
validators.maxLength(100)
|
|
593
|
+
)
|
|
594
|
+
)
|
|
595
|
+
.validate('email', validators.all(validators.required, validators.email))
|
|
596
|
+
.validate(
|
|
597
|
+
'age',
|
|
598
|
+
validators.all(
|
|
599
|
+
validators.required,
|
|
600
|
+
validators.type('number'),
|
|
601
|
+
validators.range(13, 120)
|
|
602
|
+
)
|
|
603
|
+
)
|
|
604
|
+
.validate(
|
|
605
|
+
'username',
|
|
606
|
+
validators.all(
|
|
607
|
+
validators.required,
|
|
608
|
+
validators.minLength(3),
|
|
609
|
+
validators.maxLength(20),
|
|
610
|
+
validators.pattern(
|
|
611
|
+
/^[a-zA-Z0-9_]+$/,
|
|
612
|
+
'Username can only contain letters, numbers, and underscores'
|
|
613
|
+
)
|
|
614
|
+
)
|
|
615
|
+
)
|
|
616
|
+
.validate(
|
|
617
|
+
'password',
|
|
618
|
+
validators.all(
|
|
619
|
+
validators.required,
|
|
620
|
+
validators.minLength(8),
|
|
621
|
+
validators.pattern(
|
|
622
|
+
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/,
|
|
623
|
+
'Password must contain lowercase, uppercase, and number'
|
|
624
|
+
)
|
|
625
|
+
)
|
|
626
|
+
)
|
|
627
|
+
.validate('theme', validators.oneOf(['light', 'dark', 'auto']))
|
|
628
|
+
.validate('language', validators.oneOf(['en', 'es', 'fr', 'de']))
|
|
629
|
+
|
|
630
|
+
// Custom validation
|
|
631
|
+
.validate('password', (value, context) => {
|
|
632
|
+
if (context.source.accountInfo.confirmPassword !== value) {
|
|
633
|
+
return 'Passwords do not match';
|
|
634
|
+
}
|
|
635
|
+
return true;
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
const result = await converter.convert(formData);
|
|
639
|
+
|
|
640
|
+
if (result.errors.length > 0) {
|
|
641
|
+
console.log('Validation errors:');
|
|
642
|
+
result.errors.forEach(error => {
|
|
643
|
+
console.log(`- ${error.path}: ${error.message}`);
|
|
644
|
+
});
|
|
645
|
+
} else {
|
|
646
|
+
console.log('User data is valid!');
|
|
647
|
+
console.log(result.data);
|
|
648
|
+
// {
|
|
649
|
+
// fullName: 'John Doe',
|
|
650
|
+
// email: 'john.doe@example.com',
|
|
651
|
+
// age: 28,
|
|
652
|
+
// username: 'johndoe123',
|
|
653
|
+
// password: 'SecurePass123!',
|
|
654
|
+
// subscribeToNewsletter: true,
|
|
655
|
+
// theme: 'dark',
|
|
656
|
+
// language: 'en'
|
|
657
|
+
// }
|
|
658
|
+
}
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
### Example 7: Defaults and Hooks
|
|
662
|
+
|
|
663
|
+
```javascript
|
|
664
|
+
// Student data with missing fields
|
|
665
|
+
const studentData = {
|
|
666
|
+
student: {
|
|
667
|
+
firstName: 'Alice',
|
|
668
|
+
lastName: 'Smith',
|
|
669
|
+
// age is missing
|
|
670
|
+
},
|
|
671
|
+
grades: {
|
|
672
|
+
math: 85,
|
|
673
|
+
english: 92,
|
|
674
|
+
// science grade is missing
|
|
675
|
+
},
|
|
676
|
+
// activities array is missing
|
|
677
|
+
info: {
|
|
678
|
+
grade: '5th',
|
|
679
|
+
teacher: 'Ms. Johnson',
|
|
680
|
+
// school year is missing
|
|
681
|
+
},
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
const converter = forge()
|
|
685
|
+
.from('studentReport')
|
|
686
|
+
.to('completeProfile')
|
|
687
|
+
|
|
688
|
+
// Basic mappings
|
|
689
|
+
.map('student.firstName', 'firstName')
|
|
690
|
+
.map('student.lastName', 'lastName')
|
|
691
|
+
.map('student.age', 'age')
|
|
692
|
+
.map('grades.math', 'mathGrade')
|
|
693
|
+
.map('grades.english', 'englishGrade')
|
|
694
|
+
.map('grades.science', 'scienceGrade')
|
|
695
|
+
.map('activities', 'extracurriculars')
|
|
696
|
+
.map('info.grade', 'gradeLevel')
|
|
697
|
+
.map('info.teacher', 'teacher')
|
|
698
|
+
.map('info.schoolYear', 'academicYear')
|
|
699
|
+
|
|
700
|
+
// Set default values for missing fields
|
|
701
|
+
.defaults({
|
|
702
|
+
age: 10, // Default age for 5th graders
|
|
703
|
+
scienceGrade: 80, // Default science grade
|
|
704
|
+
extracurriculars: ['reading'], // Default activity
|
|
705
|
+
academicYear: () => {
|
|
706
|
+
// Dynamic default - current school year
|
|
707
|
+
const now = new Date();
|
|
708
|
+
const year = now.getFullYear();
|
|
709
|
+
const month = now.getMonth();
|
|
710
|
+
return month >= 8 ? `${year}-${year + 1}` : `${year - 1}-${year}`;
|
|
711
|
+
},
|
|
712
|
+
status: 'active',
|
|
713
|
+
lastUpdated: () => new Date().toISOString(),
|
|
714
|
+
})
|
|
715
|
+
|
|
716
|
+
// Add before hook to log conversion start
|
|
717
|
+
.before(data => {
|
|
718
|
+
console.log('🔄 Starting conversion...');
|
|
719
|
+
console.log(
|
|
720
|
+
`Processing student: ${data.student?.firstName} ${data.student?.lastName}`
|
|
721
|
+
);
|
|
722
|
+
return data; // Return the data unchanged
|
|
723
|
+
})
|
|
724
|
+
|
|
725
|
+
// Add after hook to calculate grade average
|
|
726
|
+
.after(data => {
|
|
727
|
+
console.log('✅ Conversion completed!');
|
|
728
|
+
|
|
729
|
+
// Create full name from first and last name
|
|
730
|
+
if (data.firstName && data.lastName) {
|
|
731
|
+
data.fullName = `${data.firstName} ${data.lastName}`;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// Calculate and add grade average
|
|
735
|
+
const { mathGrade, englishGrade, scienceGrade } = data;
|
|
736
|
+
if (mathGrade && englishGrade && scienceGrade) {
|
|
737
|
+
data.gradeAverage = Math.round(
|
|
738
|
+
(mathGrade + englishGrade + scienceGrade) / 3
|
|
739
|
+
);
|
|
740
|
+
console.log(`Calculated grade average: ${data.gradeAverage}`);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
return data; // Return the modified data
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
const result = await converter.convert(studentData);
|
|
747
|
+
console.log(result.data);
|
|
748
|
+
// {
|
|
749
|
+
// firstName: 'Alice',
|
|
750
|
+
// lastName: 'Smith',
|
|
751
|
+
// mathGrade: 85,
|
|
752
|
+
// englishGrade: 92,
|
|
753
|
+
// gradeLevel: '5th',
|
|
754
|
+
// teacher: 'Ms. Johnson',
|
|
755
|
+
// age: 10,
|
|
756
|
+
// scienceGrade: 80,
|
|
757
|
+
// extracurriculars: ['reading'],
|
|
758
|
+
// academicYear: '2024-2025',
|
|
759
|
+
// status: 'active',
|
|
760
|
+
// lastUpdated: '2024-12-23T06:22:05.491Z',
|
|
761
|
+
// fullName: 'Alice Smith',
|
|
762
|
+
// gradeAverage: 86
|
|
763
|
+
// }
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
### Example 8: File Conversion
|
|
462
767
|
|
|
463
768
|
```javascript
|
|
464
769
|
// Convert YAML file to JSON structure
|
|
@@ -497,17 +802,18 @@ await result.save('./config.json');
|
|
|
497
802
|
- Basic field mapping with `map()`
|
|
498
803
|
- Nested object and array access
|
|
499
804
|
- Value transformations
|
|
500
|
-
- Default values with `defaults()
|
|
805
|
+
- **Default values with `defaults()`** ⭐ ENHANCED!
|
|
501
806
|
- **Array/object processing with `forEach()`**
|
|
502
807
|
- **Conditional mapping with `when()`**
|
|
503
|
-
- **Multi-field merging with `merge()`**
|
|
808
|
+
- **Multi-field merging with `merge()`**
|
|
809
|
+
- **Field validation with `validate()`**
|
|
810
|
+
- **Lifecycle hooks with `before()` and `after()`** ⭐ NEW!
|
|
504
811
|
- File parsing (YAML, JSON)
|
|
505
812
|
- Conversion statistics and reporting
|
|
506
|
-
-
|
|
813
|
+
- Async and sync conversion support
|
|
507
814
|
|
|
508
815
|
**🚧 Coming Soon:**
|
|
509
816
|
|
|
510
|
-
- Field validation
|
|
511
817
|
- CLI generation
|
|
512
818
|
- Plugin system
|
|
513
819
|
|
|
@@ -517,11 +823,20 @@ await result.save('./config.json');
|
|
|
517
823
|
2. **Use dot notation** for nested objects: `'user.profile.name'`
|
|
518
824
|
3. **Use bracket notation** for arrays: `'items[0]'`, `'items[1]'`
|
|
519
825
|
4. **Transform functions** get the value and context: `(value, ctx) => { ... }`
|
|
520
|
-
5. **Use conditional mapping** with `when()` for type-specific logic: `when(source => source.
|
|
826
|
+
5. **Use conditional mapping** with `when()` for type-specific logic: `when(source => source.accountType === 'premium')`
|
|
521
827
|
6. **Use merge()** to combine multiple fields: `merge(['field1', 'field2'], 'result', (a, b) => a + b)`
|
|
522
|
-
7. **
|
|
523
|
-
8. **
|
|
524
|
-
9. **
|
|
828
|
+
7. **Use validation** to ensure data quality: `validate('email', validators.email)`
|
|
829
|
+
8. **Combine validators** with `validators.all()` for multiple rules
|
|
830
|
+
9. **Always call `.end()`** after conditional mappings to return to the main converter
|
|
831
|
+
10. **Check `result.errors`** to see validation failures
|
|
832
|
+
11. **Check `result.unmapped`** to see which fields weren't mapped
|
|
833
|
+
12. **Use `result.print()`** for a nice conversion report
|
|
834
|
+
13. **Use `defaults()`** to provide fallback values for missing fields: `defaults({ status: 'active' })`
|
|
835
|
+
14. **Use function defaults** for dynamic values: `defaults({ timestamp: () => new Date().toISOString() })`
|
|
836
|
+
15. **Use `before()` hooks** to preprocess source data before conversion
|
|
837
|
+
16. **Use `after()` hooks** to postprocess target data after conversion
|
|
838
|
+
17. **Hooks can be async** - just use `async (data) => { ... }` and they'll be awaited
|
|
839
|
+
18. **Multiple hooks execute in order** - add as many as you need for complex workflows
|
|
525
840
|
|
|
526
841
|
---
|
|
527
842
|
|
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
|
|
@@ -67,6 +68,10 @@ export declare class Converter<TSource = any, TTarget = any> {
|
|
|
67
68
|
* Execute the core conversion logic
|
|
68
69
|
*/
|
|
69
70
|
private executeConversion;
|
|
71
|
+
/**
|
|
72
|
+
* Execute the core conversion logic synchronously (no async hooks)
|
|
73
|
+
*/
|
|
74
|
+
private executeConversionSync;
|
|
70
75
|
/**
|
|
71
76
|
* Apply default values to target data
|
|
72
77
|
*/
|
|
@@ -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":"AAAA,OAAO,EACL,MAAM,EACN,YAAY,EACZ,OAAO,EACP,WAAW,EACX,WAAW,EACX,MAAM,EACN,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,OAAO,EACP,SAAS,EAMV,MAAM,UAAU,CAAC;AAOlB;;GAEG;AACH,qBAAa,SAAS,CAAC,OAAO,GAAG,GAAG,EAAE,OAAO,GAAG,GAAG;IACjD,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,aAAa,CAA2B;IAChD,OAAO,CAAC,UAAU,CAAuC;IACzD,OAAO,CAAC,KAAK,CAGX;IACF,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,UAAU,CAAC,CAAM;gBAEb,OAAO,GAAE,YAAiB;IAYtC;;OAEG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI;IAc3D;;OAEG;IACH,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI;IAczD;;OAEG;IACH,GAAG,CACD,MAAM,EAAE,MAAM,OAAO,GAAG,MAAM,EAC9B,MAAM,EAAE,MAAM,OAAO,GAAG,MAAM,EAC9B,SAAS,CAAC,EAAE,WAAW,GACtB,IAAI;IAaP;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAK3C;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,GAAG,IAAI;IAKrD;;OAEG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKxB;;OAEG;IACH,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKvB;;OAEG;IACH,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,iBAAiB;IAIxD;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI;IAe/C;;OAEG;IACH,KAAK,CACH,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,SAAS,CAAC,EAAE,WAAW,GACtB,IAAI;IAiBP;;OAEG;IACG,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAuD5E;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB;IAiC7C;;OAEG;YACW,YAAY;IAU1B;;OAEG;YACW,iBAAiB;IAuG/B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAqH7B;;OAEG;IACH,OAAO,CAAC,aAAa;IAerB;;OAEG;IACH,OAAO,CAAC,cAAc;IAmCtB;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAY/B;;OAEG;IACH,OAAO,CAAC,WAAW;IAenB;;OAEG;IACH,OAAO,CAAC,cAAc;IAatB;;OAEG;IACH,OAAO,CAAC,OAAO;IAcf,OAAO,CAAC,OAAO;IAcf,OAAO,CAAC,OAAO;IAqBf;;OAEG;IACH,eAAe,IAAI,MAAM,GAAG,SAAS;IAIrC;;OAEG;IACH,eAAe,IAAI,MAAM,GAAG,SAAS;IAIrC;;OAEG;IACH,WAAW,IAAI,OAAO,EAAE;IAIxB;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAIlC;;OAEG;IACH,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAIlC;;OAEG;IACH,aAAa,IAAI,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC;IAIzC;;OAEG;IACH,QAAQ,IAAI;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE;IAOjD;;OAEG;IACH,UAAU,IAAI,YAAY;CAG3B;AAED;;GAEG;AACH,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,SAAS;gBADT,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,MAAM,GAAG,WAAW;IAGzC;;OAEG;IACH,GAAG,CACD,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,WAAW,GACtB,iBAAiB;IAiBpB;;OAEG;IACH,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,iBAAiB;IAIxD;;OAEG;IACH,GAAG,IAAI,SAAS;IAIhB;;OAEG;IACG,OAAO,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAIpD;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,GAAG,GAAG,gBAAgB;CAG1C"}
|