This repository has been archived on 2025-08-03. You can view files and clone it, but cannot push or open issues or pull requests.
network-orbit/examples/migration-examples.ts

932 lines
31 KiB
TypeScript

/**
* Comprehensive Migration Examples for DebrosFramework
*
* This file demonstrates the migration system capabilities:
* - Schema evolution with field additions and modifications
* - Data transformation and migration
* - Rollback scenarios and recovery
* - Cross-model relationship changes
* - Performance optimization migrations
* - Version management and dependency handling
*/
import { MigrationManager, Migration } from '../src/framework/migrations/MigrationManager';
import { MigrationBuilder, createMigration } from '../src/framework/migrations/MigrationBuilder';
import { SocialPlatformFramework } from './framework-integration';
export class MigrationExamples {
private migrationManager: MigrationManager;
private framework: SocialPlatformFramework;
constructor(framework: SocialPlatformFramework) {
this.framework = framework;
this.migrationManager = new MigrationManager(
(framework as any).databaseManager,
(framework as any).shardManager
);
}
async runAllExamples(): Promise<void> {
console.log('🔄 Running comprehensive migration examples...\n');
await this.createExampleMigrations();
await this.basicMigrationExamples();
await this.complexDataTransformationExamples();
await this.rollbackAndRecoveryExamples();
await this.performanceOptimizationExamples();
await this.crossModelMigrationExamples();
await this.versionManagementExamples();
console.log('✅ All migration examples completed!\n');
}
async createExampleMigrations(): Promise<void> {
console.log('📝 Creating Example Migrations');
console.log('==============================\n');
// Migration 1: Add timestamps to User model
const addTimestampsMigration = createMigration(
'add_user_timestamps',
'1.0.1',
'Add timestamps to User model'
)
.description('Add createdAt and updatedAt timestamps to User model for better tracking')
.author('Framework Team')
.tags('schema', 'timestamps', 'user')
.addTimestamps('User')
.addValidator(
'validate_timestamp_format',
'Ensure timestamp fields are valid numbers',
async (context) => {
const errors: string[] = [];
const warnings: string[] = [];
// Validate that all timestamps are valid
return { valid: errors.length === 0, errors, warnings };
}
)
.build();
// Migration 2: Add user profile enhancements
const userProfileEnhancement = createMigration(
'enhance_user_profile',
'1.1.0',
'Enhance User profile with additional fields'
)
.description('Add profile picture, location, and social links to User model')
.dependencies('add_user_timestamps')
.addField('User', 'profilePicture', {
type: 'string',
required: false,
validate: (value) => !value || value.startsWith('http')
})
.addField('User', 'location', {
type: 'string',
required: false
})
.addField('User', 'socialLinks', {
type: 'array',
required: false,
default: []
})
.addField('User', 'isVerified', {
type: 'boolean',
required: false,
default: false
})
.build();
// Migration 3: Restructure Post content
const postContentRestructure = createMigration(
'restructure_post_content',
'1.2.0',
'Restructure Post content with rich metadata'
)
.description('Transform Post content from plain text to rich content structure')
.addField('Post', 'contentType', {
type: 'string',
required: false,
default: 'text'
})
.addField('Post', 'metadata', {
type: 'object',
required: false,
default: {}
})
.transformData('Post', (post) => {
// Transform existing content to new structure
const wordCount = post.content ? post.content.split(' ').length : 0;
const hasLinks = post.content ? /https?:\/\//.test(post.content) : false;
return {
...post,
contentType: hasLinks ? 'rich' : 'text',
metadata: {
wordCount,
hasLinks,
transformedAt: Date.now()
}
};
})
.build();
// Migration 4: Add Comment threading
const commentThreading = createMigration(
'add_comment_threading',
'1.3.0',
'Add threading support to Comments'
)
.description('Add parent-child relationships to comments for threading')
.addField('Comment', 'parentId', {
type: 'string',
required: false,
default: null
})
.addField('Comment', 'threadDepth', {
type: 'number',
required: false,
default: 0
})
.addField('Comment', 'childCount', {
type: 'number',
required: false,
default: 0
})
.transformData('Comment', (comment) => {
// All existing comments become root-level comments
return {
...comment,
parentId: null,
threadDepth: 0,
childCount: 0
};
})
.build();
// Migration 5: Performance optimization
const performanceOptimization = createMigration(
'optimize_post_indexing',
'1.4.0',
'Optimize Post model for better query performance'
)
.description('Add computed fields and indexes for better query performance')
.addField('Post', 'searchText', {
type: 'string',
required: false,
default: ''
})
.addField('Post', 'popularityScore', {
type: 'number',
required: false,
default: 0
})
.transformData('Post', (post) => {
// Create searchable text and calculate popularity
const searchText = `${post.title || ''} ${post.content || ''}`.toLowerCase();
const popularityScore = (post.likeCount || 0) * 2 + (post.commentCount || 0);
return {
...post,
searchText,
popularityScore
};
})
.createIndex('Post', ['searchText'])
.createIndex('Post', ['popularityScore'], { name: 'popularity_index' })
.build();
// Register all migrations
const migrations = [
addTimestampsMigration,
userProfileEnhancement,
postContentRestructure,
commentThreading,
performanceOptimization
];
for (const migration of migrations) {
this.migrationManager.registerMigration(migration);
console.log(`✅ Registered migration: ${migration.name} (v${migration.version})`);
}
console.log(`\nRegistered ${migrations.length} example migrations\n`);
}
async basicMigrationExamples(): Promise<void> {
console.log('🔄 Basic Migration Examples');
console.log('===========================\n');
// Get pending migrations
const pendingMigrations = this.migrationManager.getPendingMigrations();
console.log(`Found ${pendingMigrations.length} pending migrations:`);
pendingMigrations.forEach(migration => {
console.log(`- ${migration.name} (v${migration.version})`);
});
// Run a single migration with dry run first
if (pendingMigrations.length > 0) {
const firstMigration = pendingMigrations[0];
console.log(`\nRunning dry run for: ${firstMigration.name}`);
const dryRunResult = await this.migrationManager.runMigration(firstMigration.id, {
dryRun: true
});
console.log('Dry run results:');
console.log(`- Success: ${dryRunResult.success}`);
console.log(`- Estimated records: ${dryRunResult.recordsProcessed}`);
console.log(`- Duration: ${dryRunResult.duration}ms`);
console.log(`- Warnings: ${dryRunResult.warnings.length}`);
// Run the actual migration
console.log(`\nRunning actual migration: ${firstMigration.name}`);
try {
const result = await this.migrationManager.runMigration(firstMigration.id, {
batchSize: 50
});
console.log('Migration results:');
console.log(`- Success: ${result.success}`);
console.log(`- Records processed: ${result.recordsProcessed}`);
console.log(`- Records modified: ${result.recordsModified}`);
console.log(`- Duration: ${result.duration}ms`);
console.log(`- Rollback available: ${result.rollbackAvailable}`);
if (result.warnings.length > 0) {
console.log('- Warnings:', result.warnings);
}
} catch (error) {
console.error(`Migration failed: ${error}`);
}
}
console.log('');
}
async complexDataTransformationExamples(): Promise<void> {
console.log('🔄 Complex Data Transformation Examples');
console.log('=======================================\n');
// Create a complex migration that transforms user data
const userDataNormalization = createMigration(
'normalize_user_data',
'2.0.0',
'Normalize and clean user data'
)
.description('Clean up user data, normalize email formats, and merge duplicate accounts')
.transformData('User', (user) => {
// Normalize email to lowercase
if (user.email) {
user.email = user.email.toLowerCase().trim();
}
// Clean up username
if (user.username) {
user.username = user.username.trim().replace(/[^a-zA-Z0-9_]/g, '');
}
// Add normalized search fields
user.searchName = (user.username || '').toLowerCase();
user.displayName = user.username || user.email?.split('@')[0] || 'Anonymous';
return user;
})
.addValidator(
'validate_email_uniqueness',
'Ensure email addresses are unique after normalization',
async (context) => {
// Simulation of validation logic
return {
valid: true,
errors: [],
warnings: ['Some duplicate emails may have been found']
};
}
)
.build();
this.migrationManager.registerMigration(userDataNormalization);
// Create a migration that handles relationship data
const postRelationshipMigration = createMigration(
'update_post_relationships',
'2.1.0',
'Update Post relationship structure'
)
.description('Restructure how posts relate to users and add engagement metrics')
.addField('Post', 'engagementScore', {
type: 'number',
required: false,
default: 0
})
.addField('Post', 'lastActivityAt', {
type: 'number',
required: false,
default: Date.now()
})
.customOperation('Post', async (context) => {
context.logger.info('Calculating engagement scores for all posts');
// Simulate complex calculation across related models
const posts = await context.databaseManager.getAllRecords('Post');
for (const post of posts) {
// Get related comments and likes
const comments = await context.databaseManager.getRelatedRecords('Comment', 'postId', post.id);
const likes = post.likeCount || 0;
// Calculate engagement score
const engagementScore = (comments.length * 2) + likes;
const lastActivityAt = comments.length > 0
? Math.max(...comments.map((c: any) => c.createdAt || 0))
: post.createdAt || Date.now();
post.engagementScore = engagementScore;
post.lastActivityAt = lastActivityAt;
await context.databaseManager.updateRecord('Post', post);
}
})
.build();
this.migrationManager.registerMigration(postRelationshipMigration);
console.log('Created complex data transformation migrations');
console.log('- User data normalization');
console.log('- Post relationship updates with engagement scoring');
console.log('');
}
async rollbackAndRecoveryExamples(): Promise<void> {
console.log('↩️ Rollback and Recovery Examples');
console.log('==================================\n');
// Create a migration that might fail
const riskyMigration = createMigration(
'risky_data_migration',
'2.2.0',
'Risky data migration (demonstration)'
)
.description('A migration that demonstrates rollback capabilities')
.addField('User', 'tempField', {
type: 'string',
required: false,
default: 'temp'
})
.customOperation('User', async (context) => {
context.logger.info('Performing risky operation that might fail');
// Simulate a 50% chance of failure for demonstration
if (Math.random() > 0.5) {
throw new Error('Simulated operation failure for rollback demonstration');
}
context.logger.info('Risky operation completed successfully');
})
.build();
this.migrationManager.registerMigration(riskyMigration);
try {
console.log('Running risky migration (may fail)...');
const result = await this.migrationManager.runMigration(riskyMigration.id);
console.log(`Migration result: ${result.success ? 'SUCCESS' : 'FAILED'}`);
if (result.success) {
console.log('Migration succeeded, demonstrating rollback...');
// Demonstrate manual rollback
const rollbackResult = await this.migrationManager.rollbackMigration(riskyMigration.id);
console.log(`Rollback result: ${rollbackResult.success ? 'SUCCESS' : 'FAILED'}`);
console.log(`Rollback duration: ${rollbackResult.duration}ms`);
}
} catch (error) {
console.log(`Migration failed as expected: ${error}`);
// Check migration history
const history = this.migrationManager.getMigrationHistory(riskyMigration.id);
console.log(`Migration attempts: ${history.length}`);
if (history.length > 0) {
const lastAttempt = history[history.length - 1];
console.log(`Last attempt result: ${lastAttempt.success ? 'SUCCESS' : 'FAILED'}`);
console.log(`Rollback available: ${lastAttempt.rollbackAvailable}`);
}
}
// Demonstrate recovery scenarios
console.log('\nDemonstrating recovery scenarios...');
const recoveryMigration = createMigration(
'recovery_migration',
'2.3.0',
'Recovery migration with validation'
)
.description('Migration with comprehensive pre and post validation')
.addValidator(
'pre_migration_check',
'Validate system state before migration',
async (context) => {
context.logger.info('Running pre-migration validation');
return {
valid: true,
errors: [],
warnings: ['System is ready for migration']
};
}
)
.addField('Post', 'recoveryField', {
type: 'string',
required: false,
default: 'recovered'
})
.addValidator(
'post_migration_check',
'Validate migration results',
async (context) => {
context.logger.info('Running post-migration validation');
return {
valid: true,
errors: [],
warnings: ['Migration completed successfully']
};
}
)
.build();
this.migrationManager.registerMigration(recoveryMigration);
console.log('Created recovery migration with validation');
console.log('');
}
async performanceOptimizationExamples(): Promise<void> {
console.log('🚀 Performance Optimization Migration Examples');
console.log('===============================================\n');
// Create migrations that optimize different aspects
const indexOptimization = createMigration(
'optimize_search_indexes',
'3.0.0',
'Optimize search and query performance'
)
.description('Add indexes and computed fields for better query performance')
.createIndex('User', ['email'], { unique: true, name: 'user_email_unique' })
.createIndex('User', ['username'], { unique: true, name: 'user_username_unique' })
.createIndex('Post', ['userId', 'createdAt'], { name: 'user_posts_timeline' })
.createIndex('Post', ['isPublic', 'popularityScore'], { name: 'public_popular_posts' })
.createIndex('Comment', ['postId', 'createdAt'], { name: 'post_comments_timeline' })
.build();
const dataArchiving = createMigration(
'archive_old_data',
'3.1.0',
'Archive old inactive data'
)
.description('Move old inactive data to archive tables for better performance')
.addField('Post', 'isArchived', {
type: 'boolean',
required: false,
default: false
})
.addField('Comment', 'isArchived', {
type: 'boolean',
required: false,
default: false
})
.customOperation('Post', async (context) => {
context.logger.info('Archiving old posts');
const cutoffDate = Date.now() - (365 * 24 * 60 * 60 * 1000); // 1 year ago
const posts = await context.databaseManager.getAllRecords('Post');
let archivedCount = 0;
for (const post of posts) {
if ((post.lastActivityAt || post.createdAt || 0) < cutoffDate &&
(post.engagementScore || 0) < 5) {
post.isArchived = true;
await context.databaseManager.updateRecord('Post', post);
archivedCount++;
}
}
context.logger.info(`Archived ${archivedCount} old posts`);
})
.build();
const cacheOptimization = createMigration(
'optimize_cache_fields',
'3.2.0',
'Add cache-friendly computed fields'
)
.description('Add denormalized fields to reduce query complexity')
.addField('User', 'postCount', {
type: 'number',
required: false,
default: 0
})
.addField('User', 'totalEngagement', {
type: 'number',
required: false,
default: 0
})
.addField('Post', 'commentCount', {
type: 'number',
required: false,
default: 0
})
.customOperation('User', async (context) => {
context.logger.info('Computing user statistics');
const users = await context.databaseManager.getAllRecords('User');
for (const user of users) {
const posts = await context.databaseManager.getRelatedRecords('Post', 'userId', user.id);
const totalEngagement = posts.reduce((sum: number, post: any) =>
sum + (post.engagementScore || 0), 0);
user.postCount = posts.length;
user.totalEngagement = totalEngagement;
await context.databaseManager.updateRecord('User', user);
}
})
.build();
// Register performance migrations
[indexOptimization, dataArchiving, cacheOptimization].forEach(migration => {
this.migrationManager.registerMigration(migration);
console.log(`✅ Registered: ${migration.name}`);
});
console.log('\nPerformance optimization migrations created:');
console.log('- Search index optimization');
console.log('- Data archiving for old content');
console.log('- Cache-friendly denormalized fields');
console.log('');
}
async crossModelMigrationExamples(): Promise<void> {
console.log('🔗 Cross-Model Migration Examples');
console.log('=================================\n');
// Migration that affects multiple models and their relationships
const relationshipRestructure = createMigration(
'restructure_follow_system',
'4.0.0',
'Restructure follow system with categories'
)
.description('Add follow categories and mutual follow detection')
.addField('Follow', 'category', {
type: 'string',
required: false,
default: 'general'
})
.addField('Follow', 'isMutual', {
type: 'boolean',
required: false,
default: false
})
.addField('Follow', 'strength', {
type: 'number',
required: false,
default: 1
})
.customOperation('Follow', async (context) => {
context.logger.info('Analyzing follow relationships');
const follows = await context.databaseManager.getAllRecords('Follow');
const mutualMap = new Map<string, Set<string>>();
// Build mutual follow map
follows.forEach((follow: any) => {
if (!mutualMap.has(follow.followerId)) {
mutualMap.set(follow.followerId, new Set());
}
mutualMap.get(follow.followerId)!.add(follow.followingId);
});
// Update mutual status
for (const follow of follows) {
const reverseExists = mutualMap.get(follow.followingId)?.has(follow.followerId);
follow.isMutual = Boolean(reverseExists);
// Calculate relationship strength based on mutual status and activity
follow.strength = follow.isMutual ? 2 : 1;
await context.databaseManager.updateRecord('Follow', follow);
}
})
.build();
const contentCategorization = createMigration(
'add_content_categories',
'4.1.0',
'Add content categorization system'
)
.description('Add categories and tags to posts and improve content discovery')
.addField('Post', 'category', {
type: 'string',
required: false,
default: 'general'
})
.addField('Post', 'subcategory', {
type: 'string',
required: false
})
.addField('Post', 'autoTags', {
type: 'array',
required: false,
default: []
})
.transformData('Post', (post) => {
// Auto-categorize posts based on content
const content = (post.content || '').toLowerCase();
let category = 'general';
let autoTags: string[] = [];
if (content.includes('tech') || content.includes('programming')) {
category = 'technology';
autoTags.push('tech');
} else if (content.includes('art') || content.includes('design')) {
category = 'creative';
autoTags.push('art');
} else if (content.includes('news') || content.includes('update')) {
category = 'news';
autoTags.push('news');
}
// Extract hashtags as auto tags
const hashtags = content.match(/#\w+/g) || [];
autoTags.push(...hashtags.map(tag => tag.slice(1)));
return {
...post,
category,
autoTags: [...new Set(autoTags)] // Remove duplicates
};
})
.build();
// Register cross-model migrations
[relationshipRestructure, contentCategorization].forEach(migration => {
this.migrationManager.registerMigration(migration);
console.log(`✅ Registered: ${migration.name}`);
});
console.log('\nCross-model migrations demonstrate:');
console.log('- Complex relationship analysis and updates');
console.log('- Multi-model data transformation');
console.log('- Automatic content categorization');
console.log('');
}
async versionManagementExamples(): Promise<void> {
console.log('📋 Version Management Examples');
console.log('==============================\n');
// Demonstrate migration ordering and dependencies
const allMigrations = this.migrationManager.getMigrations();
console.log('Migration dependency chain:');
allMigrations.forEach(migration => {
const deps = migration.dependencies?.join(', ') || 'None';
console.log(`- ${migration.name} (v${migration.version}) depends on: ${deps}`);
});
// Show pending migrations in order
const pendingMigrations = this.migrationManager.getPendingMigrations();
console.log(`\nPending migrations (${pendingMigrations.length}):`);
pendingMigrations.forEach((migration, index) => {
console.log(`${index + 1}. ${migration.name} (v${migration.version})`);
});
// Demonstrate batch migration with different strategies
console.log('\nRunning pending migrations with different strategies:');
if (pendingMigrations.length > 0) {
console.log('\n1. Dry run all pending migrations:');
try {
const dryRunResults = await this.migrationManager.runPendingMigrations({
dryRun: true,
stopOnError: false
});
console.log(`Dry run completed: ${dryRunResults.length} migrations processed`);
dryRunResults.forEach(result => {
console.log(`- ${result.migrationId}: ${result.success ? 'SUCCESS' : 'FAILED'}`);
});
} catch (error) {
console.error(`Dry run failed: ${error}`);
}
console.log('\n2. Run migrations with stop-on-error:');
try {
const results = await this.migrationManager.runPendingMigrations({
stopOnError: true,
batchSize: 25
});
console.log(`Migration batch completed: ${results.length} migrations`);
} catch (error) {
console.error(`Migration batch stopped due to error: ${error}`);
}
}
// Show migration history and statistics
const history = this.migrationManager.getMigrationHistory();
console.log(`\nMigration history (${history.length} total runs):`);
history.slice(0, 5).forEach(result => {
console.log(`- ${result.migrationId}: ${result.success ? 'SUCCESS' : 'FAILED'} ` +
`(${result.duration}ms, ${result.recordsProcessed} records)`);
});
// Show active migrations (should be empty in examples)
const activeMigrations = this.migrationManager.getActiveMigrations();
console.log(`\nActive migrations: ${activeMigrations.length}`);
console.log('');
}
async demonstrateAdvancedFeatures(): Promise<void> {
console.log('🔬 Advanced Migration Features');
console.log('==============================\n');
// Create a migration with complex validation
const complexValidation = createMigration(
'complex_validation_example',
'5.0.0',
'Migration with complex validation'
)
.description('Demonstrates advanced validation and error handling')
.addValidator(
'check_data_consistency',
'Verify data consistency across models',
async (context) => {
const errors: string[] = [];
const warnings: string[] = [];
// Simulate complex validation
const users = await context.databaseManager.getAllRecords('User');
const posts = await context.databaseManager.getAllRecords('Post');
// Check for orphaned posts
const userIds = new Set(users.map((u: any) => u.id));
const orphanedPosts = posts.filter((p: any) => !userIds.has(p.userId));
if (orphanedPosts.length > 0) {
warnings.push(`Found ${orphanedPosts.length} orphaned posts`);
}
return { valid: errors.length === 0, errors, warnings };
}
)
.addField('User', 'validationField', {
type: 'string',
required: false,
default: 'validated'
})
.build();
// Create a migration that handles large datasets
const largeMigration = createMigration(
'large_dataset_migration',
'5.1.0',
'Migration optimized for large datasets'
)
.description('Demonstrates batch processing and progress tracking')
.customOperation('Post', async (context) => {
context.logger.info('Processing large dataset with progress tracking');
const totalRecords = 10000; // Simulate large dataset
const batchSize = 100;
for (let i = 0; i < totalRecords; i += batchSize) {
const progress = ((i / totalRecords) * 100).toFixed(1);
context.logger.info(`Processing batch ${i / batchSize + 1}, Progress: ${progress}%`);
// Simulate processing time
await new Promise(resolve => setTimeout(resolve, 10));
context.progress.processedRecords = i + batchSize;
context.progress.estimatedTimeRemaining =
((totalRecords - i) / batchSize) * 10; // Rough estimate
}
})
.build();
console.log('Created advanced feature demonstrations:');
console.log('- Complex multi-model validation');
console.log('- Large dataset processing with progress tracking');
console.log('- Error handling and recovery strategies');
console.log('');
}
}
// Usage function
export async function runMigrationExamples(
orbitDBService: any,
ipfsService: any
): Promise<void> {
const framework = new SocialPlatformFramework();
try {
await framework.initialize(orbitDBService, ipfsService, 'development');
// Create sample data first
await createSampleDataForMigrations(framework);
// Run migration examples
const examples = new MigrationExamples(framework);
await examples.runAllExamples();
await examples.demonstrateAdvancedFeatures();
// Show final migration statistics
const migrationManager = (examples as any).migrationManager;
const allMigrations = migrationManager.getMigrations();
const history = migrationManager.getMigrationHistory();
console.log('📊 Final Migration Statistics:');
console.log('=============================');
console.log(`Total migrations registered: ${allMigrations.length}`);
console.log(`Total migration runs: ${history.length}`);
console.log(`Successful runs: ${history.filter((h: any) => h.success).length}`);
console.log(`Failed runs: ${history.filter((h: any) => !h.success).length}`);
const totalDuration = history.reduce((sum: number, h: any) => sum + h.duration, 0);
console.log(`Total migration time: ${totalDuration}ms`);
const totalRecords = history.reduce((sum: number, h: any) => sum + h.recordsProcessed, 0);
console.log(`Total records processed: ${totalRecords}`);
} catch (error) {
console.error('❌ Migration examples failed:', error);
} finally {
await framework.stop();
}
}
async function createSampleDataForMigrations(framework: SocialPlatformFramework): Promise<void> {
console.log('🗄️ Creating sample data for migration testing...\n');
try {
// Create users without timestamps (to demonstrate migration)
const users = [];
for (let i = 0; i < 5; i++) {
const user = await framework.createUser({
username: `migrationuser${i}`,
email: `migration${i}@example.com`,
bio: `Migration test user ${i}`
});
users.push(user);
}
// Create posts with basic structure
const posts = [];
for (let i = 0; i < 10; i++) {
const user = users[i % users.length];
const post = await framework.createPost(user.id, {
title: `Migration Test Post ${i}`,
content: `This is test content for migration testing. Post ${i} with various content types.`,
tags: ['migration', 'test'],
isPublic: true
});
posts.push(post);
}
// Create comments
for (let i = 0; i < 15; i++) {
const user = users[i % users.length];
const post = posts[i % posts.length];
await framework.createComment(
user.id,
post.id,
`Migration test comment ${i}`
);
}
// Create follow relationships
for (let i = 0; i < users.length; i++) {
for (let j = 0; j < users.length; j++) {
if (i !== j && Math.random() > 0.6) {
await framework.followUser(users[i].id, users[j].id);
}
}
}
console.log(`✅ Created sample data: ${users.length} users, ${posts.length} posts, 15 comments\n`);
} catch (error) {
console.warn('⚠️ Some sample data creation failed:', error);
}
}