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 CHANGED
@@ -103,7 +103,26 @@ const converter = forge()
103
103
  .end()
104
104
  ```
105
105
 
106
- ### 5. Set Default Values
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
- ### 6. Convert Data
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
- // Citizens NPC configuration
251
- const citizensData = {
252
- 'last-created-npc-id': 2,
253
- npc: [
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: 'Guard',
256
- uuid: '9e1d174d-bc6c-495e-bd70-17e60327b716',
257
- traits: {
258
- location: { x: 0.5, y: 98.0, z: -22.5 },
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: 'Merchant',
265
- uuid: 'fb0d6308-de11-4d3a-8c16-97338747c26f',
266
- traits: {
267
- location: { x: 33.5, y: 99.0, z: -31.5 },
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('citizens')
277
- .to('fancynpcs')
278
- .map('last-created-npc-id', 'lastId')
279
- .forEach('npc', (npc, index) => {
322
+ .from('inventory')
323
+ .to('catalog')
324
+ .map('storeId', 'storeId')
325
+ .forEach('fruits', (fruit, index) => {
280
326
  return {
281
- id: npc.uuid,
282
- name: npc.name || `NPC_${index}`,
283
- location: {
284
- x: npc.traits?.location?.x,
285
- y: npc.traits?.location?.y,
286
- z: npc.traits?.location?.z,
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(citizensData);
337
+ const result = await converter.convert(fruitData);
295
338
  console.log(result.data);
296
339
  // {
297
- // lastId: 2,
298
- // npc: [
340
+ // storeId: 'STORE-001',
341
+ // fruits: [
299
342
  // {
300
- // id: '9e1d174d-bc6c-495e-bd70-17e60327b716',
301
- // name: 'Guard',
302
- // location: { x: 0.5, y: 98.0, z: -22.5 },
303
- // skin: 'Nik_187t',
304
- // commands: ['warp plots']
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: 'fb0d6308-de11-4d3a-8c16-97338747c26f',
308
- // name: 'Merchant',
309
- // location: { x: 33.5, y: 99.0, z: -31.5 },
310
- // skin: 'FrostAMC',
311
- // commands: ['knightrank']
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 NPC types need different mappings
321
- const npcConfig = {
322
- npc: {
323
- type: 'PLAYER',
324
- name: 'Guard',
325
- uuid: '9e1d174d-bc6c-495e-bd70-17e60327b716',
326
- traits: {
327
- skintrait: { skinName: 'Steve' },
328
- commandtrait: { commands: ['warp spawn', 'heal'] },
329
- equipment: { helmet: 'diamond_helmet' },
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('citizens')
336
- .to('fancynpcs')
337
- .map('npc.name', 'name')
338
- .map('npc.uuid', 'id')
339
-
340
- // Only map skin for PLAYER NPCs
341
- .when(source => source.npc?.type === 'PLAYER')
342
- .map('npc.traits.skintrait.skinName', 'skin')
343
- .map('npc.traits.commandtrait.commands', 'commands')
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 profession for VILLAGER NPCs
347
- .when(source => source.npc?.type === 'VILLAGER')
348
- .map('npc.traits.profession', 'profession')
349
- .map('npc.traits.trades', 'trades')
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
- // String condition - only if field exists
353
- .when('npc.traits.equipment')
354
- .map('npc.traits.equipment', 'equipment')
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(npcConfig);
412
+ const result = await converter.convert(petData);
358
413
  console.log(result.data);
359
414
  // {
360
- // name: 'Guard',
361
- // id: '9e1d174d-bc6c-495e-bd70-17e60327b716',
362
- // skin: 'Steve',
363
- // commands: ['warp spawn', 'heal'],
364
- // equipment: { helmet: 'diamond_helmet' }
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: File Conversion
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()`** ⭐ NEW!
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.type === 'PLAYER')`
426
- 6. **Always call `.end()`** after conditional mappings to return to the main converter
427
- 7. **Check `result.unmapped`** to see which fields weren't mapped
428
- 8. **Use `result.print()`** for a nice conversion report
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
 
@@ -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 (placeholder - will be implemented in task 10)
52
+ * Merge multiple sources into a single target field
52
53
  */
53
- merge(_sources: string[], _target: string, _mergeFn: MergeFn): this;
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,EAKV,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;gBAElB,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,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI;IAKnE;;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;IACH,OAAO,CAAC,iBAAiB;IAoGzB;;OAEG;IACH,OAAO,CAAC,aAAa;IAarB;;OAEG;IACH,OAAO,CAAC,cAAc;IAiCtB;;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;IAepB;;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"}
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;IACH,OAAO,CAAC,iBAAiB;IAuGzB;;OAEG;IACH,OAAO,CAAC,aAAa;IAarB;;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"}
@@ -126,11 +126,22 @@ class Converter {
126
126
  return this;
127
127
  }
128
128
  /**
129
- * Merge multiple sources (placeholder - will be implemented in task 10)
129
+ * Merge multiple sources into a single target field
130
130
  */
131
- merge(_sources, _target, _mergeFn) {
132
- // This will be implemented in the merge functionality task
133
- throw new Error('merge() method not yet implemented');
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.GenericConfigForgeError(typeof result === 'string'
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.GenericConfigForgeError(`Validation error for field ${field}: ${error instanceof Error ? error.message : 'Unknown error'}`, { path: field }));
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);