UNPKG

hook-engine

Version:

Production-grade webhook engine with comprehensive adapter support, security, reliability, structured logging, and CLI tools.

282 lines (281 loc) 11.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.validateConfig = validateConfig; const base_1 = require("../errors/base"); /** * Validate the complete hook engine configuration */ function validateConfig(config) { const result = validateHookEngineConfig(config); if (!result.valid) { throw new base_1.ConfigurationError(`Configuration validation failed: ${result.errors.join(', ')}`, { errors: result.errors, warnings: result.warnings }); } // Log warnings if (result.warnings.length > 0) { console.warn('⚠️ Configuration warnings:', result.warnings); } } /** * Validate hook engine configuration */ function validateHookEngineConfig(config) { const errors = []; const warnings = []; // Validate required fields if (!config.adapters || !Array.isArray(config.adapters)) { errors.push('adapters must be an array'); } if (!config.retry) { errors.push('retry configuration is required'); } if (!config.security) { errors.push('security configuration is required'); } if (!config.observability) { errors.push('observability configuration is required'); } if (!config.storage) { errors.push('storage configuration is required'); } if (!config.environment) { errors.push('environment is required'); } // Validate environment if (config.environment && !['development', 'staging', 'production'].includes(config.environment)) { errors.push('environment must be one of: development, staging, production'); } // Validate individual sections if (config.adapters) { const adapterResult = validateAdapters(config.adapters); errors.push(...adapterResult.errors); warnings.push(...adapterResult.warnings); } if (config.retry) { const retryResult = validateRetryConfig(config.retry); errors.push(...retryResult.errors); warnings.push(...retryResult.warnings); } if (config.security) { const securityResult = validateSecurityConfig(config.security); errors.push(...securityResult.errors); warnings.push(...securityResult.warnings); } if (config.observability) { const observabilityResult = validateObservabilityConfig(config.observability); errors.push(...observabilityResult.errors); warnings.push(...observabilityResult.warnings); } if (config.storage) { const storageResult = validateStorageConfig(config.storage); errors.push(...storageResult.errors); warnings.push(...storageResult.warnings); } // Production-specific validations if (config.environment === 'production') { if (!config.security.requireHttps) { warnings.push('HTTPS should be required in production'); } if (!config.observability.logging.enabled) { warnings.push('Logging should be enabled in production'); } if (config.adapters.length === 0) { warnings.push('No adapters configured for production'); } } return { valid: errors.length === 0, errors, warnings }; } /** * Validate adapter configurations */ function validateAdapters(adapters) { const errors = []; const warnings = []; const seenSources = new Set(); for (let i = 0; i < adapters.length; i++) { const adapter = adapters[i]; const prefix = `adapters[${i}]`; if (!adapter.source) { errors.push(`${prefix}.source is required`); } if (!adapter.secret) { errors.push(`${prefix}.secret is required`); } if (typeof adapter.enabled !== 'boolean') { errors.push(`${prefix}.enabled must be a boolean`); } // Check for duplicate sources if (adapter.source) { if (seenSources.has(adapter.source)) { errors.push(`Duplicate adapter source: ${adapter.source}`); } seenSources.add(adapter.source); } // Validate secret strength (basic check) if (adapter.secret && adapter.secret.length < 16) { warnings.push(`${prefix}.secret should be at least 16 characters long`); } } return { valid: errors.length === 0, errors, warnings }; } /** * Validate retry configuration */ function validateRetryConfig(config) { const errors = []; const warnings = []; if (typeof config.maxAttempts !== 'number' || config.maxAttempts < 1) { errors.push('retry.maxAttempts must be a positive number'); } if (typeof config.initialDelayMs !== 'number' || config.initialDelayMs < 0) { errors.push('retry.initialDelayMs must be a non-negative number'); } if (typeof config.maxDelayMs !== 'number' || config.maxDelayMs < config.initialDelayMs) { errors.push('retry.maxDelayMs must be greater than or equal to initialDelayMs'); } if (typeof config.backoffMultiplier !== 'number' || config.backoffMultiplier < 1) { errors.push('retry.backoffMultiplier must be >= 1'); } if (typeof config.jitter !== 'boolean') { errors.push('retry.jitter must be a boolean'); } if (!Array.isArray(config.retryOn)) { errors.push('retry.retryOn must be an array'); } // Warnings for potentially problematic values if (config.maxAttempts > 10) { warnings.push('retry.maxAttempts > 10 may cause excessive delays'); } if (config.maxDelayMs > 300000) { // 5 minutes warnings.push('retry.maxDelayMs > 5 minutes may cause timeouts'); } return { valid: errors.length === 0, errors, warnings }; } /** * Validate security configuration */ function validateSecurityConfig(config) { const errors = []; const warnings = []; // Rate limiting validation if (config.rateLimiting) { if (typeof config.rateLimiting.enabled !== 'boolean') { errors.push('security.rateLimiting.enabled must be a boolean'); } if (typeof config.rateLimiting.maxRequests !== 'number' || config.rateLimiting.maxRequests < 1) { errors.push('security.rateLimiting.maxRequests must be a positive number'); } if (typeof config.rateLimiting.windowMs !== 'number' || config.rateLimiting.windowMs < 1000) { errors.push('security.rateLimiting.windowMs must be at least 1000ms'); } } // Request validation if (config.requestValidation) { if (typeof config.requestValidation.maxBodySize !== 'number' || config.requestValidation.maxBodySize < 1) { errors.push('security.requestValidation.maxBodySize must be a positive number'); } if (typeof config.requestValidation.timeoutMs !== 'number' || config.requestValidation.timeoutMs < 1000) { errors.push('security.requestValidation.timeoutMs must be at least 1000ms'); } if (!Array.isArray(config.requestValidation.allowedContentTypes)) { errors.push('security.requestValidation.allowedContentTypes must be an array'); } } // IP allowlist validation if (config.ipAllowlist && !Array.isArray(config.ipAllowlist)) { errors.push('security.ipAllowlist must be an array'); } if (typeof config.requireHttps !== 'boolean') { errors.push('security.requireHttps must be a boolean'); } return { valid: errors.length === 0, errors, warnings }; } /** * Validate observability configuration */ function validateObservabilityConfig(config) { const errors = []; const warnings = []; // Logging validation if (config.logging) { if (typeof config.logging.enabled !== 'boolean') { errors.push('observability.logging.enabled must be a boolean'); } if (!['debug', 'info', 'warn', 'error'].includes(config.logging.level)) { errors.push('observability.logging.level must be one of: debug, info, warn, error'); } if (!['json', 'text'].includes(config.logging.format)) { errors.push('observability.logging.format must be json or text'); } if (!['console', 'file', 'both'].includes(config.logging.destination)) { errors.push('observability.logging.destination must be console, file, or both'); } if (config.logging.destination !== 'console' && !config.logging.filePath) { errors.push('observability.logging.filePath is required when destination is file or both'); } } // Metrics validation if (config.metrics) { if (typeof config.metrics.enabled !== 'boolean') { errors.push('observability.metrics.enabled must be a boolean'); } if (typeof config.metrics.prefix !== 'string') { errors.push('observability.metrics.prefix must be a string'); } if (typeof config.metrics.collectDefaultMetrics !== 'boolean') { errors.push('observability.metrics.collectDefaultMetrics must be a boolean'); } } // Tracing validation if (config.tracing) { if (typeof config.tracing.enabled !== 'boolean') { errors.push('observability.tracing.enabled must be a boolean'); } if (typeof config.tracing.serviceName !== 'string') { errors.push('observability.tracing.serviceName must be a string'); } if (typeof config.tracing.sampleRate !== 'number' || config.tracing.sampleRate < 0 || config.tracing.sampleRate > 1) { errors.push('observability.tracing.sampleRate must be between 0 and 1'); } } return { valid: errors.length === 0, errors, warnings }; } /** * Validate storage configuration */ function validateStorageConfig(config) { const errors = []; const warnings = []; if (!['sqlite', 'memory', 'redis'].includes(config.type)) { errors.push('storage.type must be one of: sqlite, memory, redis'); } if (typeof config.ttl !== 'number' || config.ttl < 60) { errors.push('storage.ttl must be at least 60 seconds'); } if (typeof config.cleanupInterval !== 'number' || config.cleanupInterval < 60) { errors.push('storage.cleanupInterval must be at least 60 seconds'); } // Type-specific validation if (config.type === 'sqlite' && config.connection?.path && typeof config.connection.path !== 'string') { errors.push('storage.connection.path must be a string for SQLite'); } if (config.type === 'redis' && config.connection) { if (config.connection.host && typeof config.connection.host !== 'string') { errors.push('storage.connection.host must be a string for Redis'); } if (config.connection.port && (typeof config.connection.port !== 'number' || config.connection.port < 1 || config.connection.port > 65535)) { errors.push('storage.connection.port must be a valid port number for Redis'); } } // Warnings if (config.ttl > 86400 * 30) { // 30 days warnings.push('storage.ttl > 30 days may consume excessive storage'); } if (config.cleanupInterval > 86400) { // 24 hours warnings.push('storage.cleanupInterval > 24 hours may cause storage bloat'); } return { valid: errors.length === 0, errors, warnings }; }