configforge 1.5.0 → 1.6.0

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.
Files changed (35) hide show
  1. package/README.md +307 -217
  2. package/dist/core/BatchProcessor.d.ts.map +1 -1
  3. package/dist/core/BatchProcessor.js +9 -0
  4. package/dist/core/BatchProcessor.js.map +1 -1
  5. package/dist/core/ConversionResult.d.ts +81 -18
  6. package/dist/core/ConversionResult.d.ts.map +1 -1
  7. package/dist/core/ConversionResult.js +227 -56
  8. package/dist/core/ConversionResult.js.map +1 -1
  9. package/dist/core/Converter.d.ts +60 -3
  10. package/dist/core/Converter.d.ts.map +1 -1
  11. package/dist/core/Converter.js +538 -13
  12. package/dist/core/Converter.js.map +1 -1
  13. package/dist/core/IncrementalProcessor.d.ts.map +1 -1
  14. package/dist/core/IncrementalProcessor.js +9 -0
  15. package/dist/core/IncrementalProcessor.js.map +1 -1
  16. package/dist/core/Mapper.d.ts +1 -1
  17. package/dist/core/Mapper.d.ts.map +1 -1
  18. package/dist/core/Mapper.js +8 -1
  19. package/dist/core/Mapper.js.map +1 -1
  20. package/dist/core/MultiFileProcessor.d.ts.map +1 -1
  21. package/dist/core/MultiFileProcessor.js +9 -0
  22. package/dist/core/MultiFileProcessor.js.map +1 -1
  23. package/dist/core/pipeline/PipelineSteps.d.ts.map +1 -1
  24. package/dist/core/pipeline/PipelineSteps.js +21 -1
  25. package/dist/core/pipeline/PipelineSteps.js.map +1 -1
  26. package/dist/parsers/Parser.d.ts.map +1 -1
  27. package/dist/parsers/Parser.js +1 -3
  28. package/dist/parsers/Parser.js.map +1 -1
  29. package/dist/parsers/yaml.d.ts.map +1 -1
  30. package/dist/parsers/yaml.js +7 -3
  31. package/dist/parsers/yaml.js.map +1 -1
  32. package/dist/types/index.d.ts +35 -0
  33. package/dist/types/index.d.ts.map +1 -1
  34. package/dist/types/index.js.map +1 -1
  35. package/package.json +1 -1
package/README.md CHANGED
@@ -1244,235 +1244,300 @@ console.log('Backup created:', result.context?.backupPath);
1244
1244
 
1245
1245
  ### Example 10: Multi-File Processing ⭐ NEW!
1246
1246
 
1247
- ConfigForge now supports processing multiple input files into a single output (many-to-one) or splitting a single input into multiple outputs (one-to-many). This is perfect for plugin conversions like DecentHolograms to CMI.
1247
+ ConfigForge makes it easy to work with multiple files at once. Think of it like organizing your music collection - you might have individual song files that you want to combine into a playlist, or a big playlist that you want to split into smaller themed playlists.
1248
1248
 
1249
1249
  ```javascript
1250
1250
  const { forge } = require('configforge');
1251
1251
 
1252
- // Example: Converting DecentHolograms to CMI (many-to-one)
1253
- // DecentHolograms stores each hologram in separate files:
1254
- // - NPCCoinshop.yml, NPCCosmetics.yml, Welcome.yml, etc.
1255
- // CMI stores all holograms in a single file
1252
+ // Example 1: Combining Multiple Files (Many-to-One)
1253
+ // Like combining individual recipe cards into a cookbook
1256
1254
 
1257
- const decentToCmiConverter = forge()
1258
- .from('decentholograms')
1259
- .to('cmi-holograms')
1260
-
1261
- // Map location format: "spawn:0.500:103.500:-6.5" -> "world;0.5;82.75;-36.5"
1262
- .map('location', 'Loc', location => {
1263
- if (typeof location !== 'string') return location;
1264
-
1265
- const parts = location.split(':');
1266
- if (parts.length >= 4) {
1267
- const world = parts[0] || 'world';
1268
- const x = parseFloat(parts[1]) || 0;
1269
- const y = parseFloat(parts[2]) || 0;
1270
- const z = parseFloat(parts[3]) || 0;
1271
-
1272
- return `${world};${x};${y};${z}`;
1273
- }
1274
- return location;
1275
- })
1276
-
1277
- // Map other fields
1278
- .map('display-range', 'RangeExtra')
1279
- .map('update-interval', 'Interval')
1280
-
1281
- // Transform pages to CMI Lines format
1282
- .map('pages', 'Lines', pages => {
1283
- if (!Array.isArray(pages) || pages.length === 0) return [];
1284
-
1285
- const firstPage = pages[0];
1286
- if (!firstPage || !Array.isArray(firstPage.lines)) return [];
1287
-
1288
- return firstPage.lines
1289
- .map(line => {
1290
- if (typeof line === 'string') return line;
1291
- if (line && typeof line.content === 'string') {
1292
- return line.content;
1293
- }
1294
- return '';
1295
- })
1296
- .filter(line => line.length > 0);
1297
- })
1255
+ // You have separate recipe files:
1256
+ const recipeFiles = [
1257
+ {
1258
+ filename: 'chocolate-cake.yml',
1259
+ content: `
1260
+ name: Chocolate Cake
1261
+ category: dessert
1262
+ servings: 8
1263
+ ingredients:
1264
+ - flour: 2 cups
1265
+ - sugar: 1.5 cups
1266
+ - cocoa: 0.5 cups
1267
+ cookTime: 45
1268
+ difficulty: medium
1269
+ `,
1270
+ },
1271
+ {
1272
+ filename: 'pasta-salad.yml',
1273
+ content: `
1274
+ name: Pasta Salad
1275
+ category: main
1276
+ servings: 6
1277
+ ingredients:
1278
+ - pasta: 1 lb
1279
+ - tomatoes: 2 cups
1280
+ - cheese: 1 cup
1281
+ cookTime: 20
1282
+ difficulty: easy
1283
+ `,
1284
+ },
1285
+ {
1286
+ filename: 'fruit-smoothie.yml',
1287
+ content: `
1288
+ name: Fruit Smoothie
1289
+ category: drink
1290
+ servings: 2
1291
+ ingredients:
1292
+ - banana: 1
1293
+ - berries: 1 cup
1294
+ - yogurt: 0.5 cups
1295
+ cookTime: 5
1296
+ difficulty: easy
1297
+ `,
1298
+ },
1299
+ ];
1298
1300
 
1299
- // Set CMI defaults
1301
+ // Create a converter to combine recipes into a cookbook
1302
+ const cookbookConverter = forge()
1303
+ .from('*.yml')
1304
+ .to('cookbook.yml')
1305
+ // Use filename as the recipe key (removes .yml automatically)
1306
+ .useFilenameAsKey()
1307
+ // Map recipe fields to cookbook format
1308
+ .map('name', 'title')
1309
+ .map('category', 'type')
1310
+ .map('servings', 'serves')
1311
+ .map('ingredients', 'ingredientList')
1312
+ .map('cookTime', 'prepTime')
1313
+ .map('difficulty', 'skillLevel')
1314
+ // Add some defaults for the cookbook
1300
1315
  .defaults({
1301
- Filler: 245,
1316
+ tested: true,
1317
+ addedDate: () => new Date().toISOString().split('T')[0], // Just the date
1302
1318
  });
1303
1319
 
1304
- // Convert multiple DecentHolograms files to single CMI format
1305
- const inputFiles = [
1306
- 'holograms/NPCCoinshop.yml',
1307
- 'holograms/NPCCosmetics.yml',
1308
- 'holograms/Welcome.yml',
1309
- 'holograms/CrateCommon.yml',
1310
- ];
1311
-
1312
- const result = await decentToCmiConverter.convertMany(inputFiles, {
1313
- mergeStrategy: 'merge',
1314
- preserveFileNames: true,
1315
- keyExtractor: filePath => {
1316
- // Extract hologram name from filename
1317
- return path.basename(filePath, path.extname(filePath));
1318
- },
1319
- });
1320
+ // Convert all recipe files into one cookbook
1321
+ const cookbook = await cookbookConverter.convert(recipeFiles);
1320
1322
 
1321
- console.log('✅ Converted', result.fileResults.length, 'hologram files');
1322
- console.log(result.data);
1323
+ console.log(
1324
+ '📚 Created cookbook with',
1325
+ Object.keys(cookbook.data).length,
1326
+ 'recipes'
1327
+ );
1328
+ console.log(cookbook.data);
1323
1329
  // {
1324
- // NPCCoinshop: {
1325
- // Loc: 'world;-26.5;82.05;-24.5',
1326
- // RangeExtra: 50,
1327
- // Lines: ['ICON:TOTEM_OF_UNDYING', '{#FFCB6C>}&lSTORE{#FFAB3D<}'],
1328
- // Filler: 245
1330
+ // 'chocolate-cake': {
1331
+ // title: 'Chocolate Cake',
1332
+ // type: 'dessert',
1333
+ // serves: 8,
1334
+ // ingredientList: [{ flour: '2 cups' }, { sugar: '1.5 cups' }, { cocoa: '0.5 cups' }],
1335
+ // prepTime: 45,
1336
+ // skillLevel: 'medium',
1337
+ // tested: true,
1338
+ // addedDate: '2024-01-22'
1329
1339
  // },
1330
- // NPCCosmetics: {
1331
- // Loc: 'world;10.0;64.0;20.0',
1332
- // RangeExtra: 30,
1333
- // Lines: ['&6Cosmetics Shop', '&eClick to browse!'],
1334
- // Filler: 245
1340
+ // 'pasta-salad': {
1341
+ // title: 'Pasta Salad',
1342
+ // type: 'main',
1343
+ // serves: 6,
1344
+ // ingredientList: [{ pasta: '1 lb' }, { tomatoes: '2 cups' }, { cheese: '1 cup' }],
1345
+ // prepTime: 20,
1346
+ // skillLevel: 'easy',
1347
+ // tested: true,
1348
+ // addedDate: '2024-01-22'
1335
1349
  // },
1336
- // // ... more holograms
1350
+ // 'fruit-smoothie': {
1351
+ // title: 'Fruit Smoothie',
1352
+ // type: 'drink',
1353
+ // serves: 2,
1354
+ // ingredientList: [{ banana: 1 }, { berries: '1 cup' }, { yogurt: '0.5 cups' }],
1355
+ // prepTime: 5,
1356
+ // skillLevel: 'easy',
1357
+ // tested: true,
1358
+ // addedDate: '2024-01-22'
1359
+ // }
1337
1360
  // }
1338
1361
 
1339
- // Save the merged result
1340
- await result.save('cmi-holograms.yml');
1341
-
1342
- // Example: Converting CMI back to DecentHolograms (one-to-many)
1343
- const cmiToDecentConverter = forge()
1344
- .from('cmi-holograms')
1345
- .to('decentholograms')
1346
-
1347
- // Reverse the location mapping
1348
- .map('Loc', 'location', loc => {
1349
- if (typeof loc !== 'string') return loc;
1350
-
1351
- const parts = loc.split(';');
1352
- if (parts.length >= 4) {
1353
- const world = parts[0] || 'spawn';
1354
- const x = parseFloat(parts[1]) || 0;
1355
- const y = parseFloat(parts[2]) || 0;
1356
- const z = parseFloat(parts[3]) || 0;
1362
+ // Example 2: Splitting One File into Many (One-to-Many)
1363
+ // Like taking a big address book and creating separate contact cards
1364
+
1365
+ const addressBookData = {
1366
+ filename: 'contacts.yml',
1367
+ content: `
1368
+ contacts:
1369
+ john-doe:
1370
+ name: John Doe
1371
+ email: john@example.com
1372
+ phone: 555-0123
1373
+ category: friend
1374
+ city: New York
1375
+ jane-smith:
1376
+ name: Jane Smith
1377
+ email: jane@company.com
1378
+ phone: 555-0456
1379
+ category: work
1380
+ city: Boston
1381
+ mom:
1382
+ name: Mary Johnson
1383
+ email: mom@family.com
1384
+ phone: 555-0789
1385
+ category: family
1386
+ city: Chicago
1387
+ `,
1388
+ };
1357
1389
 
1358
- return `${world}:${x}:${y}:${z}`;
1359
- }
1360
- return loc;
1390
+ // Create converter to split contacts into individual cards
1391
+ const contactConverter = forge()
1392
+ .from('*.yml')
1393
+ .to('json')
1394
+ // Transform each contact
1395
+ .map('name', 'fullName')
1396
+ .map('email', 'emailAddress')
1397
+ .map('phone', 'phoneNumber')
1398
+ .map('category', 'group')
1399
+ .map('city', 'location')
1400
+ // Add some useful defaults
1401
+ .defaults({
1402
+ favorite: false,
1403
+ lastContacted: null,
1361
1404
  })
1405
+ // Split the contacts into separate files
1406
+ .split(data => {
1407
+ const result = {};
1408
+
1409
+ // Take each contact and make it a separate file
1410
+ if (data.contacts) {
1411
+ for (const [contactId, contactInfo] of Object.entries(data.contacts)) {
1412
+ // Create filename based on contact name and category
1413
+ const filename = `${contactInfo.category}-${contactId}.yml`;
1414
+ result[filename] = contactInfo;
1415
+ }
1416
+ }
1362
1417
 
1363
- .map('RangeExtra', 'display-range')
1364
- .map('Interval', 'update-interval')
1418
+ return result;
1419
+ });
1365
1420
 
1366
- // Transform CMI Lines to DecentHolograms pages
1367
- .map('Lines', 'pages', lines => {
1368
- if (!Array.isArray(lines)) return [];
1421
+ const splitContacts = await contactConverter.convert([addressBookData]);
1369
1422
 
1370
- const pageLines = lines.map(line => ({
1371
- content: line,
1372
- height: 0.3,
1373
- }));
1423
+ // Get the individual contact files
1424
+ const contactFiles = splitContacts.toFiles();
1425
+ console.log(`📇 Split into ${contactFiles.length} contact cards:`);
1374
1426
 
1375
- return [
1376
- {
1377
- lines: pageLines,
1378
- actions: {},
1379
- },
1380
- ];
1381
- })
1427
+ contactFiles.forEach(file => {
1428
+ console.log(`📄 ${file.filename}`);
1429
+ console.log(file.content.substring(0, 100) + '...');
1430
+ });
1382
1431
 
1383
- .defaults({
1384
- enabled: true,
1385
- 'update-range': 100,
1386
- facing: 0.0,
1387
- 'down-origin': false,
1388
- });
1432
+ // Output:
1433
+ // 📇 Split into 3 contact cards:
1434
+ // 📄 friend-john-doe.yml
1435
+ // fullName: John Doe
1436
+ // emailAddress: john@example.com
1437
+ // phoneNumber: 555-0123
1438
+ // group: friend
1439
+ // location: New York...
1440
+ //
1441
+ // 📄 work-jane-smith.yml
1442
+ // fullName: Jane Smith
1443
+ // emailAddress: jane@company.com
1444
+ // phoneNumber: 555-0456
1445
+ // group: work
1446
+ // location: Boston...
1447
+ //
1448
+ // 📄 family-mom.yml
1449
+ // fullName: Mary Johnson
1450
+ // emailAddress: mom@family.com
1451
+ // phoneNumber: 555-0789
1452
+ // group: family
1453
+ // location: Chicago...
1389
1454
 
1390
- // Split function to separate each hologram into its own file
1391
- const splitFn = cmiData => {
1392
- const result = {};
1455
+ // Example 3: Handling Conflicts (When Files Have Same Names)
1456
+ // Like when you have two recipes both called "cake.yml"
1393
1457
 
1394
- for (const [hologramName, hologramData] of Object.entries(cmiData)) {
1395
- if (typeof hologramData === 'object' && hologramData !== null) {
1396
- result[hologramName] = hologramData;
1397
- }
1398
- }
1458
+ const conflictingFiles = [
1459
+ {
1460
+ filename: 'cake.yml',
1461
+ content: 'name: Chocolate Cake\ntype: dessert\nrating: 5',
1462
+ },
1463
+ {
1464
+ filename: 'cake.yml', // Same filename!
1465
+ content: 'name: Vanilla Cake\ntype: dessert\nrating: 4',
1466
+ },
1467
+ ];
1399
1468
 
1400
- return result;
1401
- };
1469
+ // Option 1: Merge conflicts (combine the values)
1470
+ const mergeConverter = forge()
1471
+ .from('*.yml')
1472
+ .to('json')
1473
+ .useFilenameAsKey()
1474
+ .onKeyConflict('merge'); // Combine conflicting values into arrays
1402
1475
 
1403
- // Convert single CMI file to multiple DecentHolograms files
1404
- const splitResults = await cmiToDecentConverter.convertSplit(
1405
- 'cmi-holograms.yml',
1406
- splitFn
1407
- );
1476
+ const mergedResult = await mergeConverter.convert(conflictingFiles);
1477
+ console.log('🔀 Merged conflicts:', mergedResult.data);
1478
+ // {
1479
+ // cake: {
1480
+ // name: ['Chocolate Cake', 'Vanilla Cake'], // Both names in an array
1481
+ // type: 'dessert', // Same value, no conflict
1482
+ // rating: [5, 4] // Both ratings in an array
1483
+ // }
1484
+ // }
1408
1485
 
1409
- console.log('✅ Created', Object.keys(splitResults).length, 'hologram files');
1486
+ // Option 2: Error on conflicts (be strict)
1487
+ const strictConverter = forge()
1488
+ .from('*.yml')
1489
+ .to('json')
1490
+ .useFilenameAsKey()
1491
+ .onKeyConflict('error'); // Stop and report the problem
1410
1492
 
1411
- // Save each hologram to its own file
1412
- const outputDir = 'decent-holograms-output';
1413
- for (const [hologramName, result] of Object.entries(splitResults)) {
1414
- await result.save(`${outputDir}/${hologramName}.yml`);
1493
+ try {
1494
+ await strictConverter.convert(conflictingFiles);
1495
+ } catch (error) {
1496
+ console.log('⚠️ Conflict detected:', error.message);
1497
+ // "Key conflict detected: 'cake' appears in multiple files"
1415
1498
  }
1416
1499
 
1417
- // Advanced: Custom merge strategies
1418
- const customMergeConverter = forge()
1419
- .from('decentholograms')
1420
- .to('cmi-holograms');
1421
-
1422
- // Custom merger that groups holograms by type
1423
- const customMerger = (inputs, filePaths) => {
1424
- const grouped = {
1425
- spawn_holograms: {},
1426
- npc_holograms: {},
1427
- other_holograms: {},
1428
- };
1429
-
1430
- inputs.forEach((data, index) => {
1431
- const fileName = path.basename(filePaths[index], '.yml');
1500
+ // Real-world example: Simple server config merger
1501
+ const serverConfigs = [
1502
+ {
1503
+ filename: 'database.yml',
1504
+ content: 'host: db.example.com\nport: 5432\nname: myapp',
1505
+ },
1506
+ {
1507
+ filename: 'cache.yml',
1508
+ content: 'host: cache.example.com\nport: 6379\nttl: 3600',
1509
+ },
1510
+ ];
1432
1511
 
1433
- if (fileName.toLowerCase().includes('npc')) {
1434
- grouped.npc_holograms[fileName] = data;
1435
- } else if (data.location && data.location.includes('spawn')) {
1436
- grouped.spawn_holograms[fileName] = data;
1437
- } else {
1438
- grouped.other_holograms[fileName] = data;
1439
- }
1512
+ const serverConverter = forge()
1513
+ .from('*.yml')
1514
+ .to('config.yml')
1515
+ .useFilenameAsKey()
1516
+ .map('host', 'hostname')
1517
+ .map('port', 'port')
1518
+ .defaults({
1519
+ enabled: true,
1520
+ timeout: 30,
1440
1521
  });
1441
1522
 
1442
- return grouped;
1443
- };
1444
-
1445
- const groupedResult = await customMergeConverter.convertMany(inputFiles, {
1446
- mergeStrategy: 'custom',
1447
- customMerger: customMerger,
1448
- });
1449
-
1450
- console.log(groupedResult.data);
1523
+ const serverConfig = await serverConverter.convert(serverConfigs);
1524
+ console.log('🖥️ Server configuration:', serverConfig.data);
1451
1525
  // {
1452
- // spawn_holograms: {
1453
- // Welcome: { Loc: 'spawn;0.5;103.5;-6.5', ... }
1454
- // },
1455
- // npc_holograms: {
1456
- // NPCCoinshop: { Loc: 'world;-26.5;82.05;-24.5', ... },
1457
- // NPCCosmetics: { Loc: 'world;10.0;64.0;20.0', ... }
1526
+ // database: {
1527
+ // hostname: 'db.example.com',
1528
+ // port: 5432,
1529
+ // name: 'myapp',
1530
+ // enabled: true,
1531
+ // timeout: 30
1458
1532
  // },
1459
- // other_holograms: {
1460
- // CrateCommon: { Loc: 'world;100;64;200', ... }
1533
+ // cache: {
1534
+ // hostname: 'cache.example.com',
1535
+ // port: 6379,
1536
+ // ttl: 3600,
1537
+ // enabled: true,
1538
+ // timeout: 30
1461
1539
  // }
1462
1540
  // }
1463
-
1464
- // Multi-file processing options:
1465
- //
1466
- // mergeStrategy: 'merge' (default) - Merge objects using file names as keys
1467
- // mergeStrategy: 'array' - Combine all inputs into an array
1468
- // mergeStrategy: 'custom' - Use custom merger function
1469
- //
1470
- // preserveFileNames: true (default) - Use filenames as object keys
1471
- // preserveFileNames: false - Simple deep merge without keys
1472
- //
1473
- // keyExtractor: function - Custom function to extract keys from file paths
1474
- // continueOnError: true (default) - Continue processing other files if one fails
1475
- // rootKey: string - Wrap merged data in a root key
1476
1541
  ```
1477
1542
 
1478
1543
  ### Example 11: Batch Processing and Performance Features
@@ -1737,6 +1802,9 @@ cli.parse();
1737
1802
  - ✅ **Simple API**: Just map fields and convert
1738
1803
  - ✅ **String Input Support**: Convert raw YAML/JSON strings directly with `convertString()` ⭐ NEW!
1739
1804
  - ✅ **Multi-File Processing**: Process multiple input files into single output (many-to-one) or split single input into multiple outputs (one-to-many) ⭐ NEW!
1805
+ - ✅ **Split Functionality**: Break single input files into multiple outputs with configurable split functions ⭐ NEW!
1806
+ - ✅ **Conflict Resolution**: Handle field conflicts in multi-file processing with configurable strategies (error, overwrite, merge, suffix) ⭐ NEW!
1807
+ - ✅ **Enhanced Result Handling**: Comprehensive result objects with `getSummary()`, `toFiles()`, and enhanced `print()` methods ⭐ NEW!
1740
1808
  - ✅ **Pipeline Architecture**: Robust internal processing with configurable steps and error handling
1741
1809
  - ✅ **No direct Mapper usage needed**: The `convert()` method handles everything
1742
1810
  - ✅ **Nested object support**: Use dot notation like `user.profile.name`
@@ -1765,7 +1833,11 @@ cli.parse();
1765
1833
  - Nested object and array access
1766
1834
  - Value transformations
1767
1835
  - **String input support with `convertString()` and `convert()` with `isRawContent` option** ⭐ NEW!
1836
+ - **Enhanced result handling with `getSummary()`, `toFiles()`, and improved `print()` methods** ⭐ NEW!
1768
1837
  - **Multi-file processing with `convertMany()` (many-to-one) and `convertSplit()` (one-to-many)** ⭐ NEW!
1838
+ - **Complete pipeline integration with multi-file processing** ⭐ NEW!
1839
+ - **Multi-file conflict resolution with configurable strategies (error, overwrite, merge, suffix)** ⭐ NEW!
1840
+ - **Split functionality with `split()` method for one-to-many processing** ⭐ NEW!
1769
1841
  - **Default values with `defaults()`** ⭐ ENHANCED!
1770
1842
  - **Array/object processing with `forEach()` and `mapToObject()`** ⭐ ENHANCED!
1771
1843
  - **Conditional mapping with `when()`**
@@ -1824,31 +1896,49 @@ cli.parse();
1824
1896
  34. **Use `forEach()` with `{ output: 'object' }` to transform arrays into keyed objects** - perfect for converting indexed arrays to UUID-keyed objects
1825
1897
  35. **`forEach()` with object output merges returned objects** - return `{ [key]: value }` to create dynamic keys from your data
1826
1898
  36. **Filter with `forEach()`** - return `null` from the mapping function to exclude items from the result
1827
- 37. **Choose forEach output type** - use `{ output: 'array' }` (default) to maintain structure, `{ output: 'object' }` to create keyed objects
1828
- 38. **Track progress** - provide `progressCallback` to monitor batch processing progress in real-time
1829
- 39. **Cache management** - use `IncrementalProcessor.getCacheStats()` and `cleanupCache()` to manage incremental processing cache
1830
- 40. **Save batch results** - use `BatchProcessor.saveBatch()` to save all conversion results to an output directory
1831
- 41. **Use forge() with options** - pass `ForgeOptions` to `forge({ strict: true, parallel: true })` to configure converter behavior globally
1832
- 42. **Use convertString() for raw content** - when you have YAML or JSON as a string, use `convertString()` instead of `convert()` NEW!
1833
- 43. **Use convert() with isRawContent option** - alternatively, use `convert(stringContent, { isRawContent: true })` for string input ⭐ NEW!
1834
- 44. **Format auto-detection works with strings** - ConfigForge automatically detects YAML vs JSON format in string content NEW!
1835
- 45. **String input handles parsing errors gracefully** - malformed YAML/JSON strings will produce helpful error messages NEW!
1836
- 46. **forEach field tracking is automatic** - when you access fields inside forEach callbacks, they're automatically marked as mapped in statistics ⭐ NEW!
1837
- 47. **forEach with target renaming** - use `forEach('source', 'target', mapFn)` to rename arrays/objects in output: `forEach('npc', 'npcs', mapFn)` ⭐ NEW!
1838
- 48. **Array indices are always numbers** - in forEach for arrays, the index parameter is guaranteed to be a number, so `index + 1` works correctly ⭐ NEW!
1839
- 49. **Object keys in forEach** - for objects, forEach passes the key as the second parameter for backward compatibility ⭐ NEW!
1840
- 50. **Access object keys in context** - when processing objects with forEach, the key is also available in `context.metadata.objectKey` ⭐ NEW!
1841
- 51. **Use convertMany() for many-to-one processing** - merge multiple input files into a single output with `convertMany(inputPaths, options)` ⭐ NEW!
1842
- 52. **Use convertSplit() for one-to-many processing** - split a single input file into multiple outputs with `convertSplit(inputPath, splitFn)` ⭐ NEW!
1843
- 53. **Choose merge strategies wisely** - use `'merge'` for object merging, `'array'` for simple arrays, or `'custom'` for complex logic ⭐ NEW!
1844
- 54. **Custom key extractors for file naming** - use `keyExtractor: (filePath) => customKey` to control how filenames become object keys ⭐ NEW!
1845
- 55. **Multi-file processing is fully tested and stable** - both `convertMany()` and `convertSplit()` have comprehensive test coverage and error handlingENHANCED!
1846
- 56. **Handle file errors gracefully** - set `continueOnError: true` to process all files even if some fail, or `false` to stop on first error ⭐ NEW!
1847
- 57. **Perfect for plugin conversions** - multi-file processing is ideal for converting between plugin formats like DecentHolograms CMI ⭐ NEW!
1848
- 58. **Use rootKey for wrapping** - set `rootKey: 'holograms'` to wrap merged data in a parent object ⭐ NEW!
1849
- 59. **Check fileResults for debugging** - `result.fileResults` shows success/failure status for each processed file ⭐ NEW!
1850
- 60. **Monitor failed files** - `result.failedFiles` contains paths of files that couldn't be processed ⭐ NEW!
1851
- 61. **Split functions control output structure** - return `{ key1: data1, key2: data2 }` from split functions to create multiple output files ⭐ NEW!
1899
+ 37. **Use `result.getSummary()`** - get detailed statistics about conversion success, file counts, errors, and duration
1900
+ 38. **Use `result.toFiles()`** - convert any result to FileObject arrays for consistent multi-file handling
1901
+ 39. **Enhanced `print()` methods** - result printing now includes emojis and better formatting for easier debugging
1902
+ 40. **Multi-file results provide file-specific statistics** - track success/failure per file in batch operations
1903
+ 41. **Split results include generated file lists** - `getSummary().generatedFiles` shows all files created from split operations
1904
+ 42. **Pipeline integration works seamlessly with multi-file processing** - all pipeline steps (parse, map, transform, validate, hooks) work correctly across multiple files
1905
+ 43. **Multi-file context is available in all pipeline steps** - access filename, fileIndex, and other multi-file metadata in transforms and hooks
1906
+ 44. **Error collection spans multiple files** - validation errors and processing errors are collected across all files with proper context
1907
+ 45. **Choose forEach output type** - use `{ output: 'array' }` (default) to maintain structure, `{ output: 'object' }` to create keyed objects
1908
+ 46. **Track progress** - provide `progressCallback` to monitor batch processing progress in real-time
1909
+ 47. **Cache management** - use `IncrementalProcessor.getCacheStats()` and `cleanupCache()` to manage incremental processing cache
1910
+ 48. **Save batch results** - use `BatchProcessor.saveBatch()` to save all conversion results to an output directory
1911
+ 49. **Use forge() with options** - pass `ForgeOptions` to `forge({ strict: true, parallel: true })` to configure converter behavior globally
1912
+ 50. **Use convertString() for raw content** - when you have YAML or JSON as a string, use `convertString()` instead of `convert()` ⭐ NEW!
1913
+ 51. **Use convert() with isRawContent option** - alternatively, use `convert(stringContent, { isRawContent: true })` for string input ⭐ NEW!
1914
+ 52. **Format auto-detection works with strings** - ConfigForge automatically detects YAML vs JSON format in string content ⭐ NEW!
1915
+ 53. **String input handles parsing errors gracefully** - malformed YAML/JSON strings will produce helpful error messages ⭐ NEW!
1916
+ 54. **forEach field tracking is automatic** - when you access fields inside forEach callbacks, they're automatically marked as mapped in statistics ⭐ NEW!
1917
+ 55. **forEach with target renaming** - use `forEach('source', 'target', mapFn)` to rename arrays/objects in output: `forEach('npc', 'npcs', mapFn)` NEW!
1918
+ 56. **Array indices are always numbers** - in forEach for arrays, the index parameter is guaranteed to be a number, so `index + 1` works correctly ⭐ NEW!
1919
+ 57. **Object keys in forEach** - for objects, forEach passes the key as the second parameter for backward compatibility ⭐ NEW!
1920
+ 58. **Access object keys in context** - when processing objects with forEach, the key is also available in `context.metadata.objectKey` ⭐ NEW!
1921
+ 59. **Use convertMany() for many-to-one processing** - merge multiple input files into a single output with `convertMany(inputPaths, options)` ⭐ NEW!
1922
+ 60. **Use convertSplit() for one-to-many processing** - split a single input file into multiple outputs with `convertSplit(inputPath, splitFn)` ⭐ NEW!
1923
+ 61. **Choose merge strategies wisely** - use `'merge'` for object merging, `'array'` for simple arrays, or `'custom'` for complex logic ⭐ NEW!
1924
+ 62. **Custom key extractors for file naming** - use `keyExtractor: (filePath) => customKey` to control how filenames become object keys ⭐ NEW!
1925
+ 63. **Multi-file processing is fully tested and stable** - both `convertMany()` and `convertSplit()` have comprehensive test coverage and error handling ⭐ ENHANCED!
1926
+ 64. **Handle file errors gracefully** - set `continueOnError: true` to process all files even if some fail, or `false` to stop on first error ⭐ NEW!
1927
+ 65. **Perfect for plugin conversions** - multi-file processing is ideal for converting between plugin formats like DecentHolograms ↔ CMI ⭐ NEW!
1928
+ 66. **Use rootKey for wrapping** - set `rootKey: 'holograms'` to wrap merged data in a parent object ⭐ NEW!
1929
+ 67. **Check fileResults for debugging** - `result.fileResults` shows success/failure status for each processed file ⭐ NEW!
1930
+ 68. **Monitor failed files** - `result.failedFiles` contains paths of files that couldn't be processed ⭐ NEW!
1931
+ 69. **Split functions control output structure** - return `{ key1: data1, key2: data2 }` from split functions to create multiple output files ⭐ NEW!
1932
+ 70. **Handle field conflicts in multi-file processing** - use `onKeyConflict()` to configure how to handle conflicting field names across files ⭐ NEW!
1933
+ 71. **Choose conflict resolution strategy** - use `'error'` to fail on conflicts, `'overwrite'` for last-wins, `'merge'` to combine into arrays, or `'suffix'` to add unique suffixes ⭐ NEW!
1934
+ 72. **Merge strategy combines conflicting fields** - when using `onKeyConflict('merge')`, fields with the same name across files are combined into arrays ⭐ NEW!
1935
+ 73. **Custom conflict suffixes** - use `onKeyConflict('suffix', '_backup')` to customize the suffix added to conflicting field names ⭐ NEW!
1936
+ 74. **Force same key for conflict testing** - use `useKey('fixedKey')` to force all files to use the same key, creating conflicts for testing resolution strategies ⭐ NEW!
1937
+ 75. **Use split() for one-to-many processing** - break single input files into multiple outputs with `split(data => ({ file1: data.part1, file2: data.part2 }))` ⭐ NEW!
1938
+ 76. **Split functions control output structure** - return an object where keys become filenames and values become file content ⭐ NEW!
1939
+ 77. **Split works with all mappings and transforms** - apply field mappings, transformations, defaults, and hooks to each split output ⭐ NEW!
1940
+ 78. **Split handles errors gracefully** - if split function fails or individual conversions error, detailed error information is provided ⭐ NEW!
1941
+ 79. **Combine split with filename mapping** - use `mapFilename()` to include source filename information in each split output ⭐ NEW!
1852
1942
 
1853
1943
  ---
1854
1944
 
@@ -1 +1 @@
1
- {"version":3,"file":"BatchProcessor.d.ts","sourceRoot":"","sources":["../../src/core/BatchProcessor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,aAAa,EACb,WAAW,EAEX,gBAAgB,EAGjB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAKxC;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,OAAO,CAAyB;gBAE5B,SAAS,EAAE,SAAS,EAAE,OAAO,GAAE,YAAiB;IAU5D;;OAEG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAyDrD;;OAEG;YACW,mBAAmB;IA2CjC;;OAEG;YACW,iBAAiB;IA4E/B;;OAEG;YACW,WAAW;IAwCzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAczB;;OAEG;IACH,OAAO,CAAC,UAAU;IAQlB;;OAEG;IACG,SAAS,CACb,OAAO,EAAE,gBAAgB,EAAE,EAC3B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC,IAAI,CAAC;IAyBhB;;OAEG;IACH,sBAAsB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM;IAcvD;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAK1B"}
1
+ {"version":3,"file":"BatchProcessor.d.ts","sourceRoot":"","sources":["../../src/core/BatchProcessor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,aAAa,EACb,WAAW,EAEX,gBAAgB,EAGjB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAKxC;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,OAAO,CAAyB;gBAE5B,SAAS,EAAE,SAAS,EAAE,OAAO,GAAE,YAAiB;IAU5D;;OAEG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAyDrD;;OAEG;YACW,mBAAmB;IA2CjC;;OAEG;YACW,iBAAiB;IA4E/B;;OAEG;YACW,WAAW;IAiDzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAczB;;OAEG;IACH,OAAO,CAAC,UAAU;IAQlB;;OAEG;IACG,SAAS,CACb,OAAO,EAAE,gBAAgB,EAAE,EAC3B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC,IAAI,CAAC;IAyBhB;;OAEG;IACH,sBAAsB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM;IAcvD;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAK1B"}
@@ -191,7 +191,16 @@ class BatchProcessor {
191
191
  save: async () => { },
192
192
  toJSON: () => '{}',
193
193
  toYAML: () => '{}',
194
+ toFiles: () => [{ filename: 'error.yml', content: '{}' }], // ⭐ NEW! Required method
194
195
  print: () => { },
196
+ getSummary: () => ({
197
+ success: false,
198
+ fileCount: 1,
199
+ errorCount: 1,
200
+ warningCount: 0,
201
+ unmappedCount: 0,
202
+ duration: 0,
203
+ }),
195
204
  };
196
205
  }
197
206
  }