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
JavaScript
"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