roboot
v3.0.0
Published
A dependency injection library with support for circular dependencies and async initialization
Downloads
23
Maintainers
Readme
Roboot is a minimal dependency container and bootstrapping library for TypeScript and JavaScript. It provides just enough structure to write clean testable code without sacrificing simplicity and flexibility.
Features
- Helps avoid module side-effects and global state
- Supports circular dependencies
- Manages instance lifecycle with support for asynchronous
boot
anddispose
- Zero dependencies (excluding dev dependencies)
- Well tested
Install
npm install roboot
Lifecycle
Basic Usage
import { Container, Service } from "roboot";
class Logger extends Service {
async boot() {
await new Promise((resolve) => setTimeout(resolve, 1000));
this.info("Logger booted");
}
async dispose() {
await this.disposed(Counter);
this.info("Logger disposed");
}
info(message) {
console.log(`INFO: ${message}`);
}
}
class Counter extends Service {
private logger = this.use(Logger);
private count = 0;
private intervalHandle?: number;
async boot() {
await this.booted(Logger);
this.logger.info("Counter booted");
}
async dispose() {
clearInterval(this.intervalHandle);
await new Promise((resolve) => setTimeout(resolve, 1000));
this.logger.info("Counter disposed");
}
start() {
this.logger.info("Starting counter");
this.intervalHandle = setInterval(() => {
this.count += 1;
this.logger.info(this.count);
}, 1000);
}
}
function sigintHandler(container: Container) {
process.on("SIGINT", async () => {
await container.dispose();
process.exit(130);
});
}
new Container()
.apply(sigintHandler)
.boot(Counter)
.then((counter) => counter.start())
.catch((err) => console.error(err));
API
Container
A Container
handles resolving instances of dependencies (including circular
dependencies) and executing lifecycle hooks. It also supports binding
alternative implementations for a Provider
.
container.boot<T>(Root: ProviderClass<T>) => Promise<T>
Create an instance of a root provider resolving and booting all dependencies before the returned promise is resolved with the instance.
container.bind<T>(Provider: ProviderClass<T>, Implementation: ProviderClass<T>) => Container
Provide an alternate implementation for a given provider/service in the context
of this container. This must be called before boot()
.
container.apply((container: Container) => void)
Call a callback function with the container instance and return the container instance. This can be useful for referencing the container when chaining.
Provider<T> | Service
Provider
and Service
are both abstract base classes intended to be extended.
They enable resolving dependencies from the container and defining lifecycle
hooks. A Provider<T>
can resolve to any value by implementing the
provide() => T
method. Service
is a special case provider that always
provides itself.
provider.use<T>(Dependency: ProviderClass<T>) => T
@protected
Create or re-use an existing instance of the dependency from the container. This should be called synchronously when instantiating the class, typically via an instance field initializer.
provider.booted(Dependency: ProviderClass) => Promise<void>
@protected
The booted
method used to support initialization dependency order. The
returned promise will resolve once the boot
method of the dependency
has finished. This should only be called within the boot
method of a
Provider
or Service
child class.
provider.disposed(Dependency: ProviderClass) => Promise<void>
@protected
The disposed
method used to support clean up dependency order. The
returned promise will resolve once the dispose
method of the dependency
has finished. This should only be called within the dispose
method of a
Provider
or Service
child class.
provider.boot(instance: T) => Promise<void>
This may be optionally implemented by a child class to execute initialization
code that should run when booting the container. There is no need to call
super.boot()
.
provider.dispose(instance: T) => Promise<void>
This may be optionally implemented by a child class to execute clean up
code that should run when disposing of the container. There is no need to call
super.boot()
.
provider.provide() => T
For child classes of Provider<T>
only. Service
implements its own provide
.
The provide
method must be implemented by each child class of Provider<T>
.
It returns the value that will be returned when use
is called with this class.
registry?: ProviderClass<Registry<T>>
The provide
property allows optionally specifying a Registry
class that will
hold a reference to the resolved instance.
Registry<T>
Registry is an abstract service used for tracking a collection of Provider
s or
Services
that resolve to a compatible type. The registry will implement a
boot()
and/or dispose()
that can call the following protected methods to
interact with the registered instances.
this.forEach(callback: (T) => void) => void
Call a callback function with each registered instance.
this.map<U>(callback: (T) => U) => U[]
Create an array with the return values of calling a callback with each registered instance.
this.allBooted() => Promise<void>
Return a promise that resolves when the boot method of all registered providers has resolved.
this.allDisposed() => Promise<void>
Return a promise that resolves when the dispose method of all registered providers has resolved.
License
Roboot is licensed under the MIT license. See LICENSE.md.