UNPKG

shogun-core

Version:

SHOGUN CORE - Core library for Shogun Ecosystem

283 lines (282 loc) 9.56 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ErrorHandler = exports.ErrorType = void 0; exports.createError = createError; /** * Types of errors that can occur in the application */ var ErrorType; (function (ErrorType) { ErrorType["AUTHENTICATION"] = "AuthenticationError"; ErrorType["AUTHORIZATION"] = "AuthorizationError"; ErrorType["VALIDATION"] = "ValidationError"; ErrorType["NETWORK"] = "NetworkError"; ErrorType["DATABASE"] = "DatabaseError"; ErrorType["WALLET"] = "WalletError"; ErrorType["STORAGE"] = "StorageError"; ErrorType["ENCRYPTION"] = "EncryptionError"; ErrorType["SIGNATURE"] = "SignatureError"; ErrorType["ENVIRONMENT"] = "EnvironmentError"; ErrorType["SECURITY"] = "SecurityError"; ErrorType["GUN"] = "GunError"; ErrorType["STEALTH"] = "StealthError"; ErrorType["WEBAUTHN"] = "WebAuthnError"; ErrorType["PLUGIN"] = "PluginError"; ErrorType["UNKNOWN"] = "UnknownError"; ErrorType["CONNECTOR"] = "ConnectorError"; ErrorType["GENERAL"] = "GeneralError"; ErrorType["CONTRACT"] = "ContractError"; ErrorType["BIP32"] = "BIP32Error"; ErrorType["ETHEREUM"] = "EthereumError"; ErrorType["BITCOIN"] = "BitcoinError"; })(ErrorType || (exports.ErrorType = ErrorType = {})); /** * Wrapper to standardize errors * @param type - Error type * @param code - Error code * @param message - Error message * @param originalError - Original error * @returns A structured error object */ function createError(type, code, message, originalError) { return { type, code, message, originalError, timestamp: Date.now(), }; } /** * Centralized error handler */ class ErrorHandler { static errors = []; static maxErrors = 100; static listeners = []; static externalLogger = null; /** * Set an external logging service for production error monitoring * @param logger - External logger function to send errors to a monitoring service */ static setExternalLogger(logger) { this.externalLogger = logger; } /** * Handles an error by logging it and notifying listeners * @param error - The error to handle */ static handleError(error) { // Log essential errors only if (error.type === ErrorType.AUTHENTICATION || error.type === ErrorType.AUTHORIZATION || error.type === ErrorType.SECURITY) { console.error(`[${error.type}] ${error.code}: ${error.message}`); } // Store the error in memory this.errors.push(error); // Keep only the last maxErrors if (this.errors.length > this.maxErrors) { this.errors = this.errors.slice(-this.maxErrors); } // Send to external logger if set (for production monitoring) if (this.externalLogger) { try { this.externalLogger(error); } catch (e) { // Fallback logging for external logger errors console.error("Failed to send error to external logger:", e); } } // Notify all listeners this.listeners.forEach((listener) => { try { listener(error); } catch (e) { // Silent error to prevent infinite loops } }); } /** * Handles a raw error by converting it to ShogunError * @param type - Error type * @param code - Error code * @param message - Error message * @param originalError - Original error * @param logLevel - Log level for the error */ static handle(type, code, message, originalError, logLevel = "error") { // Create a formatted error message const finalMessage = originalError ? `${message} - ${this.formatError(originalError)}` : message; // Log the error switch (logLevel) { case "debug": console.log(`[${type}.${code}] (DEBUG) ${finalMessage}`); break; case "warn": console.log(`[${type}.${code}] (WARN) ${finalMessage}`); break; case "info": console.log(`[${type}.${code}] (INFO) ${finalMessage}`); break; case "error": default: console.log(`[${type}.${code}] (ERROR) ${finalMessage}`); if (originalError && originalError instanceof Error) { console.log(originalError.stack || "No stack trace available"); } break; } const error = createError(type, code, finalMessage, originalError); this.handleError(error); return error; } /** * Handles errors and throws them as standardized ShogunError objects * @param type - Error type * @param code - Error code * @param message - Error message * @param originalError - Original error * @throws ShogunError */ static handleAndThrow(type, code, message, originalError) { const error = this.handle(type, code, message, originalError); throw error; } /** * Retrieves the last N errors * @param count - Number of errors to retrieve * @returns List of most recent errors */ static getRecentErrors(count = 10) { return this.errors.slice(-Math.min(count, this.errors.length)); } /** * Adds a listener for errors * @param listener - Function that will be called when an error occurs */ static addListener(listener) { this.listeners.push(listener); } /** * Removes an error listener * @param listener - Function to remove */ static removeListener(listener) { const index = this.listeners.indexOf(listener); if (index !== -1) { this.listeners.splice(index, 1); } } /** * Notifies all listeners of an error * @param error - Error to notify */ static notifyListeners(error) { for (const listener of this.listeners) { try { listener(error); } catch (listenerError) { console.error(`Error in error listener: ${listenerError}`); } } } /** * Helper function to format error messages from native errors * @param error - Error to format * @returns Formatted error message */ static formatError(error) { if (!error) { return "Unknown error"; } if (error instanceof Error) { return `${error.name}: ${error.message}`; } if (typeof error === "string") { return error; } if (typeof error === "object") { try { return JSON.stringify(error); } catch (e) { return `Object: ${Object.prototype.toString.call(error)}`; } } return String(error); } /** * Error handling with retry logic */ static async withRetry(fn, errorType, errorCode, maxRetries = 3, retryDelay = 1000) { let lastError; for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await fn(); } catch (error) { lastError = error; const delay = retryDelay * attempt; if (attempt < maxRetries) { console.log(`Retrying operation after ${delay}ms (attempt ${attempt}/${maxRetries})`); await new Promise((resolve) => setTimeout(resolve, delay)); } } } // If we got here, all retries failed. // Create the error, then throw a new Error instance for better compatibility with test runners. const shogunError = this.handle(errorType, errorCode, `Operation failed after ${maxRetries} attempts`, lastError); throw new Error(shogunError.message); } /** * Clear all stored errors */ static clearErrors() { this.errors = []; } /** * Get error statistics */ static getErrorStats() { const stats = { total: this.errors.length, byType: {}, byCode: {}, }; for (const error of this.errors) { stats.byType[error.type] = (stats.byType[error.type] || 0) + 1; stats.byCode[error.code] = (stats.byCode[error.code] || 0) + 1; } return stats; } /** * Debug helper - logs messages only in development */ static debug(type, code, message, level = "debug") { // Only log debug messages in development environment if (process.env.NODE_ENV === "development") { const finalMessage = `${message}`; switch (level) { case "error": console.error(`[${type}.${code}] ${finalMessage}`); break; case "warn": console.warn(`[${type}.${code}] ${finalMessage}`); break; case "info": console.log(`[${type}.${code}] ${finalMessage}`); break; case "debug": console.log(`[${type}.${code}] (DEBUG) ${finalMessage}`); break; } } } } exports.ErrorHandler = ErrorHandler;