@juspay/neurolink
Version:
Universal AI Development Platform with working MCP integration, multi-provider support, and professional CLI. Built-in tools operational, 58+ external MCP servers discoverable. Connect to filesystem, GitHub, database operations, and more. Build, test, and
113 lines (112 loc) • 3.92 kB
JavaScript
/**
* Service Registry for Dependency Injection
* Breaks circular dependencies by providing lazy loading and centralized service management
*/
import { logger } from "../utils/logger.js";
export class ServiceRegistry {
static services = new Map();
static initializing = new Set();
/**
* Register a service with optional singleton behavior
*/
static register(name, factory, options = {}) {
if (this.services.has(name)) {
logger.warn(`Service ${name} is already registered. Overwriting.`);
}
this.services.set(name, {
factory,
singleton: options.singleton ?? true,
instance: undefined,
});
logger.debug(`Service registered: ${name} (singleton: ${options.singleton ?? true})`);
}
/**
* Get a service instance with circular dependency detection
*/
static async get(name) {
const registration = this.services.get(name);
if (!registration) {
throw new Error(`Service ${name} not registered. Available services: ${Array.from(this.services.keys()).join(", ")}`);
}
// Check for circular dependency
if (this.initializing.has(name)) {
throw new Error(`Circular dependency detected: ${name} is already being initialized. Chain: ${Array.from(this.initializing).join(" -> ")} -> ${name}`);
}
// Return existing singleton instance if available
if (registration.singleton && registration.instance !== undefined) {
return registration.instance;
}
try {
// Mark as initializing to detect circular dependencies
this.initializing.add(name);
logger.debug(`Initializing service: ${name}`);
// Create new instance
const instance = await registration.factory();
// Store singleton instance
if (registration.singleton) {
registration.instance = instance;
}
logger.debug(`Service initialized: ${name}`);
return instance;
}
catch (error) {
logger.error(`Failed to initialize service ${name}:`, error);
throw error;
}
finally {
// Remove from initializing set
this.initializing.delete(name);
}
}
/**
* Get a service synchronously (throws if async initialization required)
*/
static getSync(name) {
const registration = this.services.get(name);
if (!registration) {
throw new Error(`Service ${name} not registered`);
}
if (registration.singleton && registration.instance !== undefined) {
return registration.instance;
}
// Try synchronous initialization
const result = registration.factory();
if (result instanceof Promise) {
throw new Error(`Service ${name} requires asynchronous initialization. Use get() instead.`);
}
if (registration.singleton) {
registration.instance = result;
}
return result;
}
/**
* Check if a service is registered
*/
static has(name) {
return this.services.has(name);
}
/**
* Clear all services (useful for testing)
*/
static clear() {
this.services.clear();
this.initializing.clear();
logger.debug("Service registry cleared");
}
/**
* Get all registered service names
*/
static getRegisteredServices() {
return Array.from(this.services.keys());
}
/**
* Register multiple services at once
*/
static registerBatch(services) {
for (const [name, factory] of Object.entries(services)) {
this.register(name, factory);
}
}
}
// Export singleton instance for convenience
export const serviceRegistry = ServiceRegistry;