Skip to content

Repository files navigation

@noneforge/ioc

Powerful, type-safe dependency injection container for TypeScript

npm versionLicense: MITTypeScript


Getting Started · Core Concepts · API Reference


A modern, feature-rich Dependency Injection container for TypeScript applications. Built with developer experience in mind, featuring decorators, modules, interceptors, and comprehensive testing utilities.

Features

  • Full DI Support - Constructor injection, property injection, and factory providers
  • Type-Safe - Leverages TypeScript for compile-time safety with InjectionToken<T>
  • 5 Provider Types - Class, Value, Factory, Existing (alias), and Async providers
  • 5 Scopes - Singleton, Transient, Request, Prototype, and Scoped
  • Decorator-Based - @Injectable, @Inject, @Optional, @Lazy
  • Module System - Organize code with @Module, dynamic modules, and configurable modules
  • Interceptors - Built-in caching, logging, retry, and validation interceptors
  • Testing Utilities - TestContainer with mock, spy, and snapshot support
  • AOP Decorators - @Cached, @Log, @Transactional for cross-cutting concerns
  • Lifecycle Hooks - onInit, onDestroy for resource management
  • Hierarchical Containers - Parent/child container relationships
  • Cycle Detection - Automatic circular dependency detection with Tarjan's algorithm
  • Zero Dependencies - Only requires reflect-metadata as peer dependency

Installation

npm install @noneforge/ioc reflect-metadata

Required peer dependencies:

  • reflect-metadata >= 0.2.0
  • typescript >= 5.0

TypeScript Configuration

Add to your tsconfig.json:

{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}

Import reflect-metadata once at your application entry point:

import'reflect-metadata';

Quick Start

Basic Usage

import'reflect-metadata';import{Container,Injectable,inject,InjectionToken}from'@noneforge/ioc';// Define a service
@Injectable()classLoggerService{log(message: string){console.log(`[LOG] ${message}`);}}// Define another service with dependency using inject()
@Injectable()classUserService{privatelogger=inject(LoggerService);createUser(name: string){this.logger.log(`Creating user: ${name}`);return{id: 1, name };}}// Create container and resolveconstcontainer=newContainer();container.addProvider(LoggerService);container.addProvider(UserService);constuserService=container.get(UserService);userService.createUser('John');// [LOG] Creating user: John

Using Injection Tokens

import{InjectionToken,Container}from'@noneforge/ioc';// Create type-safe tokensconstAPI_URL=newInjectionToken<string>('API_URL');constMAX_RETRIES=newInjectionToken<number>('MAX_RETRIES');constcontainer=newContainer();// Register valuescontainer.addProvider({provide: API_URL,useValue: 'https://api.example.com'});container.addProvider({provide: MAX_RETRIES,useValue: 3});// Resolve with type safetyconstapiUrl=container.get(API_URL);// stringconstmaxRetries=container.get(MAX_RETRIES);// number

Using Modules

import{Module,Injectable,inject,InjectionToken,bootstrap}from'@noneforge/ioc';constCONFIG=newInjectionToken<{apiUrl: string}>('CONFIG');
@Injectable()classApiService{privateconfig=inject(CONFIG);getUrl(){returnthis.config.apiUrl;}}
@Module({providers: [{provide: CONFIG,useValue: {apiUrl: 'https://api.example.com'}},ApiService,],exports: [ApiService],})classApiModule{}
@Module({imports: [ApiModule],})classAppModule{}// Bootstrap the applicationconst{ app, container }=awaitbootstrap(AppModule);constapiService=container.get(ApiService);

Testing with Mocks

import{createTestContainer,createMockProvider}from'@noneforge/ioc';// Create test container with mocksconstcontainer=createTestContainer(createMockProvider(LoggerService,{log: vi.fn(),}),UserService,);constuserService=container.get(UserService);userService.createUser('Test');// Verify mock was calledexpect(container.get(LoggerService).log).toHaveBeenCalledWith('Creating user: Test');

Core Concepts

Providers

// Class Provider - instantiate a class{provide: MyService,useClass: MyService}// Value Provider - use a constant value{provide: API_KEY,useValue: 'secret-key'}// Factory Provider - create with dependencies{provide: DatabaseConnection,useFactory: (config: Config)=>newDatabase(config.dbUrl),inject: [Config]}// Existing Provider - alias to another token{provide: ILogger,useExisting: ConsoleLogger}// Async Provider - async initialization{provide: RemoteConfig,useAsync: ()=>fetch('/config').then(r=>r.json())}

Scopes

@Injectable({scope: 'singleton'})// Default - one instance per containerclassSingletonService{}
@Injectable({scope: 'transient'})// New instance every timeclassTransientService{}
@Injectable({scope: 'request'})// One instance per request IDclassRequestScopedService{}

inject() Functions (Preferred)

inject(Token)// Resolve dependencyinjectOptional(Token)// Allow null if not foundinjectLazy(Token)// Defer resolution until first accessinjectAll(Token)// Get all multi-providers

Decorators (Alternative)

@Injectable()// Mark class as injectable
@Inject(Token)// Specify injection token (alternative to inject())
@Optional()// Allow null if not found (alternative to injectOptional())
@Lazy()// Defer resolution (alternative to injectLazy())

Documentation

Comparison with Alternatives

Feature@noneforge/iocInversifyJSTSyringeTypeDI
Decorator-based DIYesYesYesYes
Injection TokensYesYesYesYes
Module SystemYesYesNoNo
Built-in InterceptorsYesYesYesNo
Testing UtilitiesYesNoNoNo
AOP DecoratorsYesNoNoNo
Cycle DetectionYesYesYesNo
Dependency GraphYesNoNoNo
Request ScopeYesYesYesYes
Async ProvidersYesYesYesYes
Conditional ProvidersYesYesNoNo
EnhancedCache (LRU/LFU)YesNoNoNo

Examples

See the examples directory for runnable code samples:

Requirements

  • Node.js >= 18.18.0
  • TypeScript >= 5.0
  • reflect-metadata >= 0.2.0

Contributing

Contributions are welcome! Please read the contributing guidelines before submitting a pull request.

License

MIT License - see LICENSE for details.

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages