UNPKG

supa-seed

Version:

A constraint-aware, framework-agnostic database seeding framework with deep PostgreSQL business logic discovery and MakerKit integration support

1,105 lines 114 kB
"use strict"; /** * MakerKit Framework Strategy * Implements framework-specific seeding logic for MakerKit applications */ 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; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.MakerKitStrategy = void 0; const identity_manager_1 = require("../../../auth/identity-manager"); const development_webhook_manager_1 = require("../../../webhooks/development-webhook-manager"); const constraint_discovery_engine_1 = require("../../analysis/constraint-discovery-engine"); const constraint_registry_1 = require("../../analysis/constraint-registry"); const business_logic_analyzer_1 = require("../../analysis/business-logic-analyzer"); const rls_compliant_seeder_1 = require("../../../schema/rls-compliant-seeder"); const rls_compliance_engine_1 = require("../../analysis/rls-compliance-engine"); const rls_compliance_validator_1 = require("../../analysis/rls-compliance-validator"); const relationship_analyzer_1 = require("../../analysis/relationship-analyzer"); const junction_table_handler_1 = require("../../../schema/junction-table-handler"); const multi_tenant_manager_1 = require("../../../schema/multi-tenant-manager"); const storage_integration_manager_1 = require("../../generation/storage/storage-integration-manager"); const logger_1 = require("../../../core/utils/logger"); const detection_integration_1 = require("../../detection/detection-integration"); const auto_configurator_1 = require("../../detection/auto-configurator"); class MakerKitStrategy { constructor() { this.name = 'makerkit'; this.detectedFeatures = []; } async initialize(client) { this.client = client; this.constraintEngine = new constraint_discovery_engine_1.ConstraintDiscoveryEngine(client); // Initialize identity manager for complete auth flows this.identityManager = new identity_manager_1.IdentityManager(client); // Initialize webhook manager for development webhook support this.webhookManager = new development_webhook_manager_1.DevelopmentWebhookManager(client); // Initialize enhanced RLS compliance validation system this.rlsComplianceEngine = new rls_compliance_engine_1.RLSComplianceEngine(client); this.rlsComplianceValidator = new rls_compliance_validator_1.RLSComplianceValidator(client); // Initialize Smart Detection and Auto-Configuration (Task 2.3.2) this.detectionEngine = new detection_integration_1.DetectionIntegrationEngine(client); this.autoConfigurator = new auto_configurator_1.AutoConfigurator(); // Set default auth flow configuration for MakerKit this.authFlowConfig = this.getDefaultAuthFlowConfig(); this.constraintRegistry = new constraint_registry_1.ConstraintRegistry({ enablePriorityHandling: true, enableFallbackHandlers: true, logHandlerSelection: true }); // Initialize business logic analyzer this.businessLogicAnalyzer = new business_logic_analyzer_1.BusinessLogicAnalyzer(client, { frameworkHints: ['makerkit'], expectedPatterns: ['auth_triggered', 'personal_account_constraint'] }); // Initialize RLS compliant seeder this.rlsCompliantSeeder = new rls_compliant_seeder_1.RLSCompliantSeeder(client, undefined, { enableRLSCompliance: true, createUserContext: true, useServiceRole: false // MakerKit prefers auth-based approach }); // Initialize relationship analyzer with MakerKit-specific options this.relationshipAnalyzer = new relationship_analyzer_1.RelationshipAnalyzer(client, { schemas: ['public'], detectJunctionTables: true, analyzeTenantScoping: true, // Important for MakerKit multi-tenant includeOptionalRelationships: true, enableCaching: true, generateRecommendations: true }); // Initialize junction table handler for many-to-many relationships this.junctionTableHandler = new junction_table_handler_1.JunctionTableHandler(client); // Initialize multi-tenant manager with MakerKit-specific configuration this.multiTenantManager = new multi_tenant_manager_1.MultiTenantManager(client, { enableMultiTenant: true, tenantColumn: 'account_id', // MakerKit uses account_id for tenant scoping tenantScopeDetection: 'auto', validationEnabled: true, strictIsolation: true, allowSharedResources: true, dataGenerationOptions: { generatePersonalAccounts: true, generateTeamAccounts: true, personalAccountRatio: 0.6, // 60% personal, 40% team dataDistributionStrategy: 'realistic', crossTenantDataAllowed: false, sharedResourcesEnabled: true, accountTypes: [ { type: 'personal', weight: 0.6, settings: { defaultPlan: 'free', features: ['basic_features'] } }, { type: 'team', weight: 0.4, settings: { minMembers: 2, maxMembers: 10, defaultPlan: 'pro', features: ['team_features', 'collaboration'] } } ], minUsersPerTenant: 1, maxUsersPerTenant: 5, minProjectsPerTenant: 1, maxProjectsPerTenant: 3, allowCrossTenantRelationships: false, sharedTables: ['plans', 'features'], respectTenantPlans: true, enforceTenantLimits: true } }); // Initialize storage integration manager with MakerKit-specific configuration this.storageIntegrationManager = new storage_integration_manager_1.StorageIntegrationManager(client, { bucketName: 'media', domain: 'outdoor-adventure', // MakerKit default theme categories: ['camping', 'hiking', 'outdoor-gear'], imagesPerSetup: 3, enableRealImages: false, // Default to mock for safety imageService: 'mock', maxFileSize: 5 * 1024 * 1024, // 5MB allowedFileTypes: ['image/jpeg', 'image/png', 'image/webp'], generateThumbnails: true, respectRLS: true, // MakerKit uses RLS heavily storageRootPath: 'supa-seed/makerkit' }); // Register MakerKit-specific handlers const handlers = this.getConstraintHandlers(); this.constraintRegistry.registerHandlers(handlers); } /** * Perform smart detection and auto-configuration (Task 2.3.2) */ async performSmartDetectionAndConfiguration(baseConfiguration) { if (!this.detectionEngine || !this.autoConfigurator) { throw new Error('Detection engine or auto-configurator not initialized'); } logger_1.Logger.info('🔍 Performing smart platform detection and auto-configuration...'); try { // Step 1: Perform unified detection const detectionStartTime = Date.now(); this.detectionResults = await this.detectionEngine.performUnifiedDetection({ enableCrossValidation: true, enableConflictResolution: true, enableCaching: true, confidenceThreshold: 0.6 }); const detectionTime = Date.now() - detectionStartTime; logger_1.Logger.success(`✅ Platform detection completed in ${detectionTime}ms`); if (this.detectionResults?.architecture) { logger_1.Logger.info(` Architecture: ${this.detectionResults.architecture.architectureType} (${(this.detectionResults.architecture.confidence * 100).toFixed(1)}% confidence)`); } if (this.detectionResults?.domain) { logger_1.Logger.info(` Domain: ${this.detectionResults.domain.primaryDomain} (${(this.detectionResults.domain.confidence * 100).toFixed(1)}% confidence)`); } if (this.detectionResults?.integration) { logger_1.Logger.info(` Overall Confidence: ${(this.detectionResults.integration.overallConfidence * 100).toFixed(1)}%`); } // Step 2: Generate auto-configuration based on detection const configStartTime = Date.now(); this.autoConfiguration = await this.autoConfigurator.generateConfiguration(this.detectionResults, { strategy: 'comprehensive', baseConfiguration, enableDomainExtensions: true, enableArchitectureOptimizations: true, confidenceThreshold: 0.6 }); const configTime = Date.now() - configStartTime; logger_1.Logger.success(`✅ Auto-configuration generated in ${configTime}ms`); if (this.autoConfiguration?.confidence !== undefined) { logger_1.Logger.info(` Configuration Confidence: ${(this.autoConfiguration.confidence * 100).toFixed(1)}%`); } if (this.autoConfiguration?.generationMetrics?.templatesApplied !== undefined) { logger_1.Logger.info(` Templates Applied: ${this.autoConfiguration.generationMetrics.templatesApplied}`); } // Log key configuration recommendations if (this.autoConfiguration?.reasoning && this.autoConfiguration.reasoning.length > 0) { logger_1.Logger.info('📋 Configuration Reasoning:'); this.autoConfiguration.reasoning.slice(0, 3).forEach(reason => { logger_1.Logger.info(` • ${reason}`); }); } // Log warnings if any if (this.autoConfiguration?.warnings && this.autoConfiguration.warnings.length > 0) { logger_1.Logger.warn('⚠️ Configuration Warnings:'); this.autoConfiguration.warnings.forEach(warning => { logger_1.Logger.warn(` • ${warning}`); }); } return this.autoConfiguration; } catch (error) { logger_1.Logger.error('Smart detection and configuration failed:', error); throw new Error(`Smart detection and configuration failed: ${error.message}`); } } /** * Get detection results (if available) */ getDetectionResults() { return this.detectionResults; } /** * Get auto-configuration results (if available) */ getAutoConfiguration() { return this.autoConfiguration; } /** * Apply auto-configuration to seeding process */ async applyAutoConfiguration(userConfiguration = {}) { if (!this.autoConfiguration) { logger_1.Logger.warn('No auto-configuration available - performing detection first'); await this.performSmartDetectionAndConfiguration(userConfiguration); } if (!this.autoConfiguration) { throw new Error('Auto-configuration failed and is not available'); } // Merge auto-configuration with user configuration (user config takes priority) const mergedConfiguration = { ...this.autoConfiguration.configuration, ...userConfiguration }; logger_1.Logger.info('🔧 Applied auto-configuration with user overrides'); // Log applied configuration summary if (mergedConfiguration.userCount) { logger_1.Logger.info(` User Count: ${mergedConfiguration.userCount}`); } if (mergedConfiguration.setupsPerUser) { logger_1.Logger.info(` Setups per User: ${mergedConfiguration.setupsPerUser}`); } if (mergedConfiguration.domain) { logger_1.Logger.info(` Domain: ${mergedConfiguration.domain}`); } if (mergedConfiguration.createTeamAccounts !== undefined) { logger_1.Logger.info(` Team Accounts: ${mergedConfiguration.createTeamAccounts}`); } return mergedConfiguration; } /** * Get platform-specific user archetypes based on detection */ getPlatformSpecificArchetypes() { if (!this.detectionResults?.architecture || !this.detectionResults?.domain) { return ['admin@test.com', 'user@test.com']; // Default archetypes } const { architectureType } = this.detectionResults.architecture; const { primaryDomain } = this.detectionResults.domain; // Generate platform-specific archetypes const archetypes = []; // Add architecture-specific archetypes switch (architectureType) { case 'individual': archetypes.push('creator@test.com', 'explorer@test.com'); break; case 'team': archetypes.push('admin@test.com', 'team-lead@test.com', 'team-member@test.com'); break; case 'hybrid': archetypes.push('admin@test.com', 'creator@test.com', 'team-member@test.com'); break; } // Add domain-specific archetypes switch (primaryDomain) { case 'outdoor': archetypes.push('gear-expert@wildernest.test', 'adventure-guide@wildernest.test'); break; case 'saas': archetypes.push('workspace-admin@saas.test', 'power-user@saas.test'); break; case 'ecommerce': archetypes.push('merchant@store.test', 'customer@store.test'); break; case 'social': archetypes.push('influencer@social.test', 'community-member@social.test'); break; } // Remove duplicates and return unique archetypes return [...new Set(archetypes)]; } /** * Get optimized seeding parameters based on detection */ getOptimizedSeedingParameters() { if (!this.detectionResults?.architecture || !this.detectionResults?.domain) { return { userCount: 5, setupsPerUser: 2, imagesPerSetup: 1, enableRealImages: false }; } const { architectureType } = this.detectionResults.architecture; const { primaryDomain } = this.detectionResults.domain; let userCount = 5; let setupsPerUser = 2; let imagesPerSetup = 1; let enableRealImages = false; // Optimize based on architecture switch (architectureType) { case 'individual': userCount = 4; setupsPerUser = 3; break; case 'team': userCount = 8; setupsPerUser = 1; break; case 'hybrid': userCount = 10; setupsPerUser = 2; break; } // Optimize based on domain switch (primaryDomain) { case 'outdoor': imagesPerSetup = 3; enableRealImages = true; break; case 'saas': imagesPerSetup = 1; enableRealImages = false; break; case 'ecommerce': setupsPerUser = Math.max(setupsPerUser, 3); // Products imagesPerSetup = 4; enableRealImages = true; break; case 'social': userCount = Math.max(userCount, 8); // More users for interactions imagesPerSetup = 2; enableRealImages = true; break; } return { userCount, setupsPerUser, imagesPerSetup, enableRealImages }; } getPriority() { return 100; // High priority for specific framework } async detect(schema) { let confidence = 0; const detectedFeatures = []; const recommendations = []; try { // Check for MakerKit-specific functions const makerKitFunctions = schema.functions.filter(fn => fn.name.includes('kit.setup_new_user') || fn.name.includes('handle_new_user') || fn.name.includes('create_profile_for_user')); if (makerKitFunctions.length > 0) { confidence += 0.4; detectedFeatures.push('makerkit_user_functions'); logger_1.Logger.debug(`Found MakerKit functions: ${makerKitFunctions.map(f => f.name).join(', ')}`); } // Check for MakerKit-specific tables and columns const accountsTable = schema.tables.find(t => t.name === 'accounts'); if (accountsTable) { const hasPersonalAccountColumn = accountsTable.columns.some(c => c.name === 'is_personal_account'); const hasSlugColumn = accountsTable.columns.some(c => c.name === 'slug'); if (hasPersonalAccountColumn) { confidence += 0.3; detectedFeatures.push('personal_account_column'); } if (hasSlugColumn) { confidence += 0.1; detectedFeatures.push('account_slug_column'); } } // Check for MakerKit constraint patterns const personalAccountConstraint = schema.constraints.find(c => c.name.includes('accounts_slug_null_if_personal_account') || c.definition.includes('is_personal_account')); if (personalAccountConstraint) { confidence += 0.2; detectedFeatures.push('personal_account_constraint'); logger_1.Logger.debug(`Found MakerKit constraint: ${personalAccountConstraint.name}`); } // Detect MakerKit version based on schema patterns if (confidence > 0.5) { this.version = this.detectMakerKitVersion(schema); if (this.version) { detectedFeatures.push(`makerkit_${this.version}`); } } // Generate recommendations if (confidence > 0.7) { recommendations.push('Use auth.admin.createUser() for user creation'); recommendations.push('Ensure accounts have is_personal_account=true for profiles'); recommendations.push('Respect MakerKit trigger-based user creation flow'); } else if (confidence > 0.3) { recommendations.push('Partial MakerKit detection - verify schema compatibility'); recommendations.push('Consider manual framework override if using MakerKit'); } this.detectedFeatures = detectedFeatures; return { framework: this.name, version: this.version, confidence, detectedFeatures, recommendations }; } catch (error) { logger_1.Logger.warn(`MakerKit detection failed: ${error.message}`); return { framework: this.name, confidence: 0, detectedFeatures: [], recommendations: ['Detection failed - check database permissions'] }; } } async createUser(data) { // Convert UserData to CompleteUserData for new auth flow const completeUserData = { email: data.email, password: data.password, name: data.name, username: data.username, avatar: data.avatar, bio: data.bio, metadata: data.metadata, identityProviders: ['email'], // Default to email provider primaryProvider: 'email', isPersonalAccount: true, emailConfirmed: true }; const result = await this.createCompleteUser(completeUserData); if (!result.success || !result.authUser) { throw new Error(`User creation failed: ${result.errors.join(', ')}`); } return { id: result.authUser.id, email: result.authUser.email, name: data.name, username: data.username, avatar: data.avatar, created_at: result.authUser.createdAt, metadata: result.authUser.userMetadata }; } /** * Create complete user with auth.users + auth.identities + accounts + profiles * Implements FR-1.1: Complete authentication flow */ async createCompleteUser(data) { if (!this.identityManager) { throw new Error('Identity manager not initialized'); } const result = { success: false, identities: [], errors: [], warnings: [], recommendations: [] }; try { logger_1.Logger.debug(`Creating complete MakerKit user: ${data.email}`); // Step 1: Create auth.users record const { data: authUser, error: authError } = await this.client.auth.admin.createUser({ email: data.email, password: data.password || 'defaultPassword123!', email_confirm: data.emailConfirmed ?? true, user_metadata: { name: data.name, username: data.username, avatar_url: data.avatar, bio: data.bio, ...data.metadata } }); if (authError) { result.errors.push(`Auth user creation failed: ${authError.message}`); return result; } if (!authUser.user) { result.errors.push('Auth user creation returned no user'); return result; } result.authUser = { id: authUser.user.id, email: authUser.user.email, emailConfirmed: authUser.user.email_confirmed_at != null, createdAt: authUser.user.created_at, updatedAt: authUser.user.updated_at || authUser.user.created_at, lastSignInAt: authUser.user.last_sign_in_at || undefined, userMetadata: authUser.user.user_metadata, appMetadata: authUser.user.app_metadata, role: authUser.user.role, aud: authUser.user.aud }; // Step 2: Create auth.identities records if (this.authFlowConfig.createIdentities && data.identityProviders?.length) { logger_1.Logger.debug(`Creating identities for ${data.identityProviders.length} providers`); for (const provider of data.identityProviders) { const providerData = this.identityManager.generateOAuthProviderData(provider, data.email); const identityResult = await this.identityManager.createIdentity({ userId: authUser.user.id, provider: provider, providerId: providerData.providerId, email: data.email, providerMetadata: providerData.metadata, createdAt: authUser.user.created_at, updatedAt: authUser.user.created_at }); if (identityResult.success && identityResult.identity) { result.identities.push({ id: identityResult.identity.id, userId: identityResult.identity.user_id, identityData: identityResult.identity.identity_data, provider: identityResult.identity.provider, lastSignInAt: identityResult.identity.last_sign_in_at, createdAt: identityResult.identity.created_at, updatedAt: identityResult.identity.updated_at }); } else { result.warnings.push(`Identity creation failed for ${provider}: ${identityResult.error}`); } result.warnings.push(...identityResult.warnings); } } // Step 2.5: Create MFA factors if enabled (FR-1.2: Add MFA Factor Support) // TODO: MFA functionality temporarily disabled // if (this.authFlowConfig.enableMFA && this.mfaManager) { // Logger.debug('Creating MFA factors for complete auth flow'); // // const mfaFactors = await this.createMFAFactorsForUser(authUser.user.id, data); // if (mfaFactors.length > 0) { // result.mfaFactors = mfaFactors; // result.recommendations.push(`Created ${mfaFactors.length} MFA factor${mfaFactors.length !== 1 ? 's' : ''} for enhanced security`); // } else { // result.warnings.push('MFA enabled but no factors were created'); // } // } // Step 3: Wait for MakerKit triggers and handle account creation if (this.authFlowConfig.useMakerKitTriggers) { await new Promise(resolve => setTimeout(resolve, 1000)); } // Step 4: Verify/create account record if (this.authFlowConfig.createAccountRecords) { const accountResult = await this.ensureAccountExists(authUser.user.id, data); if (accountResult) { result.account = accountResult; } else { result.warnings.push('Account creation failed or not completed'); } } // Step 5: Verify/create profile record if (this.authFlowConfig.createProfileRecords) { const profileResult = await this.ensureProfileExists(authUser.user.id, data); if (profileResult) { result.profile = profileResult; } else { result.warnings.push('Profile creation failed or not completed'); } } // Step 6: Trigger development webhooks if enabled (FR-1.3) if (this.authFlowConfig.setupDevelopmentWebhooks) { await this.triggerUserCreatedWebhook(result.authUser); } // Add success recommendations result.recommendations.push('Complete MakerKit auth flow successfully created', `Created ${result.identities.length} identity provider${result.identities.length !== 1 ? 's' : ''}`, 'User ready for MakerKit application testing'); if (this.authFlowConfig.setupDevelopmentWebhooks) { result.recommendations.push('Development webhooks triggered for user creation'); } result.success = true; logger_1.Logger.success(`✅ Complete MakerKit user created: ${data.email}`); return result; } catch (error) { logger_1.Logger.error(`Complete user creation failed for ${data.email}:`, error); result.errors.push(error.message); return result; } } async handleConstraints(table, data) { const appliedFixes = []; const warnings = []; let processedData = { ...data }; try { // Handle MakerKit-specific constraints if (table === 'accounts') { // Handle accounts_slug_null_if_personal_account_true constraint if (processedData.is_personal_account === true && processedData.slug !== null) { appliedFixes.push({ type: 'set_field', field: 'slug', oldValue: processedData.slug, newValue: null, reason: 'Personal accounts must have null slug (MakerKit constraint)', confidence: 0.95 }); processedData.slug = null; } // Ensure personal accounts have is_personal_account set if (processedData.is_personal_account === undefined) { appliedFixes.push({ type: 'set_field', field: 'is_personal_account', oldValue: undefined, newValue: true, reason: 'Default to personal account for user profiles', confidence: 0.9 }); processedData.is_personal_account = true; processedData.slug = null; // Ensure slug is null for personal accounts } // Handle account_id for tenant-scoped data if (!processedData.id && processedData.user_id) { processedData.id = processedData.user_id; } } // Handle profiles table if (table === 'profiles') { // Ensure profile has required fields if (!processedData.name && processedData.display_name) { appliedFixes.push({ type: 'set_field', field: 'name', oldValue: undefined, newValue: processedData.display_name, reason: 'Map display_name to name field', confidence: 0.8 }); processedData.name = processedData.display_name; } } // Handle tenant-scoped tables (require account_id) const tenantScopedTables = ['setups', 'gear_items', 'trips', 'modifications', 'reviews', 'media_attachments']; if (tenantScopedTables.includes(table) && !processedData.account_id) { warnings.push('Tenant-scoped table requires account_id - ensure proper context'); } return { success: true, originalData: data, modifiedData: processedData, appliedFixes, warnings, errors: [], bypassRequired: false }; } catch (error) { logger_1.Logger.warn(`Constraint handling failed for ${table}: ${error.message}`); return { success: false, originalData: data, modifiedData: processedData, appliedFixes, warnings: [...warnings, `Constraint handling error: ${error.message}`], errors: [error.message], bypassRequired: true }; } } getRecommendations() { const baseRecommendations = [ 'Use auth.admin.createUser() for user creation to trigger MakerKit flows', 'Ensure personal accounts have slug=null to satisfy constraints', 'Let MakerKit triggers handle account and profile creation when possible', 'Respect tenant boundaries with proper account_id foreign keys' ]; if (this.version === 'v3') { baseRecommendations.push('MakerKit v3 detected - use latest API patterns'); } if (this.detectedFeatures.includes('personal_account_constraint')) { baseRecommendations.push('Personal account constraint detected - auto-fix enabled'); } return baseRecommendations; } supportsFeature(feature) { const supportedFeatures = [ 'auth_trigger_user_creation', 'constraint_auto_fix', 'personal_account_handling', 'tenant_scoped_data', 'rls_compliance', 'business_logic_respect', 'constraint_discovery', 'framework_specific_handlers', 'mfa_factor_support', // FR-1.2: Add MFA Factor Support 'development_webhook_setup' // FR-1.3: Development Webhook Setup ]; return supportedFeatures.includes(feature); } /** * Configure MFA settings for the strategy * Implements FR-1.2: Add MFA Factor Support */ configureMFA(enableMFA, options) { this.authFlowConfig.enableMFA = enableMFA; if (enableMFA) { logger_1.Logger.info('✅ MFA support enabled for MakerKit strategy'); if (options?.enforceForRoles?.length) { logger_1.Logger.info(`MFA will be enforced for roles: ${options.enforceForRoles.join(', ')}`); } if (options?.defaultSecurityLevel) { logger_1.Logger.info(`Default MFA security level: ${options.defaultSecurityLevel}`); } } else { logger_1.Logger.info('MFA support disabled'); } } /** * Get MFA validation result for the platform * TODO: MFA functionality temporarily disabled */ async validateMFASupport() { // TODO: MFA functionality temporarily disabled return { supported: false, tableExists: false, hasPermissions: false, errors: ['MFA functionality temporarily disabled'], warnings: [] }; } /** * Configure development webhooks for the strategy * Implements FR-1.3: Development Webhook Setup */ async configureWebhooks(config) { if (!this.webhookManager) { throw new Error('Webhook manager not initialized'); } await this.webhookManager.configure(config); this.authFlowConfig.setupDevelopmentWebhooks = config.enabled; logger_1.Logger.info(`✅ Development webhooks ${config.enabled ? 'enabled' : 'disabled'} for MakerKit strategy`); } /** * Setup development webhook endpoints automatically */ async setupDevelopmentWebhooks() { if (!this.webhookManager) { throw new Error('Webhook manager not initialized'); } if (!this.authFlowConfig.setupDevelopmentWebhooks) { logger_1.Logger.debug('Development webhooks are disabled'); return { success: false, endpoints: [], errors: ['Development webhooks are disabled in auth flow configuration'] }; } logger_1.Logger.info('🔗 Setting up MakerKit development webhooks...'); const result = await this.webhookManager.setupDevelopmentEndpoints(); if (result.success) { logger_1.Logger.success(`✅ Setup ${result.endpoints.length} development webhook endpoints`); // Trigger webhooks for newly created users if enabled if (result.endpoints.some(ep => ep.events.includes('user.created'))) { logger_1.Logger.info('💡 Webhook endpoints ready for user creation events'); } } else { logger_1.Logger.error('❌ Failed to setup development webhooks:', result.errors); } return result; } /** * Generate platform-specific webhook configuration */ generatePlatformWebhookConfig(architecture = 'individual', domain = 'generic') { if (!this.webhookManager) { logger_1.Logger.warn('Webhook manager not initialized'); return null; } const config = this.webhookManager.generatePlatformWebhookConfig(architecture, domain); logger_1.Logger.info(`📋 Generated webhook configuration for ${architecture} ${domain} platform`); return config; } /** * Validate webhook support for the platform */ async validateWebhookSupport() { if (!this.webhookManager) { return { supported: false, configured: false, errors: ['Webhook manager not initialized'], warnings: [] }; } try { const config = this.webhookManager.getConfiguration(); const endpoints = await this.webhookManager.listEndpoints(); return { supported: this.authFlowConfig.setupDevelopmentWebhooks, configured: config !== null, errors: [], warnings: endpoints.length === 0 ? ['No webhook endpoints configured'] : [] }; } catch (error) { return { supported: false, configured: false, errors: [`Webhook validation failed: ${error.message}`], warnings: [] }; } } /** * Trigger webhook for user creation (integrates with createCompleteUser) */ async triggerUserCreatedWebhook(user) { if (!this.webhookManager || !this.authFlowConfig.setupDevelopmentWebhooks) { return; } try { const payload = { type: 'user.created', user: { id: user.id, email: user.email, created_at: user.createdAt, updated_at: user.updatedAt, user_metadata: user.userMetadata || {}, app_metadata: user.appMetadata || {} } }; const results = await this.webhookManager.triggerWebhook('user.created', payload); if (results.length > 0) { const successful = results.filter(r => r.success).length; logger_1.Logger.info(`📡 Triggered ${successful}/${results.length} user creation webhooks`); } } catch (error) { logger_1.Logger.warn(`Failed to trigger user creation webhook: ${error.message}`); } } /** * Discover constraints using MakerKit-aware analysis */ async discoverConstraints(tableNames) { if (!this.constraintEngine) { throw new Error('Constraint engine not initialized'); } try { logger_1.Logger.debug('Discovering constraints with MakerKit strategy'); // Use MakerKit-specific table focus if no tables specified const targetTables = tableNames || [ 'accounts', 'profiles', 'subscriptions', 'organizations', 'organization_members', 'invitations' ]; const discoveryResult = await this.constraintEngine.discoverConstraints(targetTables); // Convert discovery engine tables to constraint types format with MakerKit-specific enhancements const enhancedTables = discoveryResult.tables.map(table => { let confidence = discoveryResult.confidence; // Add MakerKit-specific confidence boost for accounts table if (table.tableName === 'accounts' && table.constraints.length > 0) { confidence = Math.min(confidence + 0.2, 1.0); } return { table: table.tableName, schema: 'public', checkConstraints: [], foreignKeyConstraints: [], uniqueConstraints: [], primaryKeyConstraints: [], notNullConstraints: [], confidence, discoveryTimestamp: new Date().toISOString() }; }); return { success: true, tables: enhancedTables, totalConstraints: discoveryResult.businessRules?.length || 0, confidence: discoveryResult.confidence, businessRules: discoveryResult.businessRules || [], errors: [], warnings: [], recommendations: [ ...this.getRecommendations(), 'Enable constraint-aware data generation for better reliability' ] }; } catch (error) { logger_1.Logger.error('MakerKit constraint discovery failed:', error); return { success: false, tables: [], totalConstraints: 0, confidence: 0, businessRules: [], errors: [error.message], warnings: [], recommendations: ['Review constraint discovery configuration'] }; } } /** * Get MakerKit-specific constraint handlers */ getConstraintHandlers() { return [ { id: 'makerkit_personal_account_slug', type: 'check', priority: 100, description: 'Handles MakerKit personal account slug constraint', canHandle: (constraint) => { return constraint.constraintName?.toLowerCase().includes('accounts_slug_null_if_personal') || (constraint.checkClause?.toLowerCase().includes('is_personal_account') && constraint.checkClause?.toLowerCase().includes('slug')); }, handle: (constraint, data) => { const result = { success: true, originalData: { ...data }, modifiedData: { ...data }, appliedFixes: [], warnings: [], errors: [], bypassRequired: false }; // If is_personal_account is true, slug must be null if (data.is_personal_account === true && data.slug !== null) { result.modifiedData.slug = null; result.appliedFixes.push({ type: 'set_field', field: 'slug', oldValue: data.slug, newValue: null, reason: 'Personal accounts must have null slug (MakerKit constraint)', confidence: 0.95 }); } // Default to personal account if not specified if (data.is_personal_account === undefined) { result.modifiedData.is_personal_account = true; result.modifiedData.slug = null; result.appliedFixes.push({ type: 'set_field', field: 'is_personal_account', oldValue: undefined, newValue: true, reason: 'Default to personal account for profile compatibility', confidence: 0.9 }); } return result; } }, { id: 'makerkit_organization_member_unique', type: 'unique', priority: 90, description: 'Handles MakerKit organization member uniqueness', canHandle: (constraint) => { return constraint.constraintName?.toLowerCase().includes('organization_member') || (constraint.columns?.includes('organization_id') && constraint.columns?.includes('user_id')); }, handle: (constraint, data) => { return { success: true, originalData: { ...data }, modifiedData: { ...data }, appliedFixes: [], warnings: data.organization_id && data.user_id ? [] : ['Organization member requires both organization_id and user_id'], errors: [], bypassRequired: false }; } } ]; } /** * Apply constraint fixes using MakerKit-aware logic */ async applyConstraintFixes(table, data, constraints) { if (!this.constraintRegistry) { throw new Error('Constraint registry not initialized'); } try { logger_1.Logger.debug(`Applying MakerKit constraint fixes for table: ${table}`); const result = this.constraintRegistry.handleTableConstraints(constraints, data); // Add MakerKit-specific enhancements if (table === 'accounts' && !result.appliedFixes.some(f => f.field === 'is_personal_account')) { // Ensure personal account defaults for MakerKit if (data.is_personal_account === undefined) { result.modifiedData.is_personal_account = true; result.modifiedData.slug = null; result.appliedFixes.push({ type: 'set_field', field: 'is_personal_account', oldValue: undefined, newValue: true, reason: 'MakerKit default: personal account for user profiles', confidence: 0.9 }); } } return result; } catch (error) { logger_1.Logger.error('MakerKit constraint fix application failed:', error); return { success: false, originalData: data, modifiedData: data, appliedFixes: [], warnings: [], errors: [error.message], bypassRequired: true }; } } /** * Analyze business logic patterns for MakerKit */ async analyzeBusinessLogic() { if (!this.businessLogicAnalyzer) { throw new Error('Business logic analyzer not initialized'); } try { logger_1.Logger.debug('Analyzing MakerKit business logic patterns'); const analysis = await this.businessLogicAnalyzer.analyzeBusinessLogic(); // Add MakerKit-specific enhancements to the analysis if (analysis.success) { analysis.framework = 'makerkit'; // Boost confidence if MakerKit patterns are detected if (analysis.triggerAnalysis.userCreationFlow?.usesAuthTriggers) { analysis.confidence = Math.min(analysis.confidence + 0.1, 1.0); } // Add MakerKit-specific recommendations const makerKitRecommendations = [ 'Use auth.admin.createUser() for proper MakerKit workflow', 'Ensure is_personal_account=true for personal profiles', 'Let triggers handle account and profile creation' ]; analysis.warnings.push(...makerKitRecommendations); } return analysis; } catch (error) { logger_1.Logger.error('MakerKit business logic analysis failed:', error); throw error; } } /** * Seed data with RLS compliance for MakerKit */ async seedWithRLSCompliance(table, data, userContext) { if (!this.rlsCompliantSeeder) { throw new Error('RLS compliant seeder not initialized'); } try { logger_1.Logger.debug(`MakerKit RLS-compliant seeding for table: ${table}`); // For MakerKit, we prefer auth-triggered workflows const result = await this.rlsCompliantSeeder.seedWithRLSCompliance(table, data, userContext); // Add MakerKit-specific context to the result if (result.success && result.userContext) { result.warnings = result.warnings || []; result.warnings.push('Used MakerKit auth-triggered workflow'); } return result; } c