UNPKG

@vibe-dev-kit/cli

Version:

Advanced Command-line toolkit that analyzes your codebase and deploys project-aware rules, memories, commands and agents to any AI coding assistant - VDK is the world's first Vibe Development Kit

171 lines (147 loc) 4.9 kB
/** * Schema Validator Utility * ----------------------- * Centralized validation for VDK schemas including commands and blueprints */ import fs from 'node:fs/promises' import path from 'node:path' import { fileURLToPath } from 'node:url' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const SCHEMAS_DIR = path.join(__dirname, '../schemas') // Cache for loaded schemas const schemaCache = new Map() /** * Load schema from file with caching */ async function loadSchema(schemaName) { if (schemaCache.has(schemaName)) { return schemaCache.get(schemaName) } try { const schemaPath = path.join(SCHEMAS_DIR, `${schemaName}.json`) const schemaContent = await fs.readFile(schemaPath, 'utf8') const schema = JSON.parse(schemaContent) schemaCache.set(schemaName, schema) return schema } catch (error) { throw new Error(`Failed to load schema '${schemaName}': ${error.message}`) } } /** * Validate data against a schema */ export async function validateSchema(data, schemaName) { const schema = await loadSchema(schemaName) const errors = [] // Validate required fields if (schema.required) { for (const field of schema.required) { if (!(field in data)) { errors.push(`Missing required field: ${field}`) } } } // Validate properties if (schema.properties) { for (const [field, fieldSchema] of Object.entries(schema.properties)) { const value = data[field] if (value === undefined || value === null) { continue // Skip validation for missing optional fields } // Type validation const expectedType = fieldSchema.type const actualType = Array.isArray(value) ? 'array' : typeof value if (expectedType && actualType !== expectedType) { errors.push(`Field '${field}' should be of type ${expectedType}, got ${actualType}`) continue } // String validations if (expectedType === 'string' && typeof value === 'string') { // Pattern validation if (fieldSchema.pattern) { const pattern = new RegExp(fieldSchema.pattern) if (!pattern.test(value)) { errors.push(`Field '${field}' does not match required pattern`) } } // Length validation if (fieldSchema.minLength && value.length < fieldSchema.minLength) { errors.push(`Field '${field}' must be at least ${fieldSchema.minLength} characters`) } if (fieldSchema.maxLength && value.length > fieldSchema.maxLength) { errors.push(`Field '${field}' must not exceed ${fieldSchema.maxLength} characters`) } // Enum validation if (fieldSchema.enum && !fieldSchema.enum.includes(value)) { errors.push(`Field '${field}' must be one of: ${fieldSchema.enum.join(', ')}`) } } // Array validation if (expectedType === 'array' && Array.isArray(value) && fieldSchema.items?.type) { for (const [index, item] of value.entries()) { const itemType = typeof item if (itemType !== fieldSchema.items.type) { errors.push( `Array '${field}' item at index ${index} should be ${fieldSchema.items.type}, got ${itemType}` ) } } } // Object validation (simplified) if (expectedType === 'object' && typeof value === 'object' && fieldSchema.properties) { for (const [subField, subSchema] of Object.entries(fieldSchema.properties)) { const subValue = value[subField] if (subValue !== undefined && subSchema.type) { const subType = Array.isArray(subValue) ? 'array' : typeof subValue if (subType !== subSchema.type) { errors.push( `Object '${field}.${subField}' should be ${subSchema.type}, got ${subType}` ) } } } } } } return { valid: errors.length === 0, errors, } } /** * Validate Claude Code command */ export async function validateCommand(commandData) { return await validateSchema(commandData, 'command-schema') } /** * Validate VDK Blueprint */ export async function validateBlueprint(blueprintData) { return await validateSchema(blueprintData, 'blueprint-schema') } /** * Get all available schemas */ export async function getAvailableSchemas() { try { const files = await fs.readdir(SCHEMAS_DIR) return files.filter((file) => file.endsWith('.json')).map((file) => file.replace('.json', '')) } catch (error) { console.warn(`Could not read schemas directory: ${error.message}`) return [] } } /** * Clear schema cache (useful for testing) */ export function clearSchemaCache() { schemaCache.clear() } export default { validateSchema, validateCommand, validateBlueprint, getAvailableSchemas, clearSchemaCache, }