fix: resolve CI test failures in schema-validator
Some checks failed
CI / test (push) Has been cancelled
Some checks failed
CI / test (push) Has been cancelled
- Created missing schema-validator.ts file - Added enum type validation with options support - Added 'yes'/'no' boolean value handling - Fixed port type detection to take priority over number
This commit is contained in:
149
src/core/schema-validator.ts
Normal file
149
src/core/schema-validator.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
import { EnvVariableSchema, EnvType, ValidationResult, ValidationError } from './types';
|
||||
|
||||
function inferTypeFromValue(value: string): EnvType {
|
||||
if (value === '') {
|
||||
return 'string';
|
||||
}
|
||||
|
||||
const lowerValue = value.toLowerCase();
|
||||
if (lowerValue === 'true' || lowerValue === 'false' || value === '1' || value === '0' || lowerValue === 'yes' || lowerValue === 'no') {
|
||||
return 'boolean';
|
||||
}
|
||||
|
||||
const port = Number(value);
|
||||
if (!isNaN(port) && port >= 1 && port <= 65535 && value.trim() !== '') {
|
||||
return 'port';
|
||||
}
|
||||
|
||||
const num = Number(value);
|
||||
if (!isNaN(num) && value.trim() !== '') {
|
||||
return 'number';
|
||||
}
|
||||
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (emailRegex.test(value)) {
|
||||
return 'email';
|
||||
}
|
||||
|
||||
try {
|
||||
new URL(value);
|
||||
return 'url';
|
||||
} catch {
|
||||
// Not a URL
|
||||
}
|
||||
|
||||
try {
|
||||
JSON.parse(value);
|
||||
return 'json';
|
||||
} catch {
|
||||
// Not JSON
|
||||
}
|
||||
|
||||
return 'string';
|
||||
}
|
||||
|
||||
function validateType(value: string, type: EnvType, options?: string[]): void {
|
||||
switch (type) {
|
||||
case 'number':
|
||||
if (isNaN(Number(value))) {
|
||||
throw new Error(`Expected number, got: ${value}`);
|
||||
}
|
||||
break;
|
||||
case 'boolean':
|
||||
if (!['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase())) {
|
||||
throw new Error(`Expected boolean, got: ${value}`);
|
||||
}
|
||||
break;
|
||||
case 'port': {
|
||||
const port = Number(value);
|
||||
if (isNaN(port) || port < 1 || port > 65535) {
|
||||
throw new Error(`Expected port number, got: ${value}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'email': {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(value)) {
|
||||
throw new Error(`Expected email, got: ${value}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'url':
|
||||
try {
|
||||
new URL(value);
|
||||
} catch {
|
||||
throw new Error(`Expected URL, got: ${value}`);
|
||||
}
|
||||
break;
|
||||
case 'json':
|
||||
try {
|
||||
JSON.parse(value);
|
||||
} catch {
|
||||
throw new Error(`Expected JSON, got: ${value}`);
|
||||
}
|
||||
break;
|
||||
case 'enum':
|
||||
if (options && !options.includes(value)) {
|
||||
throw new Error(`Expected one of: ${options.join(', ')}, got: ${value}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function cleanBooleanValue(value: string): boolean {
|
||||
const lowerValue = value.toLowerCase();
|
||||
if (lowerValue === 'true' || value === '1' || lowerValue === 'yes') {
|
||||
return true;
|
||||
}
|
||||
if (lowerValue === 'false' || value === '0' || lowerValue === 'no') {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function validateEnvVariables(
|
||||
variables: Record<string, string>,
|
||||
schema: EnvVariableSchema
|
||||
): ValidationResult {
|
||||
const errors: ValidationError[] = [];
|
||||
const cleanedEnv: Record<string, string | number | boolean> = {};
|
||||
|
||||
for (const [key, config] of Object.entries(schema)) {
|
||||
const value = variables[key];
|
||||
|
||||
if (config.required !== false && (value === undefined || value === '')) {
|
||||
errors.push({
|
||||
field: key,
|
||||
message: `Required variable "${key}" is missing`
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value !== undefined && value !== '') {
|
||||
try {
|
||||
validateType(value, config.type, config.options);
|
||||
if (config.type === 'boolean') {
|
||||
cleanedEnv[key] = cleanBooleanValue(value);
|
||||
} else if (config.type === 'number' || config.type === 'port') {
|
||||
cleanedEnv[key] = Number(value);
|
||||
} else {
|
||||
cleanedEnv[key] = value;
|
||||
}
|
||||
} catch (e) {
|
||||
errors.push({
|
||||
field: key,
|
||||
message: (e as Error).message,
|
||||
value
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: errors.length === 0,
|
||||
errors,
|
||||
cleanedEnv
|
||||
};
|
||||
}
|
||||
|
||||
export { inferTypeFromValue };
|
||||
Reference in New Issue
Block a user