supa-seed
Version:
A constraint-aware, framework-agnostic database seeding framework with deep PostgreSQL business logic discovery and MakerKit integration support
460 lines • 19.6 kB
JavaScript
;
/**
* Framework-Agnostic User Creation Logic
* Adapts user creation to any schema structure and constraints
* Replaces framework-specific assumptions with dynamic adaptation
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.FrameworkAgnosticUserCreator = void 0;
const schema_driven_adapter_1 = require("./schema-driven-adapter");
const logger_1 = require("../core/utils/logger");
class FrameworkAgnosticUserCreator {
constructor(client, config = {}) {
this.client = client;
this.config = {
fallbackToSimpleCreation: true,
enableProgressiveEnhancement: true,
enableGracefulDegradation: true,
autoDetectFramework: true,
skipOptionalConstraints: false,
generateMissingRelationships: true,
createDependenciesOnDemand: true,
enableMultipleAttempts: true,
fallbackStrategies: ['simple_profiles', 'accounts_only', 'auth_only'],
...config
};
// Initialize the schema-driven adapter
this.adapter = new schema_driven_adapter_1.SchemaDrivenAdapter(client, {
enableConstraintValidation: true,
enableRollback: true,
enableAutoFixes: true,
enableSchemaCache: true
});
}
/**
* Create a user with adaptive framework detection and constraint handling
*/
async createUser(request) {
logger_1.Logger.info(`🎯 Starting framework-agnostic user creation for ${request.email}`);
const adaptationInfo = {
frameworkDetected: 'unknown',
strategiesUsed: [],
fallbacksTriggered: [],
constraintsHandled: 0,
adaptationConfidence: 0
};
const compatibilityReport = {
fullCompatibility: false,
partialCompatibility: false,
limitationsFound: [],
enhancementsApplied: []
};
try {
// Apply pre-creation hook
if (this.config.beforeUserCreation) {
request = await this.config.beforeUserCreation(request);
adaptationInfo.strategiesUsed.push('pre_creation_hook');
}
// Attempt primary creation strategy
let response = await this.attemptPrimaryCreation(request, adaptationInfo, compatibilityReport);
// If primary strategy failed and fallback is enabled, try fallback strategies
if (!response.success && this.config.enableMultipleAttempts) {
response = await this.attemptFallbackStrategies(request, adaptationInfo, compatibilityReport);
}
// Apply post-creation hook
if (this.config.afterUserCreation) {
response = await this.config.afterUserCreation(response);
adaptationInfo.strategiesUsed.push('post_creation_hook');
}
// Determine compatibility status
if (response.success) {
if (adaptationInfo.fallbacksTriggered.length === 0) {
compatibilityReport.fullCompatibility = true;
}
else {
compatibilityReport.partialCompatibility = true;
}
}
const result = {
...response,
adaptationInfo,
compatibilityReport
};
this.logCreationResult(result);
return result;
}
catch (error) {
logger_1.Logger.error('Framework-agnostic user creation failed:', error);
// Handle error with custom handler
if (this.config.onError) {
const shouldRetry = await this.config.onError(error, 1);
if (shouldRetry) {
logger_1.Logger.info('Retrying user creation after error handler...');
return this.createUser(request);
}
}
return this.createErrorResponse(request, error, adaptationInfo, compatibilityReport);
}
}
/**
* Attempt primary creation using schema-driven approach
*/
async attemptPrimaryCreation(request, adaptationInfo, compatibilityReport) {
logger_1.Logger.debug('Attempting primary schema-driven creation...');
try {
// Get schema information for adaptation
const schemaInfo = await this.adapter.getSchemaInfo();
adaptationInfo.frameworkDetected = schemaInfo.framework.type;
adaptationInfo.adaptationConfidence = schemaInfo.framework.confidence;
adaptationInfo.strategiesUsed.push('schema_driven');
// Apply progressive enhancement based on detected framework
if (this.config.enableProgressiveEnhancement) {
request = await this.applyProgressiveEnhancement(request, schemaInfo, compatibilityReport);
}
// Attempt creation
const response = await this.adapter.createUser(request);
// Track constraint handling
if (response.validationResult) {
adaptationInfo.constraintsHandled = response.validationResult.constraintsChecked;
if (response.appliedFixes && response.appliedFixes.length > 0) {
compatibilityReport.enhancementsApplied.push(`Applied ${response.appliedFixes.length} auto-fixes`);
}
}
return response;
}
catch (error) {
logger_1.Logger.warn(`Primary creation failed: ${error.message}`);
adaptationInfo.fallbacksTriggered.push('primary_strategy_failed');
throw error;
}
}
/**
* Apply progressive enhancement based on detected framework
*/
async applyProgressiveEnhancement(request, schemaInfo, compatibilityReport) {
const enhancedRequest = { ...request };
// MakerKit-specific enhancements
if (schemaInfo.framework.type === 'makerkit') {
// Ensure username is set for MakerKit
if (!enhancedRequest.username) {
enhancedRequest.username = this.generateUsernameFromEmail(enhancedRequest.email);
compatibilityReport.enhancementsApplied.push('Generated username for MakerKit compatibility');
}
// Add MakerKit-specific metadata
enhancedRequest.metadata = {
...enhancedRequest.metadata,
framework: 'makerkit',
version: schemaInfo.framework.version,
seedingSource: 'supa-seed-framework-agnostic'
};
compatibilityReport.enhancementsApplied.push('Added MakerKit-specific metadata');
}
// Nextjs-specific enhancements
if (schemaInfo.framework.type === 'nextjs') {
// Add Next.js specific fields if needed
compatibilityReport.enhancementsApplied.push('Applied Next.js compatibility enhancements');
}
// Custom framework enhancements
if (schemaInfo.framework.type === 'custom') {
// Apply generic enhancements for custom frameworks
if (!enhancedRequest.picture_url) {
enhancedRequest.picture_url = this.generateDefaultAvatar(enhancedRequest.name);
compatibilityReport.enhancementsApplied.push('Generated default avatar for custom framework');
}
}
return enhancedRequest;
}
/**
* Attempt fallback strategies when primary creation fails
*/
async attemptFallbackStrategies(request, adaptationInfo, compatibilityReport) {
logger_1.Logger.info('Primary creation failed, attempting fallback strategies...');
for (const strategy of this.config.fallbackStrategies || []) {
try {
logger_1.Logger.debug(`Attempting fallback strategy: ${strategy}`);
const response = await this.executeFallbackStrategy(strategy, request);
if (response.success) {
adaptationInfo.fallbacksTriggered.push(strategy);
adaptationInfo.strategiesUsed.push(`fallback_${strategy}`);
compatibilityReport.limitationsFound.push(`Primary strategy failed, used ${strategy} fallback`);
logger_1.Logger.success(`✅ Fallback strategy ${strategy} succeeded`);
return response;
}
}
catch (error) {
logger_1.Logger.warn(`Fallback strategy ${strategy} failed: ${error.message}`);
compatibilityReport.limitationsFound.push(`Fallback strategy ${strategy} failed: ${error.message}`);
}
}
throw new Error('All fallback strategies failed');
}
/**
* Execute a specific fallback strategy
*/
async executeFallbackStrategy(strategy, request) {
const executionId = crypto.randomUUID();
const startTime = Date.now();
switch (strategy) {
case 'simple_profiles':
return await this.createSimpleProfileUser(request, executionId, startTime);
case 'accounts_only':
return await this.createAccountOnlyUser(request, executionId, startTime);
case 'auth_only':
return await this.createAuthOnlyUser(request, executionId, startTime);
default:
throw new Error(`Unknown fallback strategy: ${strategy}`);
}
}
/**
* Simple profiles-based user creation (most basic approach)
*/
async createSimpleProfileUser(request, executionId, startTime) {
const userId = crypto.randomUUID();
// Create auth user
const { error: authError } = await this.client.auth.admin.createUser({
id: userId,
email: request.email,
password: request.password || 'password123',
email_confirm: true,
user_metadata: {
full_name: request.name,
username: request.username
}
});
if (authError) {
throw new Error(`Auth user creation failed: ${authError.message}`);
}
// Try to create a simple profile record
try {
const { error: profileError } = await this.client
.from('profiles')
.insert({
id: userId,
email: request.email,
name: request.name,
username: request.username,
bio: request.bio,
avatar_url: request.picture_url,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString()
});
if (profileError) {
logger_1.Logger.warn(`Profile creation failed, but auth user created: ${profileError.message}`);
}
}
catch (error) {
logger_1.Logger.warn('Profile table may not exist or have different structure');
}
return this.createSuccessResponse(request, userId, executionId, Date.now() - startTime, 'simple_profiles');
}
/**
* Account-only user creation (for account-based systems)
*/
async createAccountOnlyUser(request, executionId, startTime) {
const userId = crypto.randomUUID();
// Create auth user
const { error: authError } = await this.client.auth.admin.createUser({
id: userId,
email: request.email,
password: request.password || 'password123',
email_confirm: true
});
if (authError) {
throw new Error(`Auth user creation failed: ${authError.message}`);
}
// Try to create an account record
try {
const { error: accountError } = await this.client
.from('accounts')
.insert({
id: userId,
email: request.email,
name: request.name,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString()
});
if (accountError) {
logger_1.Logger.warn(`Account creation failed, but auth user created: ${accountError.message}`);
}
}
catch (error) {
logger_1.Logger.warn('Account table may not exist or have different structure');
}
return this.createSuccessResponse(request, userId, executionId, Date.now() - startTime, 'accounts_only');
}
/**
* Auth-only user creation (most basic fallback)
*/
async createAuthOnlyUser(request, executionId, startTime) {
const userId = crypto.randomUUID();
// Create only auth user with all data in metadata
const { error: authError } = await this.client.auth.admin.createUser({
id: userId,
email: request.email,
password: request.password || 'password123',
email_confirm: true,
user_metadata: {
full_name: request.name,
username: request.username,
bio: request.bio,
picture_url: request.picture_url,
...request.metadata
}
});
if (authError) {
throw new Error(`Auth user creation failed: ${authError.message}`);
}
return this.createSuccessResponse(request, userId, executionId, Date.now() - startTime, 'auth_only');
}
/**
* Utility methods
*/
generateUsernameFromEmail(email) {
const baseUsername = email.split('@')[0].toLowerCase();
// Remove special characters and add random suffix for uniqueness
const cleanUsername = baseUsername.replace(/[^a-z0-9]/g, '');
const randomSuffix = Math.floor(Math.random() * 1000);
return `${cleanUsername}${randomSuffix}`;
}
generateDefaultAvatar(name) {
const initials = name.split(' ').map(n => n[0]).join('').toUpperCase();
return `https://ui-avatars.com/api/?name=${encodeURIComponent(initials)}&background=random&bold=true&size=200`;
}
createSuccessResponse(request, userId, executionId, duration, strategy) {
return {
success: true,
userId,
executionId,
workflow: {
id: `fallback_${strategy}_${Date.now()}`,
name: `Fallback: ${strategy}`,
description: `Fallback user creation using ${strategy} strategy`,
steps: [],
rollbackSteps: [],
metadata: {
framework: 'fallback',
version: strategy,
createdAt: new Date().toISOString(),
schemaHash: 'fallback'
}
},
executionResult: {
success: true,
userId,
executionId,
duration,
completedSteps: [`create_auth_user`, `create_${strategy}_record`],
rollbackRequired: false,
rollbackCompleted: false,
stepResults: {
auth_user: { id: userId, email: request.email },
fallback_record: { strategy }
}
},
schemaInfo: {
framework: 'fallback',
version: strategy,
confidence: 0.5,
primaryUserTable: strategy === 'simple_profiles' ? 'profiles' :
strategy === 'accounts_only' ? 'accounts' : 'auth.users'
},
performance: {
introspectionTime: 0,
workflowBuildTime: 0,
executionTime: duration,
totalTime: duration
},
recommendations: [
`Used fallback strategy: ${strategy}`,
'Consider configuring schema-driven approach for better compatibility'
]
};
}
createErrorResponse(request, error, adaptationInfo, compatibilityReport) {
return {
success: false,
executionId: crypto.randomUUID(),
workflow: {
id: 'error_workflow',
name: 'Error Workflow',
description: 'Error occurred during user creation',
steps: [],
rollbackSteps: [],
metadata: {
framework: 'error',
version: 'error',
createdAt: new Date().toISOString(),
schemaHash: 'error'
}
},
executionResult: {
success: false,
executionId: crypto.randomUUID(),
duration: 0,
completedSteps: [],
error: error.message,
rollbackRequired: false,
rollbackCompleted: false,
stepResults: {}
},
schemaInfo: {
framework: 'error',
version: 'error',
confidence: 0,
primaryUserTable: 'unknown'
},
performance: {
introspectionTime: 0,
workflowBuildTime: 0,
executionTime: 0,
totalTime: 0
},
recommendations: [`Error: ${error.message}`],
adaptationInfo,
compatibilityReport
};
}
logCreationResult(result) {
if (result.success) {
logger_1.Logger.success(`✅ Framework-agnostic user creation succeeded`);
logger_1.Logger.info(` Framework: ${result.adaptationInfo.frameworkDetected}`);
logger_1.Logger.info(` Strategies: ${result.adaptationInfo.strategiesUsed.join(', ')}`);
logger_1.Logger.info(` Compatibility: ${result.compatibilityReport.fullCompatibility ? 'Full' : 'Partial'}`);
}
else {
logger_1.Logger.error(`❌ Framework-agnostic user creation failed`);
logger_1.Logger.error(` Error: ${result.executionResult.error}`);
logger_1.Logger.error(` Fallbacks tried: ${result.adaptationInfo.fallbacksTriggered.join(', ')}`);
}
}
/**
* Public configuration methods
*/
updateConfig(newConfig) {
this.config = { ...this.config, ...newConfig };
logger_1.Logger.debug('Framework-agnostic user creator configuration updated');
}
async getCompatibilityReport() {
const schemaInfo = await this.adapter.getSchemaInfo();
return {
detectedFramework: schemaInfo.framework.type,
confidence: schemaInfo.framework.confidence,
supportedFeatures: [
'Dynamic schema introspection',
'Constraint-aware validation',
'Progressive enhancement',
'Graceful degradation',
'Multiple fallback strategies'
],
limitations: schemaInfo.framework.confidence < 0.7 ?
['Low framework detection confidence'] : [],
recommendations: [
'Enable schema caching for better performance',
'Configure custom column mappings for better accuracy',
'Use constraint validation to prevent errors'
]
};
}
}
exports.FrameworkAgnosticUserCreator = FrameworkAgnosticUserCreator;
//# sourceMappingURL=framework-agnostic-user-creator.js.map