UNPKG

@boundless-oss/atlas

Version:

Atlas - MCP Server for comprehensive startup project management

238 lines 7.08 kB
import Ajv from 'ajv'; import addFormats from 'ajv-formats'; /** * Schema validation utility using Ajv */ export class SchemaValidator { ajv; constructor() { this.ajv = new Ajv({ allErrors: true, removeAdditional: false, useDefaults: true, coerceTypes: true, strict: false }); // Add format validation (date, email, uri, etc.) addFormats(this.ajv); // Add custom formats if needed this.addCustomFormats(); } /** * Validate data against a JSON schema */ validate(schema, data) { try { const validate = this.ajv.compile(schema); const valid = validate(data); if (valid) { return { valid: true, errors: [] }; } const errors = (validate.errors || []).map(error => ({ path: error.instancePath || 'root', message: error.message || 'Validation failed', value: error.data })); return { valid: false, errors }; } catch (error) { return { valid: false, errors: [{ path: 'schema', message: `Schema compilation error: ${error instanceof Error ? error.message : 'Unknown error'}`, value: schema }] }; } } /** * Validate and return cleaned data */ validateAndClean(schema, data) { const result = this.validate(schema, data); if (result.valid) { return { valid: true, data: data }; } return { valid: false, errors: result.errors }; } /** * Create a validation function for a specific schema */ createValidator(schema) { const compiledValidator = this.ajv.compile(schema); return (data) => { const valid = compiledValidator(data); if (valid) { return { valid: true, errors: [] }; } const errors = (compiledValidator.errors || []).map(error => ({ path: error.instancePath || 'root', message: error.message || 'Validation failed', value: error.data })); return { valid: false, errors }; }; } /** * Add custom format validators */ addCustomFormats() { // Unix timestamp format this.ajv.addFormat('unix-timestamp', { type: 'number', validate: (data) => { return Number.isInteger(data) && data > 0 && data < 4000000000; // reasonable timestamp range } }); // Story points format (Fibonacci-like sequence) this.ajv.addFormat('story-points', { type: 'number', validate: (data) => { const validPoints = [0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]; return validPoints.includes(data); } }); // Priority levels this.ajv.addFormat('priority', { type: 'string', validate: (data) => { return ['low', 'medium', 'high', 'critical'].includes(data); } }); // Status values for various entities this.ajv.addFormat('story-status', { type: 'string', validate: (data) => { return ['todo', 'in_progress', 'in_review', 'done', 'backlog'].includes(data); } }); this.ajv.addFormat('sprint-status', { type: 'string', validate: (data) => { return ['planning', 'active', 'completed', 'cancelled'].includes(data); } }); this.ajv.addFormat('approval-status', { type: 'string', validate: (data) => { return ['pending', 'approved', 'rejected', 'expired'].includes(data); } }); // Documentation status this.ajv.addFormat('doc-status', { type: 'string', validate: (data) => { return ['pending', 'in_progress', 'approved', 'outdated'].includes(data); } }); } } // Global validator instance let globalValidator = null; export function getValidator() { if (!globalValidator) { globalValidator = new SchemaValidator(); } return globalValidator; } /** * Common schema definitions for reuse */ export const CommonSchemas = { id: { type: 'string', minLength: 1, maxLength: 255, pattern: '^[a-zA-Z0-9_-]+$' }, projectId: { type: 'string', minLength: 1, maxLength: 255, description: 'Project identifier' }, timestamp: { type: 'integer', format: 'unix-timestamp', description: 'Unix timestamp' }, priority: { type: 'string', format: 'priority', default: 'medium', description: 'Priority level' }, tags: { type: 'array', items: { type: 'string', minLength: 1, maxLength: 50 }, default: [], description: 'Array of tags' }, storyPoints: { type: 'integer', format: 'story-points', default: 0, description: 'Story points using Fibonacci sequence' }, status: { story: { type: 'string', format: 'story-status', default: 'todo', description: 'Story status' }, sprint: { type: 'string', format: 'sprint-status', default: 'planning', description: 'Sprint status' }, approval: { type: 'string', format: 'approval-status', default: 'pending', description: 'Approval status' } }, documentation: { url: { type: 'string', format: 'uri', description: 'Document URL' }, status: { type: 'string', format: 'doc-status', default: 'pending', description: 'Documentation status' } } }; /** * Utility function to create error responses */ export function createValidationError(errors) { const errorMessages = errors.map(e => `${e.path}: ${e.message}`); return { code: 'VALIDATION_ERROR', message: `Input validation failed: ${errorMessages.join('; ')}`, details: { validationErrors: errors, errorCount: errors.length }, suggestions: [ 'Check the input parameters against the tool schema', 'Ensure all required fields are provided', 'Verify data types match the expected format', 'Review field constraints (min/max length, allowed values)' ], recoverable: true, category: 'validation' }; } //# sourceMappingURL=validation.js.map