UNPKG

supa-seed

Version:

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

346 lines 12.6 kB
"use strict"; /** * Constraint Handler Registry * Manages registration and selection of constraint handlers */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ConstraintRegistry = void 0; const logger_1 = require("../../core/utils/logger"); class ConstraintRegistry { constructor(options = {}) { this.handlers = new Map(); this.handlersByType = new Map(); this.options = { enablePriorityHandling: true, enableFallbackHandlers: true, logHandlerSelection: false, validateHandlers: true, ...options }; // Initialize type maps this.initializeTypeMaps(); } /** * Initialize constraint type maps */ initializeTypeMaps() { const types = ['check', 'foreign_key', 'unique', 'primary_key', 'not_null']; types.forEach(type => { this.handlersByType.set(type, []); }); } /** * Register a constraint handler */ registerHandler(handler) { try { // Validate handler if enabled if (this.options.validateHandlers) { this.validateHandler(handler); } // Register handler this.handlers.set(handler.id, handler); // Add to type-specific list const typeHandlers = this.handlersByType.get(handler.type) || []; typeHandlers.push(handler); // Sort by priority (higher priority first) if (this.options.enablePriorityHandling) { typeHandlers.sort((a, b) => b.priority - a.priority); } this.handlersByType.set(handler.type, typeHandlers); logger_1.Logger.debug(`Registered constraint handler: ${handler.id} (type: ${handler.type}, priority: ${handler.priority})`); } catch (error) { logger_1.Logger.error(`Failed to register handler ${handler.id}:`, error); throw error; } } /** * Register multiple handlers */ registerHandlers(handlers) { for (const handler of handlers) { this.registerHandler(handler); } } /** * Unregister a handler */ unregisterHandler(handlerId) { const handler = this.handlers.get(handlerId); if (!handler) { return false; } // Remove from main registry this.handlers.delete(handlerId); // Remove from type-specific list const typeHandlers = this.handlersByType.get(handler.type) || []; const index = typeHandlers.findIndex(h => h.id === handlerId); if (index !== -1) { typeHandlers.splice(index, 1); this.handlersByType.set(handler.type, typeHandlers); } logger_1.Logger.debug(`Unregistered constraint handler: ${handlerId}`); return true; } /** * Find the best handler for a constraint */ findHandler(constraint, constraintType, data) { const typeHandlers = this.handlersByType.get(constraintType) || []; if (typeHandlers.length === 0) { if (this.options.logHandlerSelection) { logger_1.Logger.debug(`No handlers registered for constraint type: ${constraintType}`); } return null; } // Find handlers that can handle this constraint const candidates = []; for (const handler of typeHandlers) { try { if (handler.canHandle(constraint, data)) { candidates.push({ handler, confidence: this.calculateHandlerConfidence(handler, constraint, data), reason: 'pattern_match' }); } } catch (error) { logger_1.Logger.warn(`Handler ${handler.id} canHandle check failed:`, error); } } if (candidates.length === 0) { if (this.options.logHandlerSelection) { logger_1.Logger.debug(`No handlers can handle constraint ${constraint.constraintName || 'unknown'} of type ${constraintType}`); } return null; } // Sort by confidence (higher first) and priority candidates.sort((a, b) => { const confidenceDiff = b.confidence - a.confidence; if (Math.abs(confidenceDiff) > 0.1) { return confidenceDiff; } return b.handler.priority - a.handler.priority; }); const bestMatch = candidates[0]; if (this.options.logHandlerSelection) { logger_1.Logger.debug(`Selected handler ${bestMatch.handler.id} for ${constraintType} constraint (confidence: ${bestMatch.confidence})`); } return bestMatch; } /** * Handle a constraint using the best available handler */ handleConstraint(constraint, constraintType, data) { const match = this.findHandler(constraint, constraintType, data); if (!match) { return { success: false, originalData: data, modifiedData: data, appliedFixes: [], warnings: [`No handler available for ${constraintType} constraint`], errors: [], bypassRequired: true }; } try { const result = match.handler.handle(constraint, data); // Add handler information to result if (!result.appliedFixes) { result.appliedFixes = []; } // Log successful handling if (this.options.logHandlerSelection && result.success) { logger_1.Logger.debug(`Handler ${match.handler.id} successfully processed constraint`); } return result; } catch (error) { logger_1.Logger.error(`Handler ${match.handler.id} failed to process constraint:`, error); return { success: false, originalData: data, modifiedData: data, appliedFixes: [], warnings: [], errors: [`Handler error: ${error.message}`], bypassRequired: true }; } } /** * Handle all constraints for a table */ handleTableConstraints(tableConstraints, data) { const combinedResult = { success: true, originalData: { ...data }, modifiedData: { ...data }, appliedFixes: [], warnings: [], errors: [], bypassRequired: false }; let currentData = { ...data }; // Handle check constraints for (const constraint of tableConstraints.checkConstraints) { const result = this.handleConstraint(constraint, 'check', currentData); this.mergeResults(combinedResult, result); if (result.success && result.modifiedData) { currentData = { ...result.modifiedData }; } } // Handle foreign key constraints for (const constraint of tableConstraints.foreignKeyConstraints) { const result = this.handleConstraint(constraint, 'foreign_key', currentData); this.mergeResults(combinedResult, result); if (result.success && result.modifiedData) { currentData = { ...result.modifiedData }; } } // Handle unique constraints for (const constraint of tableConstraints.uniqueConstraints) { const result = this.handleConstraint(constraint, 'unique', currentData); this.mergeResults(combinedResult, result); if (result.success && result.modifiedData) { currentData = { ...result.modifiedData }; } } // Handle not null constraints for (const constraint of tableConstraints.notNullConstraints) { const result = this.handleConstraint(constraint, 'not_null', currentData); this.mergeResults(combinedResult, result); if (result.success && result.modifiedData) { currentData = { ...result.modifiedData }; } } combinedResult.modifiedData = currentData; return combinedResult; } /** * Get handler by ID */ getHandler(handlerId) { return this.handlers.get(handlerId); } /** * Get all handlers of a specific type */ getHandlersByType(constraintType) { return this.handlersByType.get(constraintType) || []; } /** * Get all registered handlers */ getAllHandlers() { return Array.from(this.handlers.values()); } /** * Get registry statistics */ getStats() { const stats = { totalHandlers: this.handlers.size, handlersByType: {}, handlerIds: Array.from(this.handlers.keys()) }; // Count by type for (const [type, handlers] of this.handlersByType) { stats.handlersByType[type] = handlers.length; } return stats; } /** * Clear all handlers */ clear() { this.handlers.clear(); this.initializeTypeMaps(); logger_1.Logger.debug('Cleared all constraint handlers'); } /** * Validate a handler before registration */ validateHandler(handler) { if (!handler.id || typeof handler.id !== 'string') { throw new Error('Handler must have a valid string ID'); } if (this.handlers.has(handler.id)) { throw new Error(`Handler with ID ${handler.id} is already registered`); } if (!handler.type || !['check', 'foreign_key', 'unique', 'primary_key', 'not_null'].includes(handler.type)) { throw new Error('Handler must have a valid constraint type'); } if (typeof handler.priority !== 'number') { throw new Error('Handler must have a numeric priority'); } if (typeof handler.canHandle !== 'function') { throw new Error('Handler must implement canHandle method'); } if (typeof handler.handle !== 'function') { throw new Error('Handler must implement handle method'); } } /** * Calculate confidence score for a handler */ calculateHandlerConfidence(handler, constraint, data) { // Base confidence on handler priority (normalized) let confidence = Math.min(handler.priority / 100, 1.0); // Boost confidence for specific pattern matching if (handler.id.includes('makerkit') && (constraint.constraintName?.toLowerCase().includes('makerkit') || constraint.checkClause?.toLowerCase().includes('personal_account'))) { confidence += 0.3; } // Ensure confidence is between 0 and 1 return Math.min(Math.max(confidence, 0), 1); } /** * Merge two constraint handling results */ mergeResults(target, source) { // Merge success state (false if any fails) target.success = target.success && source.success; // Merge arrays target.appliedFixes.push(...source.appliedFixes); target.warnings.push(...source.warnings); target.errors.push(...source.errors); // Set bypass required if any handler requires it target.bypassRequired = target.bypassRequired || source.bypassRequired; } /** * Test a handler against sample data */ testHandler(handlerId, sampleConstraint, sampleData) { const handler = this.handlers.get(handlerId); if (!handler) { return { canHandle: false, error: `Handler ${handlerId} not found` }; } try { const canHandle = handler.canHandle(sampleConstraint, sampleData); if (!canHandle) { return { canHandle: false }; } const result = handler.handle(sampleConstraint, sampleData); return { canHandle: true, result }; } catch (error) { return { canHandle: false, error: error.message }; } } } exports.ConstraintRegistry = ConstraintRegistry; //# sourceMappingURL=constraint-registry.js.map