UNPKG

@wizecorp/stratusjs

Version:
175 lines 5.84 kB
/** * Dependency injection container for services */ export class ServiceContainer { constructor() { Object.defineProperty(this, "services", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "factories", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "singletons", { enumerable: true, configurable: true, writable: true, value: new Set() }); Object.defineProperty(this, "initializing", { enumerable: true, configurable: true, writable: true, value: new Set() }); } /** * Register a service or factory in the container */ register(token, serviceOrFactory, options = {}) { const { singleton = true } = options; if (typeof serviceOrFactory === 'function' && !this.isServiceInstance(serviceOrFactory)) { // It's a factory function this.factories.set(token, serviceOrFactory); } else { // It's a service instance this.services.set(token, serviceOrFactory); } if (singleton) { this.singletons.add(token); } } /** * Get a service synchronously */ get(token) { // Check if already instantiated if (this.services.has(token)) { return this.services.get(token); } // Check if we have a factory if (this.factories.has(token)) { const factory = this.factories.get(token); const service = factory(); if (service instanceof Promise) { throw new Error(`Service "${String(token)}" is async and requires getAsync(). Use await container.getAsync("${String(token)}") instead.`); } // Cache if singleton if (this.singletons.has(token)) { this.services.set(token, service); } return service; } // Try to instantiate if it's a constructor if (typeof token === 'function') { const service = new token(); if (this.singletons.has(token)) { this.services.set(token, service); } return service; } throw new Error(`Service "${String(token)}" not found. Make sure it's registered in the container.`); } /** * Get a service asynchronously (supports async factories) */ async getAsync(token) { // Prevent circular dependencies if (this.initializing.has(token)) { throw new Error(`Circular dependency detected for service "${String(token)}"`); } // Check if already instantiated if (this.services.has(token)) { return this.services.get(token); } this.initializing.add(token); try { // Check if we have a factory if (this.factories.has(token)) { const factory = this.factories.get(token); const service = await factory(); // Cache if singleton if (this.singletons.has(token)) { this.services.set(token, service); } // Initialize service if it has an initialize method if (service && typeof service.initialize === 'function') { await service.initialize(); } return service; } // Try to instantiate if it's a constructor if (typeof token === 'function') { const service = new token(); if (this.singletons.has(token)) { this.services.set(token, service); } // Initialize service if it has an initialize method if (service && typeof service.initialize === 'function') { await service.initialize(); } return service; } throw new Error(`Service "${String(token)}" not found. Make sure it's registered in the container.`); } finally { this.initializing.delete(token); } } /** * Check if a service is registered */ has(token) { return this.services.has(token) || this.factories.has(token); } /** * Remove a service from the container */ remove(token) { const service = this.services.get(token); // Call destroy if the service has it if (service && typeof service.destroy === 'function') { service.destroy(); } this.services.delete(token); this.factories.delete(token); this.singletons.delete(token); } /** * Clear all services */ clear() { // Destroy all services that have a destroy method for (const service of this.services.values()) { if (service && typeof service.destroy === 'function') { service.destroy(); } } this.services.clear(); this.factories.clear(); this.singletons.clear(); this.initializing.clear(); } /** * Get all registered service tokens */ getTokens() { return [ ...Array.from(this.services.keys()), ...Array.from(this.factories.keys()) ]; } /** * Check if value is a service instance (has name property) */ isServiceInstance(value) { return value && typeof value === 'object' && 'name' in value; } } //# sourceMappingURL=ServiceContainer.js.map