npm.io
0.7.3 • Published yesterday

@resolid/di

Licence
MIT
Version
0.7.3
Deps
0
Size
10 kB
Vulns
0
Weekly
0
Stars
1

TypeScript Dependency Injection Container

GitHub License NPM Version

Documentation | Framework Bundle

A lightweight, fully-typed Dependency Injection (DI) container for TypeScript. Supports singleton & transient scopes, lazy resolution, optional dependencies, and disposable resources. Fully functional with async factories and avoids circular dependency issues.

Features

  • Fully typed with TypeScript, no any.
  • Supports singleton and transient scopes.
  • Lazy resolution for dependencies.
  • Optional dependency resolution.
  • Detects circular dependencies.
  • Handles disposable instances with dispose() support.
  • Context-aware injection with InjectionContext.

Installation

pnpm add @resolid/di
# or
npm install @resolid/di
# or
yarn add @resolid/di
# or
bun add @resolid/di

Usage

Basic
import { Container, inject } from "@resolid/di";

class LogService {
  log(message: string): void {
    console.log(`[LOG]: ${message}`);
  }
}

class UserService {
  private logger: LogService;

  constructor(logger: LogService) {
    this.logger = logger;
  }

  createUser(name: string): void {
    this.logger.log(`Creating user: ${name}`);
  }
}

const container = new Container();

container.add({
  token: LogService,
  factory: () => new LogService(),
});

container.add({
  token: UserService,
  factory: () => new UserService(inject(LogService)),
});

const userService = container.get(USER_SERVICE);

userService.createUser("John Doe"); // Output: [LOG]: Creating user: John Doe
Scopes

The container supports two scopes:

  • singleton (default): Only one instance is created and shared for all resolutions.
  • transient: A new instance is created on each resolution.
container.add({
  token: LogService,
  factory: () => new LogService(),
  scope: "singleton", // Default scope
});

container.add({
  token: Symbol("REQUEST_ID"),
  factory: () => Math.random(),
  scope: "transient", // New instance each time
});
Optional Dependencies
import { Container, inject } from "@resolid/di";

class AnalyticsService {
  private logger?: LogService;

  constructor(logger?: LogService) {
    this.logger = logger;
  }

  track(event: string): void {
    if (this.logger) {
      this.logger.log(`Analytics: ${event}`);
    } else {
      console.log(`Analytics (no logger): ${event}`);
    }
  }
}

const container = new Container();

// Logger is optional
container.add({
  token: AnalyticsService,
  factory: () => new AnalyticsService(inject(LOG_SERVICE, { optional: true })),
});

const analytics = container.get(AnalyticsService);
analytics.track("page_view"); // Output: Analytics (no logger): page_view
Lazy Resolution
import { Container, inject } from "@resolid/di";

class ReportService {
  private getLogger: () => LogService;

  constructor(private getLogger: () => LogService) {
    this.getLogger = getLogger;
  }

  generateReport(): void {
    const logger = this.getLogger();
    logger.log("Report generated");
  }
}

const container = new Container();

container.add({
  token: LogService,
  factory: () => new LogService(),
});

container.add({
  token: ReportService,
  factory: () => new ReportService(inject(LogService, { lazy: true })),
});

const reportService = container.get(ReportService);
reportService.generateReport(); // Output: [LOG]: Report generated
Circular Dependency Detection
class ApiService {
  constructor(private auth: AuthService) {}
}

class AuthService {
  constructor(private api: ApiService) {}
}

const container = new Container();

container.add({
  token: ApiService,
  factory: () => new ApiService(inject(AuthService)),
});

container.add({
  token: AuthService,
  factory: () => new AuthService(inject(ApiService)),
});

container.get(ApiService); // Throws: Circular dependency detected ApiService -> AuthService -> ApiService
Disposable Resources
import { Container } from "@resolid/di";

class DatabaseConnection {
  async dispose(): Promise<void> {
    console.log("Database connection closed");
  }
}

const container = new Container();

container.add({
  token: DatabaseConnection,
  factory: () => new DatabaseConnection(),
});

const db = container.get(DatabaseConnection);

// Dispose all singleton instances
await container.dispose(); // Output: Database connection closed

License

MIT License (MIT). Please see LICENSE for more information.