@iota-big3/sdk-gateway
Version:
Universal API Gateway with protocol translation, intelligent routing, rate limiting, health checking, and caching
321 lines • 12.7 kB
JavaScript
;
/**
* Runtime Validation Utilities
* Following Phase 2g: Provides runtime type checking that matches compile-time types
* Focused on Education industry validation patterns
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.educationValidators = exports.assertEducationContext = exports.assertEducationUser = exports.isEducationUser = void 0;
exports.createValidationResult = createValidationResult;
exports.createValidationError = createValidationError;
exports.validateEducationUser = validateEducationUser;
exports.validateEducationContext = validateEducationContext;
exports.validateResult = validateResult;
exports.validateOption = validateOption;
exports.createSafeValidator = createSafeValidator;
exports.chainValidators = chainValidators;
exports.createArrayValidator = createArrayValidator;
// ============================================
// Core Validation Functions
// ============================================
/**
* Create a validation result
*/
function createValidationResult(valid, value, errors = [], warnings) {
return {
valid,
value,
errors,
warnings,
metadata: {
validatedAt: new Date().toISOString(),
validatorVersion: '1.0.0',
checksPerformed: errors.length + (warnings?.length || 0)
}
};
}
/**
* Create a validation error
*/
function createValidationError(path, message, expected, actual, code = 'VALIDATION_ERROR') {
return {
path,
message,
expected,
actual,
code
};
}
// ============================================
// Enhanced Type Guards with Validation
// ============================================
/**
* Validate and type guard for EducationUser
*/
function validateEducationUser(value) {
const errors = [];
if (!value || typeof value !== 'object') {
errors.push(createValidationError('root', 'Value must be an object', 'object', typeof value, 'INVALID_TYPE'));
return createValidationResult(false, undefined, errors);
}
const obj = value;
// Validate base AuthUser properties
if (!obj.id || typeof obj.id !== 'string') {
errors.push(createValidationError('id', 'ID is required and must be a string', 'string', typeof obj.id, 'MISSING_ID'));
}
// Validate userType
const validUserTypes = ['student', 'teacher', 'parent', 'administrator'];
if (!obj.userType || !validUserTypes.includes(obj.userType)) {
errors.push(createValidationError('userType', `User type must be one of: ${validUserTypes.join(', ')}`, validUserTypes.join(' | '), String(obj.userType), 'INVALID_USER_TYPE'));
}
// Validate optional fields
if (obj.schoolId !== undefined && typeof obj.schoolId !== 'string') {
errors.push(createValidationError('schoolId', 'School ID must be a string', 'string', typeof obj.schoolId, 'INVALID_SCHOOL_ID'));
}
if (obj.gradeLevel !== undefined && typeof obj.gradeLevel !== 'number') {
errors.push(createValidationError('gradeLevel', 'Grade level must be a number', 'number', typeof obj.gradeLevel, 'INVALID_GRADE_LEVEL'));
}
if (obj.subjects !== undefined && !Array.isArray(obj.subjects)) {
errors.push(createValidationError('subjects', 'Subjects must be an array', 'string[]', typeof obj.subjects, 'INVALID_SUBJECTS'));
}
// Education-specific validations
const warnings = [];
// FERPA compliance checks
if (obj.userType === 'student') {
if (!obj.parentalConsent && obj.age && obj.age < 13) {
warnings.push('COPPA: Student under 13 requires parental consent');
}
if (!obj.ferpaConsent) {
warnings.push('FERPA: Educational records access requires consent documentation');
}
}
// Role-based validation
if (obj.userType === 'teacher' && !obj.subjects) {
warnings.push('Teachers should have assigned subjects');
}
if (obj.userType === 'parent' && !obj.linkedStudentIds) {
warnings.push('Parents should be linked to student accounts');
}
const valid = errors.length === 0;
const typedValue = valid ? obj : undefined;
return createValidationResult(valid, typedValue, errors, warnings);
}
/**
* Type guard for EducationUser
*/
const isEducationUser = (value) => {
const result = validateEducationUser(value);
return result.valid;
};
exports.isEducationUser = isEducationUser;
// ============================================
// Education-Specific Validators
// ============================================
/**
* Validate education context for compliance
*/
function validateEducationContext(context) {
const errors = [];
const warnings = [];
if (!context || typeof context !== 'object') {
errors.push(createValidationError('root', 'Context must be an object', 'object', typeof context, 'INVALID_CONTEXT'));
return createValidationResult(false, undefined, errors);
}
const ctx = context;
// Check for education-specific fields
if (ctx.action === 'access_grades' || ctx.action === 'view_records') {
if (!ctx.userRole || !['teacher', 'administrator', 'parent'].includes(ctx.userRole)) {
errors.push(createValidationError('userRole', 'Only teachers, administrators, and parents can access grades', 'teacher | administrator | parent', String(ctx.userRole), 'UNAUTHORIZED_ROLE'));
}
if (ctx.userRole === 'parent' && !ctx.parentOfStudent) {
errors.push(createValidationError('parentOfStudent', 'Parents must specify which student they are accessing', 'string', 'undefined', 'MISSING_STUDENT_LINK'));
}
}
// FERPA validation
if (ctx.dataType === 'educational_record') {
if (!ctx.ferpaConsent) {
warnings.push('FERPA: Access to educational records requires documented consent');
}
if (ctx.disclosureTarget && !ctx.disclosureReason) {
errors.push(createValidationError('disclosureReason', 'FERPA requires disclosure reason when sharing records', 'string', 'undefined', 'MISSING_DISCLOSURE_REASON'));
}
}
// COPPA validation for young students
if (ctx.studentAge && ctx.studentAge < 13) {
if (!ctx.parentalConsent) {
errors.push(createValidationError('parentalConsent', 'COPPA requires parental consent for students under 13', 'boolean', String(ctx.parentalConsent), 'COPPA_VIOLATION'));
}
}
const valid = errors.length === 0;
const typedValue = valid ? ctx : undefined;
return createValidationResult(valid, typedValue, errors, warnings);
}
// ============================================
// Composite Validators
// ============================================
/**
* Validate a Result type
*/
function validateResult(value, validateSuccess, validateError) {
if (!value || typeof value !== 'object') {
return createValidationResult(false, undefined, [
createValidationError('root', 'Result must be an object', 'object', typeof value, 'INVALID_RESULT')
]);
}
const obj = value;
if (obj.success === true) {
const successValidation = validateSuccess(obj.value);
if (successValidation.valid) {
return createValidationResult(true, obj);
}
return createValidationResult(false, undefined, successValidation.errors);
}
else if (obj.success === false) {
const errorValidation = validateError(obj.error);
if (errorValidation.valid) {
return createValidationResult(true, obj);
}
return createValidationResult(false, undefined, errorValidation.errors);
}
else {
return createValidationResult(false, undefined, [
createValidationError('success', 'Result must have success boolean', 'boolean', typeof obj.success, 'INVALID_RESULT')
]);
}
}
/**
* Validate an Option type
*/
function validateOption(value, validateSome) {
if (!value || typeof value !== 'object') {
return createValidationResult(false, undefined, [
createValidationError('root', 'Option must be an object', 'object', typeof value, 'INVALID_OPTION')
]);
}
const obj = value;
if (obj.some === true) {
const someValidation = validateSome(obj.value);
if (someValidation.valid) {
return createValidationResult(true, obj);
}
return createValidationResult(false, undefined, someValidation.errors);
}
else if (obj.some === false) {
return createValidationResult(true, obj);
}
else {
return createValidationResult(false, undefined, [
createValidationError('some', 'Option must have some boolean', 'boolean', typeof obj.some, 'INVALID_OPTION')
]);
}
}
// ============================================
// Type Assertion Functions
// ============================================
/**
* Assert that a value is an EducationUser
*/
const assertEducationUser = (value) => {
const result = validateEducationUser(value);
if (!result.valid) {
const errorMessages = result.errors.map(e => `${e.path}: ${e.message}`).join(', ');
throw new Error(`Invalid EducationUser: ${errorMessages}`);
}
};
exports.assertEducationUser = assertEducationUser;
/**
* Assert that a context is valid for education operations
*/
const assertEducationContext = (value) => {
const result = validateEducationContext(value);
if (!result.valid) {
const errorMessages = result.errors.map(e => `${e.path}: ${e.message}`).join(', ');
throw new Error(`Invalid Education Context: ${errorMessages}`);
}
};
exports.assertEducationContext = assertEducationContext;
// ============================================
// Utility Functions
// ============================================
/**
* Create a safe validator that returns Result<T, ValidationError[]>
*/
function createSafeValidator(validator) {
return (value) => {
const result = validator(value);
if (result.valid && result.value !== undefined) {
return { success: true, value: result.value };
}
else {
return { success: false, error: result.errors };
}
};
}
/**
* Chain multiple validators
*/
function chainValidators(...validators) {
return (value) => {
let current = value;
const allErrors = [];
const allWarnings = [];
for (const validator of validators) {
const result = validator(current);
if (!result.valid) {
allErrors.push(...result.errors);
if (result.warnings) {
allWarnings.push(...result.warnings);
}
return createValidationResult(false, undefined, allErrors, allWarnings);
}
current = result.value;
if (result.warnings) {
allWarnings.push(...result.warnings);
}
}
return createValidationResult(true, current, [], allWarnings);
};
}
/**
* Create a validator for arrays of a specific type
*/
function createArrayValidator(itemValidator) {
return (value) => {
if (!Array.isArray(value)) {
return createValidationResult(false, undefined, [
createValidationError('root', 'Value must be an array', 'array', typeof value, 'INVALID_ARRAY')
]);
}
const results = [];
const errors = [];
const warnings = [];
for (let i = 0; i < value.length; i++) {
const itemResult = itemValidator(value[i]);
if (itemResult.valid && itemResult.value !== undefined) {
results.push(itemResult.value);
if (itemResult.warnings) {
warnings.push(...itemResult.warnings.map(w => `[${i}] ${w}`));
}
}
else {
errors.push(...itemResult.errors.map(e => ({
...e,
path: `[${i}].${e.path}`
})));
}
}
if (errors.length > 0) {
return createValidationResult(false, undefined, errors, warnings);
}
return createValidationResult(true, results, [], warnings);
};
}
// Export education-specific validators for easy access
exports.educationValidators = {
user: validateEducationUser,
context: validateEducationContext,
isEducationUser: exports.isEducationUser,
assertEducationUser: exports.assertEducationUser,
assertEducationContext: exports.assertEducationContext
};
//# sourceMappingURL=validation-utilities.js.map