supa-seed
Version:
A constraint-aware, framework-agnostic database seeding framework with deep PostgreSQL business logic discovery and MakerKit integration support
223 lines • 9.17 kB
JavaScript
"use strict";
/**
* Dynamic Table Mapping Resolver
* FEAT-003: Memory Management & Schema Mapping Fixes - Phase 2
*
* Resolves table name conflicts between framework expectations and actual schema.
* Handles MakerKit base_templates vs setup_types mapping and other schema variations.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.TableMappingResolver = void 0;
exports.createTableMappingResolver = createTableMappingResolver;
exports.resolveTableName = resolveTableName;
const logger_1 = require("../core/utils/logger");
const schema_mappings_1 = require("../core/config/schema-mappings");
/**
* Resolves table name conflicts dynamically based on configuration and schema introspection
*/
class TableMappingResolver {
constructor(client, config) {
this.cachedMappings = new Map();
this.schemaValidated = false;
this.client = client;
this.config = config;
logger_1.Logger.debug('🗺️ TableMappingResolver initialized:', {
framework: config.framework,
validateWithDatabase: config.validateWithDatabase,
hasCustomMappings: !!config.customMappings
});
}
/**
* Resolve a framework-expected table name to the actual table name
*/
async resolveTableName(expectedTableName, tableType) {
const cacheKey = `${expectedTableName}:${tableType}`;
// Return cached result if available
if (this.cachedMappings.has(cacheKey)) {
return this.cachedMappings.get(cacheKey);
}
const result = await this._resolveTableNameInternal(expectedTableName, tableType);
// Cache the result
this.cachedMappings.set(cacheKey, result);
logger_1.Logger.debug(`🗺️ Table mapping resolved: ${expectedTableName} -> ${result.actualTableName}`, {
source: result.source,
exists: result.exists,
warnings: result.warnings.length
});
return result;
}
/**
* Internal table name resolution logic
*/
async _resolveTableNameInternal(expectedTableName, tableType) {
const warnings = [];
try {
// Step 1: Get schema mapping for the framework
const schemaMapping = this.getSchemaMapping();
// Step 2: Get the configured table name for this table type
const tableMapping = schemaMapping[tableType];
if (!tableMapping) {
warnings.push(`No ${tableType} configuration found for framework: ${this.config.framework}`);
return {
actualTableName: expectedTableName,
exists: false,
source: 'error',
warnings
};
}
const configuredTableName = tableMapping.name;
// Step 3: Validate with database if enabled
if (this.config.validateWithDatabase) {
const tableExists = await this.validateTableExists(configuredTableName);
if (tableExists) {
return {
actualTableName: configuredTableName,
exists: true,
source: 'config',
warnings
};
}
else {
warnings.push(`Configured table '${configuredTableName}' does not exist in database`);
// Try fallback to expected name
const fallbackExists = await this.validateTableExists(expectedTableName);
if (fallbackExists) {
warnings.push(`Using fallback table '${expectedTableName}' instead`);
return {
actualTableName: expectedTableName,
exists: true,
source: 'fallback',
warnings
};
}
else {
warnings.push(`Neither configured table '${configuredTableName}' nor expected table '${expectedTableName}' exist`);
return {
actualTableName: configuredTableName, // Use configured even if it doesn't exist
exists: false,
source: 'error',
warnings
};
}
}
}
else {
// No validation - trust configuration
return {
actualTableName: configuredTableName,
exists: true, // Assume it exists
source: 'config',
warnings
};
}
}
catch (error) {
logger_1.Logger.error('❌ Error resolving table name:', error);
warnings.push(`Error during table name resolution: ${error instanceof Error ? error.message : 'Unknown error'}`);
return {
actualTableName: expectedTableName,
exists: false,
source: 'error',
warnings
};
}
}
/**
* Get the effective schema mapping (defaults + custom overrides)
*/
getSchemaMapping() {
const defaultMapping = schema_mappings_1.SCHEMA_MAPPINGS[this.config.framework];
if (!defaultMapping) {
throw new Error(`No schema mapping found for framework: ${this.config.framework}`);
}
if (!this.config.customMappings) {
return defaultMapping;
}
// Merge custom mappings with defaults
return {
userTable: { ...defaultMapping.userTable, ...(this.config.customMappings.userTable || {}) },
setupTable: defaultMapping.setupTable ? { ...defaultMapping.setupTable, ...(this.config.customMappings.setupTable || {}) } : this.config.customMappings.setupTable,
baseTemplateTable: defaultMapping.baseTemplateTable ? { ...defaultMapping.baseTemplateTable, ...(this.config.customMappings.baseTemplateTable || {}) } : this.config.customMappings.baseTemplateTable,
mediaAttachmentsTable: defaultMapping.mediaAttachmentsTable ? { ...defaultMapping.mediaAttachmentsTable, ...(this.config.customMappings.mediaAttachmentsTable || {}) } : this.config.customMappings.mediaAttachmentsTable
};
}
/**
* Validate that a table exists in the database
*/
async validateTableExists(tableName) {
try {
const { error } = await this.client
.from(tableName)
.select('*')
.limit(1);
return !error;
}
catch {
return false;
}
}
/**
* Get all table mappings for the current configuration
*/
async getAllTableMappings() {
const mappings = {};
const schemaMapping = this.getSchemaMapping();
// Map each table type
if (schemaMapping.userTable) {
mappings.userTable = await this.resolveTableName('users', 'userTable');
}
if (schemaMapping.setupTable) {
mappings.setupTable = await this.resolveTableName('setups', 'setupTable');
}
if (schemaMapping.baseTemplateTable) {
mappings.baseTemplateTable = await this.resolveTableName('setup_types', 'baseTemplateTable');
}
if (schemaMapping.mediaAttachmentsTable) {
mappings.mediaAttachmentsTable = await this.resolveTableName('media_attachments', 'mediaAttachmentsTable');
}
return mappings;
}
/**
* Clear cached mappings (useful for testing or configuration changes)
*/
clearCache() {
this.cachedMappings.clear();
this.schemaValidated = false;
logger_1.Logger.debug('🗑️ TableMappingResolver cache cleared');
}
/**
* Get performance and mapping statistics
*/
getStats() {
return {
cachedMappings: this.cachedMappings.size,
schemaValidated: this.schemaValidated,
framework: this.config.framework,
hasCustomMappings: !!this.config.customMappings
};
}
}
exports.TableMappingResolver = TableMappingResolver;
/**
* Factory function to create a table mapping resolver
*/
function createTableMappingResolver(client, config) {
return new TableMappingResolver(client, config);
}
/**
* Utility function to resolve a single table name quickly
*/
async function resolveTableName(client, framework, expectedTableName, tableType, customMappings) {
const resolver = createTableMappingResolver(client, {
framework,
customMappings,
validateWithDatabase: true
});
const result = await resolver.resolveTableName(expectedTableName, tableType);
if (result.warnings.length > 0) {
logger_1.Logger.warn('⚠️ Table mapping warnings:', result.warnings);
}
return result.actualTableName;
}
exports.default = TableMappingResolver;
//# sourceMappingURL=table-mapping-resolver.js.map