configforge
v1.0.0
Published
Universal config converter framework with exceptional developer experience
Maintainers
Readme
ConfigForge
A TypeScript library for converting, mapping, and transforming config data in JSON and YAML.
ConfigForge is a universal config converter library with a fluent API
ConfigForge makes it easy to convert configuration files between different formats and structures. Just define your mappings and let ConfigForge handle the rest.
🚀 Quick Start
Installation
npm install configforgeBasic Usage
const { forge } = require('configforge');
// Your source configuration
const sourceConfig = {
name: 'MyApp',
version: '1.0',
author: {
firstName: 'John',
lastName: 'Doe',
},
items: ['apple', 'banana', 'cherry'],
};
// Create converter and define mappings
const converter = forge()
.from('source')
.to('target')
.map('name', 'appName')
.map('version', 'appVersion')
.map('author.firstName', 'authorName')
.map('items[0]', 'firstItem');
// Convert the data
const result = await converter.convert(sourceConfig);
console.log(result.data);
// Output:
// {
// appName: 'MyApp',
// appVersion: '1.0',
// authorName: 'John',
// firstItem: 'apple'
// }📖 How It Works
1. Create a Converter
const converter = forge()
.from('sourceSchema') // Define source
.to('targetSchema'); // Define target2. Map Fields
// Simple field mapping
.map('oldField', 'newField')
// Nested object mapping
.map('user.profile.name', 'displayName')
// Array element mapping
.map('items[0]', 'firstItem')
.map('items[1]', 'secondItem')3. Add Transformations
// Transform values during mapping
.map('name', 'displayName', (value) => value.toUpperCase())
// Access source data in transforms
.map('firstName', 'fullName', (firstName, ctx) => {
return `${firstName} ${ctx.source.lastName}`;
})4. Add Conditional Logic
// Function conditions - only apply when condition returns true
.when(source => source.user?.type === 'admin')
.map('user.permissions', 'adminPermissions')
.map('user.role', 'adminRole')
.end()
// String conditions - only apply when field exists and is truthy
.when('user.isActive')
.map('user.lastLogin', 'lastActiveLogin')
.end()5. Merge Multiple Fields
// Combine multiple source fields into one target field
.merge(['firstName', 'lastName'], 'fullName', (first, last) => `${first} ${last}`)
// Merge with transformation
.merge(['basePrice', 'tax'], 'totalPrice',
(price, tax) => price + tax,
total => `$${total.toFixed(2)}`
)
// Merge complex data
.merge(['user.profile', 'user.settings'], 'userInfo', (profile, settings) => ({
...profile,
...settings
}))6. Set Default Values
// Set static default values
.defaults({
version: '1.0.0',
enabled: true,
environment: 'production'
})
// Set dynamic default values using functions
.defaults({
timestamp: () => new Date().toISOString(),
id: () => `user_${Date.now()}`,
sessionId: () => Math.random().toString(36).substr(2, 9)
})7. Add Lifecycle Hooks
// Before hooks - run before conversion starts
.before((sourceData) => {
console.log('Starting conversion...');
// Modify source data if needed
return {
...sourceData,
processed: true
};
})
// After hooks - run after conversion completes
.after((targetData) => {
console.log('Conversion completed!');
// Add computed fields or modify target data
return {
...targetData,
processedAt: new Date().toISOString(),
checksum: calculateChecksum(targetData)
};
})
// Multiple hooks execute in order
.before(validateInput)
.before(preprocessData)
.after(addMetadata)
.after(logResults)
// Async hooks are supported
.before(async (data) => {
const enrichedData = await fetchAdditionalData(data.userId);
return { ...data, ...enrichedData };
})8. Add Validation
const { validators } = require('configforge');
// Add field validation
.validate('email', validators.email)
.validate('age', validators.all(
validators.required,
validators.type('number'),
validators.range(18, 120)
))
.validate('username', validators.all(
validators.required,
validators.minLength(3),
validators.pattern(/^[a-zA-Z0-9_]+$/, 'Only letters, numbers, and underscores allowed')
))
// Custom validation with context
.validate('password', (value, context) => {
if (context.source.confirmPassword !== value) {
return 'Passwords do not match';
}
return validators.minLength(8)(value, context);
})9. Convert Data
// Convert object data
const result = await converter.convert(sourceConfig);
// Convert from file
const result = await converter.convert('./config.yml');
// Synchronous conversion (objects only)
const result = converter.convertSync(sourceConfig);🔧 Working with Results
const result = await converter.convert(data);
// Access converted data
console.log(result.data);
// Check conversion statistics
console.log(result.stats);
// {
// fieldsProcessed: 10,
// fieldsMapped: 5,
// fieldsUnmapped: 5,
// transformsApplied: 2,
// duration: 3
// }
// See unmapped fields
console.log(result.unmapped);
// ['author.lastName', 'items[1]', 'items[2]']
// Pretty print report
result.print();
// Save to file
await result.save('./output.json'); // Saves as JSON
await result.save('./output.yml'); // Saves as YAML
// Get as string
const jsonString = result.toJSON();
const yamlString = result.toYAML();📝 Real Examples
Example 1: Simple Config Transformation
const { forge } = require('configforge');
const oldConfig = {
app_name: 'MyApp',
app_version: '2.1.0',
database: {
host: 'localhost',
port: 5432,
},
};
const converter = forge()
.from('old')
.to('new')
.map('app_name', 'name')
.map('app_version', 'version')
.map('database.host', 'db.hostname')
.map('database.port', 'db.port')
.defaults({
environment: 'development',
});
const result = await converter.convert(oldConfig);
console.log(result.data);
// {
// name: 'MyApp',
// version: '2.1.0',
// db: {
// hostname: 'localhost',
// port: 5432
// },
// environment: 'development'
// }Example 2: With Transformations
const userConfig = {
user: {
first_name: 'john',
last_name: 'doe',
email: '[email protected]',
},
settings: {
theme: 'dark',
notifications: 'true',
},
};
const converter = forge()
.from('user')
.to('profile')
.map(
'user.first_name',
'name',
name => name.charAt(0).toUpperCase() + name.slice(1)
)
.map('user.email', 'email', email => email.toLowerCase())
.map('user.first_name', 'displayName', (firstName, ctx) => {
const lastName = ctx.source.user.last_name;
return `${firstName} ${lastName}`.replace(/\b\w/g, l => l.toUpperCase());
})
.map(
'settings.notifications',
'notificationsEnabled',
value => value === 'true'
);
const result = await converter.convert(userConfig);
console.log(result.data);
// {
// name: 'John',
// email: '[email protected]',
// displayName: 'John Doe',
// notificationsEnabled: true
// }Example 3: Array Processing with forEach()
// Simple fruit inventory
const fruitData = {
storeId: 'STORE-001',
fruits: [
{
name: 'Apple',
color: 'red',
price: 1.5,
quantity: 100,
},
{
name: 'Banana',
color: 'yellow',
price: 0.75,
quantity: 80,
},
{
name: 'Orange',
color: 'orange',
price: 1.25,
quantity: 60,
},
],
};
const converter = forge()
.from('inventory')
.to('catalog')
.map('storeId', 'storeId')
.forEach('fruits', (fruit, index) => {
return {
id: index + 1,
fruitName: fruit.name,
displayColor: fruit.color,
pricePerItem: fruit.price,
inStock: fruit.quantity,
totalValue: fruit.price * fruit.quantity,
isExpensive: fruit.price > 1.0,
};
});
const result = await converter.convert(fruitData);
console.log(result.data);
// {
// storeId: 'STORE-001',
// fruits: [
// {
// id: 1,
// fruitName: 'Apple',
// displayColor: 'red',
// pricePerItem: 1.50,
// inStock: 100,
// totalValue: 150,
// isExpensive: true
// },
// {
// id: 2,
// fruitName: 'Banana',
// displayColor: 'yellow',
// pricePerItem: 0.75,
// inStock: 80,
// totalValue: 60,
// isExpensive: false
// },
// // ... more fruits
// ]
// }Example 4: Conditional Mapping with when()
// Different types of pets need different mappings
const petData = {
type: 'dog',
name: 'Buddy',
age: 3,
dogInfo: {
breed: 'Golden Retriever',
tricks: ['sit', 'stay', 'fetch'],
},
catInfo: {
breed: 'Persian',
indoor: true,
},
birdInfo: {
species: 'Parrot',
canTalk: true,
},
};
const converter = forge()
.from('petData')
.to('petProfile')
.map('name', 'petName')
.map('age', 'petAge')
.map('type', 'animalType')
// Only map dog-specific info for dogs
.when(source => source.type === 'dog')
.map('dogInfo.breed', 'breed')
.map('dogInfo.tricks', 'knownTricks')
.end()
// Only map cat-specific info for cats
.when(source => source.type === 'cat')
.map('catInfo.breed', 'breed')
.map('catInfo.indoor', 'isIndoorCat')
.end()
// Only map bird-specific info for birds
.when(source => source.type === 'bird')
.map('birdInfo.species', 'species')
.map('birdInfo.canTalk', 'canSpeak')
.end();
const result = await converter.convert(petData);
console.log(result.data);
// {
// petName: 'Buddy',
// petAge: 3,
// animalType: 'dog',
// breed: 'Golden Retriever',
// knownTricks: ['sit', 'stay', 'fetch']
// }Example 5: Merge Multiple Fields with merge()
// Simple student report card
const studentData = {
student: {
firstName: 'Alice',
lastName: 'Smith',
},
grades: {
math: 85,
english: 92,
science: 78,
},
activities: ['soccer', 'chess', 'art'],
info: {
grade: '5th',
teacher: 'Ms. Johnson',
},
};
const converter = forge()
.from('report')
.to('summary')
// Merge student name fields
.merge(
['student.firstName', 'student.lastName'],
'studentName',
(first, last) => `${first} ${last}`
)
// Calculate total with transformation
.merge(
['order.basePrice', 'order.tax', 'order.shipping'],
'subtotal',
(base, tax, shipping) => base + tax + shipping
)
.merge(
['order.basePrice', 'order.tax', 'order.shipping', 'order.discount'],
'total',
(base, tax, shipping, discount) => base + tax + shipping - discount,
total => `$${total.toFixed(2)}`
) // Transform to currency format
// Merge arrays and objects
.merge(['items', 'metadata'], 'orderSummary', (items, meta) => ({
itemCount: items.length,
items: items.join(', '),
...meta,
}))
// Simple field mappings
.map('customer.email', 'billingEmail');
const result = await converter.convert(orderData);
console.log(result.data);
// {
// customerName: 'John Doe',
// subtotal: 121.49,
// total: '$106.49',
// orderSummary: {
// itemCount: 3,
// items: 'laptop, mouse, keyboard',
// orderDate: '2023-12-01',
// source: 'web'
// },
// billingEmail: '[email protected]'
// }Example 6: Data Validation
const { forge, validators } = require('configforge');
// User registration form data
const formData = {
personalInfo: {
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
age: 28,
},
accountInfo: {
username: 'johndoe123',
password: 'SecurePass123!',
confirmPassword: 'SecurePass123!',
},
preferences: {
newsletter: 'yes',
theme: 'dark',
language: 'en',
},
};
const converter = forge()
.from('form')
.to('user')
// Map fields
.merge(
['personalInfo.firstName', 'personalInfo.lastName'],
'fullName',
(first, last) => `${first} ${last}`
)
.map('personalInfo.email', 'email')
.map('personalInfo.age', 'age')
.map('accountInfo.username', 'username')
.map('accountInfo.password', 'password')
.map(
'preferences.newsletter',
'subscribeToNewsletter',
value => value === 'yes'
)
.map('preferences.theme', 'theme')
.map('preferences.language', 'language')
// Add validation rules
.validate(
'fullName',
validators.all(
validators.required,
validators.minLength(2),
validators.maxLength(100)
)
)
.validate('email', validators.all(validators.required, validators.email))
.validate(
'age',
validators.all(
validators.required,
validators.type('number'),
validators.range(13, 120)
)
)
.validate(
'username',
validators.all(
validators.required,
validators.minLength(3),
validators.maxLength(20),
validators.pattern(
/^[a-zA-Z0-9_]+$/,
'Username can only contain letters, numbers, and underscores'
)
)
)
.validate(
'password',
validators.all(
validators.required,
validators.minLength(8),
validators.pattern(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/,
'Password must contain lowercase, uppercase, and number'
)
)
)
.validate('theme', validators.oneOf(['light', 'dark', 'auto']))
.validate('language', validators.oneOf(['en', 'es', 'fr', 'de']))
// Custom validation
.validate('password', (value, context) => {
if (context.source.accountInfo.confirmPassword !== value) {
return 'Passwords do not match';
}
return true;
});
const result = await converter.convert(formData);
if (result.errors.length > 0) {
console.log('Validation errors:');
result.errors.forEach(error => {
console.log(`- ${error.path}: ${error.message}`);
});
} else {
console.log('User data is valid!');
console.log(result.data);
// {
// fullName: 'John Doe',
// email: '[email protected]',
// age: 28,
// username: 'johndoe123',
// password: 'SecurePass123!',
// subscribeToNewsletter: true,
// theme: 'dark',
// language: 'en'
// }
}Example 7: Defaults and Hooks
// Student data with missing fields
const studentData = {
student: {
firstName: 'Alice',
lastName: 'Smith',
// age is missing
},
grades: {
math: 85,
english: 92,
// science grade is missing
},
// activities array is missing
info: {
grade: '5th',
teacher: 'Ms. Johnson',
// school year is missing
},
};
const converter = forge()
.from('studentReport')
.to('completeProfile')
// Basic mappings
.map('student.firstName', 'firstName')
.map('student.lastName', 'lastName')
.map('student.age', 'age')
.map('grades.math', 'mathGrade')
.map('grades.english', 'englishGrade')
.map('grades.science', 'scienceGrade')
.map('activities', 'extracurriculars')
.map('info.grade', 'gradeLevel')
.map('info.teacher', 'teacher')
.map('info.schoolYear', 'academicYear')
// Set default values for missing fields
.defaults({
age: 10, // Default age for 5th graders
scienceGrade: 80, // Default science grade
extracurriculars: ['reading'], // Default activity
academicYear: () => {
// Dynamic default - current school year
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth();
return month >= 8 ? `${year}-${year + 1}` : `${year - 1}-${year}`;
},
status: 'active',
lastUpdated: () => new Date().toISOString(),
})
// Add before hook to log conversion start
.before(data => {
console.log('🔄 Starting conversion...');
console.log(
`Processing student: ${data.student?.firstName} ${data.student?.lastName}`
);
return data; // Return the data unchanged
})
// Add after hook to calculate grade average
.after(data => {
console.log('✅ Conversion completed!');
// Create full name from first and last name
if (data.firstName && data.lastName) {
data.fullName = `${data.firstName} ${data.lastName}`;
}
// Calculate and add grade average
const { mathGrade, englishGrade, scienceGrade } = data;
if (mathGrade && englishGrade && scienceGrade) {
data.gradeAverage = Math.round(
(mathGrade + englishGrade + scienceGrade) / 3
);
console.log(`Calculated grade average: ${data.gradeAverage}`);
}
return data; // Return the modified data
});
const result = await converter.convert(studentData);
console.log(result.data);
// {
// firstName: 'Alice',
// lastName: 'Smith',
// mathGrade: 85,
// englishGrade: 92,
// gradeLevel: '5th',
// teacher: 'Ms. Johnson',
// age: 10,
// scienceGrade: 80,
// extracurriculars: ['reading'],
// academicYear: '2024-2025',
// status: 'active',
// lastUpdated: '2024-12-23T06:22:05.491Z',
// fullName: 'Alice Smith',
// gradeAverage: 86
// }Example 8: File Conversion
// Convert YAML file to JSON structure
const converter = forge()
.from('yaml')
.to('json')
.map('server.host', 'hostname')
.map('server.port', 'port')
.map('database.url', 'dbUrl');
// Read and convert file
const result = await converter.convert('./config.yml');
// Save as JSON
await result.save('./config.json');Example 9: Plugin System with Built-in Plugins
const {
forge,
createValidationPlugin,
createAuditPlugin,
createBackupPlugin,
} = require('configforge');
// Create converter with plugin support
const converter = forge()
.from('userForm')
.to('userProfile')
.map('personalInfo.firstName', 'firstName')
.map('personalInfo.lastName', 'lastName')
.map('personalInfo.email', 'email')
.map('accountInfo.username', 'username')
.merge(
['personalInfo.firstName', 'personalInfo.lastName'],
'fullName',
(first, last) => `${first} ${last}`
);
// Add built-in plugins
const validationPlugin = createValidationPlugin({
strictMode: true,
failOnWarnings: false,
});
const auditPlugin = createAuditPlugin({
logFile: './conversion-audit.log',
logLevel: 'detailed',
includeData: false,
});
const backupPlugin = createBackupPlugin({
backupDir: './backups',
keepVersions: 5,
timestampFormat: 'YYYY-MM-DD_HH-mm-ss',
});
// Install plugins
await converter.use(validationPlugin);
await converter.use(auditPlugin);
await converter.use(backupPlugin);
// Convert with plugin enhancements
const userData = {
personalInfo: {
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
},
accountInfo: {
username: 'johndoe123',
},
};
const result = await converter.convert(userData);
// Plugins automatically:
// - Validate data quality (ValidationPlugin)
// - Log conversion activities (AuditPlugin)
// - Create backups of source data (BackupPlugin)
// - Handle errors gracefully with detailed reporting
console.log(result.data);
// {
// firstName: 'John',
// lastName: 'Doe',
// email: '[email protected]',
// username: 'johndoe123',
// fullName: 'John Doe'
// }
// Check plugin results
console.log('Validation results:', result.context?.validationResults);
console.log('Backup created:', result.context?.backupPath);Example 10: Batch Processing and Performance Features
const {
forge,
BatchProcessor,
IncrementalProcessor,
PerformanceMonitor,
} = require('configforge');
// Create converter for processing multiple config files
const converter = forge()
.from('legacy-config')
.to('modern-config')
.map('app_name', 'application.name')
.map('app_version', 'application.version')
.map('database.host', 'db.hostname')
.map('database.port', 'db.port')
.map('server.port', 'application.port')
.defaults({
environment: 'production',
createdAt: () => new Date().toISOString(),
});
// 1. Batch Processing - Convert multiple files efficiently
const batchResult = await converter.convertBatch(
['./configs/app1.yml', './configs/app2.yml', './configs/app3.yml'],
{
parallel: true,
workers: 3,
errorStrategy: 'continue',
progressCallback: progress => {
console.log(`Progress: ${progress.completed}/${progress.total} files`);
},
}
);
console.log(
`Processed ${batchResult.stats.successfulFiles} files successfully`
);
console.log(`Failed: ${batchResult.stats.failedFiles} files`);
console.log(`Total duration: ${batchResult.stats.totalDuration}ms`);
// 2. Incremental Processing - Only process changed files
const incrementalResult = await converter.convertIncremental(
'./config.yml',
'./output/config.json',
'./.cache' // Cache directory
);
if (incrementalResult) {
console.log('File was processed (changed since last run)');
} else {
console.log('File skipped (no changes detected)');
}
// 3. Incremental Batch Processing
const incrementalBatchResult = await converter.convertIncrementalBatch(
['./configs/app1.yml', './configs/app2.yml', './configs/app3.yml'],
'./output',
{
cacheDir: './.cache',
getOutputPath: inputPath => {
const fileName = inputPath.split('/').pop().replace('.yml', '.json');
return `./output/${fileName}`;
},
}
);
console.log(`Processed: ${incrementalBatchResult.processed.length} files`);
console.log(
`Skipped: ${incrementalBatchResult.skipped.length} files (unchanged)`
);
// 4. Performance Monitoring
const monitor = new PerformanceMonitor();
monitor.start();
// Track individual steps
monitor.startStep('parse');
const result = await converter.convert('./large-config.yml');
monitor.endStep('parse');
// Record file sizes for throughput calculation
monitor.recordSizes(1024 * 1024, 800 * 1024); // 1MB input, 800KB output
// Get detailed performance metrics
const metrics = monitor.finish(result.stats);
const report = monitor.createReport(metrics);
console.log(report);
// === Performance Report ===
//
// Timing Breakdown:
// Parse: 15.23ms
// Map: 8.45ms
// Transform: 3.21ms
// Validate: 2.10ms
// Serialize: 5.67ms
// Total: 34.66ms
//
// Throughput:
// Fields/sec: 2890
// Transforms/sec: 1445
// Bytes/sec: 29.64 MB/s
//
// Memory Usage:
// Heap Used: 45.23 MB
// Heap Total: 67.89 MB
// RSS: 89.12 MB
//
// File Sizes:
// Input: 1.00 MB
// Output: 800.00 KB
// Ratio: 80.0%
// 5. Performance Benchmarking
const { PerformanceBenchmark } = require('configforge');
const sequentialOperation = async () => {
return converter.convertBatch(['./config1.yml', './config2.yml'], {
parallel: false,
});
};
const parallelOperation = async () => {
return converter.convertBatch(['./config1.yml', './config2.yml'], {
parallel: true,
workers: 2,
});
};
const benchmarks = [
{ name: 'sequential-processing', operation: sequentialOperation },
{ name: 'parallel-processing', operation: parallelOperation },
];
const benchmarkResults = await PerformanceBenchmark.compareBenchmarks(
benchmarks,
10
);
const comparisonReport =
PerformanceBenchmark.createComparisonReport(benchmarkResults);
console.log(comparisonReport);
// === Benchmark Comparison ===
//
// Results (sorted by average time):
// parallel-processing:
// Average: 45.23ms (fastest)
// Range: 42.10ms - 48.90ms
// Std Dev: 2.15ms
// Ops/sec: 22
//
// sequential-processing:
// Average: 78.45ms (1.73x slower)
// Range: 75.20ms - 82.10ms
// Std Dev: 2.89ms
// Ops/sec: 13
// 6. Advanced Batch Processing with Custom Options
const advancedBatchResult = await converter.convertBatch(
[
'./configs/*.yml', // Glob patterns supported
],
{
parallel: true,
workers: 4,
errorStrategy: 'fail-fast', // Stop on first error
progressCallback: progress => {
const percentage = Math.round(
(progress.completed / progress.total) * 100
);
console.log(`[${percentage}%] Processing: ${progress.current}`);
if (progress.failed > 0) {
console.log(`⚠️ ${progress.failed} files failed`);
}
},
}
);
// Save all results to output directory
const batchProcessor = new BatchProcessor(converter);
await batchProcessor.saveBatch(advancedBatchResult.results, './output', [
'./configs/app1.yml',
'./configs/app2.yml',
]);
// 7. Cache Management for Incremental Processing
const incrementalProcessor = new IncrementalProcessor(converter, './.cache');
// Get cache statistics
const cacheStats = incrementalProcessor.getCacheStats();
console.log(`Cache entries: ${cacheStats.totalEntries}`);
console.log(`Successful: ${cacheStats.successfulEntries}`);
console.log(`Failed: ${cacheStats.failedEntries}`);
// Clean up stale cache entries
const removedEntries = await incrementalProcessor.cleanupCache();
console.log(`Removed ${removedEntries} stale cache entries`);
// Clear entire cache
await incrementalProcessor.clearCache();
console.log('Cache cleared');Example 11: CLI Generation System
const { forge, CLIGenerator } = require('configforge');
// Create your converter
const converter = forge()
.from('legacy')
.to('modern')
.map('app_name', 'name')
.map('app_version', 'version')
.map('database.host', 'db.hostname')
.map('database.port', 'db.port')
.defaults({
environment: 'production',
});
// Generate CLI for your converter
const cli = CLIGenerator.forConverter(converter, {
name: 'config-converter',
version: '1.0.0',
description: 'Convert legacy config to modern format',
});
// Add custom commands
cli.addCommand({
name: 'migrate',
description: 'Migrate all configs in a directory',
options: [
{
flags: '-d, --directory <dir>',
description: 'Directory containing config files',
},
],
action: async options => {
// Custom migration logic
console.log(`Migrating configs in ${options.directory}`);
},
});
// Parse command line arguments
cli.parse();
// Now you can use your CLI:
// $ config-converter convert input.yml output.json
// $ config-converter validate config.yml
// $ config-converter profile save my-config
// $ config-converter profile list
// $ config-converter migrate -d ./configs🎯 Key Features
- ✅ Simple API: Just map fields and convert
- ✅ Pipeline Architecture: Robust internal processing with configurable steps and error handling
- ✅ No direct Mapper usage needed: The
convert()method handles everything - ✅ Nested object support: Use dot notation like
user.profile.name - ✅ Array access: Use bracket notation like
items[0] - ✅ Transformations: Transform values during mapping
- ✅ Conditional mapping: Use
when()for conditional logic - ✅ Multi-field merging: Use
merge()to combine multiple sources into one target - ✅ Default values: Set fallback values
- ✅ Comprehensive Error Handling: Advanced error reporting with context and suggestions ⭐ ENHANCED!
- ✅ CLI Generation System: Create command-line interfaces for converters ⭐ ENHANCED!
- ✅ Plugin System: Extensible architecture with built-in plugins for validation, auditing, and backups ⭐ ENHANCED!
- ✅ Batch Processing: Efficiently process multiple files with parallel support and progress tracking ⭐ ENHANCED!
- ✅ Incremental Processing: Only process changed files using content hashing and modification times ⭐ ENHANCED!
- ✅ Performance Monitoring: Detailed performance tracking, benchmarking, and reporting ⭐ ENHANCED!
- ✅ File support: Convert YAML, JSON files directly
- ✅ Statistics: Get detailed conversion reports
- ✅ TypeScript: Full type safety
🔄 Current Implementation Status
✅ Working Features:
- Basic field mapping with
map() - Nested object and array access
- Value transformations
- Default values with
defaults()⭐ ENHANCED! - Array/object processing with
forEach() - Conditional mapping with
when() - Multi-field merging with
merge() - Field validation with
validate() - Lifecycle hooks with
before()andafter()⭐ NEW! - Advanced error handling and reporting system ⭐ NEW!
- CLI generation system with command-line interfaces ⭐ ENHANCED!
- Plugin system foundation with built-in plugins ⭐ NEW!
- Batch processing with parallel support and progress tracking ⭐ ENHANCED!
- Incremental processing with content hashing and modification times ⭐ ENHANCED!
- Performance monitoring with detailed benchmarking and reporting ⭐ ENHANCED!
- File parsing (YAML, JSON)
- Conversion statistics and reporting
- Async and sync conversion support
🚧 Coming Soon:
- Advanced plugin ecosystem
💡 Tips
- You don't need to use the Mapper class directly - just use
converter.convert() - Use dot notation for nested objects:
'user.profile.name' - Use bracket notation for arrays:
'items[0]','items[1]' - Transform functions get the value and context:
(value, ctx) => { ... } - Use conditional mapping with
when()for type-specific logic:when(source => source.accountType === 'premium') - Use merge() to combine multiple fields:
merge(['field1', 'field2'], 'result', (a, b) => a + b) - Use validation to ensure data quality:
validate('email', validators.email) - Combine validators with
validators.all()for multiple rules - Always call
.end()after conditional mappings to return to the main converter - Check
result.errorsto see validation failures - Check
result.unmappedto see which fields weren't mapped - Use
result.print()for a nice conversion report - Use
defaults()to provide fallback values for missing fields:defaults({ status: 'active' }) - Use function defaults for dynamic values:
defaults({ timestamp: () => new Date().toISOString() }) - Use
before()hooks to preprocess source data before conversion - Use
after()hooks to postprocess target data after conversion - Hooks can be async - just use
async (data) => { ... }and they'll be awaited - Multiple hooks execute in order - add as many as you need for complex workflows
- Error handling provides helpful suggestions - when field mapping fails, you'll get suggestions for similar field names
- Errors include rich context - see exactly where and why conversions failed with detailed error information
- Use error categories to filter and handle different types of errors (validation, mapping, parsing, etc.)
- Create CLIs for converters - use
CLIGenerator.forConverter(converter)to generate command-line interfaces - Use CLI profiles - save converter configurations as profiles for reuse:
cli profile save my-converter - CLI supports batch processing - convert multiple files at once with pattern matching and parallel processing
- Use built-in plugins - leverage ValidationPlugin, AuditPlugin, and BackupPlugin for enhanced functionality
- Plugin hooks execute in order - plugins can intercept and modify data at different conversion stages
- Error hooks provide recovery - plugins can handle errors gracefully and provide fallback behavior
- Use batch processing for multiple files -
convertBatch()processes multiple files efficiently with parallel support - Enable parallel processing - set
parallel: trueand configureworkersfor faster batch processing - Use incremental processing -
convertIncremental()only processes files that have changed since last run - Monitor performance - use
PerformanceMonitorto track conversion speed, memory usage, and throughput - Benchmark different approaches - use
PerformanceBenchmarkto compare sequential vs parallel processing - Configure error strategies - use
errorStrategy: 'continue'to process all files even if some fail, or'fail-fast'to stop on first error - Track progress - provide
progressCallbackto monitor batch processing progress in real-time - Cache management - use
IncrementalProcessor.getCacheStats()andcleanupCache()to manage incremental processing cache - Save batch results - use
BatchProcessor.saveBatch()to save all conversion results to an output directory - Use forge() with options - pass
ForgeOptionstoforge({ strict: true, parallel: true })to configure converter behavior globally
That's it! ConfigForge makes config conversion simple and straightforward. No complex setup, no direct class manipulation - just define your mappings and convert.
License
This package is licensed under the PolyForm Noncommercial License 1.0.0.
See the LICENSE file for full terms.
