# @organify/di

A powerful TypeScript dependency injection library with decorators, auto-registration, and advanced features.

## Features

- 🎯 **Decorator-based DI** - Use decorators for clean, readable dependency injection
- 🔄 **Auto-registration** - Classes are automatically registered when resolved
- 🎭 **Multiple scopes** - Singleton, Transient, Request, and Session scopes
- 🔍 **Advanced resolution** - Self, SkipSelf, Optional, Lazy, and Multiple injection
- ✅ **Validation & Hooks** - Built-in validation and lifecycle hooks
- 🏗️ **Modular design** - Support for modules and conditional registration
- 🔧 **Configuration injection** - Inject config values and environment variables
- 🚀 **Performance optimized** - Efficient resolution with caching
- 🛡️ **Type-safe** - Full TypeScript support with type inference

## Installation

```bash
npm install @organify/di reflect-metadata
```

## Quick Start

```typescript
import 'reflect-metadata';
import { Container, Injectable, Inject } from '@organify/di';

// Define your services
@Injectable()
class Logger {
  log(message: string) {
    console.log(message);
  }
}

@Injectable()
class UserService {
  constructor(@Inject(Logger) private logger: Logger) {}
  
  getUser(id: string) {
    this.logger.log(`Getting user ${id}`);
    return { id, name: 'John Doe' };
  }
}

// Create container and use
const container = new Container();
const userService = await container.get(UserService);
const user = userService.getUser('123');
```

## Core Concepts

### Decorators

#### Class Decorators

```typescript
// Basic injectable
@Injectable()
class MyService {}

// With scope
@Injectable({ scope: Scope.SINGLETON })
class SingletonService {}

// Convenience scope decorators
@Singleton()
class SingletonService {}

@Transient()
class TransientService {}

@RequestScoped()
class RequestService {}

@SessionScoped()
class SessionService {}

// With priority and conditions
@Priority(10)
@Conditional(container => process.env.NODE_ENV === 'production')
class ConditionalService {}

// Auto-registration control
@AutoRegister(false)
class ManualService {}
```

#### Injection Decorators

```typescript
@Injectable()
class MyService {
  constructor(
    @Inject(Logger) private logger: Logger,
    @Optional() @Inject(OptionalService) private optional?: OptionalService,
    @Self() @Inject(SelfService) private self: SelfService,
    @SkipSelf() @Inject(ParentService) private parent: ParentService,
    @Lazy() @Inject(LazyService) private lazy: LazyService,
    @Multiple() @Inject(Plugin) private plugins: Plugin[]
  ) {}

  @AutoInject()
  private autoInjected: AutoService;

  @Config('app.name', 'Default App')
  private appName: string;

  @Env('NODE_ENV', 'development')
  private environment: string;
}
```

### Scopes

```typescript
// Singleton - One instance per container (default)
@Singleton()
class SingletonService {}

// Transient - New instance every time
@Transient()
class TransientService {}

// Request - One instance per request
@RequestScoped()
class RequestService {}

// Session - One instance per session
@SessionScoped()
class SessionService {}
```

### Advanced Features

#### Validation

```typescript
@Injectable()
@Validate(instance => {
  if (!instance.name) return 'Name is required';
  if (instance.age < 0) return 'Age must be positive';
  return true;
})
class User {
  constructor(public name: string, public age: number) {}
}
```

#### Lifecycle Hooks

```typescript
@Injectable()
@Hook('afterCreate', async (instance) => {
  await instance.initialize();
})
@Hook('beforeDestroy', async (instance) => {
  await instance.cleanup();
})
class ServiceWithHooks implements OnInit, OnDestroy {
  async onInit() {
    console.log('Service initialized');
  }

  async onDestroy() {
    console.log('Service destroyed');
  }
}
```

#### Configuration Injection

```typescript
@Injectable()
class AppConfig {
  @Config('app.port', 3000)
  port: number;

  @Config('app.host', 'localhost')
  host: string;

  @Env('NODE_ENV', 'development')
  environment: string;

  @Env('DEBUG', false)
  debug: boolean;
}
```

#### Multiple Providers

```typescript
// Register multiple providers for the same token
container.register([
  { provide: 'PLUGIN', useClass: PluginA },
  { provide: 'PLUGIN', useClass: PluginB },
  { provide: 'PLUGIN', useClass: PluginC }
]);

// Inject all providers
@Injectable()
class PluginManager {
  constructor(@Multiple() @Inject('PLUGIN') private plugins: Plugin[]) {}
}
```

### Container API

```typescript
// Create container
const container = new Container({
  autoRegisterInjectable: true,
  strictMode: false,
  enableCircularDetection: true
});

// Register providers
container.register([
  { provide: Logger, useClass: Logger },
  { provide: 'API_KEY', useValue: 'secret-key' },
  { provide: Database, useFactory: () => new Database() }
]);

// Register with options
container.register(MyService, {
  scope: Scope.SINGLETON,
  priority: 10,
  condition: container => process.env.NODE_ENV === 'production'
});

// Convenience methods
container.registerSingleton('SERVICE', () => new Service());
container.registerTransient('SERVICE', () => new Service());
container.registerRequest('SERVICE', () => new Service());

// Resolve dependencies
const service = await container.get(MyService);
const services = await container.getAll('PLUGIN');

// Check if provider exists
if (container.has(MyService)) {
  // ...
}

// Create child container
const child = container.createChild();

// Get statistics
const stats = container.getStats();

// Validate container
const validation = container.validate();
if (!validation.isValid) {
  console.error('Validation errors:', validation.errors);
}

// Cleanup
await container.dispose();
```

### Modules

```typescript
@Module({
  providers: [UserService, AuthService],
  exports: [UserService]
})
class UserModule {}

@Module({
  imports: [UserModule],
  providers: [AppService]
})
class AppModule {}
```

### Error Handling

```typescript
try {
  const service = await container.get(MyService);
} catch (error) {
  if (error instanceof ProviderNotFoundError) {
    console.error('Provider not found:', error.message);
  } else if (error instanceof CircularDependencyError) {
    console.error('Circular dependency detected:', error.message);
  } else if (error instanceof ResolutionError) {
    console.error('Resolution failed:', error.message);
  }
}
```

## Advanced Examples

### Plugin System

```typescript
interface Plugin {
  name: string;
  execute(): void;
}

@Injectable()
class PluginA implements Plugin {
  name = 'PluginA';
  execute() { console.log('Plugin A executed'); }
}

@Injectable()
class PluginB implements Plugin {
  name = 'PluginB';
  execute() { console.log('Plugin B executed'); }
}

@Injectable()
class PluginManager {
  constructor(@Multiple() @Inject(Plugin) private plugins: Plugin[]) {}

  executeAll() {
    this.plugins.forEach(plugin => plugin.execute());
  }
}
```

### Conditional Services

```typescript
@Injectable()
@Conditional(container => process.env.NODE_ENV === 'production')
class ProductionService {
  log(message: string) {
    // Production logging
  }
}

@Injectable()
@Conditional(container => process.env.NODE_ENV === 'development')
class DevelopmentService {
  log(message: string) {
    // Development logging
  }
}

@Injectable()
class AppService {
  constructor(@Inject(Logger) private logger: Logger) {}
  
  // Logger will be resolved based on environment
}
```

### Request Scoped Services

```typescript
@RequestScoped()
class RequestContext {
  constructor(
    @Inject('REQUEST_ID') private requestId: string,
    @Inject('USER') private user: User
  ) {}
}

@RequestScoped()
class UserService {
  constructor(
    @Inject(RequestContext) private context: RequestContext
  ) {}
  
  getCurrentUser() {
    return this.context.user;
  }
}
```

## Best Practices

1. **Use decorators consistently** - Prefer decorators over manual registration
2. **Keep services focused** - Each service should have a single responsibility
3. **Use appropriate scopes** - Choose the right scope for your use case
4. **Validate dependencies** - Use validation to catch issues early
5. **Handle lifecycle** - Implement OnInit and OnDestroy when needed
6. **Use TypeScript** - Leverage type safety for better development experience

## Performance Considerations

- Singleton scope provides the best performance for frequently used services
- Request scope is good for per-request state but has memory overhead
- Transient scope creates new instances but has no memory overhead
- Use circular dependency detection in development, disable in production

## Contributing

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests
5. Submit a pull request

## License

MIT 