154 lines
3.5 KiB
TypeScript
154 lines
3.5 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
export interface ValidationResult {
|
|
valid: boolean;
|
|
errors?: Array<{
|
|
path: string;
|
|
message: string;
|
|
expected?: string;
|
|
received?: string;
|
|
}>;
|
|
warnings?: Array<{
|
|
path: string;
|
|
message: string;
|
|
}>;
|
|
}
|
|
|
|
/**
|
|
* Validate configuration against a schema
|
|
*/
|
|
export function validateConfig<T>(config: unknown, schema: z.ZodSchema<T>): ValidationResult {
|
|
try {
|
|
schema.parse(config);
|
|
return { valid: true };
|
|
} catch (error) {
|
|
if (error instanceof z.ZodError) {
|
|
const errors = error.errors.map(err => ({
|
|
path: err.path.join('.'),
|
|
message: err.message,
|
|
expected: 'expected' in err ? String(err.expected) : undefined,
|
|
received: 'received' in err ? String(err.received) : undefined,
|
|
}));
|
|
|
|
return { valid: false, errors };
|
|
}
|
|
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check for required environment variables
|
|
*/
|
|
export function checkRequiredEnvVars(required: string[]): ValidationResult {
|
|
const errors: ValidationResult['errors'] = [];
|
|
|
|
for (const envVar of required) {
|
|
if (!process.env[envVar]) {
|
|
errors.push({
|
|
path: `env.${envVar}`,
|
|
message: `Required environment variable ${envVar} is not set`,
|
|
});
|
|
}
|
|
}
|
|
|
|
return {
|
|
valid: errors.length === 0,
|
|
errors: errors.length > 0 ? errors : undefined,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Validate configuration completeness
|
|
*/
|
|
export function validateCompleteness(
|
|
config: Record<string, any>,
|
|
required: string[]
|
|
): ValidationResult {
|
|
const errors: ValidationResult['errors'] = [];
|
|
|
|
for (const path of required) {
|
|
const keys = path.split('.');
|
|
let current: any = config;
|
|
let found = true;
|
|
|
|
for (const key of keys) {
|
|
if (current && typeof current === 'object' && key in current) {
|
|
current = current[key];
|
|
} else {
|
|
found = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found || current === undefined || current === null) {
|
|
errors.push({
|
|
path,
|
|
message: `Required configuration value is missing`,
|
|
});
|
|
}
|
|
}
|
|
|
|
return {
|
|
valid: errors.length === 0,
|
|
errors: errors.length > 0 ? errors : undefined,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Format validation result for display
|
|
*/
|
|
export function formatValidationResult(result: ValidationResult): string {
|
|
const lines: string[] = [];
|
|
|
|
if (result.valid) {
|
|
lines.push('✅ Configuration is valid');
|
|
} else {
|
|
lines.push('❌ Configuration validation failed');
|
|
}
|
|
|
|
if (result.errors && result.errors.length > 0) {
|
|
lines.push('\nErrors:');
|
|
for (const error of result.errors) {
|
|
lines.push(` - ${error.path}: ${error.message}`);
|
|
if (error.expected && error.received) {
|
|
lines.push(` Expected: ${error.expected}, Received: ${error.received}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result.warnings && result.warnings.length > 0) {
|
|
lines.push('\nWarnings:');
|
|
for (const warning of result.warnings) {
|
|
lines.push(` - ${warning.path}: ${warning.message}`);
|
|
}
|
|
}
|
|
|
|
return lines.join('\n');
|
|
}
|
|
|
|
/**
|
|
* Create a strict schema that doesn't allow extra properties
|
|
*/
|
|
export function createStrictSchema<T extends z.ZodRawShape>(shape: T): z.ZodObject<T, 'strict'> {
|
|
return z.object(shape).strict();
|
|
}
|
|
|
|
/**
|
|
* Merge multiple schemas
|
|
*/
|
|
export function mergeSchemas<T extends z.ZodSchema[]>(
|
|
...schemas: T
|
|
): z.ZodIntersection<T[0], T[1]> {
|
|
if (schemas.length < 2) {
|
|
throw new Error('At least two schemas required for merge');
|
|
}
|
|
|
|
let result = schemas[0]!.and(schemas[1]!);
|
|
|
|
for (let i = 2; i < schemas.length; i++) {
|
|
result = result.and(schemas[i]!) as any;
|
|
}
|
|
|
|
return result as any;
|
|
}
|