Initial Commit
This commit is contained in:
115
src/di/DependencyManager.ts
Normal file
115
src/di/DependencyManager.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
type Registration = {
|
||||
Class: new (...args: any[]) => any;
|
||||
dependencies: string[];
|
||||
lifecycle: Lifecycle;
|
||||
};
|
||||
|
||||
/**
|
||||
* Lifecycle types:
|
||||
* - 'singleton': A single shared instance cached for the lifetime of the manager
|
||||
* - 'instance': A new instance created on each resolve (transient)
|
||||
* - 'service': A new instance created on each resolve (transient, alias for 'instance')
|
||||
*/
|
||||
type Lifecycle = 'instance' | 'service' | 'singleton';
|
||||
|
||||
export class DependencyManager {
|
||||
private static readonly _supportedLifecycles: string[] = ['instance', 'service', 'singleton'];
|
||||
private readonly _resolving: Set<string>;
|
||||
private readonly _registrations: Map<string, Registration>;
|
||||
private readonly _singletons: Map<string, unknown>;
|
||||
|
||||
constructor() {
|
||||
this._resolving = new Set();
|
||||
this._registrations = new Map();
|
||||
this._singletons = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a class with the dependency manager.
|
||||
* @param name - Unique identifier for the registration
|
||||
* @param Class - The class constructor to instantiate
|
||||
* @param dependencies - Array of dependency names to inject
|
||||
* @param lifecycle - Lifecycle of the instance ('singleton', 'instance', or 'service'). Default: 'singleton'
|
||||
* @throws Error if name is already registered or lifecycle is invalid
|
||||
*/
|
||||
public register(name: string, Class: new (...args: any[]) => any, dependencies: string[], lifecycle = 'singleton') {
|
||||
if (this._registrations.has(name)) {
|
||||
throw new Error(`Error: [${name}] is already registered`);
|
||||
}
|
||||
|
||||
if (!DependencyManager._supportedLifecycles.includes(lifecycle)) {
|
||||
throw new Error(`Invalid lifecycle [${lifecycle}]. Supported lifecycles [${DependencyManager._supportedLifecycles.join(',')}]`);
|
||||
}
|
||||
|
||||
this._registrations.set(name, {
|
||||
Class,
|
||||
dependencies,
|
||||
lifecycle: lifecycle as Lifecycle
|
||||
});
|
||||
|
||||
console.debug(`Registered [${name}] with dependencies [${dependencies.join(',')}]`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves and returns an instance of the registered dependency.
|
||||
* @param name - The name of the dependency to resolve
|
||||
* @returns The resolved instance
|
||||
* @throws Error if dependency is not registered or circular dependency is detected
|
||||
*/
|
||||
public resolve<T = any>(name: string): T {
|
||||
if (!this._registrations.has(name)) {
|
||||
throw new Error(`Error: Unable to resolve unregistered [${name}]`);
|
||||
}
|
||||
|
||||
const registration = this._registrations.get(name);
|
||||
const {Class, dependencies, lifecycle} = registration!;
|
||||
|
||||
if (lifecycle === 'singleton' && this._singletons.has(name)) {
|
||||
console.debug(`↻ Using cached singleton [${name}]`);
|
||||
|
||||
return this._singletons.get(name) as T;
|
||||
}
|
||||
|
||||
// Check for circular dependencies
|
||||
if (this._resolving.has(name)) {
|
||||
throw new Error(`Circular dependency detected: [${name}]`);
|
||||
}
|
||||
|
||||
console.debug(`→ Resolving dependencies for [${name}] with lifecycle [${lifecycle}]`);
|
||||
this._resolving.add(name);
|
||||
|
||||
const resolvedDependencies: unknown[] = dependencies.map((dependency: string) => {
|
||||
console.debug(` ↳ Needs [${dependency}]`);
|
||||
return this.resolve(dependency);
|
||||
});
|
||||
|
||||
const instance: object = new Class(...resolvedDependencies);
|
||||
|
||||
this._resolving.delete(name);
|
||||
|
||||
if (lifecycle === 'singleton') {
|
||||
this._singletons.set(name, instance);
|
||||
|
||||
console.debug(` ⚡ Cached as singleton: ${name}`);
|
||||
}
|
||||
|
||||
return instance as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a dependency is registered.
|
||||
* @param name - The name of the dependency to check
|
||||
* @returns True if the dependency is registered, false otherwise
|
||||
*/
|
||||
public has(name: string): boolean {
|
||||
return this._registrations.has(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all cached singleton instances.
|
||||
* Useful for testing or resetting the dependency manager state.
|
||||
*/
|
||||
public clearSingletons(): void {
|
||||
this._singletons.clear();
|
||||
}
|
||||
}
|
||||
0
src/index.ts
Normal file
0
src/index.ts
Normal file
Reference in New Issue
Block a user