supa-seed
Version:
A constraint-aware, framework-agnostic database seeding framework with deep PostgreSQL business logic discovery and MakerKit integration support
854 lines • 28.7 kB
JavaScript
"use strict";
/**
* Configuration Layer System for SupaSeed v2.5.0
* Implements Task 5.1.2: Implement configuration layer system
* Core layer management, composition, and conflict resolution
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConfigurationLayerSystem = exports.DEFAULT_EXTENSIONS_CONFIG = exports.DEFAULT_DETECTION_CONFIG = exports.DEFAULT_UNIVERSAL_CONFIG = void 0;
const logger_1 = require("../utils/logger");
/**
* Default Universal Core Configuration
* Layer 1: Always active, provides MakerKit compatibility
*/
exports.DEFAULT_UNIVERSAL_CONFIG = {
layer: {
type: 'universal',
priority: 'highest',
enabled: true,
readonly: true
},
makerkit: {
enabled: true,
accountType: 'hybrid',
completeAuthFlow: true,
constraintCompliance: true,
rlsCompliance: true,
developmentSupport: true,
mfaSupport: true,
identityProviders: {
email: true,
google: false,
github: false,
facebook: false,
apple: false,
discord: false,
custom: []
},
databaseCompliance: {
enforceConstraints: true,
validateForeignKeys: true,
respectTimestamps: true,
handleNullables: true,
supportUUIDs: true
}
},
seeding: {
defaultUserCount: 10,
contentRatios: {
publicContent: 0.7,
privateContent: 0.2,
sharedContent: 0.1
},
relationships: {
generateRelationships: true,
relationshipDensity: 0.3,
respectConstraints: true
},
safety: {
validateBeforeInsert: true,
dryRunMode: false,
rollbackOnError: true,
maxRetries: 3
}
},
security: {
rlsCompliance: true,
policies: {
enforceUserScope: true,
validatePermissions: true,
auditAccess: false
}
},
webhook: {
enabled: false,
authentication: {
enabled: false,
method: 'jwt'
},
endpoints: {}
},
environment: {
type: 'development',
debugging: {
enableVerboseLogging: false,
logQueries: false,
logPerformance: false,
validateResults: true
},
performance: {
batchSize: 50,
parallelism: 3,
timeout: 30000,
memoryLimit: 512
}
}
};
/**
* Default Smart Detection Configuration Template
* Layer 2: Auto-configured based on platform detection
*/
exports.DEFAULT_DETECTION_CONFIG = {
platform: {
architecture: 'auto',
confidence: 0,
domain: 'auto',
evidence: {
tablePatterns: [],
relationshipPatterns: [],
constraintPatterns: [],
namingConventions: []
},
architectureSettings: {}
},
domain: {
domain: 'auto',
confidence: 0,
evidence: {
tableNames: [],
columnPatterns: [],
dataTypes: [],
businessLogicHints: []
},
domainSettings: {}
},
autoConfiguration: {
enabled: true,
confidenceThreshold: 0.8,
autoApply: false,
strategy: 'balanced'
},
optimizations: {
performance: {
suggestedBatchSize: 50,
suggestedParallelism: 3,
estimatedDataVolume: 'medium'
},
content: {
suggestedUserCount: 10,
suggestedContentRatios: {
public: 0.7,
private: 0.2,
shared: 0.1
},
recommendedArchetypes: []
},
features: {
enabledFeatures: [],
disabledFeatures: [],
optionalFeatures: []
}
}
};
/**
* Default Extensions Configuration Template
* Layer 3: Pluggable extensions and customizations
*/
exports.DEFAULT_EXTENSIONS_CONFIG = {
domainExtensions: {
enabled: []
},
archetypes: {
enabled: false,
generationConfig: {
targetArchitecture: 'individual',
targetDomain: 'generic',
usersPerArchetype: { min: 2, max: 5 },
distributionStrategy: 'weighted',
includedCategories: [],
excludedCategories: [],
experienceLevelDistribution: {
beginner: 0.3,
intermediate: 0.4,
advanced: 0.2,
expert: 0.1
},
generateRelationships: true,
relationshipDensity: 0.3,
applyDomainCustomizations: true
},
customArchetypes: [],
selectionStrategy: {
strategy: 'automatic'
}
},
customGenerators: {
contentGenerators: [],
relationshipGenerators: [],
mediaGenerators: []
},
integrations: {
apis: [],
storage: {
providers: []
},
analytics: {
providers: []
}
},
businessLogic: {
validationRules: [],
transformations: [],
constraints: []
}
};
/**
* Configuration Layer System - manages the 3-layer architecture
*/
class ConfigurationLayerSystem {
constructor() {
this.layers = new Map();
this.layerStatus = new Map();
this.changeListeners = new Map();
this.templates = new Map();
this.initializeDefaultLayers();
logger_1.Logger.debug('ConfigurationLayerSystem initialized with default layers');
}
/**
* Initialize the system with default layer configurations
*/
initializeDefaultLayers() {
// Layer 1: Universal Core (always enabled, readonly)
this.setLayer('universal', {
...exports.DEFAULT_UNIVERSAL_CONFIG,
layer: {
...exports.DEFAULT_UNIVERSAL_CONFIG.layer,
enabled: true,
readonly: true
}
});
// Layer 2: Smart Detection (auto-generated)
this.setLayer('detection', {
...exports.DEFAULT_DETECTION_CONFIG,
layer: {
type: 'detection',
priority: 'high',
enabled: true,
autoGenerated: true,
confidence: 0,
lastDetected: new Date()
}
});
// Layer 3: Extensions (user-configurable)
this.setLayer('extensions', {
...exports.DEFAULT_EXTENSIONS_CONFIG,
layer: {
type: 'extensions',
priority: 'medium',
enabled: false,
userConfigured: false,
lastModified: new Date()
}
});
}
/**
* Set configuration for a specific layer
*/
setLayer(layerType, config) {
const oldConfig = this.layers.get(layerType);
this.layers.set(layerType, config);
// Update layer status
this.updateLayerStatus(layerType, config);
// Emit change event
if (oldConfig) {
this.emitChangeEvent(layerType, 'updated', oldConfig, config);
}
else {
this.emitChangeEvent(layerType, 'created', null, config);
}
logger_1.Logger.debug(`Updated ${layerType} layer configuration`);
}
/**
* Get configuration for a specific layer
*/
getLayer(layerType) {
return this.layers.get(layerType);
}
/**
* Get all layer configurations
*/
getAllLayers() {
return {
universal: this.layers.get('universal'),
detection: this.layers.get('detection'),
extensions: this.layers.get('extensions')
};
}
/**
* Update smart detection layer with detection results
*/
updateDetectionLayer(architecture, domain, confidence, evidence) {
const currentDetection = this.getLayer('detection');
if (!currentDetection)
return;
const updatedDetection = {
...currentDetection,
layer: {
...currentDetection.layer,
confidence,
lastDetected: new Date()
},
platform: {
...currentDetection.platform,
architecture,
confidence,
evidence: evidence.platform || currentDetection.platform.evidence,
architectureSettings: this.generateArchitectureSettings(architecture)
},
domain: {
...currentDetection.domain,
domain,
confidence,
evidence: evidence.domain || currentDetection.domain.evidence,
domainSettings: this.generateDomainSettings(domain)
},
optimizations: this.generateOptimizations(architecture, domain, confidence)
};
this.setLayer('detection', updatedDetection);
logger_1.Logger.info(`Updated detection layer: ${architecture}/${domain} (confidence: ${confidence})`);
}
/**
* Enable or disable a specific layer
*/
setLayerEnabled(layerType, enabled) {
const layer = this.getLayer(layerType);
if (!layer)
return;
// Universal layer cannot be disabled
if (layerType === 'universal' && !enabled) {
logger_1.Logger.warn('Cannot disable universal layer - it is required for core functionality');
return;
}
layer.layer.enabled = enabled;
this.setLayer(layerType, layer);
logger_1.Logger.debug(`${enabled ? 'Enabled' : 'Disabled'} ${layerType} layer`);
}
/**
* Get the status of all layers
*/
getLayerStatuses() {
return {
universal: this.layerStatus.get('universal'),
detection: this.layerStatus.get('detection'),
extensions: this.layerStatus.get('extensions')
};
}
/**
* Get the status of a specific layer
*/
getLayerStatus(layerType) {
return this.layerStatus.get(layerType);
}
/**
* Validate all layers for consistency and compatibility
*/
validateLayers() {
const errors = [];
const warnings = [];
// Validate each layer individually
for (const [layerType, config] of this.layers) {
const layerValidation = this.validateLayer(layerType, config);
errors.push(...layerValidation.errors.map(e => ({
layer: layerType,
error: e.message
})));
warnings.push(...layerValidation.warnings.map(w => ({
layer: layerType,
warning: w
})));
}
// Validate layer interactions and compatibility
const interactionValidation = this.validateLayerInteractions();
errors.push(...interactionValidation.errors);
warnings.push(...interactionValidation.warnings);
return {
valid: errors.length === 0,
errors,
warnings
};
}
/**
* Register a template for use with the layer system
*/
registerTemplate(template) {
this.templates.set(template.id, template);
logger_1.Logger.debug(`Registered configuration template: ${template.name}`);
}
/**
* Apply a configuration template
*/
applyTemplate(templateId, overrides) {
const template = this.templates.get(templateId);
if (!template) {
logger_1.Logger.error(`Template not found: ${templateId}`);
return false;
}
try {
// Apply template layers
if (template.layers.universal) {
const universalConfig = this.mergeConfigurations(this.getLayer('universal'), template.layers.universal, 'merge');
this.setLayer('universal', universalConfig);
}
if (template.layers.detection) {
const detectionConfig = this.mergeConfigurations(this.getLayer('detection'), template.layers.detection, 'merge');
this.setLayer('detection', detectionConfig);
}
if (template.layers.extensions) {
const extensionsConfig = this.mergeConfigurations(this.getLayer('extensions'), template.layers.extensions, 'merge');
this.setLayer('extensions', extensionsConfig);
}
// Apply any overrides
if (overrides) {
this.applyOverrides(overrides);
}
logger_1.Logger.info(`Applied configuration template: ${template.name}`);
return true;
}
catch (error) {
logger_1.Logger.error(`Failed to apply template ${templateId}:`, error);
return false;
}
}
/**
* Get all registered templates
*/
getTemplates() {
return Array.from(this.templates.values());
}
/**
* Get templates matching specific criteria
*/
getMatchingTemplates(architecture, domain, complexity) {
return this.getTemplates().filter(template => {
const architectureMatch = !architecture ||
template.targets.architectures.includes(architecture);
const domainMatch = !domain ||
template.targets.domains.includes(domain);
const complexityMatch = !complexity ||
template.targets.complexity === complexity;
return architectureMatch && domainMatch && complexityMatch;
});
}
/**
* Add a change listener
*/
addChangeListener(id, listener) {
this.changeListeners.set(id, listener);
}
/**
* Remove a change listener
*/
removeChangeListener(id) {
this.changeListeners.delete(id);
}
/**
* Reset all layers to default configuration
*/
reset() {
this.layers.clear();
this.layerStatus.clear();
this.initializeDefaultLayers();
logger_1.Logger.info('Reset all configuration layers to defaults');
}
// Private helper methods
updateLayerStatus(layerType, config) {
const startTime = Date.now();
const validation = this.validateLayer(layerType, config);
const loadTime = Date.now() - startTime;
const status = {
layer: layerType,
status: {
enabled: config.layer?.enabled ?? true,
healthy: validation.valid,
lastUpdated: new Date(),
errors: validation.errors.map(e => e.message),
warnings: validation.warnings
},
performance: {
loadTime,
memoryUsage: this.estimateMemoryUsage(config),
cacheHitRate: 0.95 // Placeholder
},
validation
};
this.layerStatus.set(layerType, status);
}
validateLayer(layerType, config) {
const errors = [];
const warnings = [];
const suggestions = [];
// Basic layer structure validation
if (!config.layer) {
errors.push({
path: 'layer',
message: 'Missing layer metadata',
severity: 'error'
});
}
if (!config.layer?.type || config.layer.type !== layerType) {
errors.push({
path: 'layer.type',
message: `Layer type mismatch: expected ${layerType}`,
severity: 'error'
});
}
// Layer-specific validation
switch (layerType) {
case 'universal':
this.validateUniversalLayer(config, errors, warnings, suggestions);
break;
case 'detection':
this.validateDetectionLayer(config, errors, warnings, suggestions);
break;
case 'extensions':
this.validateExtensionsLayer(config, errors, warnings, suggestions);
break;
}
return {
valid: errors.filter(e => e.severity === 'error').length === 0,
errors,
warnings,
suggestions
};
}
validateUniversalLayer(config, errors, warnings, suggestions) {
if (!config.makerkit) {
errors.push({
path: 'makerkit',
message: 'Missing MakerKit configuration',
severity: 'error'
});
return;
}
if (!config.seeding) {
errors.push({
path: 'seeding',
message: 'Missing seeding configuration',
severity: 'error'
});
}
if (!config.environment) {
errors.push({
path: 'environment',
message: 'Missing environment configuration',
severity: 'error'
});
}
// Validate ranges and constraints
if (config.seeding?.contentRatios) {
const ratios = config.seeding.contentRatios;
const total = ratios.publicContent + ratios.privateContent + ratios.sharedContent;
if (Math.abs(total - 1.0) > 0.01) {
warnings.push('Content ratios should sum to 1.0');
suggestions.push({
path: 'seeding.contentRatios',
suggestion: 'Normalize content ratios to sum to 1.0',
impact: 'medium'
});
}
}
}
validateDetectionLayer(config, errors, warnings, suggestions) {
if (!config.platform) {
errors.push({
path: 'platform',
message: 'Missing platform configuration',
severity: 'error'
});
}
if (!config.domain) {
errors.push({
path: 'domain',
message: 'Missing domain configuration',
severity: 'error'
});
}
// Validate confidence scores
if (config.platform?.confidence < 0 || config.platform?.confidence > 1) {
errors.push({
path: 'platform.confidence',
message: 'Platform confidence must be between 0 and 1',
severity: 'error'
});
}
if (config.domain?.confidence < 0 || config.domain?.confidence > 1) {
errors.push({
path: 'domain.confidence',
message: 'Domain confidence must be between 0 and 1',
severity: 'error'
});
}
// Warn about low confidence
if (config.platform?.confidence < 0.5) {
warnings.push('Low platform detection confidence - consider manual verification');
}
if (config.domain?.confidence < 0.5) {
warnings.push('Low domain detection confidence - consider manual verification');
}
}
validateExtensionsLayer(config, errors, warnings, suggestions) {
if (!config.domainExtensions) {
warnings.push('No domain extensions configured');
}
if (!config.archetypes) {
warnings.push('No archetype configuration found');
}
// Validate custom generators
if (config.customGenerators) {
config.customGenerators.contentGenerators?.forEach((gen, index) => {
if (!gen.name) {
errors.push({
path: `customGenerators.contentGenerators[${index}].name`,
message: 'Custom generator must have a name',
severity: 'error'
});
}
});
}
}
validateLayerInteractions() {
const errors = [];
const warnings = [];
const universal = this.getLayer('universal');
const detection = this.getLayer('detection');
const extensions = this.getLayer('extensions');
// Check for conflicts between layers
if (universal && detection) {
// Check if detection settings conflict with universal settings
if (detection.optimizations?.performance?.suggestedBatchSize &&
detection.optimizations.performance.suggestedBatchSize > universal.environment?.performance?.batchSize * 2) {
warnings.push({
layer: 'detection',
warning: 'Detected performance settings significantly differ from universal settings'
});
}
}
if (detection && extensions) {
// Check if extensions are compatible with detected platform
if (extensions.archetypes?.enabled && detection.platform?.architecture) {
const compatibleArchetypes = extensions.archetypes.customArchetypes?.filter((arch) => arch.supportedArchitectures?.includes(detection.platform.architecture));
if (extensions.archetypes.customArchetypes?.length > 0 &&
compatibleArchetypes?.length === 0) {
warnings.push({
layer: 'extensions',
warning: 'No archetypes are compatible with detected platform architecture'
});
}
}
}
return { errors, warnings };
}
generateArchitectureSettings(architecture) {
switch (architecture) {
case 'individual':
return {
individual: {
focusOnPersonalContent: true,
enableSocialFeatures: true,
contentSharingRatio: 0.7
}
};
case 'team':
return {
team: {
enableTeamCollaboration: true,
teamSizeRange: { min: 3, max: 15 },
workspaceManagement: true,
permissionComplexity: 'moderate'
}
};
case 'hybrid':
return {
hybrid: {
balanceRatio: 0.6,
enableFlexibleRoles: true,
supportMultiContext: true
}
};
default:
return {};
}
}
generateDomainSettings(domain) {
switch (domain) {
case 'outdoor':
return {
outdoor: {
gearFocus: true,
locationData: true,
weatherAwareness: true,
safetyEmphasis: true
}
};
case 'saas':
return {
saas: {
productivityFocus: true,
integrationHeavy: true,
analyticsEnabled: true,
workflowOptimization: true
}
};
case 'ecommerce':
return {
ecommerce: {
productCatalogs: true,
inventoryManagement: true,
orderProcessing: true,
paymentIntegration: false
}
};
case 'social':
return {
social: {
networkingEmphasis: true,
contentSharing: true,
communityFeatures: true,
realTimeInteraction: false
}
};
default:
return {
generic: {
adaptiveContent: true,
broadCompatibility: true,
minimalAssumptions: true
}
};
}
}
generateOptimizations(architecture, domain, confidence) {
const baseOptimizations = {
performance: {
suggestedBatchSize: 50,
suggestedParallelism: 3,
estimatedDataVolume: 'medium'
},
content: {
suggestedUserCount: 10,
suggestedContentRatios: {
public: 0.7,
private: 0.2,
shared: 0.1
},
recommendedArchetypes: []
},
features: {
enabledFeatures: [],
disabledFeatures: [],
optionalFeatures: []
}
};
// Adjust based on architecture
if (architecture === 'team') {
baseOptimizations.content.suggestedUserCount = 15;
baseOptimizations.content.suggestedContentRatios.shared = 0.3;
baseOptimizations.content.suggestedContentRatios.private = 0.1;
baseOptimizations.content.suggestedContentRatios.public = 0.6;
}
// Adjust based on domain
if (domain === 'ecommerce') {
baseOptimizations.performance.estimatedDataVolume = 'medium';
baseOptimizations.performance.suggestedBatchSize = 100;
}
return baseOptimizations;
}
mergeConfigurations(base, override, strategy) {
switch (strategy) {
case 'override':
return { ...base, ...override };
case 'merge':
return this.deepMerge(base, override);
case 'fallback':
return this.fallbackMerge(base, override);
default:
return { ...base, ...override };
}
}
deepMerge(base, override) {
if (!base)
return override;
if (!override)
return base;
const result = { ...base };
for (const key in override) {
if (override[key] !== null && typeof override[key] === 'object' && !Array.isArray(override[key])) {
result[key] = this.deepMerge(result[key], override[key]);
}
else {
result[key] = override[key];
}
}
return result;
}
fallbackMerge(base, override) {
const result = { ...override };
for (const key in base) {
if (!(key in result) || result[key] === null || result[key] === undefined) {
result[key] = base[key];
}
}
return result;
}
applyOverrides(overrides) {
for (const [path, value] of Object.entries(overrides)) {
const [layerType, ...pathParts] = path.split('.');
if (layerType in this.layers && (layerType === 'universal' || layerType === 'detection' || layerType === 'extensions')) {
this.setNestedValue(this.layers.get(layerType), pathParts, value);
}
}
}
setNestedValue(obj, path, value) {
let current = obj;
for (let i = 0; i < path.length - 1; i++) {
if (!(path[i] in current) || typeof current[path[i]] !== 'object') {
current[path[i]] = {};
}
current = current[path[i]];
}
current[path[path.length - 1]] = value;
}
estimateMemoryUsage(config) {
// Simple estimation based on JSON string length
return JSON.stringify(config).length * 2; // Rough estimate in bytes
}
emitChangeEvent(layerType, changeType, oldValue, newValue) {
const event = {
timestamp: new Date(),
layer: layerType,
changeType,
changes: [{
path: layerType,
oldValue,
newValue,
source: 'system'
}],
impact: {
severity: 'medium',
affectedLayers: [layerType],
requiresRegeneration: true,
backwardCompatible: true
}
};
this.changeListeners.forEach(listener => {
try {
listener(event);
}
catch (error) {
logger_1.Logger.error('Error in change listener:', error);
}
});
}
}
exports.ConfigurationLayerSystem = ConfigurationLayerSystem;
//# sourceMappingURL=layer-system.js.map