han-prev-core
Version:
Core framework for Han - A powerful Node.js framework inspired by NestJS
213 lines • 8.63 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.container = void 0;
require("reflect-metadata");
const decorators_1 = require("../decorators");
const utils_1 = require("../utils");
class Container {
constructor() {
this.instances = new Map();
this.singletons = new Map();
this.processedModules = new Set();
this.metadataCache = new Map();
this.dependencyCache = new Map();
this.asyncProviders = new Map();
this.moduleMiddlewareConfigs = new Map();
this.lifecycleHooks = new Map();
}
register(token, factory, singleton = false) {
this.instances.set(token, { factory, singleton });
}
resolve(token) {
const registration = this.instances.get(token);
if (!registration) {
throw new Error(`No registration found for token: ${token}`);
}
if (registration.singleton) {
if (!this.singletons.has(token)) {
this.singletons.set(token, registration.factory());
}
return this.singletons.get(token);
}
return registration.factory();
}
registerProvider(provider) {
const isSingleton = provider.scope !== "transient";
if (provider.useValue) {
this.register(provider.provide, () => provider.useValue, true);
}
else if (provider.useFactory) {
this.register(provider.provide, () => {
const dependencies = provider.inject?.map((dep) => this.resolve(dep)) || [];
const result = provider.useFactory(...dependencies);
if (result instanceof Promise) {
if (!this.asyncProviders.has(provider.provide)) {
this.asyncProviders.set(provider.provide, result);
}
}
return result;
}, isSingleton);
}
else if (provider.useClass) {
this.register(provider.provide, () => {
const dependencies = provider.inject?.map((dep) => this.resolve(dep)) || [];
return new provider.useClass(...dependencies);
}, isSingleton);
}
}
registerController(ControllerClass) {
utils_1.Logger.debug(`Auto-registering controller: ${ControllerClass.name}`);
this.register(ControllerClass.name, () => {
return this.createInstance(ControllerClass);
}, true);
}
createInstance(target) {
let paramTypes = this.metadataCache.get(target);
if (!paramTypes) {
const metadata = Reflect.getMetadata("design:paramtypes", target);
paramTypes = metadata || [];
this.metadataCache.set(target, paramTypes);
}
const safeParamTypes = paramTypes || [];
if (safeParamTypes.length === 0) {
return new target();
}
const injectionTokens = (0, decorators_1.getInjectionTokens)(target);
const cacheKey = target.name;
let dependencies = this.dependencyCache.get(cacheKey);
if (!dependencies) {
dependencies = this.resolveDependencies(safeParamTypes, target.name, injectionTokens);
this.dependencyCache.set(cacheKey, dependencies);
}
const validDependencies = dependencies.filter((dep) => dep !== undefined);
if (validDependencies.length !== safeParamTypes.length) {
utils_1.Logger.debug(`${target.name} created with ${validDependencies.length}/${safeParamTypes.length} dependencies resolved`);
}
return new target(...validDependencies);
}
resolveDependencies(paramTypes, targetName, injectionTokens = {}) {
return paramTypes.map((paramType, index) => {
const customToken = injectionTokens[index];
if (customToken) {
try {
return this.resolve(customToken);
}
catch (error) {
utils_1.Logger.error(`Could not resolve custom injection token '${customToken}' at parameter ${index} in ${targetName}`);
throw error;
}
}
if (!paramType || !paramType.name) {
return undefined;
}
try {
return this.resolve(paramType.name);
}
catch (error) {
utils_1.Logger.debug(`Dependency resolution failed for ${paramType.name} at parameter ${index} in ${targetName}. Trying alternative resolution...`);
for (const [token] of this.instances.entries()) {
if (token.includes(paramType.name) ||
paramType.name.includes(token)) {
try {
return this.resolve(token);
}
catch {
continue;
}
}
}
utils_1.Logger.warn(`Could not resolve dependency ${paramType.name} for ${targetName}`);
return undefined;
}
});
}
registerModule(moduleClass) {
const isDynamicModule = moduleClass && moduleClass.module;
const actualModule = isDynamicModule ? moduleClass.module : moduleClass;
if (this.processedModules.has(actualModule)) {
return;
}
this.processedModules.add(actualModule);
let moduleMetadata;
if (isDynamicModule) {
moduleMetadata = moduleClass;
}
else {
moduleMetadata = decorators_1.MetadataStorage.get(actualModule.prototype, decorators_1.METADATA_KEYS.MODULE);
}
if (!moduleMetadata) {
throw new Error(`Module ${actualModule.name} is missing @Module decorator or DynamicModule metadata`);
}
if (moduleMetadata.imports) {
moduleMetadata.imports.forEach((importedModule) => {
this.registerModule(importedModule);
});
}
if (moduleMetadata.providers) {
moduleMetadata.providers.forEach((provider) => {
if (typeof provider === "function") {
this.register(provider.name, () => {
return this.createInstance(provider);
}, true);
}
else {
this.registerProvider(provider);
}
});
}
if (moduleMetadata.controllers) {
moduleMetadata.controllers.forEach((controller) => {
this.registerController(controller);
});
}
}
getModuleMetadata(moduleClass) {
return decorators_1.MetadataStorage.get(moduleClass.prototype, decorators_1.METADATA_KEYS.MODULE);
}
async callOnModuleInit() {
const initPromises = [];
for (const instance of this.singletons.values()) {
if (instance && typeof instance.onModuleInit === "function") {
const result = instance.onModuleInit();
if (result instanceof Promise) {
initPromises.push(result);
}
}
}
await Promise.all(initPromises);
}
async callOnModuleDestroy() {
const destroyPromises = [];
for (const instance of this.singletons.values()) {
if (instance && typeof instance.onModuleDestroy === "function") {
const result = instance.onModuleDestroy();
if (result instanceof Promise) {
destroyPromises.push(result);
}
}
}
await Promise.all(destroyPromises);
}
async resolveAsyncProviders() {
if (this.asyncProviders.size === 0)
return;
const entries = Array.from(this.asyncProviders.entries());
const results = await Promise.all(entries.map(([_, promise]) => promise));
entries.forEach(([token, _], index) => {
this.singletons.set(token, results[index]);
});
}
configureModuleMiddleware(moduleClass, consumer) {
this.moduleMiddlewareConfigs.set(moduleClass, consumer);
}
getModuleMiddlewareConfigs(moduleClass) {
return this.moduleMiddlewareConfigs.get(moduleClass);
}
getAllMiddlewareConfigs() {
return this.moduleMiddlewareConfigs;
}
}
const container = new Container();
exports.container = container;
container.register("Logger", () => utils_1.Logger, true);
//# sourceMappingURL=container.js.map