prisma-zod-generator
Version:
Prisma 2+ generator to emit Zod schemas from your Prisma schema
442 lines • 17.3 kB
JavaScript
"use strict";
/**
* Variant-Specific Configuration Handler
* Manages per-variant configuration including field exclusions and validation rules
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConfigurationUtils = exports.VariantConfigurationManager = exports.ConfigInheritanceLevel = void 0;
const variants_1 = require("../types/variants");
/**
* Configuration inheritance hierarchy
*/
var ConfigInheritanceLevel;
(function (ConfigInheritanceLevel) {
ConfigInheritanceLevel["DEFAULT"] = "default";
ConfigInheritanceLevel["GLOBAL"] = "global";
ConfigInheritanceLevel["VARIANT"] = "variant";
ConfigInheritanceLevel["MODEL"] = "model";
})(ConfigInheritanceLevel || (exports.ConfigInheritanceLevel = ConfigInheritanceLevel = {}));
/**
* Variant Configuration Manager
* Handles configuration inheritance, validation, and caching
*/
class VariantConfigurationManager {
constructor(globalConfig) {
this.configCache = new Map();
this.cacheTimeout = 5 * 60 * 1000; // 5 minutes
this.globalConfig = this.buildDefaultGlobalConfig();
if (globalConfig) {
this.globalConfig = this.mergeConfigs(this.globalConfig, globalConfig);
}
}
/**
* Get configuration for a specific model and variant
*/
getVariantConfig(modelName, variantType, overrides) {
const cacheKey = this.generateCacheKey(modelName, variantType, overrides);
// Check cache first
const cached = this.getCachedConfig(cacheKey);
if (cached) {
return cached.config;
}
// Build configuration hierarchy
const config = this.buildConfigurationHierarchy(modelName, variantType, overrides);
// Cache the result
this.cacheConfig(cacheKey, config, this.getInheritanceChain(modelName, variantType));
return config;
}
/**
* Build configuration hierarchy from defaults to model-specific overrides
*/
buildConfigurationHierarchy(modelName, variantType, overrides) {
// Start with defaults
let config = this.getDefaultVariantConfig(variantType);
// Apply global variant defaults
const globalDefaults = this.globalConfig.variantDefaults[variantType];
if (globalDefaults) {
config = this.mergeConfigs(config, globalDefaults);
}
// Apply model-specific overrides
const modelOverrides = this.globalConfig.modelOverrides[modelName];
if (modelOverrides && modelOverrides[variantType]) {
config = this.mergeConfigs(config, modelOverrides[variantType]);
}
// Apply explicit overrides
if (overrides) {
config = this.mergeConfigs(config, overrides);
}
return config;
}
/**
* Get default configuration for a variant type
*/
getDefaultVariantConfig(variantType) {
return {
type: variantType,
enabled: true,
naming: variants_1.DEFAULT_NAMING_CONFIGS[variantType],
fieldExclusions: variants_1.DEFAULT_FIELD_EXCLUSIONS[variantType],
validationCustomizations: variants_1.DEFAULT_VALIDATION_CUSTOMIZATIONS[variantType],
schemaOptions: variants_1.DEFAULT_SCHEMA_OPTIONS[variantType],
priority: this.getDefaultPriority(variantType),
};
}
/**
* Merge configurations with proper deep merging
*/
mergeConfigs(base, override) {
const result = { ...base };
for (const key in override) {
if (override.hasOwnProperty(key)) {
const overrideValue = override[key];
const baseValue = result[key];
if (overrideValue !== undefined) {
if (this.isPlainObject(overrideValue) && this.isPlainObject(baseValue)) {
// Deep merge objects
result[key] = this.mergeConfigs(baseValue, overrideValue);
}
else if (Array.isArray(overrideValue) && Array.isArray(baseValue)) {
// Merge arrays (override replaces by default, but could be configured)
result[key] = [...baseValue, ...overrideValue];
}
else {
// Replace primitive values
result[key] = overrideValue;
}
}
}
}
return result;
}
/**
* Validate merged configuration
*/
validateMergedConfig(config) {
const errors = [];
const warnings = [];
// Validate basic configuration
if (!Object.values(variants_1.VariantType).includes(config.type)) {
errors.push(`Invalid variant type: ${config.type}`);
}
// Validate naming configuration
if (!config.naming.suffix || !config.naming.suffix.endsWith('.ts')) {
errors.push('Naming suffix must end with .ts');
}
if (!config.naming.schemaNameSuffix) {
errors.push('Schema name suffix is required');
}
// Validate field exclusions
if (config.fieldExclusions.excludeFields) {
const invalidFields = config.fieldExclusions.excludeFields.filter((field) => typeof field !== 'string' || field.trim() === '');
if (invalidFields.length > 0) {
errors.push(`Invalid field exclusion names: ${invalidFields.join(', ')}`);
}
}
// Validate validation customizations
if (config.validationCustomizations.fieldValidations) {
for (const [field, validation] of Object.entries(config.validationCustomizations.fieldValidations)) {
if (typeof validation !== 'string' || validation.trim() === '') {
errors.push(`Invalid validation for field '${field}': must be non-empty string`);
}
}
}
// Check for conflicting configurations
if (config.fieldExclusions.excludeRelations &&
config.schemaOptions.generateTypes &&
config.type === variants_1.VariantType.RESULT) {
warnings.push('Excluding relations in result schemas may cause type issues');
}
// Validate priority
if (config.priority < 0 || config.priority > 100) {
errors.push('Priority must be between 0 and 100');
}
return {
isValid: errors.length === 0,
errors,
warnings,
level: ConfigInheritanceLevel.VARIANT,
appliedConfig: config,
};
}
/**
* Update global configuration
*/
updateGlobalConfig(updates) {
this.globalConfig = this.mergeConfigs(this.globalConfig, updates);
this.clearCache(); // Clear cache when global config changes
}
/**
* Add model-specific override
*/
addModelOverride(modelName, variantType, override) {
if (!this.globalConfig.modelOverrides[modelName]) {
this.globalConfig.modelOverrides[modelName] = {};
}
const existing = this.globalConfig.modelOverrides[modelName][variantType] || {};
this.globalConfig.modelOverrides[modelName][variantType] = this.mergeConfigs(existing, override);
// Clear affected cache entries
this.clearModelCache(modelName, variantType);
}
/**
* Remove model-specific override
*/
removeModelOverride(modelName, variantType) {
if (variantType) {
if (this.globalConfig.modelOverrides[modelName]) {
delete this.globalConfig.modelOverrides[modelName][variantType];
this.clearModelCache(modelName, variantType);
}
}
else {
delete this.globalConfig.modelOverrides[modelName];
this.clearModelCache(modelName);
}
}
/**
* Get effective field exclusions for a model variant
*/
getEffectiveFieldExclusions(modelName, variantType, fieldNames) {
const config = this.getVariantConfig(modelName, variantType);
const exclusions = config.fieldExclusions;
const excludedFields = [];
const exclusionReasons = {};
fieldNames.forEach((fieldName) => {
var _a;
const reasons = [];
// Check direct field exclusions
if ((_a = exclusions.excludeFields) === null || _a === void 0 ? void 0 : _a.includes(fieldName)) {
reasons.push('explicitly excluded');
}
// Check auto-generated exclusions
if (exclusions.excludeAutoGenerated && this.isAutoGeneratedField(fieldName)) {
reasons.push('auto-generated field');
}
// Additional exclusion logic could be added here
if (reasons.length > 0) {
excludedFields.push(fieldName);
exclusionReasons[fieldName] = reasons;
}
});
return {
excludedFields,
includedFields: fieldNames.filter((name) => !excludedFields.includes(name)),
exclusionReasons,
};
}
/**
* Get effective validation customizations
*/
getEffectiveValidationCustomizations(modelName, variantType, fieldName) {
var _a, _b, _c;
const config = this.getVariantConfig(modelName, variantType);
const customizations = config.validationCustomizations;
const validations = [];
// Add field-specific validations
if ((_a = customizations.fieldValidations) === null || _a === void 0 ? void 0 : _a[fieldName]) {
validations.push(customizations.fieldValidations[fieldName]);
}
// Add additional validations
if ((_b = customizations.additionalValidations) === null || _b === void 0 ? void 0 : _b[fieldName]) {
validations.push(...customizations.additionalValidations[fieldName]);
}
return {
validations,
isInlineDisabled: customizations.disableInlineValidations || false,
customTemplate: (_c = customizations.validationTemplates) === null || _c === void 0 ? void 0 : _c[fieldName],
};
}
/**
* Export configuration as JSON
*/
exportConfiguration() {
return JSON.stringify(this.globalConfig, null, 2);
}
/**
* Import configuration from JSON
*/
importConfiguration(configJson) {
try {
const imported = JSON.parse(configJson);
this.globalConfig = this.mergeConfigs(this.buildDefaultGlobalConfig(), imported);
this.clearCache();
}
catch (error) {
throw new Error(`Failed to import configuration: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Private helper methods
*/
buildDefaultGlobalConfig() {
return {
outputDirectory: './generated/schemas',
enableVariants: true,
variantDefaults: {
[variants_1.VariantType.PURE]: {},
[variants_1.VariantType.INPUT]: {},
[variants_1.VariantType.RESULT]: {},
},
modelOverrides: {},
globalNaming: {
useModelPrefix: true,
usePascalCase: true,
},
};
}
getDefaultPriority(variantType) {
switch (variantType) {
case variants_1.VariantType.PURE:
return 10;
case variants_1.VariantType.INPUT:
return 20;
case variants_1.VariantType.RESULT:
return 30;
default:
return 50;
}
}
generateCacheKey(modelName, variantType, overrides) {
const overrideHash = overrides ? this.hashObject(overrides) : '';
return `${modelName}:${variantType}:${overrideHash}`;
}
getCachedConfig(cacheKey) {
const entry = this.configCache.get(cacheKey);
if (entry && Date.now() - entry.timestamp < this.cacheTimeout) {
return entry;
}
return null;
}
cacheConfig(cacheKey, config, inheritanceChain) {
this.configCache.set(cacheKey, {
config,
hash: this.hashObject(config),
timestamp: Date.now(),
inheritanceChain,
});
}
clearCache() {
this.configCache.clear();
}
clearModelCache(modelName, variantType) {
const keysToDelete = Array.from(this.configCache.keys()).filter((key) => {
const parts = key.split(':');
return parts[0] === modelName && (!variantType || parts[1] === variantType);
});
keysToDelete.forEach((key) => this.configCache.delete(key));
}
getInheritanceChain(modelName, variantType) {
var _a;
const chain = [ConfigInheritanceLevel.DEFAULT];
if (this.globalConfig.variantDefaults[variantType]) {
chain.push(ConfigInheritanceLevel.GLOBAL);
}
chain.push(ConfigInheritanceLevel.VARIANT);
if ((_a = this.globalConfig.modelOverrides[modelName]) === null || _a === void 0 ? void 0 : _a[variantType]) {
chain.push(ConfigInheritanceLevel.MODEL);
}
return chain;
}
isPlainObject(value) {
return (value !== null &&
typeof value === 'object' &&
!Array.isArray(value) &&
!(value instanceof Date) &&
!(value instanceof RegExp));
}
isAutoGeneratedField(fieldName) {
const autoGeneratedFields = ['id', 'createdAt', 'updatedAt', 'deletedAt'];
return autoGeneratedFields.includes(fieldName);
}
hashObject(obj) {
return JSON.stringify(obj, Object.keys(obj).sort());
}
}
exports.VariantConfigurationManager = VariantConfigurationManager;
/**
* Configuration utility functions
*/
class ConfigurationUtils {
/**
* Compare two configurations for differences
*/
static compareConfigs(config1, config2) {
const differences = [];
this.deepCompare(config1, config2, '', differences);
return {
areSame: differences.length === 0,
differences,
};
}
/**
* Get configuration summary
*/
static getConfigSummary(config) {
var _a, _b;
return {
type: config.type,
excludedFieldsCount: (((_a = config.fieldExclusions.excludeFields) === null || _a === void 0 ? void 0 : _a.length) || 0) +
(((_b = config.fieldExclusions.excludeFieldTypes) === null || _b === void 0 ? void 0 : _b.length) || 0),
customValidationsCount: Object.keys(config.validationCustomizations.fieldValidations || {}).length +
Object.keys(config.validationCustomizations.additionalValidations || {}).length,
hasDocumentation: config.schemaOptions.includeDocumentation || false,
priority: config.priority,
};
}
/**
* Validate configuration compatibility
*/
static validateCompatibility(configs) {
const conflicts = [];
for (let i = 0; i < configs.length; i++) {
for (let j = i + 1; j < configs.length; j++) {
const config1 = configs[i];
const config2 = configs[j];
// Check for priority conflicts
if (config1.priority === config2.priority) {
conflicts.push({
config1: config1.type,
config2: config2.type,
issue: `Same priority (${config1.priority})`,
});
}
// Check for naming conflicts
if (config1.naming.schemaNameSuffix === config2.naming.schemaNameSuffix) {
conflicts.push({
config1: config1.type,
config2: config2.type,
issue: `Same schema name suffix: ${config1.naming.schemaNameSuffix}`,
});
}
}
}
return {
isCompatible: conflicts.length === 0,
conflicts,
};
}
static deepCompare(obj1, obj2, path, differences) {
if (obj1 === obj2)
return;
if (typeof obj1 !== typeof obj2) {
differences.push({ path, value1: obj1, value2: obj2 });
return;
}
if (obj1 === null || obj2 === null) {
differences.push({ path, value1: obj1, value2: obj2 });
return;
}
if (typeof obj1 === 'object') {
const keys = new Set([...Object.keys(obj1 || {}), ...Object.keys(obj2 || {})]);
for (const key of keys) {
const newPath = path ? `${path}.${key}` : key;
this.deepCompare(obj1 === null || obj1 === void 0 ? void 0 : obj1[key], obj2 === null || obj2 === void 0 ? void 0 : obj2[key], newPath, differences);
}
}
else if (obj1 !== obj2) {
differences.push({ path, value1: obj1, value2: obj2 });
}
}
}
exports.ConfigurationUtils = ConfigurationUtils;
exports.default = VariantConfigurationManager;
//# sourceMappingURL=config.js.map