UNPKG

@clipwhisperer/common

Version:

ClipWhisperer Common - Shared library providing core utilities, database schemas, authentication, bucket management, and common functionality across all ClipWhisperer microservices

206 lines (205 loc) 7.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ServiceConfig = exports.DefaultServiceConfigs = exports.CLIConfigSchema = exports.ManagerConfigSchema = exports.ServiceConfigItemSchema = exports.RestartPolicySchema = exports.EnvironmentSchema = void 0; const zod_1 = require("zod"); /** * Environment validation schema */ exports.EnvironmentSchema = zod_1.z.enum(['development', 'staging', 'production', 'test']); /** * Restart policy schema */ exports.RestartPolicySchema = zod_1.z.object({ enabled: zod_1.z.boolean().default(true), maxAttempts: zod_1.z.number().int().min(0).default(3), delay: zod_1.z.number().int().positive().default(5000), backoffMultiplier: zod_1.z.number().positive().default(1.5), maxDelay: zod_1.z.number().int().positive().default(30000), }); /** * Service configuration schema */ exports.ServiceConfigItemSchema = zod_1.z.object({ name: zod_1.z.string().min(1), port: zod_1.z.number().int().min(1000).max(65535), healthEndpoint: zod_1.z.string(), startCommand: zod_1.z.string().min(1), workingDirectory: zod_1.z.string().optional(), dependencies: zod_1.z.array(zod_1.z.string()).optional(), restartPolicy: exports.RestartPolicySchema.optional(), timeout: zod_1.z.number().int().positive().default(30000), healthCheckInterval: zod_1.z.number().int().positive().default(5000), gracefulShutdownTimeout: zod_1.z.number().int().positive().default(5000), env: zod_1.z.record(zod_1.z.string()).optional(), }); /** * Manager configuration schema */ exports.ManagerConfigSchema = zod_1.z.object({ environment: exports.EnvironmentSchema.default('development'), logLevel: zod_1.z.enum(['error', 'warn', 'info', 'debug', 'trace']).default('info'), maxConcurrentServices: zod_1.z.number().int().positive().default(10), globalTimeout: zod_1.z.number().int().positive().default(60000), healthCheckInterval: zod_1.z.number().int().positive().default(5000), enableMetrics: zod_1.z.boolean().default(true), enableAutoRestart: zod_1.z.boolean().default(true), services: zod_1.z.array(exports.ServiceConfigItemSchema).default([]), }); /** * CLI configuration schema */ exports.CLIConfigSchema = zod_1.z.object({ interactive: zod_1.z.boolean().default(false), watch: zod_1.z.boolean().default(false), verbose: zod_1.z.boolean().default(false), colors: zod_1.z.boolean().default(true), updateInterval: zod_1.z.number().int().positive().default(1000), maxLogLines: zod_1.z.number().int().positive().default(100), theme: zod_1.z.enum(['default', 'minimal', 'compact']).default('default'), }); /** * Default service configurations for ClipWhisperer */ exports.DefaultServiceConfigs = [ { name: 'Scraper', port: 9001, healthEndpoint: 'http://localhost:9001/health', startCommand: 'npm start', workingDirectory: 'Scraper', timeout: 30000, healthCheckInterval: 5000, gracefulShutdownTimeout: 5000, }, { name: 'Narrator', port: 9002, healthEndpoint: 'http://localhost:9002/health', startCommand: 'npm start', workingDirectory: 'Narrator', dependencies: ['Scraper'], timeout: 30000, healthCheckInterval: 5000, gracefulShutdownTimeout: 5000, }, { name: 'Renderer', port: 9004, healthEndpoint: 'http://localhost:9004/health', startCommand: 'npm start', workingDirectory: 'Renderer', dependencies: ['Narrator'], timeout: 30000, healthCheckInterval: 5000, gracefulShutdownTimeout: 5000, }, { name: 'Hub', port: 9003, healthEndpoint: 'http://localhost:9003/health', startCommand: 'npm start', workingDirectory: 'Hub', dependencies: ['Scraper', 'Narrator', 'Renderer'], timeout: 30000, healthCheckInterval: 5000, gracefulShutdownTimeout: 5000, }, ]; /** * Simple configuration manager for service orchestration */ class ServiceConfig { constructor(config) { // Load environment variables and apply defaults const environment = (process.env.NODE_ENV || 'development'); this.managerConfig = exports.ManagerConfigSchema.parse({ environment, logLevel: process.env.LOG_LEVEL || 'info', maxConcurrentServices: parseInt(process.env.MAX_CONCURRENT_SERVICES || '10'), globalTimeout: parseInt(process.env.GLOBAL_TIMEOUT || '60000'), healthCheckInterval: parseInt(process.env.HEALTH_CHECK_INTERVAL || '5000'), enableMetrics: process.env.ENABLE_METRICS !== 'false', enableAutoRestart: process.env.ENABLE_AUTO_RESTART !== 'false', services: exports.DefaultServiceConfigs, ...config, }); } getEnvironment() { return this.managerConfig.environment; } getServices() { return this.managerConfig.services; } getService(serviceName) { return this.managerConfig.services.find(service => service.name === serviceName); } getManagerConfig() { return { ...this.managerConfig }; } isDevelopment() { return this.managerConfig.environment === 'development'; } isProduction() { return this.managerConfig.environment === 'production'; } isTest() { return this.managerConfig.environment === 'test'; } addService(service) { const validatedService = exports.ServiceConfigItemSchema.parse(service); this.managerConfig.services.push(validatedService); } removeService(serviceName) { const index = this.managerConfig.services.findIndex(service => service.name === serviceName); if (index !== -1) { this.managerConfig.services.splice(index, 1); return true; } return false; } validateDependencies() { const errors = []; const serviceNames = new Set(this.managerConfig.services.map(s => s.name)); // Check if all dependencies exist for (const service of this.managerConfig.services) { if (service.dependencies) { for (const dep of service.dependencies) { if (!serviceNames.has(dep)) { errors.push(`Service '${service.name}' depends on '${dep}', but '${dep}' is not configured`); } } } } // Check for circular dependencies const visited = new Set(); const visiting = new Set(); const hasCycle = (serviceName) => { if (visited.has(serviceName)) return false; if (visiting.has(serviceName)) return true; visiting.add(serviceName); const service = this.getService(serviceName); if (service === null || service === void 0 ? void 0 : service.dependencies) { for (const dep of service.dependencies) { if (hasCycle(dep)) return true; } } visiting.delete(serviceName); visited.add(serviceName); return false; }; for (const service of this.managerConfig.services) { if (hasCycle(service.name)) { errors.push(`Circular dependency detected involving service '${service.name}'`); break; } } return { valid: errors.length === 0, errors, }; } } exports.ServiceConfig = ServiceConfig;