dipend
Version:
This library implements a dependency injection (DI) system in JavaScript/TypeScript, making it easier to manage dependencies in modular applications.
264 lines (262 loc) • 11.3 kB
JavaScript
/*
* Copyright 2025 Saulo V. Alvarenga. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DependencyContainer = void 0;
const add_dependency_1 = require("./commands/add-dependency");
const resolve_dependency_1 = require("./commands/resolve-dependency");
const check_for_cyclic_dependencies_1 = require("./commands/check-for-cyclic-dependencies");
const resolve_singletons_1 = require("./commands/resolve-singletons");
const dependency_1 = require("./dependency");
const dependency_2 = require("./dependency");
const token_1 = require("./token");
const enums_1 = require("./enums");
const exceptions_1 = require("./exceptions");
const exceptions_2 = require("./exceptions");
/**
* Class representing a Dependency Container for managing dependencies.
*/
class DependencyContainer {
dependencyTokenStore = new token_1.TokenStore();
dependencyStore = new dependency_2.DependencyStore();
dependencyResolver = new dependency_1.DependencyResolver(this.dependencyStore);
dependencyTokenType = new token_1.TokenTypeResolver();
dependencyTokenName = new token_1.TokenNameResolver();
exceptionHandler = new exceptions_1.ExceptionHandler(this.dependencyTokenStore, this.dependencyTokenType, this.dependencyTokenName);
addDependencyCommandHandler = new add_dependency_1.AddDependencyCommandHandler(this.dependencyTokenStore, this.dependencyStore);
checkForCyclicDependenciesCommandHandler = new check_for_cyclic_dependencies_1.CheckForCyclicDependenciesCommandHandler(this.dependencyStore);
resolveDependencyCommandHandler = new resolve_dependency_1.ResolveDependencyCommandHandler(this.dependencyTokenStore, this.dependencyResolver);
resolveSingletonsCommandHandler = new resolve_singletons_1.ResolveSingletonsCommandHandler(this.dependencyStore, this.dependencyResolver);
isContainerBuilt = false;
isBuildSingletonsRequired = false;
dependencyContainerToken = DependencyContainer;
/**
* Constructor to initialize the DependencyContainer with optional configurations.
* @param {Object} [config] - Configuration options for the container.
*/
constructor(config) {
this.loadConfigs(config);
}
loadConfigs(config) {
if (!config?.disableDefaultResolveLifecycleStrategies) {
this.dependencyResolver.setDefaultResolveLifecycleStrategies();
}
if (!config?.disableDefaultTokenTypeCheckers) {
this.dependencyTokenType.setDefaultTokenTypeCheckers();
}
if (!config?.disableDefaultTokenNameStrategies) {
this.dependencyTokenName.setDefaultTokenNameStrategies();
}
if (config?.buildSingletonsRequired !== undefined) {
this.isBuildSingletonsRequired = config?.buildSingletonsRequired;
}
if (config?.customDependencyContainerToken !== undefined) {
this.dependencyContainerToken = config.customDependencyContainerToken;
}
this.addDependency({
lifecycle: enums_1.LifecycleEnum.SINGLETON,
dependencyToken: this.dependencyContainerToken,
instance: this,
});
}
exceptionHandlerWrapper(callback) {
try {
return callback();
}
catch (err) {
if (err instanceof exceptions_2.BaseDependencyContainerException) {
throw this.exceptionHandler.handle(err);
}
throw err;
}
}
/**
* Builds all singleton dependencies.
* @returns {DependencyContainer} The current instance of the container.
*/
buildSingletons() {
this.exceptionHandlerWrapper(() => this.resolveSingletonsCommandHandler.handle());
this.isContainerBuilt = true;
return this;
}
checkForCyclicDependencies() {
this.exceptionHandlerWrapper(() => this.checkForCyclicDependenciesCommandHandler.handle());
return this;
}
addDependency(config) {
const dependencyToken = config.dependencyToken || config.classConstructor;
if (dependencyToken === undefined) {
throw new Error("Invalid configuration, missing dependency token.");
}
const addDependencyCommandInput = new add_dependency_1.AddDependencyCommandInput(dependencyToken, config.qualifierToken, config.lifecycle, config.classConstructor, config.builder, config.instance);
this.exceptionHandlerWrapper(() => this.addDependencyCommandHandler.handle(addDependencyCommandInput));
}
retrieveDependency(config) {
if (config?.dependencyToken === undefined) {
throw new Error("Missing dependency token.");
}
if (this.isContainerBuilt === false && this.isBuildSingletonsRequired === true) {
throw new Error("Dependency container not initialized. Please call the 'build()' method before attempting to retrieve dependencies.");
}
const resolveDependencyCommandInput = new resolve_dependency_1.ResolveDependencyCommandInput(config.dependencyToken, config.qualifierToken);
const output = this.exceptionHandlerWrapper(() => this.resolveDependencyCommandHandler.handle(resolveDependencyCommandInput));
return output.dependencyInstance;
}
addSingletonBuilder(config) {
if (config.builder === undefined) {
throw new Error("Invalid configuration, missing builder function.");
}
this.addDependency({
lifecycle: enums_1.LifecycleEnum.SINGLETON,
dependencyToken: config.dependencyToken,
builder: config.builder,
});
}
addMappedSingletonBuilder(config) {
if (config.qualifierToken === undefined) {
throw new Error("Invalid configuration, missing qualifier token.");
}
if (config.builder === undefined) {
throw new Error("Invalid configuration, missing builder function.");
}
this.addDependency({
lifecycle: enums_1.LifecycleEnum.SINGLETON,
dependencyToken: config.dependencyToken,
qualifierToken: config.qualifierToken,
builder: config.builder,
});
}
addSingletonInstance(config) {
if (config.instance === undefined) {
throw new Error("Invalid configuration, missing instance.");
}
this.addDependency({
lifecycle: enums_1.LifecycleEnum.SINGLETON,
dependencyToken: config.dependencyToken,
instance: config.instance,
});
}
addMappedSingletonInstance(config) {
if (config.qualifierToken === undefined) {
throw new Error("Invalid configuration, missing qualifier token.");
}
if (config.instance === undefined) {
throw new Error("Invalid configuration, missing instance.");
}
this.addDependency({
lifecycle: enums_1.LifecycleEnum.SINGLETON,
dependencyToken: config.dependencyToken,
qualifierToken: config.qualifierToken,
instance: config.instance,
});
}
addSingleton(config) {
if (config?.classConstructor === undefined) {
throw new Error("Invalid configuration, missing class constructor.");
}
this.addDependency({
lifecycle: enums_1.LifecycleEnum.SINGLETON,
dependencyToken: config.dependencyToken,
classConstructor: config.classConstructor,
});
}
addMappedSingleton(config) {
if (config.qualifierToken === undefined) {
throw new Error("Invalid configuration, missing qualifier token.");
}
if (config.classConstructor === undefined) {
throw new Error("Invalid configuration, missing class constructor.");
}
this.addDependency({
lifecycle: enums_1.LifecycleEnum.SINGLETON,
dependencyToken: config.dependencyToken,
qualifierToken: config.qualifierToken,
classConstructor: config.classConstructor,
});
}
addTransientBuilder(config) {
if (config.builder === undefined) {
throw new Error("Invalid configuration, missing builder function.");
}
this.addDependency({
lifecycle: enums_1.LifecycleEnum.TRANSIENT,
dependencyToken: config.dependencyToken,
builder: config.builder,
});
}
addMappedTransientBuilder(config) {
if (config.qualifierToken === undefined) {
throw new Error("Invalid configuration, missing qualifier token.");
}
if (config.builder === undefined) {
throw new Error("Invalid configuration, missing builder function.");
}
this.addDependency({
lifecycle: enums_1.LifecycleEnum.TRANSIENT,
dependencyToken: config.dependencyToken,
qualifierToken: config.qualifierToken,
builder: config.builder,
});
}
addTransient(config) {
if (config?.classConstructor === undefined) {
throw new Error("Invalid configuration, missing class constructor.");
}
this.addDependency({
lifecycle: enums_1.LifecycleEnum.TRANSIENT,
dependencyToken: config.dependencyToken,
classConstructor: config.classConstructor,
});
}
addMappedTransient(config) {
if (config.qualifierToken === undefined) {
throw new Error("Invalid configuration, missing qualifier token.");
}
if (config.classConstructor === undefined) {
throw new Error("Invalid configuration, missing class constructor.");
}
this.addDependency({
lifecycle: enums_1.LifecycleEnum.TRANSIENT,
dependencyToken: config.dependencyToken,
qualifierToken: config.qualifierToken,
classConstructor: config.classConstructor,
});
}
getDependency(config) {
return this.retrieveDependency({
dependencyToken: config?.dependencyToken,
});
}
getMappedDependency(config) {
if (config.dependencyToken === undefined || config.qualifierToken === undefined) {
throw new Error("Missing dependency or qualifier token.");
}
return this.retrieveDependency({
dependencyToken: config.dependencyToken,
qualifierToken: config.qualifierToken,
});
}
/**
* Resets the dependency container to its initial state.
* Remove all dependencies.
*/
reset() {
this.dependencyTokenStore.reset();
this.dependencyStore.reset();
this.isContainerBuilt = false;
}
}
exports.DependencyContainer = DependencyContainer;