/**
* Modular Service Registry using HashMap and Graph patterns
* Manages Azure services with dependency resolution and lifecycle management
* Uses graph structure for service dependencies
*/
import { logger } from './logger.js';
export interface ServiceMetadata {
name: string;
version: string;
description: string;
dependencies: string[];
cliPrefix: string;
supportedOperations: string[];
}
export interface ServiceAction {
name: string;
method: string;
requiredParams: string[];
optionalParams: string[];
riskLevel: 'low' | 'high';
description: string;
}
/**
* Graph node representing a service and its dependencies
* DSA: Directed Acyclic Graph (DAG) for dependency resolution
*/
class ServiceNode {
metadata: ServiceMetadata;
dependencies: Set<string>;
dependents: Set<string>;
instance: any;
initialized: boolean = false;
constructor(metadata: ServiceMetadata) {
this.metadata = metadata;
this.dependencies = new Set(metadata.dependencies);
this.dependents = new Set();
}
}
/**
* Service Registry with dependency resolution
* Uses topological sort for initialization order
*/
export class ServiceRegistry {
// HashMap for O(1) service lookup
private services: Map<string, ServiceNode> = new Map();
// HashMap for service actions
private serviceActions: Map<string, Map<string, ServiceAction>> = new Map();
// Cache for initialization order
private initializationOrder: string[] | null = null;
/**
* Registers a new service
* Time Complexity: O(1)
*/
register(metadata: ServiceMetadata, instance?: any): void {
if (this.services.has(metadata.name)) {
throw new Error(`Service ${metadata.name} already registered`);
}
const node = new ServiceNode(metadata);
if (instance) {
node.instance = instance;
}
this.services.set(metadata.name, node);
// Update dependent references
for (const depName of metadata.dependencies) {
const depNode = this.services.get(depName);
if (depNode) {
depNode.dependents.add(metadata.name);
}
}
// Invalidate initialization order cache
this.initializationOrder = null;
logger.info('Service registered', {
name: metadata.name,
version: metadata.version,
dependencies: metadata.dependencies
});
}
/**
* Registers actions for a service
*/
registerActions(serviceName: string, actions: ServiceAction[]): void {
if (!this.services.has(serviceName)) {
throw new Error(`Service ${serviceName} not found`);
}
const actionMap = new Map<string, ServiceAction>();
for (const action of actions) {
actionMap.set(action.name, action);
}
this.serviceActions.set(serviceName, actionMap);
logger.info('Service actions registered', {
service: serviceName,
actionCount: actions.length
});
}
/**
* Gets a service instance
* Time Complexity: O(1)
*/
get<T = any>(serviceName: string): T | null {
const node = this.services.get(serviceName);
if (!node) {
return null;
}
return node.instance as T;
}
/**
* Gets service metadata
*/
getMetadata(serviceName: string): ServiceMetadata | null {
const node = this.services.get(serviceName);
return node ? node.metadata : null;
}
/**
* Gets available actions for a service
*/
getActions(serviceName: string): Map<string, ServiceAction> | null {
return this.serviceActions.get(serviceName) || null;
}
/**
* Checks if service exists
*/
has(serviceName: string): boolean {
return this.services.has(serviceName);
}
/**
* Gets all registered service names
*/
getAllServiceNames(): string[] {
return Array.from(this.services.keys());
}
/**
* Validates service dependencies (checks for cycles)
* Uses DFS for cycle detection in graph
* Time Complexity: O(V + E) where V = services, E = dependencies
*/
validateDependencies(): { valid: boolean; errors: string[] } {
const errors: string[] = [];
const visiting = new Set<string>();
const visited = new Set<string>();
const dfs = (serviceName: string, path: string[]): boolean => {
if (visiting.has(serviceName)) {
errors.push(`Circular dependency detected: ${[...path, serviceName].join(' -> ')}`);
return false;
}
if (visited.has(serviceName)) {
return true;
}
visiting.add(serviceName);
const node = this.services.get(serviceName);
if (node) {
for (const dep of node.dependencies) {
if (!this.services.has(dep)) {
errors.push(`Service ${serviceName} depends on unknown service ${dep}`);
continue;
}
if (!dfs(dep, [...path, serviceName])) {
return false;
}
}
}
visiting.delete(serviceName);
visited.add(serviceName);
return true;
};
for (const serviceName of this.services.keys()) {
if (!visited.has(serviceName)) {
dfs(serviceName, []);
}
}
return {
valid: errors.length === 0,
errors
};
}
/**
* Gets initialization order using topological sort
* DSA: Topological Sort (Kahn's algorithm)
* Time Complexity: O(V + E)
*/
getInitializationOrder(): string[] {
if (this.initializationOrder) {
return [...this.initializationOrder];
}
const result: string[] = [];
const inDegree = new Map<string, number>();
// Calculate in-degrees
for (const [name, node] of this.services) {
inDegree.set(name, node.dependencies.size);
}
// Queue of services with no dependencies
const queue: string[] = [];
for (const [name, degree] of inDegree) {
if (degree === 0) {
queue.push(name);
}
}
// Process services
while (queue.length > 0) {
const current = queue.shift()!;
result.push(current);
const node = this.services.get(current)!;
for (const dependent of node.dependents) {
const newDegree = (inDegree.get(dependent) || 0) - 1;
inDegree.set(dependent, newDegree);
if (newDegree === 0) {
queue.push(dependent);
}
}
}
if (result.length !== this.services.size) {
throw new Error('Circular dependency detected in services');
}
this.initializationOrder = result;
return [...result];
}
/**
* Initializes all services in correct dependency order
*/
async initializeAll(initializer: (metadata: ServiceMetadata) => Promise<any>): Promise<void> {
const order = this.getInitializationOrder();
logger.info('Initializing services', { order });
for (const serviceName of order) {
const node = this.services.get(serviceName)!;
if (node.initialized) {
continue;
}
logger.info('Initializing service', { name: serviceName });
try {
const instance = await initializer(node.metadata);
node.instance = instance;
node.initialized = true;
} catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error';
logger.error('Service initialization failed', error instanceof Error ? error : undefined, {
service: serviceName,
message
});
throw new Error(`Failed to initialize service ${serviceName}: ${message}`);
}
}
logger.info('All services initialized successfully');
}
/**
* Gets service dependency graph as adjacency list
* Useful for visualization
*/
getDependencyGraph(): Map<string, string[]> {
const graph = new Map<string, string[]>();
for (const [name, node] of this.services) {
graph.set(name, Array.from(node.dependencies));
}
return graph;
}
/**
* Finds services that depend on a given service
* Uses BFS traversal
*/
findDependents(serviceName: string): Set<string> {
const dependents = new Set<string>();
const queue: string[] = [serviceName];
const visited = new Set<string>();
while (queue.length > 0) {
const current = queue.shift()!;
if (visited.has(current)) {
continue;
}
visited.add(current);
const node = this.services.get(current);
if (node) {
for (const dependent of node.dependents) {
if (dependent !== serviceName) {
dependents.add(dependent);
}
queue.push(dependent);
}
}
}
return dependents;
}
/**
* Clears all services
*/
clear(): void {
this.services.clear();
this.serviceActions.clear();
this.initializationOrder = null;
logger.info('Service registry cleared');
}
}
/**
* Pre-configured Azure service registry
*/
export function createAzureServiceRegistry(): ServiceRegistry {
const registry = new ServiceRegistry();
// Register core services
const services: ServiceMetadata[] = [
{
name: 'storage',
version: '1.0.0',
description: 'Azure Storage Service',
dependencies: [],
cliPrefix: 'storage',
supportedOperations: ['list', 'create', 'delete', 'show', 'update']
},
{
name: 'cosmos',
version: '1.0.0',
description: 'Azure Cosmos DB Service',
dependencies: [],
cliPrefix: 'cosmosdb',
supportedOperations: ['list', 'create', 'delete', 'query']
},
{
name: 'keyvault',
version: '1.0.0',
description: 'Azure Key Vault Service',
dependencies: [],
cliPrefix: 'keyvault',
supportedOperations: ['list', 'create', 'delete', 'show', 'set-secret', 'get-secret']
},
{
name: 'monitor',
version: '1.0.0',
description: 'Azure Monitor Service',
dependencies: [],
cliPrefix: 'monitor',
supportedOperations: ['metrics', 'logs', 'alerts']
},
{
name: 'appconfig',
version: '1.0.0',
description: 'Azure App Configuration Service',
dependencies: [],
cliPrefix: 'appconfig',
supportedOperations: ['list', 'create', 'delete', 'show']
}
];
for (const service of services) {
registry.register(service);
}
return registry;
}
// Global service registry instance
export const globalServiceRegistry = createAzureServiceRegistry();