vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
200 lines (199 loc) • 7.29 kB
JavaScript
import logger from '../../../logger.js';
export class ServiceLifecycleManager {
static instance = null;
services = new Map();
dependencies = [];
startupInProgress = false;
shutdownInProgress = false;
constructor() { }
static getInstance() {
if (!ServiceLifecycleManager.instance) {
ServiceLifecycleManager.instance = new ServiceLifecycleManager();
}
return ServiceLifecycleManager.instance;
}
static resetInstance() {
ServiceLifecycleManager.instance = null;
}
registerService(config) {
const service = {
...config,
isStarted: false,
isDisposed: false
};
this.services.set(config.name, service);
logger.debug(`Service registered: ${config.name}`);
}
registerDependency(service, dependsOn) {
this.dependencies.push({ service, dependsOn });
logger.debug(`Dependencies registered for ${service}: ${dependsOn.join(', ')}`);
}
async startAllServices() {
if (this.startupInProgress) {
logger.warn('Service startup already in progress, skipping');
return;
}
if (this.shutdownInProgress) {
logger.warn('Service shutdown in progress, cannot start services');
return;
}
this.startupInProgress = true;
try {
const startOrder = this.calculateStartupOrder();
logger.info(`Starting services in order: ${startOrder.join(' -> ')}`);
for (const serviceName of startOrder) {
await this.startService(serviceName);
}
logger.info('All services started successfully');
}
catch (error) {
logger.error('Failed to start all services', { error });
throw error;
}
finally {
this.startupInProgress = false;
}
}
async stopAllServices() {
if (this.shutdownInProgress) {
logger.warn('Service shutdown already in progress, skipping');
return;
}
this.shutdownInProgress = true;
try {
const stopOrder = this.calculateStartupOrder().reverse();
logger.info(`Stopping services in order: ${stopOrder.join(' -> ')}`);
for (const serviceName of stopOrder) {
await this.stopService(serviceName);
}
logger.info('All services stopped successfully');
}
catch (error) {
logger.error('Failed to stop all services', { error });
throw error;
}
finally {
this.shutdownInProgress = false;
}
}
async disposeAllServices() {
await this.stopAllServices();
const disposeOrder = this.calculateStartupOrder().reverse();
logger.info(`Disposing services in order: ${disposeOrder.join(' -> ')}`);
for (const serviceName of disposeOrder) {
await this.disposeService(serviceName);
}
this.services.clear();
this.dependencies = [];
logger.info('All services disposed and state cleared');
}
async startService(serviceName) {
const service = this.services.get(serviceName);
if (!service) {
throw new Error(`Service not registered: ${serviceName}`);
}
if (service.isStarted || service.isDisposed) {
logger.debug(`Service ${serviceName} already started or disposed, skipping`);
return;
}
try {
const deps = this.dependencies.find(d => d.service === serviceName);
if (deps) {
for (const depName of deps.dependsOn) {
const depService = this.services.get(depName);
if (!depService?.isStarted) {
throw new Error(`Dependency ${depName} not started for service ${serviceName}`);
}
}
}
if (service.startMethod && typeof service.instance[service.startMethod] === 'function') {
await service.instance[service.startMethod]();
}
service.isStarted = true;
logger.info(`Service started: ${serviceName}`);
}
catch (error) {
logger.error(`Failed to start service: ${serviceName}`, { error });
throw error;
}
}
async stopService(serviceName) {
const service = this.services.get(serviceName);
if (!service || !service.isStarted || service.isDisposed) {
return;
}
try {
if (service.stopMethod && typeof service.instance[service.stopMethod] === 'function') {
await service.instance[service.stopMethod]();
}
service.isStarted = false;
logger.info(`Service stopped: ${serviceName}`);
}
catch (error) {
logger.error(`Failed to stop service: ${serviceName}`, { error });
}
}
async disposeService(serviceName) {
const service = this.services.get(serviceName);
if (!service || service.isDisposed) {
return;
}
try {
if (service.isStarted) {
await this.stopService(serviceName);
}
if (service.disposeMethod && typeof service.instance[service.disposeMethod] === 'function') {
await service.instance[service.disposeMethod]();
}
if (service.resetStaticMethod && typeof service.instance.constructor[service.resetStaticMethod] === 'function') {
service.instance.constructor[service.resetStaticMethod]();
}
service.isDisposed = true;
logger.info(`Service disposed: ${serviceName}`);
}
catch (error) {
logger.error(`Failed to dispose service: ${serviceName}`, { error });
}
}
calculateStartupOrder() {
const visited = new Set();
const visiting = new Set();
const order = [];
const visit = (serviceName) => {
if (visited.has(serviceName)) {
return;
}
if (visiting.has(serviceName)) {
throw new Error(`Circular dependency detected involving service: ${serviceName}`);
}
visiting.add(serviceName);
const deps = this.dependencies.find(d => d.service === serviceName);
if (deps) {
for (const depName of deps.dependsOn) {
visit(depName);
}
}
visiting.delete(serviceName);
visited.add(serviceName);
order.push(serviceName);
};
for (const serviceName of this.services.keys()) {
visit(serviceName);
}
return order;
}
getServiceStatus(serviceName) {
return this.services.get(serviceName) || null;
}
getAllServiceStatuses() {
return new Map(this.services);
}
areAllServicesHealthy() {
for (const service of this.services.values()) {
if (!service.isStarted || service.isDisposed) {
return false;
}
}
return true;
}
}