@solid-stack/di
v1.0.4
Published
A lightweight, type-safe dependency injection framework with support for Stage 3 decorators, multi-tokens, value tokens, and async factories.
Readme
@solid-stack-digital/di
A lightweight, high-performance, type-safe Dependency Injection (DI) framework for TypeScript and Node.js. Built with first-class support for ECMAScript Stage 3 decorators, zero-registration auto-wiring, value tokens, multi-tokens (plugins/middleware), and robust async resolution.
✨ Features
- Zero-Registration Resolution: Resolve concrete classes immediately without boilerplate mapping.
- Stage 3 Decorators: Modern
@MakeInjectabledecorator designed for ECMAScript standard decorators. - Full Type Safety: Auto-infers constructor parameters from static
depsmaps. - Lifecycle Scopes: Supports
singleton(default) andtransientlifecycles. - Value Tokens: Type-safe injection of primitives, configurations, and objects with schema validation.
- Multi-Tokens: Collect multiple implementations with priority ordering (ideal for plugins & middleware).
- Async & Sync Resolution: Seamless
resolve()for synchronous graphs andresolveAsync()for graphs containing asynchronous factories. - AsyncLocalStorage Isolation: Circular dependency and deadlock detection that prevents false positives across concurrent async requests.
- Zero Runtime Dependencies: Completely self-contained using Node.js built-ins.
📦 Installation
pnpm add @solid-stack-digital/di
# or
npm install @solid-stack-digital/di
# or
yarn add @solid-stack-digital/di🚀 Quick Start
import { Container, MakeInjectable, ValueToken } from "@solid-stack-digital/di";
// 1. Define a Value Token for configuration
class PortToken extends ValueToken<number> {}
// 2. Define Injectable Services
@MakeInjectable
class Logger {
static deps = {};
constructor(public deps?: any) {}
log(msg: string) {
console.log(`[LOG]: ${msg}`);
}
}
@MakeInjectable
class Server {
static deps = {
logger: Logger,
port: PortToken,
};
constructor(
public deps: {
logger: Logger;
port: number;
},
) {}
start() {
this.deps.logger.log(`Server listening on port ${this.deps.port}`);
}
}
// 3. Create Container, Provide Values, and Resolve
const container = new Container();
container.provideValue(PortToken, 3000);
const server = container.resolve(Server);
server.start();📖 Key Concepts
Abstract Interface Binding
Decouple interface contracts from implementations:
abstract class PaymentGateway {
abstract process(amount: number): Promise<boolean>;
}
@MakeInjectable
class StripeGateway extends PaymentGateway {
static deps = {};
constructor(public deps?: any) {
super();
}
async process(amount: number) {
return true;
}
}
const container = new Container();
container.provide(PaymentGateway, StripeGateway);
const gateway = container.resolve(PaymentGateway);MultiTokens (Plugins / Middleware)
Register multiple providers for a single token, ordered by priority:
import { MultiToken } from "@solid-stack-digital/di";
abstract class Plugin {
abstract name: string;
}
class PluginToken extends MultiToken<Plugin> {}
@MakeInjectable
class AuthPlugin extends Plugin {
static deps = {};
name = "Auth";
constructor(public deps?: any) {
super();
}
}
@MakeInjectable
class LoggingPlugin extends Plugin {
static deps = {};
name = "Logging";
constructor(public deps?: any) {
super();
}
}
const container = new Container();
container.provideMulti(PluginToken, LoggingPlugin, { priority: 10 });
container.provideMulti(PluginToken, AuthPlugin, { priority: 20 });
const plugins = container.resolve(PluginToken);
// plugins sorted by priority descending: [AuthPlugin, LoggingPlugin]Modular Configuration (DIModule)
Organize application modules cleanly:
import type { DIModule } from "@solid-stack-digital/di";
const databaseModule: DIModule = (c) => {
c.provideValue(DbHostToken, "localhost");
c.provide(IDatabase, PostgresDatabase);
};
const container = new Container().load(databaseModule);🛠️ Development & Building
# Install dependencies
pnpm install
# Run test suite
pnpm test
# Build ESM + CJS + Type Declarations
pnpm build📄 License
MIT © Solid Stack Digital
