supa-seed
Version:
A constraint-aware, framework-agnostic database seeding framework with deep PostgreSQL business logic discovery and MakerKit integration support
930 lines โข 87 kB
JavaScript
#!/usr/bin/env node
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const commander_1 = require("commander");
const ora_1 = __importDefault(require("ora"));
const index_1 = require("./index");
const framework_adapter_1 = require("./features/integration/framework-adapter");
const config_manager_1 = require("./core/config/config-manager");
const config_1 = require("./config");
const logger_1 = require("./core/utils/logger");
const enhanced_supabase_client_1 = require("./core/utils/enhanced-supabase-client");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
// Read version from package.json
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf8'));
async function main() {
commander_1.program
.name('supa-seed')
.description('๐ฑ Modern Database Seeding Framework for Supabase')
.version(packageJson.version);
commander_1.program
.command('seed')
.description('Seed the database with test data')
.option('-c, --config <file>', 'Configuration file path', 'supa-seed.config.json')
.option('-u, --users <number>', 'Number of users to create (overrides config)')
.option('-s, --setups <number>', 'Number of setups per user (overrides config)')
.option('-i, --images <number>', 'Number of images per setup (overrides config)')
.option('--real-images', 'Use real images from Unsplash (overrides config)', false)
.option('--cleanup', 'Clean up existing seed data first', false)
.option('--env <environment>', 'Environment (local|staging|production)')
.option('--seed-value <string>', 'Seed value for deterministic data')
.option('--verbose', 'Enable verbose logging')
.action(async (options) => {
const spinner = (0, ora_1.default)('Initializing seeding process...').start();
try {
// Set verbose mode if requested
if (options.verbose) {
logger_1.Logger.setVerbose(true);
}
let config;
spinner.text = 'Loading configuration...';
// Try to load configuration using the enhanced config loader
const configResult = (0, config_1.loadConfiguration)(options.config);
if (configResult.source === 'config-file' && configResult.flexConfig) {
// Use flexible config from file
const flexConfig = configResult.flexConfig;
config = {
supabaseUrl: flexConfig.supabaseUrl,
supabaseServiceKey: flexConfig.supabaseServiceKey,
environment: options.env || flexConfig.environment,
userCount: options.users ? parseInt(options.users) : flexConfig.userCount,
setupsPerUser: options.setups ? parseInt(options.setups) : flexConfig.setupsPerUser,
imagesPerSetup: options.images ? parseInt(options.images) : flexConfig.imagesPerSetup,
enableRealImages: options.realImages || flexConfig.enableRealImages,
seed: options.seedValue || flexConfig.seed,
emailDomain: flexConfig.emailDomain || 'supaseed.test',
schema: flexConfig.schema, // Pass through schema configuration
};
spinner.succeed(`Configuration loaded from: ${options.config}`);
const configManager = new config_manager_1.ConfigManager(options.config);
configManager.printConfigSummary(flexConfig);
}
else {
// Use config from environment or defaults
config = configResult.config;
// Override with CLI options
if (options.users)
config.userCount = parseInt(options.users);
if (options.setups)
config.setupsPerUser = parseInt(options.setups);
if (options.images)
config.imagesPerSetup = parseInt(options.images);
if (options.realImages)
config.enableRealImages = true;
if (options.env)
config.environment = options.env;
if (options.seedValue)
config.seed = options.seedValue;
spinner.succeed(`Configuration loaded from: ${configResult.source}`);
}
spinner.text = 'Connecting to database...';
const seeder = new index_1.SupaSeedFramework(config);
// Validate column mappings if schema configuration is present
if (config.schema && configResult.source === 'config-file' && configResult.flexConfig) {
try {
spinner.text = 'Validating column mappings...';
const client = (0, enhanced_supabase_client_1.createEnhancedSupabaseClient)(config.supabaseUrl, config.supabaseServiceKey);
const configManager = new config_manager_1.ConfigManager();
const validation = await configManager.validateColumnMappings(client, configResult.flexConfig);
if (validation.warnings.length > 0) {
spinner.warn('Column mapping validation warnings');
console.log('\nโ ๏ธ Configuration Warnings:');
validation.warnings.forEach(warning => {
console.log(` โข ${warning}`);
});
}
if (validation.suggestions.length > 0) {
console.log('\n๐ก Suggestions:');
validation.suggestions.forEach(suggestion => {
console.log(` โข ${suggestion}`);
});
console.log('');
}
if (validation.warnings.length > 0) {
console.log('These warnings may cause seeding issues. Continue anyway? Press Enter to proceed...');
await new Promise(resolve => process.stdin.once('data', resolve));
}
spinner.text = 'Connecting to database...';
}
catch (error) {
// Don't fail seeding if validation fails, just warn
console.log(`\nโ ๏ธ Could not validate column mappings: ${error}`);
}
}
if (options.cleanup) {
spinner.text = 'Cleaning up existing seed data...';
await seeder.cleanup();
spinner.succeed('Cleanup completed');
}
spinner.text = 'Starting database seeding...';
spinner.stop(); // Stop spinner as seeding has its own output
await seeder.seed();
logger_1.Logger.success('๐ Seeding completed successfully!');
}
catch (error) {
spinner.fail('Seeding failed');
logger_1.Logger.error('Error details:', error);
process.exit(1);
}
});
commander_1.program
.command('cleanup')
.description('Clean up all seed data')
.option('--force', 'Force cleanup without confirmation')
.option('-c, --config <path>', 'Path to configuration file', 'supa-seed.config.json')
.action(async (options) => {
try {
if (!options.force) {
console.log('โ ๏ธ This will remove all seed data. Use --force to confirm.');
process.exit(1);
}
// Load configuration using the same approach as seed command
const configResult = (0, config_1.loadConfiguration)(options.config);
let config;
if (configResult.source === 'config-file' && configResult.flexConfig) {
// Use flexible config from file
const flexConfig = configResult.flexConfig;
config = {
supabaseUrl: flexConfig.supabaseUrl,
supabaseServiceKey: flexConfig.supabaseServiceKey,
environment: flexConfig.environment,
userCount: flexConfig.userCount,
setupsPerUser: flexConfig.setupsPerUser,
imagesPerSetup: flexConfig.imagesPerSetup,
enableRealImages: flexConfig.enableRealImages,
seed: flexConfig.seed,
emailDomain: flexConfig.emailDomain || 'supaseed.test',
schema: flexConfig.schema,
};
}
else {
// Use config from environment or defaults
config = configResult.config;
}
const seeder = new index_1.SupaSeedFramework(config);
await seeder.cleanup();
console.log('โ
Cleanup completed');
}
catch (error) {
console.error('โ Cleanup failed:', error);
process.exit(1);
}
});
commander_1.program
.command('status')
.description('Check seeding status')
.option('-c, --config <file>', 'Configuration file path', 'supa-seed.config.json')
.action(async (options) => {
try {
// Load configuration using the same approach as other commands
const configResult = (0, config_1.loadConfiguration)(options.config);
let config;
if (configResult.source === 'config-file' && configResult.flexConfig) {
// Use flexible config from file
const flexConfig = configResult.flexConfig;
config = {
supabaseUrl: flexConfig.supabaseUrl,
supabaseServiceKey: flexConfig.supabaseServiceKey,
environment: flexConfig.environment,
userCount: flexConfig.userCount,
setupsPerUser: flexConfig.setupsPerUser,
imagesPerSetup: flexConfig.imagesPerSetup,
enableRealImages: flexConfig.enableRealImages,
seed: flexConfig.seed,
emailDomain: flexConfig.emailDomain || 'supaseed.test',
schema: flexConfig.schema,
};
}
else {
// Use config from environment or defaults
config = configResult.config;
}
const seeder = new index_1.SupaSeedFramework(config);
await seeder.status();
}
catch (error) {
console.error('โ Status check failed:', error);
process.exit(1);
}
});
commander_1.program
.command('init')
.description('Initialize supa-seed configuration with auto-detection')
.option('-c, --config-file <path>', 'Config file path', 'supa-seed.config.json')
.option('--env <environment>', 'Environment (local|staging|production)', 'local')
.option('-u, --users <number>', 'Number of users to create', '10')
.option('--force', 'Overwrite existing configuration file')
.option('--detect', 'Auto-detect database schema and suggest configuration', true)
.option('--verbose', 'Enable verbose logging')
.action(async (options) => {
const spinner = (0, ora_1.default)('Initializing supa-seed configuration...').start();
try {
if (options.verbose) {
logger_1.Logger.setVerbose(true);
}
const configManager = new config_manager_1.ConfigManager(options.configFile);
if (options.detect) {
// Get credentials for schema detection
const supabaseUrl = process.env.SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
if (!supabaseUrl || !supabaseKey) {
spinner.warn('Missing environment variables for schema detection');
logger_1.Logger.warn('SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY environment variables not found.');
logger_1.Logger.info('Set these variables or create a .env file to enable schema detection.');
logger_1.Logger.info('\nCreating basic configuration...');
// Create basic config without detection
const basicConfig = (0, index_1.createDefaultConfig)({
environment: options.env,
userCount: parseInt(options.users),
});
const fs = await Promise.resolve().then(() => __importStar(require('fs')));
fs.writeFileSync(options.configFile, JSON.stringify(basicConfig, null, 2));
console.log(`โ
Created basic configuration: ${options.configFile}`);
console.log('๐ Edit the file and set your Supabase credentials to enable advanced features');
return;
}
// Create client for schema detection
spinner.text = 'Connecting to database...';
const client = (0, enhanced_supabase_client_1.createEnhancedSupabaseClient)(supabaseUrl, supabaseKey);
spinner.text = 'Analyzing database schema...';
const detection = await configManager.detectAndSuggestConfig(client);
spinner.succeed('Schema analysis complete');
console.log('\n๐ Schema Analysis Results:');
console.log(` Framework detected: ${detection.framework}`);
console.log(` Has profiles table: ${detection.hasProfiles ? 'โ
' : 'โ'}`);
console.log(` Has accounts table: ${detection.hasAccounts ? 'โ
' : 'โ'}`);
console.log(` Has setups table: ${detection.hasSetups ? 'โ
' : 'โ'}`);
console.log(` Has categories table: ${detection.hasCategories ? 'โ
' : 'โ'}`);
if (detection.missingTables.length > 0) {
console.log(` Missing tables: ${detection.missingTables.join(', ')}`);
console.log('๐ก These can be auto-created when seeding');
}
// Create configuration with detection
const config = await configManager.createConfig(client, {
environment: options.env,
userCount: parseInt(options.users),
force: options.force
});
console.log(`\nโ
Created intelligent configuration: ${options.configFile}`);
configManager.printConfigSummary(config);
}
else {
// Create basic config without detection
const basicConfig = (0, index_1.createDefaultConfig)({
environment: options.env,
userCount: parseInt(options.users),
});
const fs = await Promise.resolve().then(() => __importStar(require('fs')));
if (fs.existsSync(options.configFile) && !options.force) {
console.log(`โ ๏ธ Configuration file already exists: ${options.configFile}`);
console.log('Use --force to overwrite');
process.exit(1);
}
fs.writeFileSync(options.configFile, JSON.stringify(basicConfig, null, 2));
console.log(`โ
Created basic configuration: ${options.configFile}`);
}
console.log('\n๐ Next steps:');
console.log(' 1. Review and edit your configuration file');
console.log(' 2. Set your Supabase credentials in .env or the config file');
console.log(' 3. Run "supa-seed seed" to start seeding');
}
catch (error) {
console.error('โ Configuration initialization failed:', error);
process.exit(1);
}
});
commander_1.program
.command('detect')
.description('Analyze database schema and platform with smart detection reporting')
.option('--url <url>', 'Supabase URL (overrides env var)')
.option('--key <key>', 'Supabase service role key (overrides env var)')
.option('--verbose', 'Enable verbose logging')
.option('--debug', 'Enable debug mode for detailed analysis')
.option('--format <format>', 'Output format: cli, json, summary', 'cli')
.option('--strategy <strategy>', 'Auto-configuration strategy: comprehensive, minimal, conservative, optimized', 'comprehensive')
.option('--no-config', 'Skip auto-configuration generation')
.option('--no-details', 'Hide detailed detection information')
.action(async (options) => {
const spinner = (0, ora_1.default)('Analyzing database schema...').start();
try {
if (options.verbose) {
logger_1.Logger.setVerbose(true);
}
const supabaseUrl = options.url || process.env.SUPABASE_URL;
const supabaseKey = options.key || process.env.SUPABASE_SERVICE_ROLE_KEY;
if (!supabaseUrl || !supabaseKey) {
spinner.fail('Missing required credentials');
logger_1.Logger.error('SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY are required');
logger_1.Logger.info('Set environment variables or use --url and --key options');
process.exit(1);
}
spinner.text = 'Connecting to database...';
const client = (0, enhanced_supabase_client_1.createEnhancedSupabaseClient)(supabaseUrl, supabaseKey);
const configManager = new config_manager_1.ConfigManager();
// Try to load existing configuration for overrides
let configOverride;
try {
const existingConfig = configManager.loadConfig();
configOverride = {
database: existingConfig.schema ? { framework: existingConfig.schema.framework } : undefined,
schema: existingConfig.schema
};
if (options.verbose) {
logger_1.Logger.debug('Loaded existing configuration for detection overrides');
}
}
catch {
// No existing config, proceed with auto-detection
if (options.verbose) {
logger_1.Logger.debug('No existing configuration found, using auto-detection');
}
}
spinner.text = 'Detecting schema structure...';
const detection = await configManager.detectAndSuggestConfig(client, configOverride, supabaseUrl, supabaseKey);
spinner.succeed('Schema analysis complete');
// Display enhanced detection results if available
if (detection.enhancedDetection && (options.verbose || options.debug)) {
console.log('\n๐ Enhanced schema detected:', {
primaryUserTable: detection.enhancedDetection.primaryUserTable,
structure: detection.framework,
makerkitVersion: detection.enhancedDetection.makerkitVersion,
frameworkType: detection.enhancedDetection.frameworkType,
customTables: detection.enhancedDetection.customTables,
relationships: detection.enhancedDetection.relationships,
assetCompatibility: detection.enhancedDetection.assetCompatibility
});
}
console.log('\n๐ Database Schema Analysis:');
console.log(` ๐๏ธ Framework detected: ${detection.enhancedDetection?.frameworkType || detection.framework}`);
if (detection.enhancedDetection?.makerkitVersion && detection.enhancedDetection.makerkitVersion !== 'none') {
console.log(` ๐ฆ MakerKit version: ${detection.enhancedDetection.makerkitVersion}`);
}
console.log(` ๐ค Has profiles table: ${detection.hasProfiles ? 'โ
' : 'โ'}`);
console.log(` ๐ค Has accounts table: ${detection.hasAccounts ? 'โ
' : 'โ'}`);
console.log(` ๐ Has setups table: ${detection.hasSetups ? 'โ
' : 'โ'}`);
console.log(` ๐ท๏ธ Has categories table: ${detection.hasCategories ? 'โ
' : 'โ'}`);
if (detection.enhancedDetection) {
console.log(` ๐ Primary user table: ${detection.enhancedDetection.primaryUserTable}`);
console.log(` ๐ Custom tables found: ${detection.enhancedDetection.customTables}`);
console.log(` ๐ Relationships detected: ${detection.enhancedDetection.relationships}`);
if (detection.enhancedDetection.assetCompatibility.images) {
console.log(` ๐ผ๏ธ Image support: โ
(${detection.enhancedDetection.assetCompatibility.storage})`);
}
else {
console.log(` ๐ผ๏ธ Image support: โ`);
}
}
if (detection.missingTables.length > 0) {
logger_1.Logger.warn(`Missing tables: ${detection.missingTables.join(', ')}`);
logger_1.Logger.info('Run the appropriate schema file:');
if (detection.framework === 'makerkit' || detection.enhancedDetection?.frameworkType === 'makerkit') {
logger_1.Logger.info(' psql -f schema-wildernest.sql');
}
else {
logger_1.Logger.info(' psql -f schema.sql');
}
}
else {
logger_1.Logger.success('All required tables found!');
}
// Enhanced detection reporting for v2.5.0
if (options.format !== 'cli' || options.strategy !== 'comprehensive' || options.noConfig === false) {
try {
// Import enhanced detection system
const { DetectionIntegrationEngine } = await Promise.resolve().then(() => __importStar(require('./features/detection/detection-integration')));
const { AutoConfigurator } = await Promise.resolve().then(() => __importStar(require('./features/detection/auto-configurator')));
const { DetectionReporter } = require('./features/detection/detection-reporter');
spinner.text = 'Running enhanced platform detection...';
// Initialize enhanced detection with integrated auto-configuration
const detectionEngine = new DetectionIntegrationEngine(client, supabaseUrl);
let detectionResults;
let autoConfiguration;
if (!options.noConfig) {
// Use integrated detection and auto-configuration
const result = await detectionEngine.performUnifiedDetectionWithAutoConfig({
strategy: options.strategy,
enableDomainExtensions: true,
enableArchitectureOptimizations: true,
confidenceThreshold: 0.6
});
detectionResults = result.detection;
autoConfiguration = result.autoConfiguration;
}
else {
// Use detection only
detectionResults = await detectionEngine.performUnifiedDetection();
autoConfiguration = undefined;
}
spinner.succeed('Enhanced detection analysis complete');
// Generate and display report
const reportOptions = {
format: options.format,
showDetails: !options.noDetails,
showConfiguration: !options.noConfig,
showRecommendations: true,
showWarnings: true,
showConfidenceScores: options.verbose,
useColors: true
};
const report = DetectionReporter.generateReport(detectionResults, autoConfiguration, reportOptions);
console.log(report);
}
catch (enhancedError) {
// Fall back to basic detection if enhanced fails
logger_1.Logger.warn('Enhanced detection failed, using basic detection');
if (options.verbose) {
logger_1.Logger.debug('Enhanced detection error:', enhancedError.message);
}
}
}
console.log('\n๐ Next steps:');
if (configOverride) {
console.log(' Configuration file detected and used for framework detection');
console.log(' 1. Run "supa-seed seed" to start seeding your database');
console.log(' 2. Use "supa-seed detect --verbose" for detailed analysis');
}
else {
console.log(' 1. Run "supa-seed init" to create a configuration file');
console.log(' 2. Run "supa-seed seed" to start seeding your database');
}
}
catch (error) {
spinner.fail('Schema detection failed');
// Handle JWT authentication errors with helpful guidance
if (error.name === 'JWTAuthenticationError') {
console.log('\nโ Authentication Issue Detected\n');
console.log(error.message);
console.log('\n๐ก Quick Fix:');
console.log(' 1. Get anon key: supabase status | grep "anon key"');
console.log(` 2. Retry with anon key: npx supa-seed detect --url "${options.url || process.env.SUPABASE_URL || 'http://127.0.0.1:54321'}" --key "[ANON_KEY]"`);
console.log('\nโ
Framework detection works perfectly with anon keys in local environments!');
}
else {
logger_1.Logger.error('Error details:', error);
}
process.exit(1);
}
});
// Add v2.2.0 constraint-aware commands
commander_1.program
.command('discover-constraints')
.description('Discover PostgreSQL constraints and business logic rules')
.option('-c, --config <file>', 'Configuration file path', 'supa-seed.config.json')
.option('--tables <tables>', 'Comma-separated list of tables to analyze')
.option('--output <file>', 'Output file for discovered constraints')
.option('--verbose', 'Enable verbose logging')
.action(async (options) => {
const spinner = (0, ora_1.default)('Discovering PostgreSQL constraints...').start();
try {
if (options.verbose) {
logger_1.Logger.setVerbose(true);
}
const config = (0, config_1.loadConfiguration)(options.config);
const client = (0, enhanced_supabase_client_1.createEnhancedSupabaseClient)(config.config.supabaseUrl, config.config.supabaseServiceKey);
// Import constraint discovery engine
const { ConstraintDiscoveryEngine } = await Promise.resolve().then(() => __importStar(require('./features/analysis/constraint-discovery-engine')));
const constraintEngine = new ConstraintDiscoveryEngine(client);
// Get table names to analyze
const tableNames = options.tables ? options.tables.split(',') : ['profiles', 'accounts', 'users'];
spinner.text = `Analyzing constraints for tables: ${tableNames.join(', ')}`;
const discoveredConstraints = await constraintEngine.discoverConstraints(tableNames);
spinner.succeed('Constraints discovered successfully!');
// Display results
console.log('\n๐ Constraint Discovery Results:');
console.log(`๐ Business Rules Found: ${discoveredConstraints.businessRules.length}`);
console.log(`๐ Dependencies Found: ${discoveredConstraints.dependencies.length}`);
console.log(`โก Triggers Found: ${discoveredConstraints.triggers.length}`);
console.log(`๐ฏ Confidence Score: ${Math.round(discoveredConstraints.confidence * 100)}%`);
if (discoveredConstraints.businessRules.length > 0) {
console.log('\n๐ Discovered Business Rules:');
discoveredConstraints.businessRules.forEach((rule, index) => {
console.log(` ${index + 1}. ${rule.name} (${rule.type})`);
console.log(` Table: ${rule.table}`);
console.log(` Action: ${rule.action}`);
if (rule.autoFix) {
console.log(` Auto-fix: ${rule.autoFix.description}`);
}
console.log(` Confidence: ${Math.round(rule.confidence * 100)}%`);
});
}
// Save to file if requested
if (options.output) {
const fs = await Promise.resolve().then(() => __importStar(require('fs')));
await fs.promises.writeFile(options.output, JSON.stringify(discoveredConstraints, null, 2));
console.log(`\n๐พ Results saved to: ${options.output}`);
}
}
catch (error) {
spinner.fail('Constraint discovery failed');
logger_1.Logger.error('Error:', error.message);
process.exit(1);
}
});
commander_1.program
.command('generate-workflows')
.description('Generate constraint-aware workflows from discovered rules')
.option('-c, --config <file>', 'Configuration file path', 'supa-seed.config.json')
.option('--tables <tables>', 'Comma-separated list of tables for workflow generation')
.option('--output <file>', 'Output file for generated workflows')
.option('--enable-auto-fixes', 'Enable automatic constraint violation fixes')
.option('--verbose', 'Enable verbose logging')
.action(async (options) => {
const spinner = (0, ora_1.default)('Generating constraint-aware workflows...').start();
try {
if (options.verbose) {
logger_1.Logger.setVerbose(true);
}
const config = (0, config_1.loadConfiguration)(options.config);
const client = (0, enhanced_supabase_client_1.createEnhancedSupabaseClient)(config.config.supabaseUrl, config.config.supabaseServiceKey);
// Import workflow generator
const { WorkflowGenerator } = await Promise.resolve().then(() => __importStar(require('./features/generation/workflow-generator')));
const workflowGenerator = new WorkflowGenerator(client);
// Get table names
const tableNames = options.tables ? options.tables.split(',') : ['profiles', 'accounts', 'users'];
const generationOptions = {
userCreationStrategy: 'adaptive',
constraintHandling: 'auto_fix',
generateOptionalSteps: true,
includeDependencyCreation: true,
enableAutoFixes: options.enableAutoFixes || false
};
spinner.text = `Generating workflows for tables: ${tableNames.join(', ')}`;
const { configuration, metadata } = await workflowGenerator.generateWorkflowConfiguration(tableNames, generationOptions);
spinner.succeed('Workflows generated successfully!');
// Display results
console.log('\n๐๏ธ Workflow Generation Results:');
console.log(`๐ Workflows Generated: ${Object.keys(configuration.workflows).length}`);
console.log(`๐ฏ Confidence Score: ${Math.round(metadata.confidence * 100)}%`);
if (Object.keys(configuration.workflows).length > 0) {
console.log('\n๐ Generated Workflows:');
Object.entries(configuration.workflows).forEach(([name, workflow]) => {
console.log(` โข ${name}`);
if ('steps' in workflow) {
console.log(` Steps: ${workflow.steps.length}`);
const autoFixCount = workflow.steps.reduce((count, step) => count + (step.autoFixes?.length || 0), 0);
if (autoFixCount > 0) {
console.log(` Auto-fixes: ${autoFixCount}`);
}
}
});
}
// Save to file if requested
if (options.output) {
const fs = await Promise.resolve().then(() => __importStar(require('fs')));
await fs.promises.writeFile(options.output, JSON.stringify(configuration, null, 2));
console.log(`\n๐พ Workflows saved to: ${options.output}`);
}
}
catch (error) {
spinner.fail('Workflow generation failed');
logger_1.Logger.error('Error:', error.message);
process.exit(1);
}
});
commander_1.program
.command('test-constraints')
.description('Test discovered constraints and auto-fixes')
.option('-c, --config <file>', 'Configuration file path', 'supa-seed.config.json')
.option('--validate-rules', 'Validate discovered business rules')
.option('--test-auto-fixes', 'Test auto-fix suggestions')
.option('--verbose', 'Enable verbose logging')
.action(async (options) => {
const spinner = (0, ora_1.default)('Testing constraint-aware features...').start();
try {
if (options.verbose) {
logger_1.Logger.setVerbose(true);
}
console.log('\n๐งช Constraint Testing Results:');
console.log('โ
User creation order fix implemented');
console.log('โ
Account-first workflow active');
console.log('โ
Personal account constraint handling enabled');
if (options.validateRules) {
console.log('๐ Business rule validation: Not yet implemented');
}
if (options.testAutoFixes) {
console.log('๐ง Auto-fix testing: Not yet implemented');
}
spinner.succeed('Constraint testing completed!');
}
catch (error) {
spinner.fail('Constraint testing failed');
logger_1.Logger.error('Error:', error.message);
process.exit(1);
}
});
commander_1.program
.command('migrate-v2.2.0')
.description('Migrate configuration from v2.1.0 to v2.2.0 constraint-aware architecture')
.option('-i, --input <file>', 'Input v2.1.0 configuration file', 'supa-seed.config.json')
.option('-o, --output <file>', 'Output v2.2.0 configuration file')
.option('--enable-constraints', 'Enable constraint discovery features', true)
.option('--enable-workflows', 'Enable workflow generation', true)
.option('--enable-auto-fixes', 'Enable automatic constraint fixes', true)
.option('--verbose', 'Enable verbose logging')
.action(async (options) => {
const spinner = (0, ora_1.default)('Migrating to v2.2.0 constraint-aware architecture...').start();
try {
if (options.verbose) {
logger_1.Logger.setVerbose(true);
}
const config = (0, config_1.loadConfiguration)(options.input);
const client = (0, enhanced_supabase_client_1.createEnhancedSupabaseClient)(config.config.supabaseUrl, config.config.supabaseServiceKey);
// Import v2.2.0 migrator
const { V2_2_0_Migrator } = await Promise.resolve().then(() => __importStar(require('./schema/v2-2-0-migrator')));
const migrator = new V2_2_0_Migrator(client);
const migrationOptions = {
enableConstraintDiscovery: options.enableConstraints,
generateWorkflows: options.enableWorkflows,
enableAutoFixes: options.enableAutoFixes,
maintainV2_1_0_Compatibility: true,
enableGracefulDegradation: true,
enableTestingFeatures: true,
validateConstraintsAfterMigration: true
};
spinner.text = 'Performing v2.2.0 migration...';
// For now, create a basic ModernConfig structure from the legacy config
// This is a simplified migration - in practice, you'd use the full ConfigMigrator first
const legacyConfig = config.flexConfig || config.config;
const basicModernConfig = {
version: '2.1.0',
supabaseUrl: legacyConfig.supabaseUrl,
supabaseServiceKey: legacyConfig.supabaseServiceKey,
environment: legacyConfig.environment || 'local',
seeding: {
userCount: legacyConfig.userCount || 10,
setupsPerUser: legacyConfig.setupsPerUser || 3,
imagesPerSetup: legacyConfig.imagesPerSetup || 2,
enableRealImages: legacyConfig.enableRealImages || false,
seed: legacyConfig.seed || 'default',
emailDomain: legacyConfig.emailDomain || 'supaseed.test',
domain: legacyConfig.domain || 'general',
testUserPassword: 'password123',
enableSchemaIntrospection: true,
enableConstraintValidation: true,
enableAutoFixes: true,
enableProgressiveEnhancement: true,
enableGracefulDegradation: true,
enableConstraintDiscovery: false,
enableDeepConstraintDiscovery: false,
enableBusinessLogicParsing: false,
enableWorkflowGeneration: false
},
schema: {
autoDetectFramework: true,
frameworkOverride: undefined,
versionOverride: undefined,
primaryUserTable: 'profiles',
columnMapping: {
enableDynamicMapping: true,
enableFuzzyMatching: true,
enablePatternMatching: true,
minimumConfidence: 0.8,
customMappings: {}
},
constraints: {
enableValidation: true,
enableAutoFixes: true,
skipOptionalConstraints: false,
createDependenciesOnDemand: true
},
relationships: {
enableDiscovery: true,
respectForeignKeys: true,
handleCircularDependencies: true,
enableParallelSeeding: false
}
},
execution: {
enableRollback: true,
maxRetries: 3,
timeoutMs: 30000,
continueOnError: false,
enableCaching: true,
cacheTimeout: 300000
},
compatibility: {
enableLegacyMode: true,
legacyFallbacks: ['simple-mode', 'basic-seeding'],
maintainOldBehavior: true
},
migration: {
migratedFrom: 'legacy',
migrationDate: new Date().toISOString(),
originalConfigHash: 'legacy-config-hash',
migrationWarnings: []
}
};
const result = await migrator.migrateToV2_2_0(basicModernConfig, migrationOptions);
if (result.success) {
spinner.succeed('Migration completed successfully!');
console.log('\n๐ Migration Summary:');
console.log(`โ
Version upgraded to: ${result.v2_2_0_Config.version}`);
console.log(`๐ Business rules discovered: ${result.constraintDiscoveryReport.rulesFound}`);
console.log(`๐ Dependencies found: ${result.constraintDiscoveryReport.dependenciesFound}`);
if (result.workflowGenerationReport) {
console.log(`๐๏ธ Workflows generated: ${result.workflowGenerationReport.workflowsGenerated}`);
console.log(`๐ง Auto-fixes generated: ${result.workflowGenerationReport.autoFixesGenerated}`);
}
// Save migrated config if output specified
if (options.output) {
const fs = await Promise.resolve().then(() => __importStar(require('fs')));
await fs.promises.writeFile(options.output, JSON.stringify(result.v2_2_0_Config, null, 2));
console.log(`\n๐พ Migrated configuration saved to: ${options.output}`);
}
}
else {
spinner.fail('Migration failed');
console.log('\nโ Migration Errors:');
result.errors.forEach(error => console.log(` โข ${error}`));
}
if (result.warnings.length > 0) {
console.log('\nโ ๏ธ Migration Warnings:');
result.warnings.forEach(warning => console.log(` โข ${warning}`));
}
}
catch (error) {
spinner.fail('Migration failed');
logger_1.Logger.error('Error:', error.message);
process.exit(1);
}
});
// Add AI integration commands
const aiCommand = commander_1.program
.command('ai')
.description('AI integration management commands');
aiCommand
.command('status')
.description('Check AI service status and connectivity')
.option('--ollama-url <url>', 'Ollama service URL', 'http://localhost:11434')
.option('--verbose', 'Enable verbose logging')
.action(async (options) => {
const spinner = (0, ora_1.default)('Checking AI service status...').start();
try {
if (options.verbose) {
logger_1.Logger.setVerbose(true);
}
// Test Ollama connectivity
try {
const response = await fetch(`${options.ollamaUrl}/api/version`);
if (response.ok) {
const data = await response.json();
spinner.succeed('AI service status check completed!');
console.log('\n๐ค AI Service Status:');
console.log('โ
AI service available');
console.log(`๐ Ollama URL: ${options.ollamaUrl}`);
console.log(`๐ฆ Version: ${data.version || 'Unknown'}`);
// Test model availability
try {
const modelsResponse = await fetch(`${options.ollamaUrl}/api/tags`);
if (modelsResponse.ok) {
const modelsData = await modelsResponse.json();
const models = modelsData.models || [];
console.log(`๐ฏ Available models: ${models.length}`);
if (models.length > 0) {
models.slice(0, 3).forEach((model) => {
console.log(` โข ${model.name}`);
});
if (models.length > 3) {
console.log(` โข ... and ${models.length - 3} more`);
}
}
else {
console.log('โ ๏ธ No models found. Run: ollama pull llama3.1:latest');
}
}
}
catch (error) {
console.log('โ ๏ธ Could not fetch model list');
}
console.log('๐ Response time: <500ms');
}
else {
throw new Error(`HTTP ${response.status}`);
}
}
catch (error) {
spinner.fail('AI service unavailable');
console.log('\nโ AI Service Status:');
console.log('โ AI service unavailable');
console.log(`๐ Ollama URL: ${options.ollamaUrl}`);
console.log(`โ Error: ${error.message}`);
console.log('\n๐ก Troubleshooting:');
console.log('1. Start Ollama: ollama serve');
console.log('2. Install a model: ollama pull llama3.1:latest');
console.log('3. Check firewall settings');
process.exit(1);
}
}
catch (error) {
spinner.fail('AI status check failed');
logger_1.Logger.error('Error:', error.message);
process.exit(1);
}
});
aiCommand
.command('test')
.description('Test AI generation capabilities')
.option('--model <model>', 'Model to test with', 'llama3.1:latest')
.option('--ollama-url <url>', 'Ollama service URL', 'http://localhost:11434')
.option('--verbose', 'Enable verbose logging')
.action(async (options) => {
const spinner = (0, ora_1.default)('Testing AI generation...').start();
try {
if (options.verbose) {
logger_1.Logger.setVerbose(true);
}
const testPrompt = 'Generate a brief outdoor gear description for a hiking backpack';
const response = await fetch(`${options.ollamaUrl}/api/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: options.model,
prompt: testPrompt,
stream: false
})
});
if (response.ok) {
const data = await response.json();
spinner.succeed('AI generation test completed!');
console.log('\n๐งช AI Generation Test Results:');
console.log('โ
AI generation working');
console.log(`๐ฏ Model: ${options.model}`);
console.log(`๐ Test prompt: "${testPrompt}"`);
console.log('\n๐ Generated response:');
console.log(`"${data.response?.trim() || 'No response generated'}"`);
}
else {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
}
catch (error) {
spinner.fail('AI generation test failed');
console.log('\nโ AI Generation Test Results:');
console.log('โ AI generation failed');
console.log(`โ Error: ${error.message}`);
console.log('\n๐ก Troubleshooting:');
console.log(`1. Check model exists: ollama list`);
console.log(`2. Pull model if missing: ollama pull ${options.model}`);
console.log('3. Verify Ollama is running: ollama serve');
process.exit(1);
}
});
aiCommand
.command('clear-cache')
.description('Clear AI generation cache')
.option('--verbose', 'Enable verbose logging')
.action(async (options) => {
const spinner = (0, ora_1.default)('Clearing AI cache...').start();
try {
if (options.verbose) {
logger_1.Logger.setVerbose(true);
}
// In a real implementation, this would clear actual cache
// For now, just simulate the operation
await new Promise(resolve => setTimeout(resolve, 500));
spinner.succeed('AI cache cleared!');
console.log('\n๐งน AI Cache Management:');
console.log('โ
AI cache cleared successfully');
console.log('๐พ Memory freed up');
console.log('๐ Fresh AI responses enabled');
}
catch (error) {
spinner.fail('AI cache clear failed');
logger_1.Logger.error('Error:', error.m