UNPKG

@ahmedhegazee/nestjs-telescope

Version:

Advanced observability and monitoring solution for NestJS applications with ML-powered analytics, enterprise features, and production-ready scaling

270 lines 11.8 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var TelescopeConfigValidator_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.TelescopeConfigValidator = void 0; const common_1 = require("@nestjs/common"); let TelescopeConfigValidator = TelescopeConfigValidator_1 = class TelescopeConfigValidator { constructor() { this.logger = new common_1.Logger(TelescopeConfigValidator_1.name); this.defaults = { enabled: true, storage: { driver: 'memory', batch: { enabled: true, size: 100, flushInterval: 5000 } }, devtools: { enabled: true, bridge: { resilience: { circuitBreakerEnabled: true, fallbackEnabled: true, maxRetries: 3, retryDelayMs: 1000, healthCheckIntervalMs: 30000 } } }, features: { realTimeUpdates: true, dashboard: true, metrics: true } }; this.supportedDrivers = ['memory', 'file', 'database', 'redis']; this.requiredFields = ['enabled', 'storage', 'devtools']; } validate(config) { const errors = []; const warnings = []; try { this.validateRequiredFields(config, errors); this.validateStorageConfig(config.storage, errors, warnings); this.validateDevToolsConfig(config.devtools, errors, warnings); this.validateFeaturesConfig(config.features, errors, warnings); this.validateWatchersConfig(config.watchers, errors, warnings); this.validatePerformanceConfig(config, errors, warnings); this.validateSecurityConfig(config, errors, warnings); return { isValid: errors.length === 0, errors, warnings }; } catch (error) { this.logger.error('Configuration validation failed:', error); return { isValid: false, errors: [`Validation failed: ${error.message}`], warnings }; } } applyDefaults(config) { const mergedConfig = this.deepMerge(this.defaults, config); this.logger.debug('Configuration after applying defaults:', JSON.stringify(mergedConfig, null, 2)); return mergedConfig; } validateAndApplyDefaults(config) { const validation = this.validate(config); if (!validation.isValid) { throw new common_1.BadRequestException(`Configuration validation failed: ${validation.errors.join(', ')}`); } if (validation.warnings.length > 0) { this.logger.warn('Configuration warnings:', validation.warnings); } return this.applyDefaults(config); } validateRequiredFields(config, errors) { for (const field of this.requiredFields) { if (!(field in config)) { errors.push(`Missing required field: ${field}`); } } } validateStorageConfig(storage, errors, warnings) { if (!storage) { errors.push('Storage configuration is required'); return; } if (storage.driver && !this.supportedDrivers.includes(storage.driver)) { errors.push(`Unsupported storage driver: ${storage.driver}. Supported: ${this.supportedDrivers.join(', ')}`); } if (storage.batch) { if (storage.batch.size !== undefined) { if (typeof storage.batch.size !== 'number' || storage.batch.size <= 0) { errors.push('Batch size must be a positive number'); } else if (storage.batch.size > 1000) { warnings.push('Large batch size may impact performance'); } } if (storage.batch.flushInterval !== undefined) { if (typeof storage.batch.flushInterval !== 'number' || storage.batch.flushInterval <= 0) { errors.push('Batch flush interval must be a positive number'); } else if (storage.batch.flushInterval < 1000) { warnings.push('Short flush interval may impact performance'); } } } if (storage.driver === 'file') { if (storage.file && !storage.file.directory) { errors.push('File storage requires directory configuration'); } } if (storage.driver === 'database') { if (storage.database && !storage.database.connection) { errors.push('Database storage requires connection configuration'); } } if (storage.driver === 'redis') { if (storage.redis && !storage.redis.host) { errors.push('Redis storage requires host configuration'); } } } validateDevToolsConfig(devtools, errors, warnings) { if (!devtools) { errors.push('DevTools configuration is required'); return; } if (devtools.bridge) { if (devtools.bridge.resilience) { const resilience = devtools.bridge.resilience; if (resilience.maxRetries !== undefined) { if (typeof resilience.maxRetries !== 'number' || resilience.maxRetries < 0) { errors.push('Max retries must be a non-negative number'); } else if (resilience.maxRetries > 10) { warnings.push('High retry count may cause delays'); } } if (resilience.retryDelayMs !== undefined) { if (typeof resilience.retryDelayMs !== 'number' || resilience.retryDelayMs < 0) { errors.push('Retry delay must be a non-negative number'); } } if (resilience.healthCheckIntervalMs !== undefined) { if (typeof resilience.healthCheckIntervalMs !== 'number' || resilience.healthCheckIntervalMs < 1000) { errors.push('Health check interval must be at least 1000ms'); } } } } } validateFeaturesConfig(features, errors, warnings) { if (!features) { return; } const booleanFields = ['realTimeUpdates', 'dashboard', 'metrics']; for (const field of booleanFields) { if (features[field] !== undefined && typeof features[field] !== 'boolean') { errors.push(`Feature ${field} must be a boolean`); } } if (features.dashboard === false && features.realTimeUpdates === true) { warnings.push('Real-time updates enabled but dashboard disabled'); } } validateWatchersConfig(watchers, errors, warnings) { if (!watchers) { return; } const validWatchers = ['request', 'query', 'exception', 'job', 'cache']; for (const [watcherName, watcherConfig] of Object.entries(watchers)) { if (!validWatchers.includes(watcherName)) { warnings.push(`Unknown watcher: ${watcherName}`); } if (typeof watcherConfig === 'object' && watcherConfig !== null) { this.validateWatcherConfig(watcherName, watcherConfig, errors, warnings); } } } validateWatcherConfig(name, config, errors, warnings) { if (config.enabled !== undefined && typeof config.enabled !== 'boolean') { errors.push(`Watcher ${name} enabled must be a boolean`); } if (config.sampling !== undefined) { if (typeof config.sampling !== 'number' || config.sampling < 0 || config.sampling > 100) { errors.push(`Watcher ${name} sampling must be a number between 0 and 100`); } } if (name === 'request') { if (config.excludePaths && !Array.isArray(config.excludePaths)) { errors.push('Request watcher excludePaths must be an array'); } } if (name === 'query') { if (config.slowQueryThreshold !== undefined) { if (typeof config.slowQueryThreshold !== 'number' || config.slowQueryThreshold <= 0) { errors.push('Query watcher slowQueryThreshold must be a positive number'); } } } } validatePerformanceConfig(config, errors, warnings) { if (config.storage?.batch?.size > 500) { warnings.push('Large batch size may cause memory issues'); } if (config.storage?.batch?.flushInterval < 1000) { warnings.push('Short flush interval may cause high CPU usage'); } if (config.devtools?.bridge?.resilience?.maxRetries > 5) { warnings.push('High retry count may cause long delays'); } if (config.features?.realTimeUpdates === true && config.storage?.batch?.flushInterval > 10000) { warnings.push('Real-time updates with long flush interval may cause delays'); } } validateSecurityConfig(config, errors, warnings) { if (config.storage?.driver === 'file' && config.storage?.file?.directory?.startsWith('/')) { warnings.push('Using absolute path for file storage may have security implications'); } if (config.features?.dashboard === true && !config.auth) { warnings.push('Dashboard enabled without authentication configuration'); } if (this.containsSensitiveData(config)) { errors.push('Configuration contains sensitive data that should be in environment variables'); } } containsSensitiveData(obj) { const sensitiveKeys = ['password', 'secret', 'key', 'token', 'apikey']; const json = JSON.stringify(obj).toLowerCase(); return sensitiveKeys.some(key => json.includes(key)); } deepMerge(target, source) { const result = { ...target }; for (const key in source) { if (source.hasOwnProperty(key)) { if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { result[key] = this.deepMerge(result[key] || {}, source[key]); } else { result[key] = source[key]; } } } return result; } getDefaults() { return { ...this.defaults }; } getSupportedDrivers() { return [...this.supportedDrivers]; } }; exports.TelescopeConfigValidator = TelescopeConfigValidator; exports.TelescopeConfigValidator = TelescopeConfigValidator = TelescopeConfigValidator_1 = __decorate([ (0, common_1.Injectable)() ], TelescopeConfigValidator); //# sourceMappingURL=telescope-config.validator.js.map