fortify-schema
Version:
A modern TypeScript validation library designed around familiar interface syntax and powerful conditional validation. Experience schema validation that feels natural to TypeScript developers while unlocking advanced runtime validation capabilities.
522 lines (519 loc) • 22 kB
JavaScript
import { FieldPrecompilers } from './FieldPrecompilers.js';
import { MAX_COMPILATION_DEPTH } from '../../../../../constants/VALIDATION_CONSTANTS.js';
import { ErrorHandler } from '../errors/ErrorHandler.js';
import { ErrorCode } from '../errors/types/errors.type.js';
import { SUPPORTED_VALIDATOR_TYPES } from '../../../../types/ValidatorTypes.js';
/**
* ULTRA-PERFORMANCE Schema Precompilation Module
*
* This module precompiles entire schemas at creation time to eliminate
* runtime overhead while maintaining full security and validation.
*
*/
var OptimizationLevel;
(function (OptimizationLevel) {
OptimizationLevel[OptimizationLevel["NONE"] = 0] = "NONE";
OptimizationLevel[OptimizationLevel["BASIC"] = 1] = "BASIC";
OptimizationLevel[OptimizationLevel["AGGRESSIVE"] = 2] = "AGGRESSIVE";
OptimizationLevel[OptimizationLevel["ULTRA"] = 3] = "ULTRA";
})(OptimizationLevel || (OptimizationLevel = {}));
class SchemaPrecompiler {
/**
* MAIN ENTRY POINT: Precompile entire schema for maximum performance
*/
static precompileSchema(schemaDefinition, options = {}) {
const startTime = performance.now();
// SAFETY: Check compilation depth to prevent infinite recursion
if (this.compilationDepth >= this.MAX_COMPILATION_DEPTH) {
throw new Error(`Schema compilation depth exceeded (${this.MAX_COMPILATION_DEPTH}). Possible circular reference detected.`);
}
// Generate cache key
const schemaHash = this.generateSchemaHash(schemaDefinition, options);
// SAFETY: Check for circular references
if (this.compilationStack.has(schemaHash)) {
throw new Error(`Circular reference detected in schema compilation: ${schemaHash}`);
}
// Check cache first
const cached = this.compilationCache.get(schemaHash);
if (cached) {
this.performanceStats.cacheHits++;
return cached.validator;
}
// SAFETY: Track compilation
this.compilationDepth++;
this.compilationStack.add(schemaHash);
try {
// Analyze schema complexity and determine optimization level
const optimizationLevel = this.analyzeOptimizationLevel(schemaDefinition);
// Compile fields
const compiledFields = this.compileFields(schemaDefinition, options);
// Generate ultra-optimized validator
const validator = this.generateOptimizedValidator(compiledFields, optimizationLevel, schemaHash);
const compilationTime = performance.now() - startTime;
// Cache the compilation
const compilation = {
fields: compiledFields,
validator,
optimizationLevel,
compilationTime,
estimatedPerformanceGain: this.estimatePerformanceGain(compiledFields),
};
this.compilationCache.set(schemaHash, compilation);
this.performanceStats.compilations++;
this.performanceStats.totalCompilationTime += compilationTime;
return validator;
}
catch (error) {
// console.error('Schema precompilation failed:', error);
throw error;
}
finally {
this.compilationDepth--;
this.compilationStack.delete(schemaHash);
}
}
/**
* ULTRA-FAST: Generate specialized validator based on schema patterns
*/
static generateOptimizedValidator(fields, optimizationLevel, schemaHash) {
// For ULTRA optimization, generate specialized code paths
if (optimizationLevel === OptimizationLevel.ULTRA) {
return this.generateUltraOptimizedValidator(fields, schemaHash);
}
// For AGGRESSIVE optimization, use compiled field validators
if (optimizationLevel === OptimizationLevel.AGGRESSIVE) {
return this.generateAggressiveValidator(fields, schemaHash);
}
// For BASIC optimization, use cached validators
return this.generateBasicValidator(fields, schemaHash);
}
/**
* ULTRA-OPTIMIZED: Generate the fastest possible validator
* Uses specialized code paths for common patterns
*/
static generateUltraOptimizedValidator(fields, schemaHash) {
// Separate fields by type for specialized handling
const simpleFields = fields.filter((f) => f.isSimpleType);
const unionFields = fields.filter((f) => f.isUnion);
const arrayFields = fields.filter((f) => f.isArray);
const nestedFields = fields.filter((f) => f.isNested);
const validator = (data) => {
// ULTRA-FAST: Type check with early return
if (typeof data !== "object" || data === null) {
return {
success: false,
errors: [ErrorHandler.createTypeError([], "object", data)],
warnings: [],
data: undefined,
};
}
const validatedData = {};
const errors = [];
// OPTIMIZED: Process simple fields first (fastest path)
for (const field of simpleFields) {
const value = data[field.fieldName];
const result = field.validator(value);
if (!result.success) {
// Add field path to errors
const errorsWithPath = result.errors.map((error) => ({
...error,
path: [field.fieldName, ...error.path],
message: error.message.includes(" in field ")
? error.message
: `${error.message} in field "${field.fieldName}"`,
}));
errors.push(...errorsWithPath);
}
else {
validatedData[field.fieldName] = result.data;
}
}
// OPTIMIZED: Process union fields with precompiled validators
for (const field of unionFields) {
const value = data[field.fieldName];
const result = field.validator(value);
if (!result.success) {
// Add field path to errors
const errorsWithPath = result.errors.map((error) => ({
...error,
path: [field.fieldName, ...error.path],
message: error.message.includes(" in field ")
? error.message
: `${error.message} in field "${field.fieldName}"`,
}));
errors.push(...errorsWithPath);
}
else {
validatedData[field.fieldName] = result.data;
}
}
// OPTIMIZED: Process array fields
for (const field of arrayFields) {
const value = data[field.fieldName];
const result = field.validator(value);
if (!result.success) {
// Add field path to errors
const errorsWithPath = result.errors.map((error) => ({
...error,
path: [field.fieldName, ...error.path],
message: error.message.includes(" in field ")
? error.message
: `${error.message} in field "${field.fieldName}"`,
}));
errors.push(...errorsWithPath);
}
else {
validatedData[field.fieldName] = result.data;
}
}
// OPTIMIZED: Process nested fields last
for (const field of nestedFields) {
const value = data[field.fieldName];
const result = field.validator(value);
if (!result.success) {
// Add field path to errors
const errorsWithPath = result.errors.map((error) => ({
...error,
path: [field.fieldName, ...error.path],
message: error.message.includes(" in field ")
? error.message
: `${error.message} in field "${field.fieldName}"`,
}));
errors.push(...errorsWithPath);
}
else {
validatedData[field.fieldName] = result.data;
}
}
// ULTRA-FAST: Return result
if (errors.length === 0) {
return {
success: true,
errors: [],
warnings: [],
data: validatedData,
};
}
else {
return {
success: false,
errors,
warnings: [],
data: undefined,
};
}
};
// Mark as precompiled
validator._isPrecompiled = true;
validator._schemaHash = schemaHash;
validator._optimizationLevel = OptimizationLevel.ULTRA;
return validator;
}
/**
* Analyze schema to determine optimal optimization level
*/
static analyzeOptimizationLevel(schema) {
const fieldCount = Object.keys(schema).length;
let complexityScore = 0;
for (const [key, value] of Object.entries(schema)) {
if (typeof value === "string") {
if (value.includes("|"))
complexityScore += 2; // Union
if (value.includes("[]"))
complexityScore += 2; // Array
if (value.includes("when"))
complexityScore += 5; // Conditional
if (value.includes("("))
complexityScore += 1; // Constraints
}
else if (typeof value === "object") {
complexityScore += 10; // Nested object
}
}
// Determine optimization level based on complexity
if (fieldCount <= 5 && complexityScore <= 10) {
return OptimizationLevel.ULTRA;
}
else if (fieldCount <= 15 && complexityScore <= 30) {
return OptimizationLevel.AGGRESSIVE;
}
else if (fieldCount <= 50) {
return OptimizationLevel.BASIC;
}
else {
return OptimizationLevel.NONE;
}
}
/**
* Generate schema hash for caching
*/
static generateSchemaHash(schema, options) {
const schemaStr = JSON.stringify(schema, Object.keys(schema).sort());
const optionsStr = JSON.stringify(options);
return `${this.simpleHash(schemaStr)}_${this.simpleHash(optionsStr)}`;
}
static simpleHash(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash; // Convert to 32-bit integer
}
return hash.toString(36);
}
/**
* Compile individual fields with specialized optimizations
*/
static compileFields(schema, options) {
const fields = [];
for (const [fieldName, fieldType] of Object.entries(schema)) {
const compilation = this.compileField(fieldName, fieldType, options, 0);
fields.push(compilation);
}
return fields;
}
/**
* Compile a single field with maximum optimization
*/
static compileField(fieldName, fieldType, options, depth = 0) {
// Handle nested objects properly
if (typeof fieldType === "object" &&
fieldType !== null &&
!Array.isArray(fieldType)) {
// This is a nested object - create a validator that recursively validates the nested schema
const validator = (value) => {
if (typeof value !== "object" ||
value === null ||
Array.isArray(value)) {
return {
success: false,
errors: [ErrorHandler.createTypeError([], "object", value)],
warnings: [],
data: undefined,
};
}
// Recursively validate nested object with depth tracking
const validatedData = {};
const errors = [];
const warnings = [];
for (const [key, nestedFieldType] of Object.entries(fieldType)) {
const nestedValue = value[key];
// For nested fields, use FieldPrecompilers directly for string types
if (typeof nestedFieldType === "string") {
const fieldValidator = FieldPrecompilers.parseAndCompile(nestedFieldType);
const result = fieldValidator(nestedValue);
if (!result.success) {
errors.push(...result.errors);
}
else {
validatedData[key] = result.data;
if (result.warnings)
warnings.push(...result.warnings);
}
}
else if (typeof nestedFieldType === "object" &&
nestedFieldType !== null) {
// For nested objects, validate recursively with depth limit
if (depth < this.MAX_COMPILATION_DEPTH - 1) {
// Safe to recurse - compile and validate the nested object
const nestedField = this.compileField(key, nestedFieldType, options, depth + 1);
const nestedResult = nestedField.validator(nestedValue);
if (!nestedResult.success) {
errors.push(...nestedResult.errors);
}
else {
validatedData[key] = nestedResult.data;
if (nestedResult.warnings)
warnings.push(...nestedResult.warnings);
}
}
else {
// Depth limit reached - do basic type checking only
if (typeof nestedValue === "object" && nestedValue !== null) {
validatedData[key] = nestedValue;
warnings.push(`${key}: Maximum nesting depth reached - basic validation only`);
}
else {
errors.push(ErrorHandler.createTypeError([], "object", nestedValue));
}
}
}
else {
// Unknown field type
errors.push(ErrorHandler.createSimpleError(`Unknown field type: ${nestedFieldType}`, ErrorCode.TYPE_MISMATCH));
}
}
return {
success: errors.length === 0,
errors,
warnings,
data: errors.length === 0 ? validatedData : undefined,
};
};
return {
fieldName,
fieldType: "[nested object]",
validator: validator,
isOptional: false,
hasDefault: false,
isSimpleType: false,
isUnion: false,
isArray: false,
isNested: true,
};
}
// Handle string field types
const fieldTypeStr = String(fieldType);
// Create precompiled validator for this field
const validator = FieldPrecompilers.parseAndCompile(fieldTypeStr);
return {
fieldName,
fieldType: fieldTypeStr,
validator,
isOptional: fieldTypeStr.includes("?"),
hasDefault: false, // TODO: Extract default values
isSimpleType: this.isSimpleType(fieldType),
isUnion: fieldTypeStr.includes("|"),
isArray: fieldTypeStr.includes("[]"),
isNested: typeof fieldType === "object",
};
}
static isSimpleType(fieldType) {
if (typeof fieldType !== "string")
return false;
// Handle constants (=value) as simple types
if (fieldType.startsWith("="))
return true;
// Handle record types as simple types (they have precompiled validators)
if (fieldType.startsWith("record<") || fieldType.startsWith("Record<")) {
return true;
}
const simpleTypes = SUPPORTED_VALIDATOR_TYPES;
return simpleTypes.some((type) => fieldType.startsWith(type));
}
static generateAggressiveValidator(fields, schemaHash) {
// AGGRESSIVE optimization: Use the basic validator for now
// TODO: Implement more aggressive optimizations like field grouping, early exits, etc.
const basicValidator = this.generateBasicValidator(fields, schemaHash);
// Mark as aggressive optimization level
basicValidator._optimizationLevel = OptimizationLevel.AGGRESSIVE;
return basicValidator;
}
static generateBasicValidator(fields, schemaHash) {
// BASIC optimization: Use compiled field validators with simple iteration
const validator = (data) => {
// Fast path for non-objects
if (typeof data !== "object" || data === null || Array.isArray(data)) {
return {
success: false,
errors: [ErrorHandler.createTypeError([], "object", data)],
warnings: [],
data: undefined,
};
}
const validatedData = {};
const errors = [];
const warnings = [];
// Validate each field using compiled validators
for (const field of fields) {
const value = data[field.fieldName];
// Handle optional fields
if (value === undefined) {
if (!field.isOptional) {
errors.push(ErrorHandler.createMissingFieldError([field.fieldName], field.fieldName));
}
else if (field.hasDefault) {
validatedData[field.fieldName] = field.defaultValue;
}
continue;
}
// Use the compiled field validator
const result = field.validator(value);
if (!result.success) {
// Add field path to errors
const errorsWithPath = result.errors.map((error) => ({
...error,
path: [field.fieldName, ...error.path],
message: error.message.includes(" in field ")
? error.message
: `${error.message} in field "${field.fieldName}"`,
}));
errors.push(...errorsWithPath);
}
else {
validatedData[field.fieldName] = result.data;
}
// Collect warnings
if (result.warnings && result.warnings.length > 0) {
warnings.push(...result.warnings);
}
}
// Return validation result
if (errors.length === 0) {
return {
success: true,
errors: [],
warnings,
data: validatedData,
};
}
else {
return {
success: false,
errors,
warnings,
data: undefined,
};
}
};
validator._isPrecompiled = true;
validator._schemaHash = schemaHash;
validator._optimizationLevel = OptimizationLevel.BASIC;
return validator;
}
static estimatePerformanceGain(fields) {
// Estimate performance gain based on field types
let gain = 1.0;
for (const field of fields) {
if (field.isUnion)
gain += 0.5;
if (field.isArray)
gain += 0.3;
if (field.isSimpleType)
gain += 0.2;
}
return Math.min(gain, 5.0); // Cap at 5x improvement
}
/**
* Get compilation statistics
*/
static getStats() {
return { ...this.performanceStats };
}
/**
* Clear compilation cache
*/
static clearCache() {
this.compilationCache.clear();
this.performanceStats = {
compilations: 0,
cacheHits: 0,
totalCompilationTime: 0,
averageSpeedup: 0,
};
}
}
SchemaPrecompiler.compilationCache = new Map();
SchemaPrecompiler.performanceStats = {
compilations: 0,
cacheHits: 0,
totalCompilationTime: 0,
averageSpeedup: 0,
};
// SAFETY: Track compilation depth to prevent infinite recursion
SchemaPrecompiler.compilationDepth = 0;
SchemaPrecompiler.MAX_COMPILATION_DEPTH = MAX_COMPILATION_DEPTH;
// SAFETY: Track circular references
SchemaPrecompiler.compilationStack = new Set();
export { OptimizationLevel, SchemaPrecompiler };
//# sourceMappingURL=SchemaPrecompiler.js.map