UNPKG

rynex

Version:

A minimalist TypeScript framework for building reactive web applications with no virtual DOM

348 lines 12.5 kB
/** * Rynex Error System * Comprehensive error handling and validation for the framework */ // Error severity levels export var ErrorSeverity; (function (ErrorSeverity) { ErrorSeverity["WARNING"] = "warning"; ErrorSeverity["ERROR"] = "error"; ErrorSeverity["CRITICAL"] = "critical"; })(ErrorSeverity || (ErrorSeverity = {})); // Error categories export var ErrorCategory; (function (ErrorCategory) { ErrorCategory["VALIDATION"] = "validation"; ErrorCategory["RUNTIME"] = "runtime"; ErrorCategory["DOM"] = "dom"; ErrorCategory["STATE"] = "state"; ErrorCategory["COMPONENT"] = "component"; ErrorCategory["ROUTER"] = "router"; ErrorCategory["LIFECYCLE"] = "lifecycle"; ErrorCategory["PROPS"] = "props"; ErrorCategory["CHILDREN"] = "children"; })(ErrorCategory || (ErrorCategory = {})); // Base error class for Rynex export class RynexError extends Error { constructor(message, category, severity = ErrorSeverity.ERROR, context) { super(message); this.name = 'RynexError'; this.severity = severity; this.category = category; this.timestamp = Date.now(); this.context = context; // Maintain proper stack trace if (Error.captureStackTrace) { Error.captureStackTrace(this, RynexError); } } toString() { return `[${this.severity.toUpperCase()}] [${this.category}] ${this.message}`; } } // Specific error types export class ValidationError extends RynexError { constructor(message, context) { super(message, ErrorCategory.VALIDATION, ErrorSeverity.ERROR, context); this.name = 'ValidationError'; } } export class DOMError extends RynexError { constructor(message, severity = ErrorSeverity.ERROR, context) { super(message, ErrorCategory.DOM, severity, context); this.name = 'DOMError'; } } export class StateError extends RynexError { constructor(message, context) { super(message, ErrorCategory.STATE, ErrorSeverity.ERROR, context); this.name = 'StateError'; } } export class ComponentError extends RynexError { constructor(message, severity = ErrorSeverity.ERROR, context) { super(message, ErrorCategory.COMPONENT, severity, context); this.name = 'ComponentError'; } } export class RouterError extends RynexError { constructor(message, context) { super(message, ErrorCategory.ROUTER, ErrorSeverity.ERROR, context); this.name = 'RouterError'; } } export class LifecycleError extends RynexError { constructor(message, context) { super(message, ErrorCategory.LIFECYCLE, ErrorSeverity.ERROR, context); this.name = 'LifecycleError'; } } class ErrorHandler { constructor() { this.config = { throwOnError: true, logErrors: true, captureStackTrace: true }; this.errorLog = []; this.maxLogSize = 100; } configure(config) { this.config = { ...this.config, ...config }; } handle(error) { // Add to log this.errorLog.push(error); if (this.errorLog.length > this.maxLogSize) { this.errorLog.shift(); } // Log to console if enabled if (this.config.logErrors) { const style = this.getConsoleStyle(error.severity); console.error(`%c${error.toString()}`, style, error.context ? `\nContext:` : '', error.context || ''); if (this.config.captureStackTrace && error.stack) { console.error(error.stack); } } // Call custom error handler if (this.config.onError) { this.config.onError(error); } // Throw if configured if (this.config.throwOnError && error.severity === ErrorSeverity.CRITICAL) { throw error; } } getConsoleStyle(severity) { switch (severity) { case ErrorSeverity.WARNING: return 'color: #ffc107; font-weight: bold;'; case ErrorSeverity.ERROR: return 'color: #dc3545; font-weight: bold;'; case ErrorSeverity.CRITICAL: return 'color: #ff0000; font-weight: bold; font-size: 14px;'; default: return ''; } } getErrors() { return [...this.errorLog]; } clearErrors() { this.errorLog = []; } getErrorsByCategory(category) { return this.errorLog.filter(err => err.category === category); } getErrorsBySeverity(severity) { return this.errorLog.filter(err => err.severity === severity); } } // Global error handler instance export const errorHandler = new ErrorHandler(); // Validation utilities export const validators = { /** * Validate that a value is not null or undefined */ required(value, fieldName, context) { if (value === null || value === undefined) { throw new ValidationError(`${fieldName} is required but received ${value}`, { fieldName, value, ...context }); } }, /** * Validate type of a value */ type(value, expectedType, fieldName, context) { const actualType = typeof value; if (actualType !== expectedType) { throw new ValidationError(`${fieldName} expected type '${expectedType}' but received '${actualType}'`, { fieldName, expectedType, actualType, value, ...context }); } }, /** * Validate that value is a function */ isFunction(value, fieldName, context) { if (typeof value !== 'function') { throw new ValidationError(`${fieldName} must be a function but received ${typeof value}`, { fieldName, value, ...context }); } }, /** * Validate that value is an object */ isObject(value, fieldName, context) { if (typeof value !== 'object' || value === null || Array.isArray(value)) { throw new ValidationError(`${fieldName} must be an object but received ${typeof value}`, { fieldName, value, ...context }); } }, /** * Validate that value is an array */ isArray(value, fieldName, context) { if (!Array.isArray(value)) { throw new ValidationError(`${fieldName} must be an array but received ${typeof value}`, { fieldName, value, ...context }); } }, /** * Validate that value is a string */ isString(value, fieldName, context) { if (typeof value !== 'string') { throw new ValidationError(`${fieldName} must be a string but received ${typeof value}`, { fieldName, value, ...context }); } }, /** * Validate that value is a number */ isNumber(value, fieldName, context) { if (typeof value !== 'number' || isNaN(value)) { throw new ValidationError(`${fieldName} must be a valid number but received ${typeof value}`, { fieldName, value, ...context }); } }, /** * Validate that value is a boolean */ isBoolean(value, fieldName, context) { if (typeof value !== 'boolean') { throw new ValidationError(`${fieldName} must be a boolean but received ${typeof value}`, { fieldName, value, ...context }); } }, /** * Validate that value is an HTMLElement */ isHTMLElement(value, fieldName, context) { if (!(value instanceof HTMLElement)) { throw new DOMError(`${fieldName} must be an HTMLElement but received ${value?.constructor?.name || typeof value}`, ErrorSeverity.ERROR, { fieldName, value, ...context }); } }, /** * Validate that element exists in DOM */ elementExists(element, fieldName, context) { if (!element) { throw new DOMError(`${fieldName} element not found in DOM`, ErrorSeverity.ERROR, { fieldName, ...context }); } }, /** * Validate that selector is valid */ validSelector(selector, fieldName, context) { try { document.querySelector(selector); } catch (error) { throw new DOMError(`${fieldName} contains invalid CSS selector: ${selector}`, ErrorSeverity.ERROR, { fieldName, selector, error, ...context }); } }, /** * Validate that value is one of allowed values */ oneOf(value, allowedValues, fieldName, context) { if (!allowedValues.includes(value)) { throw new ValidationError(`${fieldName} must be one of [${allowedValues.join(', ')}] but received '${value}'`, { fieldName, value, allowedValues, ...context }); } }, /** * Validate array is not empty */ notEmpty(arr, fieldName, context) { if (!Array.isArray(arr) || arr.length === 0) { throw new ValidationError(`${fieldName} must be a non-empty array`, { fieldName, value: arr, ...context }); } }, /** * Validate string is not empty */ notEmptyString(value, fieldName, context) { if (typeof value !== 'string' || value.trim().length === 0) { throw new ValidationError(`${fieldName} must be a non-empty string`, { fieldName, value, ...context }); } }, /** * Validate number is in range */ inRange(value, min, max, fieldName, context) { if (typeof value !== 'number' || value < min || value > max) { throw new ValidationError(`${fieldName} must be between ${min} and ${max} but received ${value}`, { fieldName, value, min, max, ...context }); } }, /** * Validate props object */ validProps(props, fieldName = 'props', context) { if (props !== null && props !== undefined) { if (typeof props !== 'object' || Array.isArray(props)) { throw new ValidationError(`${fieldName} must be an object or null/undefined but received ${typeof props}`, { fieldName, props, ...context }); } } }, /** * Validate children */ validChildren(children, fieldName = 'children', context) { const validateChild = (child) => { return (child === null || child === undefined || child === false || child === true || typeof child === 'string' || typeof child === 'number' || child instanceof HTMLElement || child instanceof SVGElement || child instanceof Text); }; if (Array.isArray(children)) { const flatChildren = children.flat(Infinity); for (let i = 0; i < flatChildren.length; i++) { if (!validateChild(flatChildren[i])) { throw new ValidationError(`Invalid child at index ${i}: expected HTMLElement, string, number, or null/undefined but received ${typeof flatChildren[i]}`, { fieldName, index: i, child: flatChildren[i], ...context }); } } } else if (!validateChild(children)) { throw new ValidationError(`${fieldName} must be a valid DOM child (HTMLElement, string, number, or null/undefined) but received ${typeof children}`, { fieldName, children, ...context }); } } }; // Safe execution wrapper export function safeExecute(fn, errorMessage, category = ErrorCategory.RUNTIME, context) { try { return fn(); } catch (error) { const rynexError = new RynexError(`${errorMessage}: ${error instanceof Error ? error.message : String(error)}`, category, ErrorSeverity.ERROR, { originalError: error, ...context }); errorHandler.handle(rynexError); return null; } } // Development mode flag let isDevelopment = true; export function setDevelopmentMode(enabled) { isDevelopment = enabled; } export function isDevelopmentMode() { return isDevelopment; } // Validation wrapper that only runs in development export function devValidate(validationFn) { if (isDevelopment) { try { validationFn(); } catch (error) { if (error instanceof RynexError) { errorHandler.handle(error); } else { throw error; } } } } // Assert function for critical validations export function assert(condition, message, context) { if (!condition) { throw new RynexError(message, ErrorCategory.VALIDATION, ErrorSeverity.CRITICAL, context); } } //# sourceMappingURL=errors.js.map