{"owner":"rolling-scopes","repo":"rsschool-app","hasSkills":true,"totalSkillsCount":49,"totalTokensCount":57599,"categories":["anthropic-skill","copilot-instructions","root-instruction","plugin-manifest"],"hasMcp":false,"mcpConfig":null,"found":[".agents/skills/nestjs-best-practices/AGENTS.md",".agents/skills/nestjs-best-practices/README.md",".agents/skills/nestjs-best-practices/SKILL.md",".agents/skills/nestjs-best-practices/rules/api-use-dto-serialization.md",".agents/skills/nestjs-best-practices/rules/api-use-interceptors.md",".agents/skills/nestjs-best-practices/rules/api-use-pipes.md",".agents/skills/nestjs-best-practices/rules/api-versioning.md",".agents/skills/nestjs-best-practices/rules/arch-avoid-circular-deps.md",".agents/skills/nestjs-best-practices/rules/arch-feature-modules.md",".agents/skills/nestjs-best-practices/rules/arch-module-sharing.md",".agents/skills/nestjs-best-practices/rules/arch-single-responsibility.md",".agents/skills/nestjs-best-practices/rules/arch-use-events.md",".agents/skills/nestjs-best-practices/rules/arch-use-repository-pattern.md",".agents/skills/nestjs-best-practices/rules/db-avoid-n-plus-one.md",".agents/skills/nestjs-best-practices/rules/db-use-migrations.md",".agents/skills/nestjs-best-practices/rules/db-use-transactions.md",".agents/skills/nestjs-best-practices/rules/devops-graceful-shutdown.md",".agents/skills/nestjs-best-practices/rules/devops-use-config-module.md",".agents/skills/nestjs-best-practices/rules/devops-use-logging.md",".agents/skills/nestjs-best-practices/rules/di-avoid-service-locator.md",".agents/skills/nestjs-best-practices/rules/di-interface-segregation.md",".agents/skills/nestjs-best-practices/rules/di-liskov-substitution.md",".agents/skills/nestjs-best-practices/rules/di-prefer-constructor-injection.md",".agents/skills/nestjs-best-practices/rules/di-scope-awareness.md",".agents/skills/nestjs-best-practices/rules/di-use-interfaces-tokens.md",".agents/skills/nestjs-best-practices/rules/error-handle-async-errors.md",".agents/skills/nestjs-best-practices/rules/error-throw-http-exceptions.md",".agents/skills/nestjs-best-practices/rules/error-use-exception-filters.md",".agents/skills/nestjs-best-practices/rules/micro-use-health-checks.md",".agents/skills/nestjs-best-practices/rules/micro-use-patterns.md",".agents/skills/nestjs-best-practices/rules/micro-use-queues.md",".agents/skills/nestjs-best-practices/rules/perf-async-hooks.md",".agents/skills/nestjs-best-practices/rules/perf-lazy-loading.md",".agents/skills/nestjs-best-practices/rules/perf-optimize-database.md",".agents/skills/nestjs-best-practices/rules/perf-use-caching.md",".agents/skills/nestjs-best-practices/rules/security-auth-jwt.md",".agents/skills/nestjs-best-practices/rules/security-rate-limiting.md",".agents/skills/nestjs-best-practices/rules/security-sanitize-output.md",".agents/skills/nestjs-best-practices/rules/security-use-guards.md",".agents/skills/nestjs-best-practices/rules/security-validate-all-input.md",".agents/skills/nestjs-best-practices/rules/test-e2e-supertest.md",".agents/skills/nestjs-best-practices/rules/test-mock-external-services.md",".agents/skills/nestjs-best-practices/rules/test-use-testing-module.md",".agents/skills/nestjs-best-practices/scripts/package.json",".agents/skills/typeorm/SKILL.md",".agents/skills/typescript-advanced-types/SKILL.md",".github/copilot-instructions.md","AGENTS.md","tools/sloths/README.md"],"skills":{".agents/skills/nestjs-best-practices/AGENTS.md":"# NestJS Best Practices\n\n**Version 1.1.0**\nNestJS Best Practices\nJanuary 2026\n\n> **Note:**\n> This document is mainly for agents and LLMs to follow when maintaining,\n> generating, or refactoring NestJS codebases. Humans may also find it\n> useful, but guidance here is optimized for automation and consistency\n> by AI-assisted workflows.\n\n---\n\n## Abstract\n\nComprehensive best practices and architecture guide for NestJS applications, designed for AI agents and LLMs. Contains 40 rules across 10 categories, prioritized by impact from critical (architecture, dependency injection) to incremental (DevOps patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.\n\n---\n\n## Table of Contents\n\n1. [Architecture](#1-architecture) — **CRITICAL**\n   - 1.1 [Avoid Circular Dependencies](#11-avoid-circular-dependencies)\n   - 1.2 [Organize by Feature Modules](#12-organize-by-feature-modules)\n   - 1.3 [Use Proper Module Sharing Patterns](#13-use-proper-module-sharing-patterns)\n   - 1.4 [Single Responsibility for Services](#14-single-responsibility-for-services)\n   - 1.5 [Use Event-Driven Architecture for Decoupling](#15-use-event-driven-architecture-for-decoupling)\n   - 1.6 [Use Repository Pattern for Data Access](#16-use-repository-pattern-for-data-access)\n2. [Dependency Injection](#2-dependency-injection) — **CRITICAL**\n   - 2.1 [Avoid Service Locator Anti-Pattern](#21-avoid-service-locator-anti-pattern)\n   - 2.2 [Apply Interface Segregation Principle](#22-apply-interface-segregation-principle)\n   - 2.3 [Honor Liskov Substitution Principle](#23-honor-liskov-substitution-principle)\n   - 2.4 [Prefer Constructor Injection](#24-prefer-constructor-injection)\n   - 2.5 [Understand Provider Scopes](#25-understand-provider-scopes)\n   - 2.6 [Use Injection Tokens for Interfaces](#26-use-injection-tokens-for-interfaces)\n3. [Error Handling](#3-error-handling) — **HIGH**\n   - 3.1 [Handle Async Errors Properly](#31-handle-async-errors-properly)\n   - 3.2 [Throw HTTP Exceptions from Services](#32-throw-http-exceptions-from-services)\n   - 3.3 [Use Exception Filters for Error Handling](#33-use-exception-filters-for-error-handling)\n4. [Security](#4-security) — **HIGH**\n   - 4.1 [Implement Secure JWT Authentication](#41-implement-secure-jwt-authentication)\n   - 4.2 [Implement Rate Limiting](#42-implement-rate-limiting)\n   - 4.3 [Sanitize Output to Prevent XSS](#43-sanitize-output-to-prevent-xss)\n   - 4.4 [Use Guards for Authentication and Authorization](#44-use-guards-for-authentication-and-authorization)\n   - 4.5 [Validate All Input with DTOs and Pipes](#45-validate-all-input-with-dtos-and-pipes)\n5. [Performance](#5-performance) — **HIGH**\n   - 5.1 [Use Async Lifecycle Hooks Correctly](#51-use-async-lifecycle-hooks-correctly)\n   - 5.2 [Use Lazy Loading for Large Modules](#52-use-lazy-loading-for-large-modules)\n   - 5.3 [Optimize Database Queries](#53-optimize-database-queries)\n   - 5.4 [Use Caching Strategically](#54-use-caching-strategically)\n6. [Testing](#6-testing) — **MEDIUM-HIGH**\n   - 6.1 [Use Supertest for E2E Testing](#61-use-supertest-for-e2e-testing)\n   - 6.2 [Mock External Services in Tests](#62-mock-external-services-in-tests)\n   - 6.3 [Use Testing Module for Unit Tests](#63-use-testing-module-for-unit-tests)\n7. [Database & ORM](#7-database-orm) — **MEDIUM-HIGH**\n   - 7.1 [Avoid N+1 Query Problems](#71-avoid-n-1-query-problems)\n   - 7.2 [Use Database Migrations](#72-use-database-migrations)\n   - 7.3 [Use Transactions for Multi-Step Operations](#73-use-transactions-for-multi-step-operations)\n8. [API Design](#8-api-design) — **MEDIUM**\n   - 8.1 [Use DTOs and Serialization for API Responses](#81-use-dtos-and-serialization-for-api-responses)\n   - 8.2 [Use Interceptors for Cross-Cutting Concerns](#82-use-interceptors-for-cross-cutting-concerns)\n   - 8.3 [Use Pipes for Input Transformation](#83-use-pipes-for-input-transformation)\n   - 8.4 [Use API Versioning for Breaking Changes](#84-use-api-versioning-for-breaking-changes)\n9. [Microservices](#9-microservices) — **MEDIUM**\n   - 9.1 [Implement Health Checks for Microservices](#91-implement-health-checks-for-microservices)\n   - 9.2 [Use Message and Event Patterns Correctly](#92-use-message-and-event-patterns-correctly)\n   - 9.3 [Use Message Queues for Background Jobs](#93-use-message-queues-for-background-jobs)\n10. [DevOps & Deployment](#10-devops-deployment) — **LOW-MEDIUM**\n\n- 10.1 [Implement Graceful Shutdown](#101-implement-graceful-shutdown)\n- 10.2 [Use ConfigModule for Environment Configuration](#102-use-configmodule-for-environment-configuration)\n- 10.3 [Use Structured Logging](#103-use-structured-logging)\n\n---\n\n## 1. Architecture\n\n**Section Impact: CRITICAL**\n\n### 1.1 Avoid Circular Dependencies\n\n**Impact: CRITICAL** — \"#1 cause of runtime crashes\"\n\nCircular dependencies occur when Module A imports Module B, and Module B imports Module A (directly or transitively). NestJS can sometimes resolve these through forward references, but they indicate architectural problems and should be avoided. This is the #1 cause of runtime crashes in NestJS applications.\n\n**Incorrect (circular module imports):**\n\n```typescript\n// users.module.ts\n@Module({\n  imports: [OrdersModule], // Orders needs Users, Users needs Orders = circular\n  providers: [UsersService],\n  exports: [UsersService],\n})\nexport class UsersModule {}\n\n// orders.module.ts\n@Module({\n  imports: [UsersModule], // Circular dependency!\n  providers: [OrdersService],\n  exports: [OrdersService],\n})\nexport class OrdersModule {}\n```\n\n**Correct (extract shared logic or use events):**\n\n```typescript\n// Option 1: Extract shared logic to a third module\n// shared.module.ts\n@Module({\n  providers: [SharedService],\n  exports: [SharedService],\n})\nexport class SharedModule {}\n\n// users.module.ts\n@Module({\n  imports: [SharedModule],\n  providers: [UsersService],\n})\nexport class UsersModule {}\n\n// orders.module.ts\n@Module({\n  imports: [SharedModule],\n  providers: [OrdersService],\n})\nexport class OrdersModule {}\n\n// Option 2: Use events for decoupled communication\n// users.service.ts\n@Injectable()\nexport class UsersService {\n  constructor(private eventEmitter: EventEmitter2) {}\n\n  async createUser(data: CreateUserDto) {\n    const user = await this.userRepo.save(data);\n    this.eventEmitter.emit('user.created', user);\n    return user;\n  }\n}\n\n// orders.service.ts\n@Injectable()\nexport class OrdersService {\n  @OnEvent('user.created')\n  handleUserCreated(user: User) {\n    // React to user creation without direct dependency\n  }\n}\n```\n\nReference: [NestJS Circular Dependency](https://docs.nestjs.com/fundamentals/circular-dependency)\n\n---\n\n### 1.2 Organize by Feature Modules\n\n**Impact: CRITICAL** — \"3-5x faster onboarding and development\"\n\nOrganize your application into feature modules that encapsulate related functionality. Each feature module should be self-contained with its own controllers, services, entities, and DTOs. Avoid organizing by technical layer (all controllers together, all services together). This enables 3-5x faster onboarding and feature development.\n\n**Incorrect (technical layer organization):**\n\n```typescript\n// Technical layer organization (anti-pattern)\nsrc/\n├── controllers/\n│   ├── users.controller.ts\n│   ├── orders.controller.ts\n│   └── products.controller.ts\n├── services/\n│   ├── users.service.ts\n│   ├── orders.service.ts\n│   └── products.service.ts\n├── entities/\n│   ├── user.entity.ts\n│   ├── order.entity.ts\n│   └── product.entity.ts\n└── app.module.ts  // Imports everything directly\n```\n\n**Correct (feature module organization):**\n\n```typescript\n// Feature module organization\nsrc/\n├── users/\n│   ├── dto/\n│   │   ├── create-user.dto.ts\n│   │   └── update-user.dto.ts\n│   ├── entities/\n│   │   └── user.entity.ts\n│   ├── users.controller.ts\n│   ├── users.service.ts\n│   ├── users.repository.ts\n│   └── users.module.ts\n├── orders/\n│   ├── dto/\n│   ├── entities/\n│   ├── orders.controller.ts\n│   ├── orders.service.ts\n│   └── orders.module.ts\n├── shared/\n│   ├── guards/\n│   ├── interceptors/\n│   ├── filters/\n│   └── shared.module.ts\n└── app.module.ts\n\n// users.module.ts\n@Module({\n  imports: [TypeOrmModule.forFeature([User])],\n  controllers: [UsersController],\n  providers: [UsersService, UsersRepository],\n  exports: [UsersService], // Only export what others need\n})\nexport class UsersModule {}\n\n// app.module.ts\n@Module({\n  imports: [\n    ConfigModule.forRoot(),\n    TypeOrmModule.forRoot(),\n    UsersModule,\n    OrdersModule,\n    SharedModule,\n  ],\n})\nexport class AppModule {}\n```\n\nReference: [NestJS Modules](https://docs.nestjs.com/modules)\n\n---\n\n### 1.3 Use Proper Module Sharing Patterns\n\n**Impact: CRITICAL** — Prevents duplicate instances, memory leaks, and state inconsistency\n\nNestJS modules are singletons by default. When a service is properly exported from a module and that module is imported elsewhere, the same instance is shared. However, providing a service in multiple modules creates separate instances, leading to memory waste, state inconsistency, and confusing behavior. Always encapsulate services in dedicated modules, export them explicitly, and import the module where needed.\n\n**Incorrect (service provided in multiple modules):**\n\n```typescript\n// StorageService provided directly in multiple modules - WRONG\n// storage.service.ts\n@Injectable()\nexport class StorageService {\n  private cache = new Map(); // Each instance has separate state!\n\n  store(key: string, value: any) {\n    this.cache.set(key, value);\n  }\n}\n\n// app.module.ts\n@Module({\n  providers: [StorageService], // Instance #1\n  controllers: [AppController],\n})\nexport class AppModule {}\n\n// videos.module.ts\n@Module({\n  providers: [StorageService], // Instance #2 - different from AppModule!\n  controllers: [VideosController],\n})\nexport class VideosModule {}\n\n// Problems:\n// 1. Two separate StorageService instances exist\n// 2. cache.set() in VideosModule doesn't affect AppModule's cache\n// 3. Memory wasted on duplicate instances\n// 4. Debugging nightmares when state doesn't sync\n```\n\n**Correct (dedicated module with exports):**\n\n```typescript\n// storage/storage.module.ts\n@Module({\n  providers: [StorageService],\n  exports: [StorageService], // Make available to importers\n})\nexport class StorageModule {}\n\n// videos/videos.module.ts\n@Module({\n  imports: [StorageModule], // Import the module, not the service\n  controllers: [VideosController],\n  providers: [VideosService],\n})\nexport class VideosModule {}\n\n// channels/channels.module.ts\n@Module({\n  imports: [StorageModule], // Same instance shared\n  controllers: [ChannelsController],\n  providers: [ChannelsService],\n})\nexport class ChannelsModule {}\n\n// app.module.ts\n@Module({\n  imports: [\n    StorageModule, // Only if AppModule itself needs StorageService\n    VideosModule,\n    ChannelsModule,\n  ],\n})\nexport class AppModule {}\n\n// Now all modules share the SAME StorageService instance\n```\n\n**When to use @Global() (sparingly):**\n\n```typescript\n// ONLY for truly cross-cutting concerns\n@Global()\n@Module({\n  providers: [ConfigService, LoggerService],\n  exports: [ConfigService, LoggerService],\n})\nexport class CoreModule {}\n\n// Import once in AppModule\n@Module({\n  imports: [CoreModule], // Registered globally, available everywhere\n})\nexport class AppModule {}\n\n// Other modules don't need to import CoreModule\n@Module({\n  controllers: [UsersController],\n  providers: [UsersService], // Can inject ConfigService without importing\n})\nexport class UsersModule {}\n\n// WARNING: Don't make everything global!\n// - Hides dependencies (can't see what a module needs from imports)\n// - Makes testing harder\n// - Reserve for: config, logging, database connections\n```\n\n**Module re-exporting pattern:**\n\n```typescript\n// common.module.ts - shared utilities\n@Module({\n  providers: [DateService, ValidationService],\n  exports: [DateService, ValidationService],\n})\nexport class CommonModule {}\n\n// core.module.ts - re-exports common for convenience\n@Module({\n  imports: [CommonModule, DatabaseModule],\n  exports: [CommonModule, DatabaseModule], // Re-export for consumers\n})\nexport class CoreModule {}\n\n// feature.module.ts - imports CoreModule, gets both\n@Module({\n  imports: [CoreModule], // Gets CommonModule + DatabaseModule\n  controllers: [FeatureController],\n})\nexport class FeatureModule {}\n```\n\nReference: [NestJS Modules](https://docs.nestjs.com/modules#shared-modules)\n\n---\n\n### 1.4 Single Responsibility for Services\n\n**Impact: CRITICAL** — \"40%+ improvement in testability\"\n\nEach service should have a single, well-defined responsibility. Avoid \"god services\" that handle multiple unrelated concerns. If a service name includes \"And\" or handles more than one domain concept, it likely violates single responsibility. This reduces complexity and improves testability by 40%+.\n\n**Incorrect (god service anti-pattern):**\n\n```typescript\n// God service anti-pattern\n@Injectable()\nexport class UserAndOrderService {\n  constructor(\n    private userRepo: UserRepository,\n    private orderRepo: OrderRepository,\n    private mailer: MailService,\n    private payment: PaymentService,\n  ) {}\n\n  async createUser(dto: CreateUserDto) {\n    const user = await this.userRepo.save(dto);\n    await this.mailer.sendWelcome(user);\n    return user;\n  }\n\n  async createOrder(userId: string, dto: CreateOrderDto) {\n    const order = await this.orderRepo.save({ userId, ...dto });\n    await this.payment.charge(order);\n    await this.mailer.sendOrderConfirmation(order);\n    return order;\n  }\n\n  async calculateOrderStats(userId: string) {\n    // Stats logic mixed in\n  }\n\n  async validatePayment(orderId: string) {\n    // Payment logic mixed in\n  }\n}\n```\n\n**Correct (focused services with single responsibility):**\n\n```typescript\n// Focused services with single responsibility\n@Injectable()\nexport class UsersService {\n  constructor(private userRepo: UserRepository) {}\n\n  async create(dto: CreateUserDto): Promise<User> {\n    return this.userRepo.save(dto);\n  }\n\n  async findById(id: string): Promise<User> {\n    return this.userRepo.findOneOrFail({ where: { id } });\n  }\n}\n\n@Injectable()\nexport class OrdersService {\n  constructor(private orderRepo: OrderRepository) {}\n\n  async create(userId: string, dto: CreateOrderDto): Promise<Order> {\n    return this.orderRepo.save({ userId, ...dto });\n  }\n\n  async findByUser(userId: string): Promise<Order[]> {\n    return this.orderRepo.find({ where: { userId } });\n  }\n}\n\n@Injectable()\nexport class OrderStatsService {\n  constructor(private orderRepo: OrderRepository) {}\n\n  async calculateForUser(userId: string): Promise<OrderStats> {\n    // Focused stats calculation\n  }\n}\n\n// Orchestration in controller or dedicated orchestrator\n@Controller('orders')\nexport class OrdersController {\n  constructor(\n    private orders: OrdersService,\n    private payment: PaymentService,\n    private notifications: NotificationService,\n  ) {}\n\n  @Post()\n  async create(@CurrentUser() user: User, @Body() dto: CreateOrderDto) {\n    const order = await this.orders.create(user.id, dto);\n    await this.payment.charge(order);\n    await this.notifications.sendOrderConfirmation(order);\n    return order;\n  }\n}\n```\n\nReference: [NestJS Providers](https://docs.nestjs.com/providers)\n\n---\n\n### 1.5 Use Event-Driven Architecture for Decoupling\n\n**Impact: MEDIUM-HIGH** — Enables async processing and modularity\n\nUse `@nestjs/event-emitter` for intra-service events and message brokers for inter-service communication. Events allow modules to react to changes without direct dependencies, improving modularity and enabling async processing.\n\n**Incorrect (direct service coupling):**\n\n```typescript\n// Direct service coupling\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private inventoryService: InventoryService,\n    private emailService: EmailService,\n    private analyticsService: AnalyticsService,\n    private notificationService: NotificationService,\n    private loyaltyService: LoyaltyService,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Tight coupling - OrdersService knows about all consumers\n    await this.inventoryService.reserve(order.items);\n    await this.emailService.sendConfirmation(order);\n    await this.analyticsService.track('order_created', order);\n    await this.notificationService.push(order.userId, 'Order placed');\n    await this.loyaltyService.addPoints(order.userId, order.total);\n\n    // Adding new behavior requires modifying this service\n    return order;\n  }\n}\n```\n\n**Correct (event-driven decoupling):**\n\n```typescript\n// Use EventEmitter for decoupling\nimport { EventEmitter2 } from '@nestjs/event-emitter';\n\n// Define event\nexport class OrderCreatedEvent {\n  constructor(\n    public readonly orderId: string,\n    public readonly userId: string,\n    public readonly items: OrderItem[],\n    public readonly total: number,\n  ) {}\n}\n\n// Service emits events\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private eventEmitter: EventEmitter2,\n    private repo: Repository<Order>,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Emit event - no knowledge of consumers\n    this.eventEmitter.emit('order.created', new OrderCreatedEvent(order.id, order.userId, order.items, order.total));\n\n    return order;\n  }\n}\n\n// Listeners in separate modules\n@Injectable()\nexport class InventoryListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.inventoryService.reserve(event.items);\n  }\n}\n\n@Injectable()\nexport class EmailListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.emailService.sendConfirmation(event.orderId);\n  }\n}\n\n@Injectable()\nexport class AnalyticsListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.analyticsService.track('order_created', {\n      orderId: event.orderId,\n      total: event.total,\n    });\n  }\n}\n```\n\nReference: [NestJS Events](https://docs.nestjs.com/techniques/events)\n\n---\n\n### 1.6 Use Repository Pattern for Data Access\n\n**Impact: HIGH** — Decouples business logic from database\n\nCreate custom repositories to encapsulate complex queries and database logic. This keeps services focused on business logic, makes testing easier with mock repositories, and allows changing database implementations without affecting business code.\n\n**Incorrect (complex queries in services):**\n\n```typescript\n// Complex queries in services\n@Injectable()\nexport class UsersService {\n  constructor(@InjectRepository(User) private repo: Repository<User>) {}\n\n  async findActiveWithOrders(minOrders: number): Promise<User[]> {\n    // Complex query logic mixed with business logic\n    return this.repo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.orders', 'order')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('user.deletedAt IS NULL')\n      .groupBy('user.id')\n      .having('COUNT(order.id) >= :min', { min: minOrders })\n      .orderBy('user.createdAt', 'DESC')\n      .getMany();\n  }\n\n  // Service becomes bloated with query logic\n}\n```\n\n**Correct (custom repository with encapsulated queries):**\n\n```typescript\n// Custom repository with encapsulated queries\n@Injectable()\nexport class UsersRepository {\n  constructor(@InjectRepository(User) private repo: Repository<User>) {}\n\n  async findById(id: string): Promise<User | null> {\n    return this.repo.findOne({ where: { id } });\n  }\n\n  async findByEmail(email: string): Promise<User | null> {\n    return this.repo.findOne({ where: { email } });\n  }\n\n  async findActiveWithMinOrders(minOrders: number): Promise<User[]> {\n    return this.repo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.orders', 'order')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('user.deletedAt IS NULL')\n      .groupBy('user.id')\n      .having('COUNT(order.id) >= :min', { min: minOrders })\n      .orderBy('user.createdAt', 'DESC')\n      .getMany();\n  }\n\n  async save(user: User): Promise<User> {\n    return this.repo.save(user);\n  }\n}\n\n// Clean service with business logic only\n@Injectable()\nexport class UsersService {\n  constructor(private usersRepo: UsersRepository) {}\n\n  async getActiveUsersWithOrders(): Promise<User[]> {\n    return this.usersRepo.findActiveWithMinOrders(1);\n  }\n\n  async create(dto: CreateUserDto): Promise<User> {\n    const existing = await this.usersRepo.findByEmail(dto.email);\n    if (existing) {\n      throw new ConflictException('Email already registered');\n    }\n\n    const user = new User();\n    user.email = dto.email;\n    user.name = dto.name;\n    return this.usersRepo.save(user);\n  }\n}\n```\n\nReference: [Repository Pattern](https://martinfowler.com/eaaCatalog/repository.html)\n\n---\n\n## 2. Dependency Injection\n\n**Section Impact: CRITICAL**\n\n### 2.1 Avoid Service Locator Anti-Pattern\n\n**Impact: HIGH** — Hides dependencies and breaks testability\n\nAvoid using `ModuleRef.get()` or global containers to resolve dependencies at runtime. This hides dependencies, makes code harder to test, and breaks the benefits of dependency injection. Use constructor injection instead.\n\n**Incorrect (service locator anti-pattern):**\n\n```typescript\n// Use ModuleRef to get dependencies dynamically\n@Injectable()\nexport class OrdersService {\n  constructor(private moduleRef: ModuleRef) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    // Dependencies are hidden - not visible in constructor\n    const usersService = this.moduleRef.get(UsersService);\n    const inventoryService = this.moduleRef.get(InventoryService);\n    const paymentService = this.moduleRef.get(PaymentService);\n\n    const user = await usersService.findOne(dto.userId);\n    // ... rest of logic\n  }\n}\n\n// Global singleton container\nclass ServiceContainer {\n  private static instance: ServiceContainer;\n  private services = new Map<string, any>();\n\n  static getInstance(): ServiceContainer {\n    if (!this.instance) {\n      this.instance = new ServiceContainer();\n    }\n    return this.instance;\n  }\n\n  get<T>(key: string): T {\n    return this.services.get(key);\n  }\n}\n```\n\n**Correct (constructor injection with explicit dependencies):**\n\n```typescript\n// Use constructor injection - dependencies are explicit\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private usersService: UsersService,\n    private inventoryService: InventoryService,\n    private paymentService: PaymentService,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const user = await this.usersService.findOne(dto.userId);\n    const inventory = await this.inventoryService.check(dto.items);\n    // Dependencies are clear and testable\n  }\n}\n\n// Easy to test with mocks\ndescribe('OrdersService', () => {\n  let service: OrdersService;\n\n  beforeEach(async () => {\n    const module = await Test.createTestingModule({\n      providers: [\n        OrdersService,\n        { provide: UsersService, useValue: mockUsersService },\n        { provide: InventoryService, useValue: mockInventoryService },\n        { provide: PaymentService, useValue: mockPaymentService },\n      ],\n    }).compile();\n\n    service = module.get(OrdersService);\n  });\n});\n\n// VALID: Factory pattern for dynamic instantiation\n@Injectable()\nexport class HandlerFactory {\n  constructor(private moduleRef: ModuleRef) {}\n\n  getHandler(type: string): Handler {\n    switch (type) {\n      case 'email':\n        return this.moduleRef.get(EmailHandler);\n      case 'sms':\n        return this.moduleRef.get(SmsHandler);\n      default:\n        return this.moduleRef.get(DefaultHandler);\n    }\n  }\n}\n```\n\nReference: [NestJS Module Reference](https://docs.nestjs.com/fundamentals/module-ref)\n\n---\n\n### 2.2 Apply Interface Segregation Principle\n\n**Impact: HIGH** — Reduces coupling and improves testability by 30-50%\n\nClients should not be forced to depend on interfaces they don't use. In NestJS, this means keeping interfaces small and focused on specific capabilities rather than creating \"fat\" interfaces that bundle unrelated methods. When a service only needs to send emails, it shouldn't depend on an interface that also includes SMS, push notifications, and logging. Split large interfaces into role-based ones.\n\n**Incorrect (fat interface forcing unused dependencies):**\n\n```typescript\n// Fat interface - forces all consumers to depend on everything\ninterface NotificationService {\n  sendEmail(to: string, subject: string, body: string): Promise<void>;\n  sendSms(phone: string, message: string): Promise<void>;\n  sendPush(userId: string, notification: PushPayload): Promise<void>;\n  sendSlack(channel: string, message: string): Promise<void>;\n  logNotification(type: string, payload: any): Promise<void>;\n  getDeliveryStatus(id: string): Promise<DeliveryStatus>;\n  retryFailed(id: string): Promise<void>;\n  scheduleNotification(dto: ScheduleDto): Promise<string>;\n}\n\n// Consumer only needs email, but must mock everything for tests\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private notifications: NotificationService, // Depends on 8 methods, uses 1\n  ) {}\n\n  async confirmOrder(order: Order): Promise<void> {\n    await this.notifications.sendEmail(\n      order.customer.email,\n      'Order Confirmed',\n      `Your order ${order.id} has been confirmed.`,\n    );\n  }\n}\n\n// Testing is painful - must mock unused methods\nconst mockNotificationService = {\n  sendEmail: jest.fn(),\n  sendSms: jest.fn(), // Never used, but required\n  sendPush: jest.fn(), // Never used, but required\n  sendSlack: jest.fn(), // Never used, but required\n  logNotification: jest.fn(), // Never used, but required\n  getDeliveryStatus: jest.fn(), // Never used, but required\n  retryFailed: jest.fn(), // Never used, but required\n  scheduleNotification: jest.fn(), // Never used, but required\n};\n```\n\n**Correct (segregated interfaces by capability):**\n\n```typescript\n// Segregated interfaces - each focused on one capability\ninterface EmailSender {\n  sendEmail(to: string, subject: string, body: string): Promise<void>;\n}\n\ninterface SmsSender {\n  sendSms(phone: string, message: string): Promise<void>;\n}\n\ninterface PushSender {\n  sendPush(userId: string, notification: PushPayload): Promise<void>;\n}\n\ninterface NotificationLogger {\n  logNotification(type: string, payload: any): Promise<void>;\n}\n\ninterface NotificationScheduler {\n  scheduleNotification(dto: ScheduleDto): Promise<string>;\n}\n\n// Implementation can implement multiple interfaces\n@Injectable()\nexport class NotificationService implements EmailSender, SmsSender, PushSender {\n  async sendEmail(to: string, subject: string, body: string): Promise<void> {\n    // Email implementation\n  }\n\n  async sendSms(phone: string, message: string): Promise<void> {\n    // SMS implementation\n  }\n\n  async sendPush(userId: string, notification: PushPayload): Promise<void> {\n    // Push implementation\n  }\n}\n\n// Or separate implementations\n@Injectable()\nexport class SendGridEmailService implements EmailSender {\n  async sendEmail(to: string, subject: string, body: string): Promise<void> {\n    // SendGrid-specific implementation\n  }\n}\n\n// Consumer depends only on what it needs\n@Injectable()\nexport class OrdersService {\n  constructor(\n    @Inject(EMAIL_SENDER) private emailSender: EmailSender, // Minimal dependency\n  ) {}\n\n  async confirmOrder(order: Order): Promise<void> {\n    await this.emailSender.sendEmail(\n      order.customer.email,\n      'Order Confirmed',\n      `Your order ${order.id} has been confirmed.`,\n    );\n  }\n}\n\n// Testing is simple - only mock what's used\nconst mockEmailSender: EmailSender = {\n  sendEmail: jest.fn(),\n};\n\n// Module registration with tokens\nexport const EMAIL_SENDER = Symbol('EMAIL_SENDER');\nexport const SMS_SENDER = Symbol('SMS_SENDER');\n\n@Module({\n  providers: [\n    { provide: EMAIL_SENDER, useClass: SendGridEmailService },\n    { provide: SMS_SENDER, useClass: TwilioSmsService },\n  ],\n  exports: [EMAIL_SENDER, SMS_SENDER],\n})\nexport class NotificationModule {}\n```\n\n**Combining interfaces when needed:**\n\n```typescript\n// Sometimes a consumer legitimately needs multiple capabilities\ninterface EmailAndSmsSender extends EmailSender, SmsSender {}\n\n// Or use intersection types\ntype MultiChannelSender = EmailSender & SmsSender & PushSender;\n\n// Consumer that genuinely needs multiple channels\n@Injectable()\nexport class AlertService {\n  constructor(\n    @Inject(MULTI_CHANNEL_SENDER)\n    private sender: EmailSender & SmsSender,\n  ) {}\n\n  async sendCriticalAlert(user: User, message: string): Promise<void> {\n    await Promise.all([\n      this.sender.sendEmail(user.email, 'Critical Alert', message),\n      this.sender.sendSms(user.phone, message),\n    ]);\n  }\n}\n```\n\nReference: [Interface Segregation Principle](https://en.wikipedia.org/wiki/Interface_segregation_principle)\n\n---\n\n### 2.3 Honor Liskov Substitution Principle\n\n**Impact: HIGH** — Ensures implementations are truly interchangeable without breaking callers\n\nSubtypes must be substitutable for their base types without altering program correctness. In NestJS with dependency injection, this means any implementation of an interface or abstract class must honor the contract completely. A mock payment service used in tests must behave like a real payment service (return similar shapes, handle errors the same way). Violating LSP causes subtle bugs when swapping implementations.\n\n**Incorrect (implementation violates the contract):**\n\n```typescript\n// Base interface with clear contract\ninterface PaymentGateway {\n  /**\n   * Charges the specified amount.\n   * @returns PaymentResult on success\n   * @throws PaymentFailedException on payment failure\n   */\n  charge(amount: number, currency: string): Promise<PaymentResult>;\n}\n\n// Production implementation - follows the contract\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    const response = await this.stripe.charges.create({ amount, currency });\n    return { success: true, transactionId: response.id, amount };\n  }\n}\n\n// Mock that violates LSP - different behavior!\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    // VIOLATION 1: Throws for valid input (contract says return PaymentResult)\n    if (amount > 1000) {\n      throw new Error('Mock does not support large amounts');\n    }\n\n    // VIOLATION 2: Returns null instead of PaymentResult\n    if (currency !== 'USD') {\n      return null as any; // Real service would convert or reject properly\n    }\n\n    // VIOLATION 3: Missing required field\n    return { success: true } as PaymentResult; // Missing transactionId!\n  }\n}\n\n// Consumer trusts the contract\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async checkout(order: Order): Promise<void> {\n    const result = await this.payment.charge(order.total, order.currency);\n    // These fail with MockPaymentService:\n    await this.saveTransaction(result.transactionId); // undefined!\n    await this.sendReceipt(result); // might be null!\n  }\n}\n```\n\n**Correct (implementations honor the contract):**\n\n```typescript\n// Well-defined interface with documented behavior\ninterface PaymentGateway {\n  /**\n   * Charges the specified amount.\n   * @param amount - Amount in smallest currency unit (cents)\n   * @param currency - ISO 4217 currency code\n   * @returns PaymentResult with transactionId, success status, and amount\n   * @throws PaymentFailedException if charge is declined\n   * @throws InvalidCurrencyException if currency is not supported\n   */\n  charge(amount: number, currency: string): Promise<PaymentResult>;\n\n  /**\n   * Refunds a previous charge.\n   * @throws TransactionNotFoundException if transactionId is invalid\n   */\n  refund(transactionId: string, amount?: number): Promise<RefundResult>;\n}\n\n// Production implementation\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    try {\n      const response = await this.stripe.charges.create({ amount, currency });\n      return {\n        success: true,\n        transactionId: response.id,\n        amount: response.amount,\n      };\n    } catch (error) {\n      if (error.type === 'card_error') {\n        throw new PaymentFailedException(error.message);\n      }\n      throw error;\n    }\n  }\n\n  async refund(transactionId: string, amount?: number): Promise<RefundResult> {\n    // Implementation...\n  }\n}\n\n// Mock that honors LSP - same contract, same behavior shape\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  private transactions = new Map<string, PaymentResult>();\n\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    // Honor the contract: validate currency like real service would\n    if (!['USD', 'EUR', 'GBP'].includes(currency)) {\n      throw new InvalidCurrencyException(`Unsupported currency: ${currency}`);\n    }\n\n    // Simulate decline for specific test scenarios\n    if (amount === 99999) {\n      throw new PaymentFailedException('Card declined (test scenario)');\n    }\n\n    // Return same shape as production\n    const result: PaymentResult = {\n      success: true,\n      transactionId: `mock_${Date.now()}_${Math.random().toString(36)}`,\n      amount,\n    };\n\n    this.transactions.set(result.transactionId, result);\n    return result;\n  }\n\n  async refund(transactionId: string, amount?: number): Promise<RefundResult> {\n    // Honor the contract: throw if transaction not found\n    if (!this.transactions.has(transactionId)) {\n      throw new TransactionNotFoundException(transactionId);\n    }\n\n    return {\n      success: true,\n      refundId: `refund_${transactionId}`,\n      amount: amount ?? this.transactions.get(transactionId)!.amount,\n    };\n  }\n}\n\n// Consumer can swap implementations safely\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async checkout(order: Order): Promise<Order> {\n    try {\n      const result = await this.payment.charge(order.total, order.currency);\n      // Works with both StripeService and MockPaymentService\n      order.transactionId = result.transactionId;\n      order.status = 'paid';\n      return order;\n    } catch (error) {\n      if (error instanceof PaymentFailedException) {\n        order.status = 'payment_failed';\n        return order;\n      }\n      throw error;\n    }\n  }\n}\n```\n\n**Testing LSP compliance:**\n\n```typescript\n// Shared test suite that any implementation must pass\nfunction testPaymentGatewayContract(createGateway: () => PaymentGateway) {\n  describe('PaymentGateway contract', () => {\n    let gateway: PaymentGateway;\n\n    beforeEach(() => {\n      gateway = createGateway();\n    });\n\n    it('returns PaymentResult with all required fields', async () => {\n      const result = await gateway.charge(1000, 'USD');\n      expect(result).toHaveProperty('success');\n      expect(result).toHaveProperty('transactionId');\n      expect(result).toHaveProperty('amount');\n      expect(typeof result.transactionId).toBe('string');\n    });\n\n    it('throws InvalidCurrencyException for unsupported currency', async () => {\n      await expect(gateway.charge(1000, 'INVALID')).rejects.toThrow(InvalidCurrencyException);\n    });\n\n    it('throws TransactionNotFoundException for invalid refund', async () => {\n      await expect(gateway.refund('nonexistent')).rejects.toThrow(TransactionNotFoundException);\n    });\n  });\n}\n\n// Run against all implementations\ndescribe('StripeService', () => {\n  testPaymentGatewayContract(() => new StripeService(mockStripeClient));\n});\n\ndescribe('MockPaymentService', () => {\n  testPaymentGatewayContract(() => new MockPaymentService());\n});\n```\n\nReference: [Liskov Substitution Principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle)\n\n---\n\n### 2.4 Prefer Constructor Injection\n\n**Impact: CRITICAL** — Required for proper DI and testing\n\nAlways use constructor injection over property injection. Constructor injection makes dependencies explicit, enables TypeScript type checking, ensures dependencies are available when the class is instantiated, and improves testability. This is required for proper DI, testing, and TypeScript support.\n\n**Incorrect (property injection with hidden dependencies):**\n\n```typescript\n// Property injection - avoid unless necessary\n@Injectable()\nexport class UsersService {\n  @Inject()\n  private userRepo: UserRepository; // Hidden dependency\n\n  @Inject('CONFIG')\n  private config: ConfigType; // Also hidden\n\n  async findAll() {\n    return this.userRepo.find();\n  }\n}\n\n// Problems:\n// 1. Dependencies not visible in constructor\n// 2. Service can be instantiated without dependencies in tests\n// 3. TypeScript can't enforce dependency types at instantiation\n```\n\n**Correct (constructor injection with explicit dependencies):**\n\n```typescript\n// Constructor injection - explicit and testable\n@Injectable()\nexport class UsersService {\n  constructor(\n    private readonly userRepo: UserRepository,\n    @Inject('CONFIG') private readonly config: ConfigType,\n  ) {}\n\n  async findAll(): Promise<User[]> {\n    return this.userRepo.find();\n  }\n}\n\n// Testing is straightforward\ndescribe('UsersService', () => {\n  let service: UsersService;\n  let mockRepo: jest.Mocked<UserRepository>;\n\n  beforeEach(() => {\n    mockRepo = {\n      find: jest.fn(),\n      save: jest.fn(),\n    } as any;\n\n    service = new UsersService(mockRepo, { dbUrl: 'test' });\n  });\n\n  it('should find all users', async () => {\n    mockRepo.find.mockResolvedValue([{ id: '1', name: 'Test' }]);\n    const result = await service.findAll();\n    expect(result).toHaveLength(1);\n  });\n});\n\n// Only use property injection for optional dependencies\n@Injectable()\nexport class LoggingService {\n  @Optional()\n  @Inject('ANALYTICS')\n  private analytics?: AnalyticsService;\n\n  log(message: string) {\n    console.log(message);\n    this.analytics?.track('log', message); // Optional enhancement\n  }\n}\n```\n\nReference: [NestJS Providers](https://docs.nestjs.com/providers)\n\n---\n\n### 2.5 Understand Provider Scopes\n\n**Impact: CRITICAL** — Prevents data leaks and performance issues\n\nNestJS has three provider scopes: DEFAULT (singleton), REQUEST (per-request instance), and TRANSIENT (new instance for each injection). Most providers should be singletons. Request-scoped providers have performance implications as they bubble up through the dependency tree. Understanding scopes prevents memory leaks and incorrect data sharing.\n\n**Incorrect (wrong scope usage):**\n\n```typescript\n// Request-scoped when not needed (performance hit)\n@Injectable({ scope: Scope.REQUEST })\nexport class UsersService {\n  // This creates a new instance for EVERY request\n  // All dependencies also become request-scoped\n  async findAll() {\n    return this.userRepo.find();\n  }\n}\n\n// Singleton with mutable request state\n@Injectable() // Default: singleton\nexport class RequestContextService {\n  private userId: string; // DANGER: Shared across all requests!\n\n  setUser(userId: string) {\n    this.userId = userId; // Overwrites for all concurrent requests\n  }\n\n  getUser() {\n    return this.userId; // Returns wrong user!\n  }\n}\n```\n\n**Correct (appropriate scope for each use case):**\n\n```typescript\n// Singleton for stateless services (default, most common)\n@Injectable()\nexport class UsersService {\n  constructor(private readonly userRepo: UserRepository) {}\n\n  async findById(id: string): Promise<User> {\n    return this.userRepo.findOne({ where: { id } });\n  }\n}\n\n// Request-scoped ONLY when you need request context\n@Injectable({ scope: Scope.REQUEST })\nexport class RequestContextService {\n  private userId: string;\n\n  setUser(userId: string) {\n    this.userId = userId;\n  }\n\n  getUser(): string {\n    return this.userId;\n  }\n}\n\n// Better: Use NestJS built-in request context\nimport { REQUEST } from '@nestjs/core';\nimport { Request } from 'express';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class AuditService {\n  constructor(@Inject(REQUEST) private request: Request) {}\n\n  log(action: string) {\n    console.log(`User ${this.request.user?.id} performed ${action}`);\n  }\n}\n\n// Best: Use ClsModule for async context (no scope bubble-up)\nimport { ClsService } from 'nestjs-cls';\n\n@Injectable() // Stays singleton!\nexport class AuditService {\n  constructor(private cls: ClsService) {}\n\n  log(action: string) {\n    const userId = this.cls.get('userId');\n    console.log(`User ${userId} performed ${action}`);\n  }\n}\n```\n\nReference: [NestJS Injection Scopes](https://docs.nestjs.com/fundamentals/injection-scopes)\n\n---\n\n### 2.6 Use Injection Tokens for Interfaces\n\n**Impact: HIGH** — Enables interface-based DI at runtime\n\nTypeScript interfaces are erased at compile time and can't be used as injection tokens. Use string tokens, symbols, or abstract classes when you want to inject implementations of interfaces. This enables swapping implementations for testing or different environments.\n\n**Incorrect (interface can't be used as token):**\n\n```typescript\n// Interface can't be used as injection token\ninterface PaymentGateway {\n  charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  charge(amount: number) {\n    /* ... */\n  }\n}\n\n@Injectable()\nexport class OrdersService {\n  // This WON'T work - PaymentGateway doesn't exist at runtime\n  constructor(private payment: PaymentGateway) {}\n}\n```\n\n**Correct (symbol tokens or abstract classes):**\n\n```typescript\n// Option 1: String/Symbol tokens (most flexible)\nexport const PAYMENT_GATEWAY = Symbol('PAYMENT_GATEWAY');\n\nexport interface PaymentGateway {\n  charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    // Stripe implementation\n  }\n}\n\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    return { success: true, id: 'mock-id' };\n  }\n}\n\n// Module registration\n@Module({\n  providers: [\n    {\n      provide: PAYMENT_GATEWAY,\n      useClass: process.env.NODE_ENV === 'test' ? MockPaymentService : StripeService,\n    },\n  ],\n  exports: [PAYMENT_GATEWAY],\n})\nexport class PaymentModule {}\n\n// Injection\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async createOrder(dto: CreateOrderDto) {\n    await this.payment.charge(dto.amount);\n  }\n}\n\n// Option 2: Abstract class (carries runtime type info)\nexport abstract class PaymentGateway {\n  abstract charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService extends PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    // Implementation\n  }\n}\n\n// No @Inject needed with abstract class\n@Injectable()\nexport class OrdersService {\n  constructor(private payment: PaymentGateway) {}\n}\n```\n\nReference: [NestJS Custom Providers](https://docs.nestjs.com/fundamentals/custom-providers)\n\n---\n\n## 3. Error Handling\n\n**Section Impact: HIGH**\n\n### 3.1 Handle Async Errors Properly\n\n**Impact: HIGH** — Prevents process crashes from unhandled rejections\n\nNestJS automatically catches errors from async route handlers, but errors from background tasks, event handlers, and manually created promises can crash your application. Always handle async errors explicitly and use global handlers as a safety net.\n\n**Incorrect (fire-and-forget without error handling):**\n\n```typescript\n// Fire-and-forget without error handling\n@Injectable()\nexport class UsersService {\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Fire and forget - if this fails, error is unhandled!\n    this.emailService.sendWelcome(user.email);\n\n    return user;\n  }\n}\n\n// Unhandled promise in event handler\n@Injectable()\nexport class OrdersService {\n  @OnEvent('order.created')\n  handleOrderCreated(event: OrderCreatedEvent) {\n    // This returns a promise but it's not awaited!\n    this.processOrder(event);\n    // Errors will crash the process\n  }\n\n  private async processOrder(event: OrderCreatedEvent): Promise<void> {\n    await this.inventoryService.reserve(event.items);\n    await this.notificationService.send(event.userId);\n  }\n}\n\n// Missing try-catch in scheduled tasks\n@Cron('0 0 * * *')\nasync dailyCleanup(): Promise<void> {\n  await this.cleanupService.run();\n  // If this throws, no error handling\n}\n```\n\n**Correct (explicit async error handling):**\n\n```typescript\n// Handle fire-and-forget with explicit catch\n@Injectable()\nexport class UsersService {\n  private readonly logger = new Logger(UsersService.name);\n\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Explicitly catch and log errors\n    this.emailService.sendWelcome(user.email).catch(error => {\n      this.logger.error('Failed to send welcome email', error.stack);\n      // Optionally queue for retry\n    });\n\n    return user;\n  }\n}\n\n// Properly handle async event handlers\n@Injectable()\nexport class OrdersService {\n  private readonly logger = new Logger(OrdersService.name);\n\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    try {\n      await this.processOrder(event);\n    } catch (error) {\n      this.logger.error('Failed to process order', { event, error });\n      // Don't rethrow - would crash the process\n      await this.deadLetterQueue.add('order.created', event);\n    }\n  }\n}\n\n// Safe scheduled tasks\n@Injectable()\nexport class CleanupService {\n  private readonly logger = new Logger(CleanupService.name);\n\n  @Cron('0 0 * * *')\n  async dailyCleanup(): Promise<void> {\n    try {\n      await this.cleanupService.run();\n      this.logger.log('Daily cleanup completed');\n    } catch (error) {\n      this.logger.error('Daily cleanup failed', error.stack);\n      // Alert or retry logic\n    }\n  }\n}\n\n// Global unhandled rejection handler in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  const logger = new Logger('Bootstrap');\n\n  process.on('unhandledRejection', (reason, promise) => {\n    logger.error('Unhandled Rejection at:', promise, 'reason:', reason);\n  });\n\n  process.on('uncaughtException', error => {\n    logger.error('Uncaught Exception:', error);\n    process.exit(1);\n  });\n\n  await app.listen(3000);\n}\n```\n\nReference: [Node.js Unhandled Rejections](https://nodejs.org/api/process.html#event-unhandledrejection)\n\n---\n\n### 3.2 Throw HTTP Exceptions from Services\n\n**Impact: HIGH** — Keeps controllers thin and simplifies error handling\n\nIt's acceptable (and often preferable) to throw `HttpException` subclasses from services in HTTP applications. This keeps controllers thin and allows services to communicate appropriate error states. For truly layer-agnostic services, use domain exceptions that map to HTTP status codes.\n\n**Incorrect (return error objects instead of throwing):**\n\n```typescript\n// Return error objects instead of throwing\n@Injectable()\nexport class UsersService {\n  async findById(id: string): Promise<{ user?: User; error?: string }> {\n    const user = await this.repo.findOne({ where: { id } });\n    if (!user) {\n      return { error: 'User not found' }; // Controller must check this\n    }\n    return { user };\n  }\n}\n\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string) {\n    const result = await this.usersService.findById(id);\n    if (result.error) {\n      throw new NotFoundException(result.error);\n    }\n    return result.user;\n  }\n}\n```\n\n**Correct (throw exceptions directly from service):**\n\n```typescript\n// Throw exceptions directly from service\n@Injectable()\nexport class UsersService {\n  constructor(private readonly repo: UserRepository) {}\n\n  async findById(id: string): Promise<User> {\n    const user = await this.repo.findOne({ where: { id } });\n    if (!user) {\n      throw new NotFoundException(`User #${id} not found`);\n    }\n    return user;\n  }\n\n  async create(dto: CreateUserDto): Promise<User> {\n    const existing = await this.repo.findOne({\n      where: { email: dto.email },\n    });\n    if (existing) {\n      throw new ConflictException('Email already registered');\n    }\n    return this.repo.save(dto);\n  }\n\n  async update(id: string, dto: UpdateUserDto): Promise<User> {\n    const user = await this.findById(id); // Throws if not found\n    Object.assign(user, dto);\n    return this.repo.save(user);\n  }\n}\n\n// Controller stays thin\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findById(id);\n  }\n\n  @Post()\n  create(@Body() dto: CreateUserDto): Promise<User> {\n    return this.usersService.create(dto);\n  }\n}\n\n// For layer-agnostic services, use domain exceptions\nexport class EntityNotFoundException extends Error {\n  constructor(\n    public readonly entity: string,\n    public readonly id: string,\n  ) {\n    super(`${entity} with ID \"${id}\" not found`);\n  }\n}\n\n// Map to HTTP in exception filter\n@Catch(EntityNotFoundException)\nexport class EntityNotFoundFilter implements ExceptionFilter {\n  catch(exception: EntityNotFoundException, host: ArgumentsHost) {\n    const ctx = host.switchToHttp();\n    const response = ctx.getResponse<Response>();\n\n    response.status(404).json({\n      statusCode: 404,\n      message: exception.message,\n      entity: exception.entity,\n      id: exception.id,\n    });\n  }\n}\n```\n\nReference: [NestJS Exception Filters](https://docs.nestjs.com/exception-filters)\n\n---\n\n### 3.3 Use Exception Filters for Error Handling\n\n**Impact: HIGH** — Consistent\n\n<!-- [TRUNCATED AT 50KB] -->",".agents/skills/nestjs-best-practices/README.md":"# NestJS Best Practices\n\n📖 [For Humans <3](https://kadajett.github.io/agent-nestjs-skills/)\n\nA structured repository for creating and maintaining NestJS Best Practices optimized for agents and LLMs.\n\n## Installation\n\nInstall this skill using [skills](https://github.com/vercel-labs/skills):\n\n```bash\n# GitHub shorthand\nnpx skills add Kadajett/agent-nestjs-skills\n\n# Install globally (available across all projects)\nnpx skills add Kadajett/agent-nestjs-skills --global\n\n# Install for specific agents\nnpx skills add Kadajett/agent-nestjs-skills -a claude-code -a cursor\n```\n\n### Supported Agents\n\n- Claude Code\n- OpenCode\n- Codex\n- Cursor\n- Antigravity\n- Roo Code\n\n## Structure\n\n- `rules/` - Individual rule files (one per rule)\n  - `_sections.md` - Section metadata (titles, impacts, descriptions)\n  - `_template.md` - Template for creating new rules\n  - `area-description.md` - Individual rule files\n- `scripts/` - Build scripts and utilities\n- `metadata.json` - Document metadata (version, organization, abstract)\n- **`AGENTS.md`** - Compiled output (generated)\n\n## Getting Started\n\n1. Install dependencies:\n\n   ```bash\n   cd scripts && npm install\n   ```\n\n2. Build AGENTS.md from rules:\n   ```bash\n   npm run build\n   # or\n   ./scripts/build.sh\n   ```\n\n## Creating a New Rule\n\n1. Copy `rules/_template.md` to `rules/area-description.md`\n2. Choose the appropriate area prefix:\n   - `arch-` for Architecture (Section 1)\n   - `di-` for Dependency Injection (Section 2)\n   - `error-` for Error Handling (Section 3)\n   - `security-` for Security (Section 4)\n   - `perf-` for Performance (Section 5)\n   - `test-` for Testing (Section 6)\n   - `db-` for Database & ORM (Section 7)\n   - `api-` for API Design (Section 8)\n   - `micro-` for Microservices (Section 9)\n   - `devops-` for DevOps & Deployment (Section 10)\n3. Fill in the frontmatter and content\n4. Ensure you have clear examples with explanations\n5. Run the build script to regenerate AGENTS.md\n\n## Rule File Structure\n\nEach rule file should follow this structure:\n\n````markdown\n---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: Optional description\ntags: tag1, tag2, tag3\n---\n\n## Rule Title Here\n\nBrief explanation of the rule and why it matters.\n\n**Incorrect (description of what's wrong):**\n\n```typescript\n// Bad code example\n```\n````\n\n**Correct (description of what's right):**\n\n```typescript\n// Good code example\n```\n\nOptional explanatory text after examples.\n\nReference: [NestJS Documentation](https://docs.nestjs.com)\n\n## File Naming Convention\n\n- Files starting with `_` are special (excluded from build)\n- Rule files: `area-description.md` (e.g., `arch-avoid-circular-deps.md`)\n- Section is automatically inferred from filename prefix\n- Rules are sorted alphabetically by title within each section\n- IDs (e.g., 1.1, 1.2) are auto-generated during build\n\n## Impact Levels\n\n| Level       | Description                                                                           |\n| ----------- | ------------------------------------------------------------------------------------- |\n| CRITICAL    | Violations cause runtime errors, security vulnerabilities, or architectural breakdown |\n| HIGH        | Significant impact on reliability, security, or maintainability                       |\n| MEDIUM-HIGH | Notable impact on quality and developer experience                                    |\n| MEDIUM      | Moderate impact on code quality and best practices                                    |\n| LOW-MEDIUM  | Minor improvements for consistency and maintainability                                |\n\n## Scripts\n\n- `npm run build` (in scripts/) - Compile rules into AGENTS.md\n\n## Contributing\n\nWhen adding or modifying rules:\n\n1. Use the correct filename prefix for your section\n2. Follow the `_template.md` structure\n3. Include clear bad/good examples with explanations\n4. Add appropriate tags\n5. Run the build script to regenerate AGENTS.md\n6. Rules are automatically sorted by title - no need to manage numbers!\n\n## Documentation Website\n\nThe documentation website source code lives on the [`docs` branch](https://github.com/Kadajett/agent-nestjs-skills/tree/docs/website). This separation keeps the skill installation lightweight while maintaining the full documentation site.\n\nTo contribute to the website:\n\n```bash\ngit checkout docs\ncd website\nnpm install\nnpm run dev\n```\n\n## Acknowledgments\n\n- Inspired by the [Vercel React Best Practices](https://github.com/vercel-labs/agent-skills) skill structure\n- Compatible with [skills](https://github.com/vercel-labs/skills) for easy installation across coding agents\n\n## Compatible Agents\n\nThese NestJS skills work with:\n\n- [Claude Code](https://claude.ai/code) - Anthropic's official CLI\n- [AdaL](https://sylph.ai/adal) - Self-evolving AI coding agent with MCP support",".agents/skills/nestjs-best-practices/SKILL.md":"---\nname: nestjs-best-practices\ndescription: NestJS best practices and architecture patterns for building production-ready applications. This skill should be used when writing, reviewing, or refactoring NestJS code to ensure proper patterns for modules, dependency injection, security, and performance.\nlicense: MIT\nmetadata:\n  author: Kadajett\n  version: '1.1.0'\n---\n\n# NestJS Best Practices\n\nComprehensive best practices guide for NestJS applications. Contains 40 rules across 10 categories, prioritized by impact to guide automated refactoring and code generation.\n\n## When to Apply\n\nReference these guidelines when:\n\n- Writing new NestJS modules, controllers, or services\n- Implementing authentication and authorization\n- Reviewing code for architecture and security issues\n- Refactoring existing NestJS codebases\n- Optimizing performance or database queries\n- Building microservices architectures\n\n## Rule Categories by Priority\n\n| Priority | Category             | Impact      | Prefix      |\n| -------- | -------------------- | ----------- | ----------- |\n| 1        | Architecture         | CRITICAL    | `arch-`     |\n| 2        | Dependency Injection | CRITICAL    | `di-`       |\n| 3        | Error Handling       | HIGH        | `error-`    |\n| 4        | Security             | HIGH        | `security-` |\n| 5        | Performance          | HIGH        | `perf-`     |\n| 6        | Testing              | MEDIUM-HIGH | `test-`     |\n| 7        | Database & ORM       | MEDIUM-HIGH | `db-`       |\n| 8        | API Design           | MEDIUM      | `api-`      |\n| 9        | Microservices        | MEDIUM      | `micro-`    |\n| 10       | DevOps & Deployment  | LOW-MEDIUM  | `devops-`   |\n\n## Quick Reference\n\n### 1. Architecture (CRITICAL)\n\n- `arch-avoid-circular-deps` - Avoid circular module dependencies\n- `arch-feature-modules` - Organize by feature, not technical layer\n- `arch-module-sharing` - Proper module exports/imports, avoid duplicate providers\n- `arch-single-responsibility` - Focused services over \"god services\"\n- `arch-use-repository-pattern` - Abstract database logic for testability\n- `arch-use-events` - Event-driven architecture for decoupling\n\n### 2. Dependency Injection (CRITICAL)\n\n- `di-avoid-service-locator` - Avoid service locator anti-pattern\n- `di-interface-segregation` - Interface Segregation Principle (ISP)\n- `di-liskov-substitution` - Liskov Substitution Principle (LSP)\n- `di-prefer-constructor-injection` - Constructor over property injection\n- `di-scope-awareness` - Understand singleton/request/transient scopes\n- `di-use-interfaces-tokens` - Use injection tokens for interfaces\n\n### 3. Error Handling (HIGH)\n\n- `error-use-exception-filters` - Centralized exception handling\n- `error-throw-http-exceptions` - Use NestJS HTTP exceptions\n- `error-handle-async-errors` - Handle async errors properly\n\n### 4. Security (HIGH)\n\n- `security-auth-jwt` - Secure JWT authentication\n- `security-validate-all-input` - Validate with class-validator\n- `security-use-guards` - Authentication and authorization guards\n- `security-sanitize-output` - Prevent XSS attacks\n- `security-rate-limiting` - Implement rate limiting\n\n### 5. Performance (HIGH)\n\n- `perf-async-hooks` - Proper async lifecycle hooks\n- `perf-use-caching` - Implement caching strategies\n- `perf-optimize-database` - Optimize database queries\n- `perf-lazy-loading` - Lazy load modules for faster startup\n\n### 6. Testing (MEDIUM-HIGH)\n\n- `test-use-testing-module` - Use NestJS testing utilities\n- `test-e2e-supertest` - E2E testing with Supertest\n- `test-mock-external-services` - Mock external dependencies\n\n### 7. Database & ORM (MEDIUM-HIGH)\n\n- `db-use-transactions` - Transaction management\n- `db-avoid-n-plus-one` - Avoid N+1 query problems\n- `db-use-migrations` - Use migrations for schema changes\n\n### 8. API Design (MEDIUM)\n\n- `api-use-dto-serialization` - DTO and response serialization\n- `api-use-interceptors` - Cross-cutting concerns\n- `api-versioning` - API versioning strategies\n- `api-use-pipes` - Input transformation with pipes\n\n### 9. Microservices (MEDIUM)\n\n- `micro-use-patterns` - Message and event patterns\n- `micro-use-health-checks` - Health checks for orchestration\n- `micro-use-queues` - Background job processing\n\n### 10. DevOps & Deployment (LOW-MEDIUM)\n\n- `devops-use-config-module` - Environment configuration\n- `devops-use-logging` - Structured logging\n- `devops-graceful-shutdown` - Zero-downtime deployments\n\n## How to Use\n\nRead individual rule files for detailed explanations and code examples:\n\n```\nrules/arch-avoid-circular-deps.md\nrules/security-validate-all-input.md\nrules/_sections.md\n```\n\nEach rule file contains:\n\n- Brief explanation of why it matters\n- Incorrect code example with explanation\n- Correct code example with explanation\n- Additional context and references\n\n## Full Compiled Document\n\nFor the complete guide with all rules expanded: `AGENTS.md`",".agents/skills/nestjs-best-practices/rules/api-use-dto-serialization.md":"---\ntitle: Use DTOs and Serialization for API Responses\nimpact: MEDIUM\nimpactDescription: Response DTOs prevent accidental data exposure and ensure consistency\ntags: api, dto, serialization, class-transformer\n---\n\n## Use DTOs and Serialization for API Responses\n\nNever return entity objects directly from controllers. Use response DTOs with class-transformer's `@Exclude()` and `@Expose()` decorators to control exactly what data is sent to clients. This prevents accidental exposure of sensitive fields and provides a stable API contract.\n\n**Incorrect (returning entities directly or manual spreading):**\n\n```typescript\n// Return entities directly\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findById(id);\n    // Returns: { id, email, passwordHash, ssn, internalNotes, ... }\n    // Exposes sensitive data!\n  }\n}\n\n// Manual object spreading (error-prone)\n@Get(':id')\nasync findOne(@Param('id') id: string) {\n  const user = await this.usersService.findById(id);\n  return {\n    id: user.id,\n    email: user.email,\n    name: user.name,\n    // Easy to forget to exclude sensitive fields\n    // Hard to maintain across endpoints\n  };\n}\n```\n\n**Correct (use class-transformer with @Exclude and response DTOs):**\n\n```typescript\n// Enable class-transformer globally\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));\n  await app.listen(3000);\n}\n\n// Entity with serialization control\n@Entity()\nexport class User {\n  @PrimaryGeneratedColumn('uuid')\n  id: string;\n\n  @Column()\n  email: string;\n\n  @Column()\n  name: string;\n\n  @Column()\n  @Exclude() // Never include in responses\n  passwordHash: string;\n\n  @Column({ nullable: true })\n  @Exclude()\n  ssn: string;\n\n  @Column({ default: false })\n  @Exclude({ toPlainOnly: true }) // Exclude from response, allow in requests\n  isAdmin: boolean;\n\n  @CreateDateColumn()\n  createdAt: Date;\n\n  @Column()\n  @Exclude()\n  internalNotes: string;\n}\n\n// Now returning entity is safe\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findById(id);\n    // Returns: { id, email, name, createdAt }\n    // Sensitive fields excluded automatically\n  }\n}\n\n// For different response shapes, use explicit DTOs\nexport class UserResponseDto {\n  @Expose()\n  id: string;\n\n  @Expose()\n  email: string;\n\n  @Expose()\n  name: string;\n\n  @Expose()\n  @Transform(({ obj }) => obj.posts?.length || 0)\n  postCount: number;\n\n  constructor(partial: Partial<User>) {\n    Object.assign(this, partial);\n  }\n}\n\nexport class UserDetailResponseDto extends UserResponseDto {\n  @Expose()\n  createdAt: Date;\n\n  @Expose()\n  @Type(() => PostResponseDto)\n  posts: PostResponseDto[];\n}\n\n// Controller with explicit DTOs\n@Controller('users')\nexport class UsersController {\n  @Get()\n  @SerializeOptions({ type: UserResponseDto })\n  async findAll(): Promise<UserResponseDto[]> {\n    const users = await this.usersService.findAll();\n    return users.map(u => plainToInstance(UserResponseDto, u));\n  }\n\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<UserDetailResponseDto> {\n    const user = await this.usersService.findByIdWithPosts(id);\n    return plainToInstance(UserDetailResponseDto, user, {\n      excludeExtraneousValues: true,\n    });\n  }\n}\n\n// Groups for conditional serialization\nexport class UserDto {\n  @Expose()\n  id: string;\n\n  @Expose()\n  name: string;\n\n  @Expose({ groups: ['admin'] })\n  email: string;\n\n  @Expose({ groups: ['admin'] })\n  createdAt: Date;\n\n  @Expose({ groups: ['admin', 'owner'] })\n  settings: UserSettings;\n}\n\n@Controller('users')\nexport class UsersController {\n  @Get()\n  @SerializeOptions({ groups: ['public'] })\n  async findAllPublic(): Promise<UserDto[]> {\n    // Returns: { id, name }\n  }\n\n  @Get('admin')\n  @UseGuards(AdminGuard)\n  @SerializeOptions({ groups: ['admin'] })\n  async findAllAdmin(): Promise<UserDto[]> {\n    // Returns: { id, name, email, createdAt }\n  }\n\n  @Get('me')\n  @SerializeOptions({ groups: ['owner'] })\n  async getProfile(@CurrentUser() user: User): Promise<UserDto> {\n    // Returns: { id, name, settings }\n  }\n}\n```\n\nReference: [NestJS Serialization](https://docs.nestjs.com/techniques/serialization)",".agents/skills/nestjs-best-practices/rules/api-use-interceptors.md":"---\ntitle: Use Interceptors for Cross-Cutting Concerns\nimpact: MEDIUM-HIGH\nimpactDescription: Interceptors provide clean separation for cross-cutting logic\ntags: api, interceptors, logging, caching\n---\n\n## Use Interceptors for Cross-Cutting Concerns\n\nInterceptors can transform responses, add logging, handle caching, and measure performance without polluting your business logic. They wrap the route handler execution, giving you access to both the request and response streams.\n\n**Incorrect (logging and transformation in every method):**\n\n```typescript\n// Logging in every controller method\n@Controller('users')\nexport class UsersController {\n  @Get()\n  async findAll(): Promise<User[]> {\n    const start = Date.now();\n    this.logger.log('findAll called');\n\n    const users = await this.usersService.findAll();\n\n    this.logger.log(`findAll completed in ${Date.now() - start}ms`);\n    return users;\n  }\n\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    const start = Date.now();\n    this.logger.log(`findOne called with id: ${id}`);\n\n    const user = await this.usersService.findOne(id);\n\n    this.logger.log(`findOne completed in ${Date.now() - start}ms`);\n    return user;\n  }\n  // Repeated in every method!\n}\n\n// Manual response wrapping\n@Get()\nasync findAll(): Promise<{ data: User[]; meta: Meta }> {\n  const users = await this.usersService.findAll();\n  return {\n    data: users,\n    meta: { timestamp: new Date(), count: users.length },\n  };\n}\n```\n\n**Correct (use interceptors for cross-cutting concerns):**\n\n```typescript\n// Logging interceptor\n@Injectable()\nexport class LoggingInterceptor implements NestInterceptor {\n  private readonly logger = new Logger('HTTP');\n\n  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n    const request = context.switchToHttp().getRequest();\n    const { method, url, body } = request;\n    const now = Date.now();\n\n    return next.handle().pipe(\n      tap({\n        next: (data) => {\n          const response = context.switchToHttp().getResponse();\n          this.logger.log(\n            `${method} ${url} ${response.statusCode} - ${Date.now() - now}ms`,\n          );\n        },\n        error: (error) => {\n          this.logger.error(\n            `${method} ${url} ${error.status || 500} - ${Date.now() - now}ms`,\n            error.stack,\n          );\n        },\n      }),\n    );\n  }\n}\n\n// Response transformation interceptor\n@Injectable()\nexport class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {\n  intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {\n    return next.handle().pipe(\n      map((data) => ({\n        data,\n        meta: {\n          timestamp: new Date().toISOString(),\n          path: context.switchToHttp().getRequest().url,\n        },\n      })),\n    );\n  }\n}\n\n// Timeout interceptor\n@Injectable()\nexport class TimeoutInterceptor implements NestInterceptor {\n  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n    return next.handle().pipe(\n      timeout(5000),\n      catchError((err) => {\n        if (err instanceof TimeoutError) {\n          throw new RequestTimeoutException('Request timed out');\n        }\n        throw err;\n      }),\n    );\n  }\n}\n\n// Apply globally or per-controller\n@Module({\n  providers: [\n    { provide: APP_INTERCEPTOR, useClass: LoggingInterceptor },\n    { provide: APP_INTERCEPTOR, useClass: TransformInterceptor },\n  ],\n})\nexport class AppModule {}\n\n// Or per-controller\n@Controller('users')\n@UseInterceptors(LoggingInterceptor)\nexport class UsersController {\n  @Get()\n  async findAll(): Promise<User[]> {\n    // Clean business logic only\n    return this.usersService.findAll();\n  }\n}\n\n// Custom cache interceptor with TTL\n@Injectable()\nexport class HttpCacheInterceptor implements NestInterceptor {\n  constructor(\n    private cacheManager: Cache,\n    private reflector: Reflector,\n  ) {}\n\n  async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {\n    const request = context.switchToHttp().getRequest();\n\n    // Only cache GET requests\n    if (request.method !== 'GET') {\n      return next.handle();\n    }\n\n    const cacheKey = this.generateKey(request);\n    const ttl = this.reflector.get<number>('cacheTTL', context.getHandler()) || 300;\n\n    const cached = await this.cacheManager.get(cacheKey);\n    if (cached) {\n      return of(cached);\n    }\n\n    return next.handle().pipe(\n      tap((response) => {\n        this.cacheManager.set(cacheKey, response, ttl);\n      }),\n    );\n  }\n\n  private generateKey(request: Request): string {\n    return `cache:${request.url}:${JSON.stringify(request.query)}`;\n  }\n}\n\n// Usage with custom TTL\n@Get()\n@SetMetadata('cacheTTL', 600)\n@UseInterceptors(HttpCacheInterceptor)\nasync findAll(): Promise<User[]> {\n  return this.usersService.findAll();\n}\n\n// Error mapping interceptor\n@Injectable()\nexport class ErrorMappingInterceptor implements NestInterceptor {\n  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n    return next.handle().pipe(\n      catchError((error) => {\n        if (error instanceof EntityNotFoundError) {\n          throw new NotFoundException(error.message);\n        }\n        if (error instanceof QueryFailedError) {\n          if (error.message.includes('duplicate')) {\n            throw new ConflictException('Resource already exists');\n          }\n        }\n        throw error;\n      }),\n    );\n  }\n}\n```\n\nReference: [NestJS Interceptors](https://docs.nestjs.com/interceptors)",".agents/skills/nestjs-best-practices/rules/api-use-pipes.md":"---\ntitle: Use Pipes for Input Transformation\nimpact: MEDIUM\nimpactDescription: Pipes ensure clean, validated data reaches your handlers\ntags: api, pipes, validation, transformation\n---\n\n## Use Pipes for Input Transformation\n\nUse built-in pipes like `ParseIntPipe`, `ParseUUIDPipe`, and `DefaultValuePipe` for common transformations. Create custom pipes for business-specific transformations. Pipes separate validation/transformation logic from controllers.\n\n**Incorrect (manual type parsing in handlers):**\n\n```typescript\n// Manual type parsing in handlers\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    // Manual validation in every handler\n    const uuid = id.trim();\n    if (!isUUID(uuid)) {\n      throw new BadRequestException('Invalid UUID');\n    }\n    return this.usersService.findOne(uuid);\n  }\n\n  @Get()\n  async findAll(\n    @Query('page') page: string,\n    @Query('limit') limit: string,\n  ): Promise<User[]> {\n    // Manual parsing and defaults\n    const pageNum = parseInt(page) || 1;\n    const limitNum = parseInt(limit) || 10;\n    return this.usersService.findAll(pageNum, limitNum);\n  }\n}\n\n// Type coercion without validation\n@Get()\nasync search(@Query('price') price: string): Promise<Product[]> {\n  const priceNum = +price; // NaN if invalid, no error\n  return this.productsService.findByPrice(priceNum);\n}\n```\n\n**Correct (use built-in and custom pipes):**\n\n```typescript\n// Use built-in pipes for common transformations\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id', ParseUUIDPipe) id: string): Promise<User> {\n    // id is guaranteed to be a valid UUID\n    return this.usersService.findOne(id);\n  }\n\n  @Get()\n  async findAll(\n    @Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number,\n    @Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number,\n  ): Promise<User[]> {\n    // Automatic defaults and type conversion\n    return this.usersService.findAll(page, limit);\n  }\n\n  @Get('by-status/:status')\n  async findByStatus(\n    @Param('status', new ParseEnumPipe(UserStatus)) status: UserStatus,\n  ): Promise<User[]> {\n    return this.usersService.findByStatus(status);\n  }\n}\n\n// Custom pipe for business logic\n@Injectable()\nexport class ParseDatePipe implements PipeTransform<string, Date> {\n  transform(value: string): Date {\n    const date = new Date(value);\n    if (isNaN(date.getTime())) {\n      throw new BadRequestException('Invalid date format');\n    }\n    return date;\n  }\n}\n\n@Get('reports')\nasync getReports(\n  @Query('from', ParseDatePipe) from: Date,\n  @Query('to', ParseDatePipe) to: Date,\n): Promise<Report[]> {\n  return this.reportsService.findBetween(from, to);\n}\n\n// Custom transformation pipes\n@Injectable()\nexport class NormalizeEmailPipe implements PipeTransform<string, string> {\n  transform(value: string): string {\n    if (!value) return value;\n    return value.trim().toLowerCase();\n  }\n}\n\n// Parse comma-separated values\n@Injectable()\nexport class ParseArrayPipe implements PipeTransform<string, string[]> {\n  transform(value: string): string[] {\n    if (!value) return [];\n    return value.split(',').map((v) => v.trim()).filter(Boolean);\n  }\n}\n\n@Get('products')\nasync findProducts(\n  @Query('ids', ParseArrayPipe) ids: string[],\n  @Query('email', NormalizeEmailPipe) email: string,\n): Promise<Product[]> {\n  // ids is already an array, email is normalized\n  return this.productsService.findByIds(ids);\n}\n\n// Sanitize HTML input\n@Injectable()\nexport class SanitizeHtmlPipe implements PipeTransform<string, string> {\n  transform(value: string): string {\n    if (!value) return value;\n    return sanitizeHtml(value, { allowedTags: [] });\n  }\n}\n\n// Global validation pipe with transformation\napp.useGlobalPipes(\n  new ValidationPipe({\n    whitelist: true, // Strip non-DTO properties\n    transform: true, // Auto-transform to DTO types\n    transformOptions: {\n      enableImplicitConversion: true, // Convert query strings to numbers\n    },\n    forbidNonWhitelisted: true, // Throw on extra properties\n  }),\n);\n\n// DTO with transformation decorators\nexport class FindProductsDto {\n  @IsOptional()\n  @Type(() => Number)\n  @IsInt()\n  @Min(1)\n  page?: number = 1;\n\n  @IsOptional()\n  @Type(() => Number)\n  @IsInt()\n  @Min(1)\n  @Max(100)\n  limit?: number = 10;\n\n  @IsOptional()\n  @Transform(({ value }) => value?.toLowerCase())\n  @IsString()\n  search?: string;\n\n  @IsOptional()\n  @Transform(({ value }) => value?.split(','))\n  @IsArray()\n  @IsString({ each: true })\n  categories?: string[];\n}\n\n@Get()\nasync findAll(@Query() dto: FindProductsDto): Promise<Product[]> {\n  // dto is already transformed and validated\n  return this.productsService.findAll(dto);\n}\n\n// Pipe error customization\n@Injectable()\nexport class CustomParseIntPipe extends ParseIntPipe {\n  constructor() {\n    super({\n      exceptionFactory: (error) =>\n        new BadRequestException(`${error} must be a valid integer`),\n    });\n  }\n}\n\n// Or use options on built-in pipes\n@Get(':id')\nasync findOne(\n  @Param(\n    'id',\n    new ParseIntPipe({\n      errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE,\n      exceptionFactory: () => new NotAcceptableException('ID must be numeric'),\n    }),\n  )\n  id: number,\n): Promise<Item> {\n  return this.itemsService.findOne(id);\n}\n```\n\nReference: [NestJS Pipes](https://docs.nestjs.com/pipes)",".agents/skills/nestjs-best-practices/rules/api-versioning.md":"---\ntitle: Use API Versioning for Breaking Changes\nimpact: MEDIUM\nimpactDescription: Versioning allows you to evolve APIs without breaking existing clients\ntags: api, versioning, breaking-changes, compatibility\n---\n\n## Use API Versioning for Breaking Changes\n\nUse NestJS built-in versioning when making breaking changes to your API. Choose a versioning strategy (URI, header, or media type) and apply it consistently. This allows old clients to continue working while new clients use updated endpoints.\n\n**Incorrect (breaking changes without versioning):**\n\n```typescript\n// Breaking changes without versioning\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    // Original response: { id, name, email }\n    // Later changed to: { id, firstName, lastName, emailAddress }\n    // Old clients break!\n    return this.usersService.findOne(id);\n  }\n}\n\n// Manual versioning in routes\n@Controller('v1/users')\nexport class UsersV1Controller {}\n\n@Controller('v2/users')\nexport class UsersV2Controller {}\n// Inconsistent, error-prone, hard to maintain\n```\n\n**Correct (use NestJS built-in versioning):**\n\n```typescript\n// Enable versioning in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n\n  // URI versioning: /v1/users, /v2/users\n  app.enableVersioning({\n    type: VersioningType.URI,\n    defaultVersion: '1',\n  });\n\n  // Or header versioning: X-API-Version: 1\n  app.enableVersioning({\n    type: VersioningType.HEADER,\n    header: 'X-API-Version',\n    defaultVersion: '1',\n  });\n\n  // Or media type: Accept: application/json;v=1\n  app.enableVersioning({\n    type: VersioningType.MEDIA_TYPE,\n    key: 'v=',\n    defaultVersion: '1',\n  });\n\n  await app.listen(3000);\n}\n\n// Version-specific controllers\n@Controller('users')\n@Version('1')\nexport class UsersV1Controller {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<UserV1Response> {\n    const user = await this.usersService.findOne(id);\n    // V1 response format\n    return {\n      id: user.id,\n      name: user.name,\n      email: user.email,\n    };\n  }\n}\n\n@Controller('users')\n@Version('2')\nexport class UsersV2Controller {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<UserV2Response> {\n    const user = await this.usersService.findOne(id);\n    // V2 response format with breaking changes\n    return {\n      id: user.id,\n      firstName: user.firstName,\n      lastName: user.lastName,\n      emailAddress: user.email,\n      createdAt: user.createdAt,\n    };\n  }\n}\n\n// Per-route versioning - different versions for different routes\n@Controller('users')\nexport class UsersController {\n  @Get()\n  @Version('1')\n  findAllV1(): Promise<UserV1Response[]> {\n    return this.usersService.findAllV1();\n  }\n\n  @Get()\n  @Version('2')\n  findAllV2(): Promise<UserV2Response[]> {\n    return this.usersService.findAllV2();\n  }\n\n  @Get(':id')\n  @Version(['1', '2']) // Same handler for multiple versions\n  findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findOne(id);\n  }\n\n  @Post()\n  @Version(VERSION_NEUTRAL) // Available in all versions\n  create(@Body() dto: CreateUserDto): Promise<User> {\n    return this.usersService.create(dto);\n  }\n}\n\n// Shared service with version-specific logic\n@Injectable()\nexport class UsersService {\n  async findOne(id: string, version: string): Promise<any> {\n    const user = await this.repo.findOne({ where: { id } });\n\n    if (version === '1') {\n      return this.toV1Response(user);\n    }\n    return this.toV2Response(user);\n  }\n\n  private toV1Response(user: User): UserV1Response {\n    return {\n      id: user.id,\n      name: `${user.firstName} ${user.lastName}`,\n      email: user.email,\n    };\n  }\n\n  private toV2Response(user: User): UserV2Response {\n    return {\n      id: user.id,\n      firstName: user.firstName,\n      lastName: user.lastName,\n      emailAddress: user.email,\n      createdAt: user.createdAt,\n    };\n  }\n}\n\n// Controller extracts version\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string, @Headers('X-API-Version') version: string = '1'): Promise<any> {\n    return this.usersService.findOne(id, version);\n  }\n}\n\n// Deprecation strategy - mark old versions as deprecated\n@Controller('users')\n@Version('1')\n@UseInterceptors(DeprecationInterceptor)\nexport class UsersV1Controller {\n  // All V1 routes will include deprecation warning\n}\n\n@Injectable()\nexport class DeprecationInterceptor implements NestInterceptor {\n  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n    const response = context.switchToHttp().getResponse();\n    response.setHeader('Deprecation', 'true');\n    response.setHeader('Sunset', 'Sat, 1 Jan 2025 00:00:00 GMT');\n    response.setHeader('Link', '</v2/users>; rel=\"successor-version\"');\n\n    return next.handle();\n  }\n}\n```\n\nReference: [NestJS Versioning](https://docs.nestjs.com/techniques/versioning)",".agents/skills/nestjs-best-practices/rules/arch-avoid-circular-deps.md":"---\ntitle: Avoid Circular Dependencies\nimpact: CRITICAL\nimpactDescription: '#1 cause of runtime crashes'\ntags: architecture, modules, dependencies\n---\n\n## Avoid Circular Dependencies\n\nCircular dependencies occur when Module A imports Module B, and Module B imports Module A (directly or transitively). NestJS can sometimes resolve these through forward references, but they indicate architectural problems and should be avoided. This is the #1 cause of runtime crashes in NestJS applications.\n\n**Incorrect (circular module imports):**\n\n```typescript\n// users.module.ts\n@Module({\n  imports: [OrdersModule], // Orders needs Users, Users needs Orders = circular\n  providers: [UsersService],\n  exports: [UsersService],\n})\nexport class UsersModule {}\n\n// orders.module.ts\n@Module({\n  imports: [UsersModule], // Circular dependency!\n  providers: [OrdersService],\n  exports: [OrdersService],\n})\nexport class OrdersModule {}\n```\n\n**Correct (extract shared logic or use events):**\n\n```typescript\n// Option 1: Extract shared logic to a third module\n// shared.module.ts\n@Module({\n  providers: [SharedService],\n  exports: [SharedService],\n})\nexport class SharedModule {}\n\n// users.module.ts\n@Module({\n  imports: [SharedModule],\n  providers: [UsersService],\n})\nexport class UsersModule {}\n\n// orders.module.ts\n@Module({\n  imports: [SharedModule],\n  providers: [OrdersService],\n})\nexport class OrdersModule {}\n\n// Option 2: Use events for decoupled communication\n// users.service.ts\n@Injectable()\nexport class UsersService {\n  constructor(private eventEmitter: EventEmitter2) {}\n\n  async createUser(data: CreateUserDto) {\n    const user = await this.userRepo.save(data);\n    this.eventEmitter.emit('user.created', user);\n    return user;\n  }\n}\n\n// orders.service.ts\n@Injectable()\nexport class OrdersService {\n  @OnEvent('user.created')\n  handleUserCreated(user: User) {\n    // React to user creation without direct dependency\n  }\n}\n```\n\nReference: [NestJS Circular Dependency](https://docs.nestjs.com/fundamentals/circular-dependency)",".agents/skills/nestjs-best-practices/rules/arch-feature-modules.md":"---\ntitle: Organize by Feature Modules\nimpact: CRITICAL\nimpactDescription: '3-5x faster onboarding and development'\ntags: architecture, modules, organization\n---\n\n## Organize by Feature Modules\n\nOrganize your application into feature modules that encapsulate related functionality. Each feature module should be self-contained with its own controllers, services, entities, and DTOs. Avoid organizing by technical layer (all controllers together, all services together). This enables 3-5x faster onboarding and feature development.\n\n**Incorrect (technical layer organization):**\n\n```typescript\n// Technical layer organization (anti-pattern)\nsrc/\n├── controllers/\n│   ├── users.controller.ts\n│   ├── orders.controller.ts\n│   └── products.controller.ts\n├── services/\n│   ├── users.service.ts\n│   ├── orders.service.ts\n│   └── products.service.ts\n├── entities/\n│   ├── user.entity.ts\n│   ├── order.entity.ts\n│   └── product.entity.ts\n└── app.module.ts  // Imports everything directly\n```\n\n**Correct (feature module organization):**\n\n```typescript\n// Feature module organization\nsrc/\n├── users/\n│   ├── dto/\n│   │   ├── create-user.dto.ts\n│   │   └── update-user.dto.ts\n│   ├── entities/\n│   │   └── user.entity.ts\n│   ├── users.controller.ts\n│   ├── users.service.ts\n│   ├── users.repository.ts\n│   └── users.module.ts\n├── orders/\n│   ├── dto/\n│   ├── entities/\n│   ├── orders.controller.ts\n│   ├── orders.service.ts\n│   └── orders.module.ts\n├── shared/\n│   ├── guards/\n│   ├── interceptors/\n│   ├── filters/\n│   └── shared.module.ts\n└── app.module.ts\n\n// users.module.ts\n@Module({\n  imports: [TypeOrmModule.forFeature([User])],\n  controllers: [UsersController],\n  providers: [UsersService, UsersRepository],\n  exports: [UsersService], // Only export what others need\n})\nexport class UsersModule {}\n\n// app.module.ts\n@Module({\n  imports: [\n    ConfigModule.forRoot(),\n    TypeOrmModule.forRoot(),\n    UsersModule,\n    OrdersModule,\n    SharedModule,\n  ],\n})\nexport class AppModule {}\n```\n\nReference: [NestJS Modules](https://docs.nestjs.com/modules)",".agents/skills/nestjs-best-practices/rules/arch-module-sharing.md":"---\ntitle: Use Proper Module Sharing Patterns\nimpact: CRITICAL\nimpactDescription: Prevents duplicate instances, memory leaks, and state inconsistency\ntags: architecture, modules, sharing, exports\n---\n\n## Use Proper Module Sharing Patterns\n\nNestJS modules are singletons by default. When a service is properly exported from a module and that module is imported elsewhere, the same instance is shared. However, providing a service in multiple modules creates separate instances, leading to memory waste, state inconsistency, and confusing behavior. Always encapsulate services in dedicated modules, export them explicitly, and import the module where needed.\n\n**Incorrect (service provided in multiple modules):**\n\n```typescript\n// StorageService provided directly in multiple modules - WRONG\n// storage.service.ts\n@Injectable()\nexport class StorageService {\n  private cache = new Map(); // Each instance has separate state!\n\n  store(key: string, value: any) {\n    this.cache.set(key, value);\n  }\n}\n\n// app.module.ts\n@Module({\n  providers: [StorageService], // Instance #1\n  controllers: [AppController],\n})\nexport class AppModule {}\n\n// videos.module.ts\n@Module({\n  providers: [StorageService], // Instance #2 - different from AppModule!\n  controllers: [VideosController],\n})\nexport class VideosModule {}\n\n// Problems:\n// 1. Two separate StorageService instances exist\n// 2. cache.set() in VideosModule doesn't affect AppModule's cache\n// 3. Memory wasted on duplicate instances\n// 4. Debugging nightmares when state doesn't sync\n```\n\n**Correct (dedicated module with exports):**\n\n```typescript\n// storage/storage.module.ts\n@Module({\n  providers: [StorageService],\n  exports: [StorageService], // Make available to importers\n})\nexport class StorageModule {}\n\n// videos/videos.module.ts\n@Module({\n  imports: [StorageModule], // Import the module, not the service\n  controllers: [VideosController],\n  providers: [VideosService],\n})\nexport class VideosModule {}\n\n// channels/channels.module.ts\n@Module({\n  imports: [StorageModule], // Same instance shared\n  controllers: [ChannelsController],\n  providers: [ChannelsService],\n})\nexport class ChannelsModule {}\n\n// app.module.ts\n@Module({\n  imports: [\n    StorageModule, // Only if AppModule itself needs StorageService\n    VideosModule,\n    ChannelsModule,\n  ],\n})\nexport class AppModule {}\n\n// Now all modules share the SAME StorageService instance\n```\n\n**When to use @Global() (sparingly):**\n\n```typescript\n// ONLY for truly cross-cutting concerns\n@Global()\n@Module({\n  providers: [ConfigService, LoggerService],\n  exports: [ConfigService, LoggerService],\n})\nexport class CoreModule {}\n\n// Import once in AppModule\n@Module({\n  imports: [CoreModule], // Registered globally, available everywhere\n})\nexport class AppModule {}\n\n// Other modules don't need to import CoreModule\n@Module({\n  controllers: [UsersController],\n  providers: [UsersService], // Can inject ConfigService without importing\n})\nexport class UsersModule {}\n\n// WARNING: Don't make everything global!\n// - Hides dependencies (can't see what a module needs from imports)\n// - Makes testing harder\n// - Reserve for: config, logging, database connections\n```\n\n**Module re-exporting pattern:**\n\n```typescript\n// common.module.ts - shared utilities\n@Module({\n  providers: [DateService, ValidationService],\n  exports: [DateService, ValidationService],\n})\nexport class CommonModule {}\n\n// core.module.ts - re-exports common for convenience\n@Module({\n  imports: [CommonModule, DatabaseModule],\n  exports: [CommonModule, DatabaseModule], // Re-export for consumers\n})\nexport class CoreModule {}\n\n// feature.module.ts - imports CoreModule, gets both\n@Module({\n  imports: [CoreModule], // Gets CommonModule + DatabaseModule\n  controllers: [FeatureController],\n})\nexport class FeatureModule {}\n```\n\nReference: [NestJS Modules](https://docs.nestjs.com/modules#shared-modules)",".agents/skills/nestjs-best-practices/rules/arch-single-responsibility.md":"---\ntitle: Single Responsibility for Services\nimpact: CRITICAL\nimpactDescription: '40%+ improvement in testability'\ntags: architecture, services, single-responsibility\n---\n\n## Single Responsibility for Services\n\nEach service should have a single, well-defined responsibility. Avoid \"god services\" that handle multiple unrelated concerns. If a service name includes \"And\" or handles more than one domain concept, it likely violates single responsibility. This reduces complexity and improves testability by 40%+.\n\n**Incorrect (god service anti-pattern):**\n\n```typescript\n// God service anti-pattern\n@Injectable()\nexport class UserAndOrderService {\n  constructor(\n    private userRepo: UserRepository,\n    private orderRepo: OrderRepository,\n    private mailer: MailService,\n    private payment: PaymentService,\n  ) {}\n\n  async createUser(dto: CreateUserDto) {\n    const user = await this.userRepo.save(dto);\n    await this.mailer.sendWelcome(user);\n    return user;\n  }\n\n  async createOrder(userId: string, dto: CreateOrderDto) {\n    const order = await this.orderRepo.save({ userId, ...dto });\n    await this.payment.charge(order);\n    await this.mailer.sendOrderConfirmation(order);\n    return order;\n  }\n\n  async calculateOrderStats(userId: string) {\n    // Stats logic mixed in\n  }\n\n  async validatePayment(orderId: string) {\n    // Payment logic mixed in\n  }\n}\n```\n\n**Correct (focused services with single responsibility):**\n\n```typescript\n// Focused services with single responsibility\n@Injectable()\nexport class UsersService {\n  constructor(private userRepo: UserRepository) {}\n\n  async create(dto: CreateUserDto): Promise<User> {\n    return this.userRepo.save(dto);\n  }\n\n  async findById(id: string): Promise<User> {\n    return this.userRepo.findOneOrFail({ where: { id } });\n  }\n}\n\n@Injectable()\nexport class OrdersService {\n  constructor(private orderRepo: OrderRepository) {}\n\n  async create(userId: string, dto: CreateOrderDto): Promise<Order> {\n    return this.orderRepo.save({ userId, ...dto });\n  }\n\n  async findByUser(userId: string): Promise<Order[]> {\n    return this.orderRepo.find({ where: { userId } });\n  }\n}\n\n@Injectable()\nexport class OrderStatsService {\n  constructor(private orderRepo: OrderRepository) {}\n\n  async calculateForUser(userId: string): Promise<OrderStats> {\n    // Focused stats calculation\n  }\n}\n\n// Orchestration in controller or dedicated orchestrator\n@Controller('orders')\nexport class OrdersController {\n  constructor(\n    private orders: OrdersService,\n    private payment: PaymentService,\n    private notifications: NotificationService,\n  ) {}\n\n  @Post()\n  async create(@CurrentUser() user: User, @Body() dto: CreateOrderDto) {\n    const order = await this.orders.create(user.id, dto);\n    await this.payment.charge(order);\n    await this.notifications.sendOrderConfirmation(order);\n    return order;\n  }\n}\n```\n\nReference: [NestJS Providers](https://docs.nestjs.com/providers)",".agents/skills/nestjs-best-practices/rules/arch-use-events.md":"---\ntitle: Use Event-Driven Architecture for Decoupling\nimpact: MEDIUM-HIGH\nimpactDescription: Enables async processing and modularity\ntags: architecture, events, decoupling\n---\n\n## Use Event-Driven Architecture for Decoupling\n\nUse `@nestjs/event-emitter` for intra-service events and message brokers for inter-service communication. Events allow modules to react to changes without direct dependencies, improving modularity and enabling async processing.\n\n**Incorrect (direct service coupling):**\n\n```typescript\n// Direct service coupling\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private inventoryService: InventoryService,\n    private emailService: EmailService,\n    private analyticsService: AnalyticsService,\n    private notificationService: NotificationService,\n    private loyaltyService: LoyaltyService,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Tight coupling - OrdersService knows about all consumers\n    await this.inventoryService.reserve(order.items);\n    await this.emailService.sendConfirmation(order);\n    await this.analyticsService.track('order_created', order);\n    await this.notificationService.push(order.userId, 'Order placed');\n    await this.loyaltyService.addPoints(order.userId, order.total);\n\n    // Adding new behavior requires modifying this service\n    return order;\n  }\n}\n```\n\n**Correct (event-driven decoupling):**\n\n```typescript\n// Use EventEmitter for decoupling\nimport { EventEmitter2 } from '@nestjs/event-emitter';\n\n// Define event\nexport class OrderCreatedEvent {\n  constructor(\n    public readonly orderId: string,\n    public readonly userId: string,\n    public readonly items: OrderItem[],\n    public readonly total: number,\n  ) {}\n}\n\n// Service emits events\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private eventEmitter: EventEmitter2,\n    private repo: Repository<Order>,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Emit event - no knowledge of consumers\n    this.eventEmitter.emit('order.created', new OrderCreatedEvent(order.id, order.userId, order.items, order.total));\n\n    return order;\n  }\n}\n\n// Listeners in separate modules\n@Injectable()\nexport class InventoryListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.inventoryService.reserve(event.items);\n  }\n}\n\n@Injectable()\nexport class EmailListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.emailService.sendConfirmation(event.orderId);\n  }\n}\n\n@Injectable()\nexport class AnalyticsListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.analyticsService.track('order_created', {\n      orderId: event.orderId,\n      total: event.total,\n    });\n  }\n}\n```\n\nReference: [NestJS Events](https://docs.nestjs.com/techniques/events)",".agents/skills/nestjs-best-practices/rules/arch-use-repository-pattern.md":"---\ntitle: Use Repository Pattern for Data Access\nimpact: HIGH\nimpactDescription: Decouples business logic from database\ntags: architecture, repository, data-access\n---\n\n## Use Repository Pattern for Data Access\n\nCreate custom repositories to encapsulate complex queries and database logic. This keeps services focused on business logic, makes testing easier with mock repositories, and allows changing database implementations without affecting business code.\n\n**Incorrect (complex queries in services):**\n\n```typescript\n// Complex queries in services\n@Injectable()\nexport class UsersService {\n  constructor(@InjectRepository(User) private repo: Repository<User>) {}\n\n  async findActiveWithOrders(minOrders: number): Promise<User[]> {\n    // Complex query logic mixed with business logic\n    return this.repo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.orders', 'order')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('user.deletedAt IS NULL')\n      .groupBy('user.id')\n      .having('COUNT(order.id) >= :min', { min: minOrders })\n      .orderBy('user.createdAt', 'DESC')\n      .getMany();\n  }\n\n  // Service becomes bloated with query logic\n}\n```\n\n**Correct (custom repository with encapsulated queries):**\n\n```typescript\n// Custom repository with encapsulated queries\n@Injectable()\nexport class UsersRepository {\n  constructor(@InjectRepository(User) private repo: Repository<User>) {}\n\n  async findById(id: string): Promise<User | null> {\n    return this.repo.findOne({ where: { id } });\n  }\n\n  async findByEmail(email: string): Promise<User | null> {\n    return this.repo.findOne({ where: { email } });\n  }\n\n  async findActiveWithMinOrders(minOrders: number): Promise<User[]> {\n    return this.repo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.orders', 'order')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('user.deletedAt IS NULL')\n      .groupBy('user.id')\n      .having('COUNT(order.id) >= :min', { min: minOrders })\n      .orderBy('user.createdAt', 'DESC')\n      .getMany();\n  }\n\n  async save(user: User): Promise<User> {\n    return this.repo.save(user);\n  }\n}\n\n// Clean service with business logic only\n@Injectable()\nexport class UsersService {\n  constructor(private usersRepo: UsersRepository) {}\n\n  async getActiveUsersWithOrders(): Promise<User[]> {\n    return this.usersRepo.findActiveWithMinOrders(1);\n  }\n\n  async create(dto: CreateUserDto): Promise<User> {\n    const existing = await this.usersRepo.findByEmail(dto.email);\n    if (existing) {\n      throw new ConflictException('Email already registered');\n    }\n\n    const user = new User();\n    user.email = dto.email;\n    user.name = dto.name;\n    return this.usersRepo.save(user);\n  }\n}\n```\n\nReference: [Repository Pattern](https://martinfowler.com/eaaCatalog/repository.html)",".agents/skills/nestjs-best-practices/rules/db-avoid-n-plus-one.md":"---\ntitle: Avoid N+1 Query Problems\nimpact: HIGH\nimpactDescription: N+1 queries are one of the most common performance killers\ntags: database, n-plus-one, queries, performance\n---\n\n## Avoid N+1 Query Problems\n\nN+1 queries occur when you fetch a list of entities, then make an additional query for each entity to load related data. Use eager loading with `relations`, query builder joins, or DataLoader to batch queries efficiently.\n\n**Incorrect (lazy loading in loops causes N+1):**\n\n```typescript\n// Lazy loading in loops causes N+1\n@Injectable()\nexport class OrdersService {\n  async getOrdersWithItems(userId: string): Promise<Order[]> {\n    const orders = await this.orderRepo.find({ where: { userId } });\n    // 1 query for orders\n\n    for (const order of orders) {\n      // N additional queries - one per order!\n      order.items = await this.itemRepo.find({ where: { orderId: order.id } });\n    }\n\n    return orders;\n  }\n}\n\n// Accessing lazy relations without loading\n@Controller('users')\nexport class UsersController {\n  @Get()\n  async findAll(): Promise<User[]> {\n    const users = await this.userRepo.find();\n    // If User.posts is lazy-loaded, serializing triggers N queries\n    return users; // Each user.posts access = 1 query\n  }\n}\n```\n\n**Correct (use relations for eager loading):**\n\n```typescript\n// Use relations option for eager loading\n@Injectable()\nexport class OrdersService {\n  async getOrdersWithItems(userId: string): Promise<Order[]> {\n    // Single query with JOIN\n    return this.orderRepo.find({\n      where: { userId },\n      relations: ['items', 'items.product'],\n    });\n  }\n}\n\n// Use QueryBuilder for complex joins\n@Injectable()\nexport class UsersService {\n  async getUsersWithPostCounts(): Promise<UserWithPostCount[]> {\n    return this.userRepo\n      .createQueryBuilder('user')\n      .leftJoin('user.posts', 'post')\n      .select('user.id', 'id')\n      .addSelect('user.name', 'name')\n      .addSelect('COUNT(post.id)', 'postCount')\n      .groupBy('user.id')\n      .getRawMany();\n  }\n\n  async getActiveUsersWithPosts(): Promise<User[]> {\n    return this.userRepo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.posts', 'post')\n      .leftJoinAndSelect('post.comments', 'comment')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('post.status = :status', { status: 'published' })\n      .getMany();\n  }\n}\n\n// Use find options for specific fields\nasync getOrderSummaries(userId: string): Promise<OrderSummary[]> {\n  return this.orderRepo.find({\n    where: { userId },\n    relations: ['items'],\n    select: {\n      id: true,\n      total: true,\n      status: true,\n      items: {\n        id: true,\n        quantity: true,\n        price: true,\n      },\n    },\n  });\n}\n\n// Use DataLoader for GraphQL to batch and cache queries\nimport DataLoader from 'dataloader';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class PostsLoader {\n  constructor(private postsService: PostsService) {}\n\n  readonly batchPosts = new DataLoader<string, Post[]>(async (userIds) => {\n    // Single query for all users' posts\n    const posts = await this.postsService.findByUserIds([...userIds]);\n\n    // Group by userId\n    const postsMap = new Map<string, Post[]>();\n    for (const post of posts) {\n      const userPosts = postsMap.get(post.userId) || [];\n      userPosts.push(post);\n      postsMap.set(post.userId, userPosts);\n    }\n\n    // Return in same order as input\n    return userIds.map((id) => postsMap.get(id) || []);\n  });\n}\n\n// In resolver\n@ResolveField()\nasync posts(@Parent() user: User): Promise<Post[]> {\n  // DataLoader batches multiple calls into single query\n  return this.postsLoader.batchPosts.load(user.id);\n}\n\n// Enable query logging in development to detect N+1\nTypeOrmModule.forRoot({\n  logging: ['query', 'error'],\n  logger: 'advanced-console',\n});\n```\n\nReference: [TypeORM Relations](https://typeorm.io/relations)",".agents/skills/nestjs-best-practices/rules/db-use-migrations.md":"---\ntitle: Use Database Migrations\nimpact: HIGH\nimpactDescription: Enables safe, repeatable database schema changes\ntags: database, migrations, typeorm, schema\n---\n\n## Use Database Migrations\n\nNever use `synchronize: true` in production. Use migrations for all schema changes. Migrations provide version control for your database, enable safe rollbacks, and ensure consistency across all environments.\n\n**Incorrect (using synchronize or manual SQL):**\n\n```typescript\n// Use synchronize in production\nTypeOrmModule.forRoot({\n  type: 'postgres',\n  synchronize: true, // DANGEROUS in production!\n  // Can drop columns, tables, or data\n});\n\n// Manual SQL in production\n@Injectable()\nexport class DatabaseService {\n  async addColumn(): Promise<void> {\n    await this.dataSource.query('ALTER TABLE users ADD COLUMN age INT');\n    // No version control, no rollback, inconsistent across envs\n  }\n}\n\n// Modify entities without migration\n@Entity()\nexport class User {\n  @Column()\n  email: string;\n\n  @Column() // Added without migration\n  newField: string; // Will crash in production if synchronize is false\n}\n```\n\n**Correct (use migrations for all schema changes):**\n\n```typescript\n// Configure TypeORM for migrations\n// data-source.ts\nexport const dataSource = new DataSource({\n  type: 'postgres',\n  host: process.env.DB_HOST,\n  port: parseInt(process.env.DB_PORT),\n  username: process.env.DB_USERNAME,\n  password: process.env.DB_PASSWORD,\n  database: process.env.DB_NAME,\n  entities: ['dist/**/*.entity.js'],\n  migrations: ['dist/migrations/*.js'],\n  synchronize: false, // Always false in production\n  migrationsRun: true, // Run migrations on startup\n});\n\n// app.module.ts\nTypeOrmModule.forRootAsync({\n  inject: [ConfigService],\n  useFactory: (config: ConfigService) => ({\n    type: 'postgres',\n    host: config.get('DB_HOST'),\n    synchronize: config.get('NODE_ENV') === 'development', // Only in dev\n    migrations: ['dist/migrations/*.js'],\n    migrationsRun: true,\n  }),\n});\n\n// migrations/1705312800000-AddUserAge.ts\nimport { MigrationInterface, QueryRunner } from 'typeorm';\n\nexport class AddUserAge1705312800000 implements MigrationInterface {\n  name = 'AddUserAge1705312800000';\n\n  public async up(queryRunner: QueryRunner): Promise<void> {\n    // Add column with default to handle existing rows\n    await queryRunner.query(`\n      ALTER TABLE \"users\" ADD \"age\" integer DEFAULT 0\n    `);\n\n    // Add index for frequently queried columns\n    await queryRunner.query(`\n      CREATE INDEX \"IDX_users_age\" ON \"users\" (\"age\")\n    `);\n  }\n\n  public async down(queryRunner: QueryRunner): Promise<void> {\n    // Always implement down for rollback\n    await queryRunner.query(`DROP INDEX \"IDX_users_age\"`);\n    await queryRunner.query(`ALTER TABLE \"users\" DROP COLUMN \"age\"`);\n  }\n}\n\n// Safe column rename (two-step)\nexport class RenameNameToFullName1705312900000 implements MigrationInterface {\n  public async up(queryRunner: QueryRunner): Promise<void> {\n    // Step 1: Add new column\n    await queryRunner.query(`\n      ALTER TABLE \"users\" ADD \"full_name\" varchar(255)\n    `);\n\n    // Step 2: Copy data\n    await queryRunner.query(`\n      UPDATE \"users\" SET \"full_name\" = \"name\"\n    `);\n\n    // Step 3: Add NOT NULL constraint\n    await queryRunner.query(`\n      ALTER TABLE \"users\" ALTER COLUMN \"full_name\" SET NOT NULL\n    `);\n\n    // Step 4: Drop old column (after verifying app works)\n    await queryRunner.query(`\n      ALTER TABLE \"users\" DROP COLUMN \"name\"\n    `);\n  }\n\n  public async down(queryRunner: QueryRunner): Promise<void> {\n    await queryRunner.query(`ALTER TABLE \"users\" ADD \"name\" varchar(255)`);\n    await queryRunner.query(`UPDATE \"users\" SET \"name\" = \"full_name\"`);\n    await queryRunner.query(`ALTER TABLE \"users\" DROP COLUMN \"full_name\"`);\n  }\n}\n```\n\nReference: [TypeORM Migrations](https://typeorm.io/migrations)",".agents/skills/nestjs-best-practices/rules/db-use-transactions.md":"---\ntitle: Use Transactions for Multi-Step Operations\nimpact: HIGH\nimpactDescription: Ensures data consistency in multi-step operations\ntags: database, transactions, typeorm, consistency\n---\n\n## Use Transactions for Multi-Step Operations\n\nWhen multiple database operations must succeed or fail together, wrap them in a transaction. This prevents partial updates that leave your data in an inconsistent state. Use TypeORM's transaction APIs or the DataSource query runner for complex scenarios.\n\n**Incorrect (multiple saves without transaction):**\n\n```typescript\n// Multiple saves without transaction\n@Injectable()\nexport class OrdersService {\n  async createOrder(userId: string, items: OrderItem[]): Promise<Order> {\n    // If any step fails, data is inconsistent\n    const order = await this.orderRepo.save({ userId, status: 'pending' });\n\n    for (const item of items) {\n      await this.orderItemRepo.save({ orderId: order.id, ...item });\n      await this.inventoryRepo.decrement({ productId: item.productId }, 'stock', item.quantity);\n    }\n\n    await this.paymentService.charge(order.id);\n    // If payment fails, order and inventory are already modified!\n\n    return order;\n  }\n}\n```\n\n**Correct (use DataSource.transaction for automatic rollback):**\n\n```typescript\n// Use DataSource.transaction() for automatic rollback\n@Injectable()\nexport class OrdersService {\n  constructor(private dataSource: DataSource) {}\n\n  async createOrder(userId: string, items: OrderItem[]): Promise<Order> {\n    return this.dataSource.transaction(async manager => {\n      // All operations use the same transactional manager\n      const order = await manager.save(Order, { userId, status: 'pending' });\n\n      for (const item of items) {\n        await manager.save(OrderItem, { orderId: order.id, ...item });\n        await manager.decrement(Inventory, { productId: item.productId }, 'stock', item.quantity);\n      }\n\n      // If this throws, everything rolls back\n      await this.paymentService.chargeWithManager(manager, order.id);\n\n      return order;\n    });\n  }\n}\n\n// QueryRunner for manual transaction control\n@Injectable()\nexport class TransferService {\n  constructor(private dataSource: DataSource) {}\n\n  async transfer(fromId: string, toId: string, amount: number): Promise<void> {\n    const queryRunner = this.dataSource.createQueryRunner();\n    await queryRunner.connect();\n    await queryRunner.startTransaction();\n\n    try {\n      // Debit source account\n      await queryRunner.manager.decrement(Account, { id: fromId }, 'balance', amount);\n\n      // Verify sufficient funds\n      const source = await queryRunner.manager.findOne(Account, {\n        where: { id: fromId },\n      });\n      if (source.balance < 0) {\n        throw new BadRequestException('Insufficient funds');\n      }\n\n      // Credit destination account\n      await queryRunner.manager.increment(Account, { id: toId }, 'balance', amount);\n\n      // Log the transaction\n      await queryRunner.manager.save(TransactionLog, {\n        fromId,\n        toId,\n        amount,\n        timestamp: new Date(),\n      });\n\n      await queryRunner.commitTransaction();\n    } catch (error) {\n      await queryRunner.rollbackTransaction();\n      throw error;\n    } finally {\n      await queryRunner.release();\n    }\n  }\n}\n\n// Repository method with transaction support\n@Injectable()\nexport class UsersRepository {\n  constructor(\n    @InjectRepository(User) private repo: Repository<User>,\n    private dataSource: DataSource,\n  ) {}\n\n  async createWithProfile(userData: CreateUserDto, profileData: CreateProfileDto): Promise<User> {\n    return this.dataSource.transaction(async manager => {\n      const user = await manager.save(User, userData);\n      await manager.save(Profile, { ...profileData, userId: user.id });\n      return user;\n    });\n  }\n}\n```\n\nReference: [TypeORM Transactions](https://typeorm.io/transactions)",".agents/skills/nestjs-best-practices/rules/devops-graceful-shutdown.md":"---\ntitle: Implement Graceful Shutdown\nimpact: MEDIUM-HIGH\nimpactDescription: Proper shutdown handling ensures zero-downtime deployments\ntags: devops, graceful-shutdown, lifecycle, kubernetes\n---\n\n## Implement Graceful Shutdown\n\nHandle SIGTERM and SIGINT signals to gracefully shutdown your NestJS application. Stop accepting new requests, wait for in-flight requests to complete, close database connections, and clean up resources. This prevents data loss and connection errors during deployments.\n\n**Incorrect (ignoring shutdown signals):**\n\n```typescript\n// Ignore shutdown signals\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  await app.listen(3000);\n  // App crashes immediately on SIGTERM\n  // In-flight requests fail\n  // Database connections are abruptly closed\n}\n\n// Long-running tasks without cancellation\n@Injectable()\nexport class ProcessingService {\n  async processLargeFile(file: File): Promise<void> {\n    // No way to interrupt this during shutdown\n    for (let i = 0; i < file.chunks.length; i++) {\n      await this.processChunk(file.chunks[i]);\n      // May run for minutes, blocking shutdown\n    }\n  }\n}\n```\n\n**Correct (enable shutdown hooks and handle cleanup):**\n\n```typescript\n// Enable shutdown hooks in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n\n  // Enable shutdown hooks\n  app.enableShutdownHooks();\n\n  // Optional: Add timeout for forced shutdown\n  const server = await app.listen(3000);\n  server.setTimeout(30000); // 30 second timeout\n\n  // Handle graceful shutdown\n  const signals = ['SIGTERM', 'SIGINT'];\n  signals.forEach(signal => {\n    process.on(signal, async () => {\n      console.log(`Received ${signal}, starting graceful shutdown...`);\n\n      // Stop accepting new connections\n      server.close(async () => {\n        console.log('HTTP server closed');\n        await app.close();\n        process.exit(0);\n      });\n\n      // Force exit after timeout\n      setTimeout(() => {\n        console.error('Forced shutdown after timeout');\n        process.exit(1);\n      }, 30000);\n    });\n  });\n}\n\n// Lifecycle hooks for cleanup\n@Injectable()\nexport class DatabaseService implements OnApplicationShutdown {\n  private readonly connections: Connection[] = [];\n\n  async onApplicationShutdown(signal?: string): Promise<void> {\n    console.log(`Database service shutting down on ${signal}`);\n\n    // Close all connections gracefully\n    await Promise.all(this.connections.map(conn => conn.close()));\n\n    console.log('All database connections closed');\n  }\n}\n\n// Queue processor with graceful shutdown\n@Injectable()\nexport class QueueService implements OnApplicationShutdown, OnModuleDestroy {\n  private isShuttingDown = false;\n\n  onModuleDestroy(): void {\n    this.isShuttingDown = true;\n  }\n\n  async onApplicationShutdown(): Promise<void> {\n    // Wait for current jobs to complete\n    await this.queue.close();\n  }\n\n  async processJob(job: Job): Promise<void> {\n    if (this.isShuttingDown) {\n      throw new Error('Service is shutting down');\n    }\n    await this.doWork(job);\n  }\n}\n\n// WebSocket gateway cleanup\n@WebSocketGateway()\nexport class EventsGateway implements OnApplicationShutdown {\n  @WebSocketServer()\n  server: Server;\n\n  async onApplicationShutdown(): Promise<void> {\n    // Notify all connected clients\n    this.server.emit('shutdown', { message: 'Server is shutting down' });\n\n    // Close all connections\n    this.server.disconnectSockets();\n  }\n}\n\n// Health check integration\n@Injectable()\nexport class ShutdownService {\n  private isShuttingDown = false;\n\n  startShutdown(): void {\n    this.isShuttingDown = true;\n  }\n\n  isShutdown(): boolean {\n    return this.isShuttingDown;\n  }\n}\n\n@Controller('health')\nexport class HealthController {\n  constructor(private shutdownService: ShutdownService) {}\n\n  @Get('ready')\n  @HealthCheck()\n  readiness(): Promise<HealthCheckResult> {\n    // Return 503 during shutdown - k8s stops sending traffic\n    if (this.shutdownService.isShutdown()) {\n      throw new ServiceUnavailableException('Shutting down');\n    }\n\n    return this.health.check([() => this.db.pingCheck('database')]);\n  }\n}\n\n// Integrate with shutdown\n@Injectable()\nexport class AppShutdownService implements OnApplicationShutdown {\n  constructor(private shutdownService: ShutdownService) {}\n\n  async onApplicationShutdown(): Promise<void> {\n    // Mark as unhealthy first\n    this.shutdownService.startShutdown();\n\n    // Wait for k8s to update endpoints\n    await this.sleep(5000);\n\n    // Then proceed with cleanup\n  }\n}\n\n// Request tracking for in-flight requests\n@Injectable()\nexport class RequestTracker implements NestMiddleware, OnApplicationShutdown {\n  private activeRequests = 0;\n  private isShuttingDown = false;\n  private shutdownPromise: Promise<void> | null = null;\n  private resolveShutdown: (() => void) | null = null;\n\n  use(req: Request, res: Response, next: NextFunction): void {\n    if (this.isShuttingDown) {\n      res.status(503).send('Service Unavailable');\n      return;\n    }\n\n    this.activeRequests++;\n\n    res.on('finish', () => {\n      this.activeRequests--;\n      if (this.isShuttingDown && this.activeRequests === 0 && this.resolveShutdown) {\n        this.resolveShutdown();\n      }\n    });\n\n    next();\n  }\n\n  async onApplicationShutdown(): Promise<void> {\n    this.isShuttingDown = true;\n\n    if (this.activeRequests > 0) {\n      console.log(`Waiting for ${this.activeRequests} requests to complete`);\n      this.shutdownPromise = new Promise(resolve => {\n        this.resolveShutdown = resolve;\n      });\n\n      // Wait with timeout\n      await Promise.race([this.shutdownPromise, new Promise(resolve => setTimeout(resolve, 30000))]);\n    }\n\n    console.log('All requests completed');\n  }\n}\n```\n\nReference: [NestJS Lifecycle Events](https://docs.nestjs.com/fundamentals/lifecycle-events)",".agents/skills/nestjs-best-practices/rules/devops-use-config-module.md":"---\ntitle: Use ConfigModule for Environment Configuration\nimpact: LOW-MEDIUM\nimpactDescription: Proper configuration prevents deployment failures\ntags: devops, configuration, environment, validation\n---\n\n## Use ConfigModule for Environment Configuration\n\nUse `@nestjs/config` for environment-based configuration. Validate configuration at startup to fail fast on misconfigurations. Use namespaced configuration for organization and type safety.\n\n**Incorrect (accessing process.env directly):**\n\n```typescript\n// Access process.env directly\n@Injectable()\nexport class DatabaseService {\n  constructor() {\n    // No validation, can fail at runtime\n    this.connection = new Pool({\n      host: process.env.DB_HOST,\n      port: parseInt(process.env.DB_PORT), // NaN if missing\n      password: process.env.DB_PASSWORD, // undefined if missing\n    });\n  }\n}\n\n// Scattered env access\n@Injectable()\nexport class EmailService {\n  sendEmail() {\n    // Different services access env differently\n    const apiKey = process.env.SENDGRID_API_KEY || 'default';\n    // Typos go unnoticed: process.env.SENDGRID_API_KY\n  }\n}\n```\n\n**Correct (use @nestjs/config with validation):**\n\n```typescript\n// Setup validated configuration\nimport { ConfigModule, ConfigService, registerAs } from '@nestjs/config';\nimport * as Joi from 'joi';\n\n// config/database.config.ts\nexport const databaseConfig = registerAs('database', () => ({\n  host: process.env.DB_HOST,\n  port: parseInt(process.env.DB_PORT, 10),\n  username: process.env.DB_USERNAME,\n  password: process.env.DB_PASSWORD,\n  database: process.env.DB_NAME,\n}));\n\n// config/app.config.ts\nexport const appConfig = registerAs('app', () => ({\n  port: parseInt(process.env.PORT, 10) || 3000,\n  environment: process.env.NODE_ENV || 'development',\n  apiPrefix: process.env.API_PREFIX || 'api',\n}));\n\n// config/validation.schema.ts\nexport const validationSchema = Joi.object({\n  NODE_ENV: Joi.string().valid('development', 'production', 'test').default('development'),\n  PORT: Joi.number().default(3000),\n  DB_HOST: Joi.string().required(),\n  DB_PORT: Joi.number().default(5432),\n  DB_USERNAME: Joi.string().required(),\n  DB_PASSWORD: Joi.string().required(),\n  DB_NAME: Joi.string().required(),\n  JWT_SECRET: Joi.string().min(32).required(),\n  REDIS_URL: Joi.string().uri().required(),\n});\n\n// app.module.ts\n@Module({\n  imports: [\n    ConfigModule.forRoot({\n      isGlobal: true, // Available everywhere without importing\n      load: [databaseConfig, appConfig],\n      validationSchema,\n      validationOptions: {\n        abortEarly: true, // Stop on first error\n        allowUnknown: true, // Allow other env vars\n      },\n    }),\n    TypeOrmModule.forRootAsync({\n      inject: [ConfigService],\n      useFactory: (config: ConfigService) => ({\n        type: 'postgres',\n        host: config.get('database.host'),\n        port: config.get('database.port'),\n        username: config.get('database.username'),\n        password: config.get('database.password'),\n        database: config.get('database.database'),\n        autoLoadEntities: true,\n      }),\n    }),\n  ],\n})\nexport class AppModule {}\n\n// Type-safe configuration access\nexport interface AppConfig {\n  port: number;\n  environment: 'development' | 'production' | 'test';\n  apiPrefix: string;\n}\n\nexport interface DatabaseConfig {\n  host: string;\n  port: number;\n  username: string;\n  password: string;\n  database: string;\n}\n\n// Type-safe access\n@Injectable()\nexport class AppService {\n  constructor(private config: ConfigService) {}\n\n  getPort(): number {\n    // Type-safe with generic\n    return this.config.get<number>('app.port');\n  }\n\n  getDatabaseConfig(): DatabaseConfig {\n    return this.config.get<DatabaseConfig>('database');\n  }\n}\n\n// Inject namespaced config directly\n@Injectable()\nexport class DatabaseService {\n  constructor(\n    @Inject(databaseConfig.KEY)\n    private dbConfig: ConfigType<typeof databaseConfig>,\n  ) {\n    // Full type inference!\n    const host = this.dbConfig.host; // string\n    const port = this.dbConfig.port; // number\n  }\n}\n\n// Environment files support\nConfigModule.forRoot({\n  envFilePath: [`.env.${process.env.NODE_ENV}.local`, `.env.${process.env.NODE_ENV}`, '.env.local', '.env'],\n});\n\n// .env.development\n// DB_HOST=localhost\n// DB_PORT=5432\n\n// .env.production\n// DB_HOST=prod-db.example.com\n// DB_PORT=5432\n```\n\nReference: [NestJS Configuration](https://docs.nestjs.com/techniques/configuration)",".agents/skills/nestjs-best-practices/rules/devops-use-logging.md":"---\ntitle: Use Structured Logging\nimpact: MEDIUM-HIGH\nimpactDescription: Structured logging enables effective debugging and monitoring\ntags: devops, logging, structured-logs, pino\n---\n\n## Use Structured Logging\n\nUse NestJS Logger with structured JSON output in production. Include contextual information (request ID, user ID, operation) to trace requests across services. Avoid console.log and implement proper log levels.\n\n**Incorrect (using console.log in production):**\n\n```typescript\n// Use console.log in production\n@Injectable()\nexport class UsersService {\n  async createUser(dto: CreateUserDto): Promise<User> {\n    console.log('Creating user:', dto);\n    // Not structured, no levels, lost in production logs\n\n    try {\n      const user = await this.repo.save(dto);\n      console.log('User created:', user.id);\n      return user;\n    } catch (error) {\n      console.log('Error:', error); // Using log for errors\n      throw error;\n    }\n  }\n}\n\n// Log sensitive data\nconsole.log('Login attempt:', { email, password }); // SECURITY RISK!\n\n// Inconsistent log format\nlogger.log('User ' + userId + ' created at ' + new Date());\n// Hard to parse, no structure\n```\n\n**Correct (use structured logging with context):**\n\n```typescript\n// Configure logger in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule, {\n    logger:\n      process.env.NODE_ENV === 'production' ? ['error', 'warn', 'log'] : ['error', 'warn', 'log', 'debug', 'verbose'],\n  });\n}\n\n// Use NestJS Logger with context\n@Injectable()\nexport class UsersService {\n  private readonly logger = new Logger(UsersService.name);\n\n  async createUser(dto: CreateUserDto): Promise<User> {\n    this.logger.log('Creating user', { email: dto.email });\n\n    try {\n      const user = await this.repo.save(dto);\n      this.logger.log('User created', { userId: user.id });\n      return user;\n    } catch (error) {\n      this.logger.error('Failed to create user', error.stack, {\n        email: dto.email,\n      });\n      throw error;\n    }\n  }\n}\n\n// Custom logger for JSON output\n@Injectable()\nexport class JsonLogger implements LoggerService {\n  log(message: string, context?: object): void {\n    console.log(\n      JSON.stringify({\n        level: 'info',\n        timestamp: new Date().toISOString(),\n        message,\n        ...context,\n      }),\n    );\n  }\n\n  error(message: string, trace?: string, context?: object): void {\n    console.error(\n      JSON.stringify({\n        level: 'error',\n        timestamp: new Date().toISOString(),\n        message,\n        trace,\n        ...context,\n      }),\n    );\n  }\n\n  warn(message: string, context?: object): void {\n    console.warn(\n      JSON.stringify({\n        level: 'warn',\n        timestamp: new Date().toISOString(),\n        message,\n        ...context,\n      }),\n    );\n  }\n\n  debug(message: string, context?: object): void {\n    console.debug(\n      JSON.stringify({\n        level: 'debug',\n        timestamp: new Date().toISOString(),\n        message,\n        ...context,\n      }),\n    );\n  }\n}\n\n// Request context logging with ClsModule\nimport { ClsModule, ClsService } from 'nestjs-cls';\n\n@Module({\n  imports: [\n    ClsModule.forRoot({\n      global: true,\n      middleware: {\n        mount: true,\n        generateId: true,\n      },\n    }),\n  ],\n})\nexport class AppModule {}\n\n// Middleware to set request context\n@Injectable()\nexport class RequestContextMiddleware implements NestMiddleware {\n  constructor(private cls: ClsService) {}\n\n  use(req: Request, res: Response, next: NextFunction): void {\n    const requestId = req.headers['x-request-id'] || randomUUID();\n    this.cls.set('requestId', requestId);\n    this.cls.set('userId', req.user?.id);\n\n    res.setHeader('x-request-id', requestId);\n    next();\n  }\n}\n\n// Logger that includes request context\n@Injectable()\nexport class ContextLogger {\n  constructor(private cls: ClsService) {}\n\n  log(message: string, data?: object): void {\n    console.log(\n      JSON.stringify({\n        level: 'info',\n        timestamp: new Date().toISOString(),\n        requestId: this.cls.get('requestId'),\n        userId: this.cls.get('userId'),\n        message,\n        ...data,\n      }),\n    );\n  }\n\n  error(message: string, error: Error, data?: object): void {\n    console.error(\n      JSON.stringify({\n        level: 'error',\n        timestamp: new Date().toISOString(),\n        requestId: this.cls.get('requestId'),\n        userId: this.cls.get('userId'),\n        message,\n        error: error.message,\n        stack: error.stack,\n        ...data,\n      }),\n    );\n  }\n}\n\n// Pino integration for high-performance logging\nimport { LoggerModule } from 'nestjs-pino';\n\n@Module({\n  imports: [\n    LoggerModule.forRoot({\n      pinoHttp: {\n        level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',\n        transport: process.env.NODE_ENV !== 'production' ? { target: 'pino-pretty' } : undefined,\n        redact: ['req.headers.authorization', 'req.body.password'],\n        serializers: {\n          req: req => ({\n            method: req.method,\n            url: req.url,\n            query: req.query,\n          }),\n          res: res => ({\n            statusCode: res.statusCode,\n          }),\n        },\n      },\n    }),\n  ],\n})\nexport class AppModule {}\n\n// Usage with Pino\n@Injectable()\nexport class UsersService {\n  constructor(private logger: PinoLogger) {\n    this.logger.setContext(UsersService.name);\n  }\n\n  async findOne(id: string): Promise<User> {\n    this.logger.info({ userId: id }, 'Finding user');\n    // Pino uses first arg for data, second for message\n  }\n}\n```\n\nReference: [NestJS Logger](https://docs.nestjs.com/techniques/logger)",".agents/skills/nestjs-best-practices/rules/di-avoid-service-locator.md":"---\ntitle: Avoid Service Locator Anti-Pattern\nimpact: HIGH\nimpactDescription: Hides dependencies and breaks testability\ntags: dependency-injection, anti-patterns, testing\n---\n\n## Avoid Service Locator Anti-Pattern\n\nAvoid using `ModuleRef.get()` or global containers to resolve dependencies at runtime. This hides dependencies, makes code harder to test, and breaks the benefits of dependency injection. Use constructor injection instead.\n\n**Incorrect (service locator anti-pattern):**\n\n```typescript\n// Use ModuleRef to get dependencies dynamically\n@Injectable()\nexport class OrdersService {\n  constructor(private moduleRef: ModuleRef) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    // Dependencies are hidden - not visible in constructor\n    const usersService = this.moduleRef.get(UsersService);\n    const inventoryService = this.moduleRef.get(InventoryService);\n    const paymentService = this.moduleRef.get(PaymentService);\n\n    const user = await usersService.findOne(dto.userId);\n    // ... rest of logic\n  }\n}\n\n// Global singleton container\nclass ServiceContainer {\n  private static instance: ServiceContainer;\n  private services = new Map<string, any>();\n\n  static getInstance(): ServiceContainer {\n    if (!this.instance) {\n      this.instance = new ServiceContainer();\n    }\n    return this.instance;\n  }\n\n  get<T>(key: string): T {\n    return this.services.get(key);\n  }\n}\n```\n\n**Correct (constructor injection with explicit dependencies):**\n\n```typescript\n// Use constructor injection - dependencies are explicit\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private usersService: UsersService,\n    private inventoryService: InventoryService,\n    private paymentService: PaymentService,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const user = await this.usersService.findOne(dto.userId);\n    const inventory = await this.inventoryService.check(dto.items);\n    // Dependencies are clear and testable\n  }\n}\n\n// Easy to test with mocks\ndescribe('OrdersService', () => {\n  let service: OrdersService;\n\n  beforeEach(async () => {\n    const module = await Test.createTestingModule({\n      providers: [\n        OrdersService,\n        { provide: UsersService, useValue: mockUsersService },\n        { provide: InventoryService, useValue: mockInventoryService },\n        { provide: PaymentService, useValue: mockPaymentService },\n      ],\n    }).compile();\n\n    service = module.get(OrdersService);\n  });\n});\n\n// VALID: Factory pattern for dynamic instantiation\n@Injectable()\nexport class HandlerFactory {\n  constructor(private moduleRef: ModuleRef) {}\n\n  getHandler(type: string): Handler {\n    switch (type) {\n      case 'email':\n        return this.moduleRef.get(EmailHandler);\n      case 'sms':\n        return this.moduleRef.get(SmsHandler);\n      default:\n        return this.moduleRef.get(DefaultHandler);\n    }\n  }\n}\n```\n\nReference: [NestJS Module Reference](https://docs.nestjs.com/fundamentals/module-ref)",".agents/skills/nestjs-best-practices/rules/di-interface-segregation.md":"---\ntitle: Apply Interface Segregation Principle\nimpact: HIGH\nimpactDescription: Reduces coupling and improves testability by 30-50%\ntags: dependency-injection, interfaces, solid, isp\n---\n\n## Apply Interface Segregation Principle\n\nClients should not be forced to depend on interfaces they don't use. In NestJS, this means keeping interfaces small and focused on specific capabilities rather than creating \"fat\" interfaces that bundle unrelated methods. When a service only needs to send emails, it shouldn't depend on an interface that also includes SMS, push notifications, and logging. Split large interfaces into role-based ones.\n\n**Incorrect (fat interface forcing unused dependencies):**\n\n```typescript\n// Fat interface - forces all consumers to depend on everything\ninterface NotificationService {\n  sendEmail(to: string, subject: string, body: string): Promise<void>;\n  sendSms(phone: string, message: string): Promise<void>;\n  sendPush(userId: string, notification: PushPayload): Promise<void>;\n  sendSlack(channel: string, message: string): Promise<void>;\n  logNotification(type: string, payload: any): Promise<void>;\n  getDeliveryStatus(id: string): Promise<DeliveryStatus>;\n  retryFailed(id: string): Promise<void>;\n  scheduleNotification(dto: ScheduleDto): Promise<string>;\n}\n\n// Consumer only needs email, but must mock everything for tests\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private notifications: NotificationService, // Depends on 8 methods, uses 1\n  ) {}\n\n  async confirmOrder(order: Order): Promise<void> {\n    await this.notifications.sendEmail(\n      order.customer.email,\n      'Order Confirmed',\n      `Your order ${order.id} has been confirmed.`,\n    );\n  }\n}\n\n// Testing is painful - must mock unused methods\nconst mockNotificationService = {\n  sendEmail: jest.fn(),\n  sendSms: jest.fn(), // Never used, but required\n  sendPush: jest.fn(), // Never used, but required\n  sendSlack: jest.fn(), // Never used, but required\n  logNotification: jest.fn(), // Never used, but required\n  getDeliveryStatus: jest.fn(), // Never used, but required\n  retryFailed: jest.fn(), // Never used, but required\n  scheduleNotification: jest.fn(), // Never used, but required\n};\n```\n\n**Correct (segregated interfaces by capability):**\n\n```typescript\n// Segregated interfaces - each focused on one capability\ninterface EmailSender {\n  sendEmail(to: string, subject: string, body: string): Promise<void>;\n}\n\ninterface SmsSender {\n  sendSms(phone: string, message: string): Promise<void>;\n}\n\ninterface PushSender {\n  sendPush(userId: string, notification: PushPayload): Promise<void>;\n}\n\ninterface NotificationLogger {\n  logNotification(type: string, payload: any): Promise<void>;\n}\n\ninterface NotificationScheduler {\n  scheduleNotification(dto: ScheduleDto): Promise<string>;\n}\n\n// Implementation can implement multiple interfaces\n@Injectable()\nexport class NotificationService implements EmailSender, SmsSender, PushSender {\n  async sendEmail(to: string, subject: string, body: string): Promise<void> {\n    // Email implementation\n  }\n\n  async sendSms(phone: string, message: string): Promise<void> {\n    // SMS implementation\n  }\n\n  async sendPush(userId: string, notification: PushPayload): Promise<void> {\n    // Push implementation\n  }\n}\n\n// Or separate implementations\n@Injectable()\nexport class SendGridEmailService implements EmailSender {\n  async sendEmail(to: string, subject: string, body: string): Promise<void> {\n    // SendGrid-specific implementation\n  }\n}\n\n// Consumer depends only on what it needs\n@Injectable()\nexport class OrdersService {\n  constructor(\n    @Inject(EMAIL_SENDER) private emailSender: EmailSender, // Minimal dependency\n  ) {}\n\n  async confirmOrder(order: Order): Promise<void> {\n    await this.emailSender.sendEmail(\n      order.customer.email,\n      'Order Confirmed',\n      `Your order ${order.id} has been confirmed.`,\n    );\n  }\n}\n\n// Testing is simple - only mock what's used\nconst mockEmailSender: EmailSender = {\n  sendEmail: jest.fn(),\n};\n\n// Module registration with tokens\nexport const EMAIL_SENDER = Symbol('EMAIL_SENDER');\nexport const SMS_SENDER = Symbol('SMS_SENDER');\n\n@Module({\n  providers: [\n    { provide: EMAIL_SENDER, useClass: SendGridEmailService },\n    { provide: SMS_SENDER, useClass: TwilioSmsService },\n  ],\n  exports: [EMAIL_SENDER, SMS_SENDER],\n})\nexport class NotificationModule {}\n```\n\n**Combining interfaces when needed:**\n\n```typescript\n// Sometimes a consumer legitimately needs multiple capabilities\ninterface EmailAndSmsSender extends EmailSender, SmsSender {}\n\n// Or use intersection types\ntype MultiChannelSender = EmailSender & SmsSender & PushSender;\n\n// Consumer that genuinely needs multiple channels\n@Injectable()\nexport class AlertService {\n  constructor(\n    @Inject(MULTI_CHANNEL_SENDER)\n    private sender: EmailSender & SmsSender,\n  ) {}\n\n  async sendCriticalAlert(user: User, message: string): Promise<void> {\n    await Promise.all([\n      this.sender.sendEmail(user.email, 'Critical Alert', message),\n      this.sender.sendSms(user.phone, message),\n    ]);\n  }\n}\n```\n\nReference: [Interface Segregation Principle](https://en.wikipedia.org/wiki/Interface_segregation_principle)",".agents/skills/nestjs-best-practices/rules/di-liskov-substitution.md":"---\ntitle: Honor Liskov Substitution Principle\nimpact: HIGH\nimpactDescription: Ensures implementations are truly interchangeable without breaking callers\ntags: dependency-injection, inheritance, solid, lsp\n---\n\n## Honor Liskov Substitution Principle\n\nSubtypes must be substitutable for their base types without altering program correctness. In NestJS with dependency injection, this means any implementation of an interface or abstract class must honor the contract completely. A mock payment service used in tests must behave like a real payment service (return similar shapes, handle errors the same way). Violating LSP causes subtle bugs when swapping implementations.\n\n**Incorrect (implementation violates the contract):**\n\n```typescript\n// Base interface with clear contract\ninterface PaymentGateway {\n  /**\n   * Charges the specified amount.\n   * @returns PaymentResult on success\n   * @throws PaymentFailedException on payment failure\n   */\n  charge(amount: number, currency: string): Promise<PaymentResult>;\n}\n\n// Production implementation - follows the contract\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    const response = await this.stripe.charges.create({ amount, currency });\n    return { success: true, transactionId: response.id, amount };\n  }\n}\n\n// Mock that violates LSP - different behavior!\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    // VIOLATION 1: Throws for valid input (contract says return PaymentResult)\n    if (amount > 1000) {\n      throw new Error('Mock does not support large amounts');\n    }\n\n    // VIOLATION 2: Returns null instead of PaymentResult\n    if (currency !== 'USD') {\n      return null as any; // Real service would convert or reject properly\n    }\n\n    // VIOLATION 3: Missing required field\n    return { success: true } as PaymentResult; // Missing transactionId!\n  }\n}\n\n// Consumer trusts the contract\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async checkout(order: Order): Promise<void> {\n    const result = await this.payment.charge(order.total, order.currency);\n    // These fail with MockPaymentService:\n    await this.saveTransaction(result.transactionId); // undefined!\n    await this.sendReceipt(result); // might be null!\n  }\n}\n```\n\n**Correct (implementations honor the contract):**\n\n```typescript\n// Well-defined interface with documented behavior\ninterface PaymentGateway {\n  /**\n   * Charges the specified amount.\n   * @param amount - Amount in smallest currency unit (cents)\n   * @param currency - ISO 4217 currency code\n   * @returns PaymentResult with transactionId, success status, and amount\n   * @throws PaymentFailedException if charge is declined\n   * @throws InvalidCurrencyException if currency is not supported\n   */\n  charge(amount: number, currency: string): Promise<PaymentResult>;\n\n  /**\n   * Refunds a previous charge.\n   * @throws TransactionNotFoundException if transactionId is invalid\n   */\n  refund(transactionId: string, amount?: number): Promise<RefundResult>;\n}\n\n// Production implementation\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    try {\n      const response = await this.stripe.charges.create({ amount, currency });\n      return {\n        success: true,\n        transactionId: response.id,\n        amount: response.amount,\n      };\n    } catch (error) {\n      if (error.type === 'card_error') {\n        throw new PaymentFailedException(error.message);\n      }\n      throw error;\n    }\n  }\n\n  async refund(transactionId: string, amount?: number): Promise<RefundResult> {\n    // Implementation...\n  }\n}\n\n// Mock that honors LSP - same contract, same behavior shape\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  private transactions = new Map<string, PaymentResult>();\n\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    // Honor the contract: validate currency like real service would\n    if (!['USD', 'EUR', 'GBP'].includes(currency)) {\n      throw new InvalidCurrencyException(`Unsupported currency: ${currency}`);\n    }\n\n    // Simulate decline for specific test scenarios\n    if (amount === 99999) {\n      throw new PaymentFailedException('Card declined (test scenario)');\n    }\n\n    // Return same shape as production\n    const result: PaymentResult = {\n      success: true,\n      transactionId: `mock_${Date.now()}_${Math.random().toString(36)}`,\n      amount,\n    };\n\n    this.transactions.set(result.transactionId, result);\n    return result;\n  }\n\n  async refund(transactionId: string, amount?: number): Promise<RefundResult> {\n    // Honor the contract: throw if transaction not found\n    if (!this.transactions.has(transactionId)) {\n      throw new TransactionNotFoundException(transactionId);\n    }\n\n    return {\n      success: true,\n      refundId: `refund_${transactionId}`,\n      amount: amount ?? this.transactions.get(transactionId)!.amount,\n    };\n  }\n}\n\n// Consumer can swap implementations safely\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async checkout(order: Order): Promise<Order> {\n    try {\n      const result = await this.payment.charge(order.total, order.currency);\n      // Works with both StripeService and MockPaymentService\n      order.transactionId = result.transactionId;\n      order.status = 'paid';\n      return order;\n    } catch (error) {\n      if (error instanceof PaymentFailedException) {\n        order.status = 'payment_failed';\n        return order;\n      }\n      throw error;\n    }\n  }\n}\n```\n\n**Testing LSP compliance:**\n\n```typescript\n// Shared test suite that any implementation must pass\nfunction testPaymentGatewayContract(createGateway: () => PaymentGateway) {\n  describe('PaymentGateway contract', () => {\n    let gateway: PaymentGateway;\n\n    beforeEach(() => {\n      gateway = createGateway();\n    });\n\n    it('returns PaymentResult with all required fields', async () => {\n      const result = await gateway.charge(1000, 'USD');\n      expect(result).toHaveProperty('success');\n      expect(result).toHaveProperty('transactionId');\n      expect(result).toHaveProperty('amount');\n      expect(typeof result.transactionId).toBe('string');\n    });\n\n    it('throws InvalidCurrencyException for unsupported currency', async () => {\n      await expect(gateway.charge(1000, 'INVALID')).rejects.toThrow(InvalidCurrencyException);\n    });\n\n    it('throws TransactionNotFoundException for invalid refund', async () => {\n      await expect(gateway.refund('nonexistent')).rejects.toThrow(TransactionNotFoundException);\n    });\n  });\n}\n\n// Run against all implementations\ndescribe('StripeService', () => {\n  testPaymentGatewayContract(() => new StripeService(mockStripeClient));\n});\n\ndescribe('MockPaymentService', () => {\n  testPaymentGatewayContract(() => new MockPaymentService());\n});\n```\n\nReference: [Liskov Substitution Principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle)",".agents/skills/nestjs-best-practices/rules/di-prefer-constructor-injection.md":"---\ntitle: Prefer Constructor Injection\nimpact: CRITICAL\nimpactDescription: Required for proper DI and testing\ntags: dependency-injection, constructor, testing\n---\n\n## Prefer Constructor Injection\n\nAlways use constructor injection over property injection. Constructor injection makes dependencies explicit, enables TypeScript type checking, ensures dependencies are available when the class is instantiated, and improves testability. This is required for proper DI, testing, and TypeScript support.\n\n**Incorrect (property injection with hidden dependencies):**\n\n```typescript\n// Property injection - avoid unless necessary\n@Injectable()\nexport class UsersService {\n  @Inject()\n  private userRepo: UserRepository; // Hidden dependency\n\n  @Inject('CONFIG')\n  private config: ConfigType; // Also hidden\n\n  async findAll() {\n    return this.userRepo.find();\n  }\n}\n\n// Problems:\n// 1. Dependencies not visible in constructor\n// 2. Service can be instantiated without dependencies in tests\n// 3. TypeScript can't enforce dependency types at instantiation\n```\n\n**Correct (constructor injection with explicit dependencies):**\n\n```typescript\n// Constructor injection - explicit and testable\n@Injectable()\nexport class UsersService {\n  constructor(\n    private readonly userRepo: UserRepository,\n    @Inject('CONFIG') private readonly config: ConfigType,\n  ) {}\n\n  async findAll(): Promise<User[]> {\n    return this.userRepo.find();\n  }\n}\n\n// Testing is straightforward\ndescribe('UsersService', () => {\n  let service: UsersService;\n  let mockRepo: jest.Mocked<UserRepository>;\n\n  beforeEach(() => {\n    mockRepo = {\n      find: jest.fn(),\n      save: jest.fn(),\n    } as any;\n\n    service = new UsersService(mockRepo, { dbUrl: 'test' });\n  });\n\n  it('should find all users', async () => {\n    mockRepo.find.mockResolvedValue([{ id: '1', name: 'Test' }]);\n    const result = await service.findAll();\n    expect(result).toHaveLength(1);\n  });\n});\n\n// Only use property injection for optional dependencies\n@Injectable()\nexport class LoggingService {\n  @Optional()\n  @Inject('ANALYTICS')\n  private analytics?: AnalyticsService;\n\n  log(message: string) {\n    console.log(message);\n    this.analytics?.track('log', message); // Optional enhancement\n  }\n}\n```\n\nReference: [NestJS Providers](https://docs.nestjs.com/providers)",".agents/skills/nestjs-best-practices/rules/di-scope-awareness.md":"---\ntitle: Understand Provider Scopes\nimpact: CRITICAL\nimpactDescription: Prevents data leaks and performance issues\ntags: dependency-injection, scopes, request-context\n---\n\n## Understand Provider Scopes\n\nNestJS has three provider scopes: DEFAULT (singleton), REQUEST (per-request instance), and TRANSIENT (new instance for each injection). Most providers should be singletons. Request-scoped providers have performance implications as they bubble up through the dependency tree. Understanding scopes prevents memory leaks and incorrect data sharing.\n\n**Incorrect (wrong scope usage):**\n\n```typescript\n// Request-scoped when not needed (performance hit)\n@Injectable({ scope: Scope.REQUEST })\nexport class UsersService {\n  // This creates a new instance for EVERY request\n  // All dependencies also become request-scoped\n  async findAll() {\n    return this.userRepo.find();\n  }\n}\n\n// Singleton with mutable request state\n@Injectable() // Default: singleton\nexport class RequestContextService {\n  private userId: string; // DANGER: Shared across all requests!\n\n  setUser(userId: string) {\n    this.userId = userId; // Overwrites for all concurrent requests\n  }\n\n  getUser() {\n    return this.userId; // Returns wrong user!\n  }\n}\n```\n\n**Correct (appropriate scope for each use case):**\n\n```typescript\n// Singleton for stateless services (default, most common)\n@Injectable()\nexport class UsersService {\n  constructor(private readonly userRepo: UserRepository) {}\n\n  async findById(id: string): Promise<User> {\n    return this.userRepo.findOne({ where: { id } });\n  }\n}\n\n// Request-scoped ONLY when you need request context\n@Injectable({ scope: Scope.REQUEST })\nexport class RequestContextService {\n  private userId: string;\n\n  setUser(userId: string) {\n    this.userId = userId;\n  }\n\n  getUser(): string {\n    return this.userId;\n  }\n}\n\n// Better: Use NestJS built-in request context\nimport { REQUEST } from '@nestjs/core';\nimport { Request } from 'express';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class AuditService {\n  constructor(@Inject(REQUEST) private request: Request) {}\n\n  log(action: string) {\n    console.log(`User ${this.request.user?.id} performed ${action}`);\n  }\n}\n\n// Best: Use ClsModule for async context (no scope bubble-up)\nimport { ClsService } from 'nestjs-cls';\n\n@Injectable() // Stays singleton!\nexport class AuditService {\n  constructor(private cls: ClsService) {}\n\n  log(action: string) {\n    const userId = this.cls.get('userId');\n    console.log(`User ${userId} performed ${action}`);\n  }\n}\n```\n\nReference: [NestJS Injection Scopes](https://docs.nestjs.com/fundamentals/injection-scopes)",".agents/skills/nestjs-best-practices/rules/di-use-interfaces-tokens.md":"---\ntitle: Use Injection Tokens for Interfaces\nimpact: HIGH\nimpactDescription: Enables interface-based DI at runtime\ntags: dependency-injection, tokens, interfaces\n---\n\n## Use Injection Tokens for Interfaces\n\nTypeScript interfaces are erased at compile time and can't be used as injection tokens. Use string tokens, symbols, or abstract classes when you want to inject implementations of interfaces. This enables swapping implementations for testing or different environments.\n\n**Incorrect (interface can't be used as token):**\n\n```typescript\n// Interface can't be used as injection token\ninterface PaymentGateway {\n  charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  charge(amount: number) {\n    /* ... */\n  }\n}\n\n@Injectable()\nexport class OrdersService {\n  // This WON'T work - PaymentGateway doesn't exist at runtime\n  constructor(private payment: PaymentGateway) {}\n}\n```\n\n**Correct (symbol tokens or abstract classes):**\n\n```typescript\n// Option 1: String/Symbol tokens (most flexible)\nexport const PAYMENT_GATEWAY = Symbol('PAYMENT_GATEWAY');\n\nexport interface PaymentGateway {\n  charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    // Stripe implementation\n  }\n}\n\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    return { success: true, id: 'mock-id' };\n  }\n}\n\n// Module registration\n@Module({\n  providers: [\n    {\n      provide: PAYMENT_GATEWAY,\n      useClass: process.env.NODE_ENV === 'test' ? MockPaymentService : StripeService,\n    },\n  ],\n  exports: [PAYMENT_GATEWAY],\n})\nexport class PaymentModule {}\n\n// Injection\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async createOrder(dto: CreateOrderDto) {\n    await this.payment.charge(dto.amount);\n  }\n}\n\n// Option 2: Abstract class (carries runtime type info)\nexport abstract class PaymentGateway {\n  abstract charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService extends PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    // Implementation\n  }\n}\n\n// No @Inject needed with abstract class\n@Injectable()\nexport class OrdersService {\n  constructor(private payment: PaymentGateway) {}\n}\n```\n\nReference: [NestJS Custom Providers](https://docs.nestjs.com/fundamentals/custom-providers)",".agents/skills/nestjs-best-practices/rules/error-handle-async-errors.md":"---\ntitle: Handle Async Errors Properly\nimpact: HIGH\nimpactDescription: Prevents process crashes from unhandled rejections\ntags: error-handling, async, promises\n---\n\n## Handle Async Errors Properly\n\nNestJS automatically catches errors from async route handlers, but errors from background tasks, event handlers, and manually created promises can crash your application. Always handle async errors explicitly and use global handlers as a safety net.\n\n**Incorrect (fire-and-forget without error handling):**\n\n```typescript\n// Fire-and-forget without error handling\n@Injectable()\nexport class UsersService {\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Fire and forget - if this fails, error is unhandled!\n    this.emailService.sendWelcome(user.email);\n\n    return user;\n  }\n}\n\n// Unhandled promise in event handler\n@Injectable()\nexport class OrdersService {\n  @OnEvent('order.created')\n  handleOrderCreated(event: OrderCreatedEvent) {\n    // This returns a promise but it's not awaited!\n    this.processOrder(event);\n    // Errors will crash the process\n  }\n\n  private async processOrder(event: OrderCreatedEvent): Promise<void> {\n    await this.inventoryService.reserve(event.items);\n    await this.notificationService.send(event.userId);\n  }\n}\n\n// Missing try-catch in scheduled tasks\n@Cron('0 0 * * *')\nasync dailyCleanup(): Promise<void> {\n  await this.cleanupService.run();\n  // If this throws, no error handling\n}\n```\n\n**Correct (explicit async error handling):**\n\n```typescript\n// Handle fire-and-forget with explicit catch\n@Injectable()\nexport class UsersService {\n  private readonly logger = new Logger(UsersService.name);\n\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Explicitly catch and log errors\n    this.emailService.sendWelcome(user.email).catch(error => {\n      this.logger.error('Failed to send welcome email', error.stack);\n      // Optionally queue for retry\n    });\n\n    return user;\n  }\n}\n\n// Properly handle async event handlers\n@Injectable()\nexport class OrdersService {\n  private readonly logger = new Logger(OrdersService.name);\n\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    try {\n      await this.processOrder(event);\n    } catch (error) {\n      this.logger.error('Failed to process order', { event, error });\n      // Don't rethrow - would crash the process\n      await this.deadLetterQueue.add('order.created', event);\n    }\n  }\n}\n\n// Safe scheduled tasks\n@Injectable()\nexport class CleanupService {\n  private readonly logger = new Logger(CleanupService.name);\n\n  @Cron('0 0 * * *')\n  async dailyCleanup(): Promise<void> {\n    try {\n      await this.cleanupService.run();\n      this.logger.log('Daily cleanup completed');\n    } catch (error) {\n      this.logger.error('Daily cleanup failed', error.stack);\n      // Alert or retry logic\n    }\n  }\n}\n\n// Global unhandled rejection handler in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  const logger = new Logger('Bootstrap');\n\n  process.on('unhandledRejection', (reason, promise) => {\n    logger.error('Unhandled Rejection at:', promise, 'reason:', reason);\n  });\n\n  process.on('uncaughtException', error => {\n    logger.error('Uncaught Exception:', error);\n    process.exit(1);\n  });\n\n  await app.listen(3000);\n}\n```\n\nReference: [Node.js Unhandled Rejections](https://nodejs.org/api/process.html#event-unhandledrejection)",".agents/skills/nestjs-best-practices/rules/error-throw-http-exceptions.md":"---\ntitle: Throw HTTP Exceptions from Services\nimpact: HIGH\nimpactDescription: Keeps controllers thin and simplifies error handling\ntags: error-handling, exceptions, services\n---\n\n## Throw HTTP Exceptions from Services\n\nIt's acceptable (and often preferable) to throw `HttpException` subclasses from services in HTTP applications. This keeps controllers thin and allows services to communicate appropriate error states. For truly layer-agnostic services, use domain exceptions that map to HTTP status codes.\n\n**Incorrect (return error objects instead of throwing):**\n\n```typescript\n// Return error objects instead of throwing\n@Injectable()\nexport class UsersService {\n  async findById(id: string): Promise<{ user?: User; error?: string }> {\n    const user = await this.repo.findOne({ where: { id } });\n    if (!user) {\n      return { error: 'User not found' }; // Controller must check this\n    }\n    return { user };\n  }\n}\n\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string) {\n    const result = await this.usersService.findById(id);\n    if (result.error) {\n      throw new NotFoundException(result.error);\n    }\n    return result.user;\n  }\n}\n```\n\n**Correct (throw exceptions directly from service):**\n\n```typescript\n// Throw exceptions directly from service\n@Injectable()\nexport class UsersService {\n  constructor(private readonly repo: UserRepository) {}\n\n  async findById(id: string): Promise<User> {\n    const user = await this.repo.findOne({ where: { id } });\n    if (!user) {\n      throw new NotFoundException(`User #${id} not found`);\n    }\n    return user;\n  }\n\n  async create(dto: CreateUserDto): Promise<User> {\n    const existing = await this.repo.findOne({\n      where: { email: dto.email },\n    });\n    if (existing) {\n      throw new ConflictException('Email already registered');\n    }\n    return this.repo.save(dto);\n  }\n\n  async update(id: string, dto: UpdateUserDto): Promise<User> {\n    const user = await this.findById(id); // Throws if not found\n    Object.assign(user, dto);\n    return this.repo.save(user);\n  }\n}\n\n// Controller stays thin\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findById(id);\n  }\n\n  @Post()\n  create(@Body() dto: CreateUserDto): Promise<User> {\n    return this.usersService.create(dto);\n  }\n}\n\n// For layer-agnostic services, use domain exceptions\nexport class EntityNotFoundException extends Error {\n  constructor(\n    public readonly entity: string,\n    public readonly id: string,\n  ) {\n    super(`${entity} with ID \"${id}\" not found`);\n  }\n}\n\n// Map to HTTP in exception filter\n@Catch(EntityNotFoundException)\nexport class EntityNotFoundFilter implements ExceptionFilter {\n  catch(exception: EntityNotFoundException, host: ArgumentsHost) {\n    const ctx = host.switchToHttp();\n    const response = ctx.getResponse<Response>();\n\n    response.status(404).json({\n      statusCode: 404,\n      message: exception.message,\n      entity: exception.entity,\n      id: exception.id,\n    });\n  }\n}\n```\n\nReference: [NestJS Exception Filters](https://docs.nestjs.com/exception-filters)",".agents/skills/nestjs-best-practices/rules/error-use-exception-filters.md":"---\ntitle: Use Exception Filters for Error Handling\nimpact: HIGH\nimpactDescription: Consistent, centralized error handling\ntags: error-handling, exception-filters, consistency\n---\n\n## Use Exception Filters for Error Handling\n\nNever catch exceptions and manually format error responses in controllers. Use NestJS exception filters to handle errors consistently across your application. Create custom exception filters for specific error types and a global filter for unhandled exceptions.\n\n**Incorrect (manual error handling in controllers):**\n\n```typescript\n// Manual error handling in controllers\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string, @Res() res: Response) {\n    try {\n      const user = await this.usersService.findById(id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found',\n        });\n      }\n      return res.json(user);\n    } catch (error) {\n      console.error(error);\n      return res.status(500).json({\n        statusCode: 500,\n        message: 'Internal server error',\n      });\n    }\n  }\n}\n```\n\n**Correct (exception filters with consistent handling):**\n\n```typescript\n// Use built-in and custom exceptions\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    const user = await this.usersService.findById(id);\n    if (!user) {\n      throw new NotFoundException(`User #${id} not found`);\n    }\n    return user;\n  }\n}\n\n// Custom domain exception\nexport class UserNotFoundException extends NotFoundException {\n  constructor(userId: string) {\n    super({\n      statusCode: 404,\n      error: 'Not Found',\n      message: `User with ID \"${userId}\" not found`,\n      code: 'USER_NOT_FOUND',\n    });\n  }\n}\n\n// Custom exception filter for domain errors\n@Catch(DomainException)\nexport class DomainExceptionFilter implements ExceptionFilter {\n  catch(exception: DomainException, host: ArgumentsHost) {\n    const ctx = host.switchToHttp();\n    const response = ctx.getResponse<Response>();\n    const request = ctx.getRequest<Request>();\n\n    const status = exception.getStatus?.() || 400;\n\n    response.status(status).json({\n      statusCode: status,\n      code: exception.code,\n      message: exception.message,\n      timestamp: new Date().toISOString(),\n      path: request.url,\n    });\n  }\n}\n\n// Global exception filter for unhandled errors\n@Catch()\nexport class AllExceptionsFilter implements ExceptionFilter {\n  constructor(private readonly logger: Logger) {}\n\n  catch(exception: unknown, host: ArgumentsHost) {\n    const ctx = host.switchToHttp();\n    const response = ctx.getResponse<Response>();\n    const request = ctx.getRequest<Request>();\n\n    const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;\n\n    const message = exception instanceof HttpException ? exception.message : 'Internal server error';\n\n    this.logger.error(`${request.method} ${request.url}`, exception instanceof Error ? exception.stack : exception);\n\n    response.status(status).json({\n      statusCode: status,\n      message,\n      timestamp: new Date().toISOString(),\n      path: request.url,\n    });\n  }\n}\n\n// Register globally in main.ts\napp.useGlobalFilters(new AllExceptionsFilter(app.get(Logger)), new DomainExceptionFilter());\n\n// Or via module\n@Module({\n  providers: [\n    {\n      provide: APP_FILTER,\n      useClass: AllExceptionsFilter,\n    },\n  ],\n})\nexport class AppModule {}\n```\n\nReference: [NestJS Exception Filters](https://docs.nestjs.com/exception-filters)",".agents/skills/nestjs-best-practices/rules/micro-use-health-checks.md":"---\ntitle: Implement Health Checks for Microservices\nimpact: MEDIUM-HIGH\nimpactDescription: Health checks enable orchestrators to manage service lifecycle\ntags: microservices, health-checks, terminus, kubernetes\n---\n\n## Implement Health Checks for Microservices\n\nImplement liveness and readiness probes using `@nestjs/terminus`. Liveness checks determine if the service should be restarted. Readiness checks determine if the service can accept traffic. Proper health checks enable Kubernetes and load balancers to route traffic correctly.\n\n**Incorrect (simple ping that doesn't check dependencies):**\n\n```typescript\n// Simple ping that doesn't check dependencies\n@Controller('health')\nexport class HealthController {\n  @Get()\n  check(): string {\n    return 'OK'; // Service might be unhealthy but returns OK\n  }\n}\n\n// Health check that blocks on slow dependencies\n@Controller('health')\nexport class HealthController {\n  @Get()\n  async check(): Promise<string> {\n    // If database is slow, health check times out\n    await this.userRepo.findOne({ where: { id: '1' } });\n    await this.redis.ping();\n    await this.externalApi.healthCheck();\n    return 'OK';\n  }\n}\n```\n\n**Correct (use @nestjs/terminus for comprehensive health checks):**\n\n```typescript\n// Use @nestjs/terminus for comprehensive health checks\nimport {\n  HealthCheckService,\n  HttpHealthIndicator,\n  TypeOrmHealthIndicator,\n  HealthCheck,\n  DiskHealthIndicator,\n  MemoryHealthIndicator,\n} from '@nestjs/terminus';\n\n@Controller('health')\nexport class HealthController {\n  constructor(\n    private health: HealthCheckService,\n    private http: HttpHealthIndicator,\n    private db: TypeOrmHealthIndicator,\n    private disk: DiskHealthIndicator,\n    private memory: MemoryHealthIndicator,\n  ) {}\n\n  // Liveness probe - is the service alive?\n  @Get('live')\n  @HealthCheck()\n  liveness() {\n    return this.health.check([\n      // Basic checks only\n      () => this.memory.checkHeap('memory_heap', 200 * 1024 * 1024), // 200MB\n    ]);\n  }\n\n  // Readiness probe - can the service handle traffic?\n  @Get('ready')\n  @HealthCheck()\n  readiness() {\n    return this.health.check([\n      () => this.db.pingCheck('database'),\n      () =>\n        this.http.pingCheck('redis', 'http://redis:6379', { timeout: 1000 }),\n      () =>\n        this.disk.checkStorage('disk', { path: '/', thresholdPercent: 0.9 }),\n    ]);\n  }\n\n  // Deep health check for debugging\n  @Get('deep')\n  @HealthCheck()\n  deepCheck() {\n    return this.health.check([\n      () => this.db.pingCheck('database'),\n      () => this.memory.checkHeap('memory_heap', 200 * 1024 * 1024),\n      () => this.memory.checkRSS('memory_rss', 300 * 1024 * 1024),\n      () =>\n        this.disk.checkStorage('disk', { path: '/', thresholdPercent: 0.9 }),\n      () =>\n        this.http.pingCheck('external-api', 'https://api.example.com/health'),\n    ]);\n  }\n}\n\n// Custom indicator for business-specific health\n@Injectable()\nexport class QueueHealthIndicator extends HealthIndicator {\n  constructor(private queueService: QueueService) {\n    super();\n  }\n\n  async isHealthy(key: string): Promise<HealthIndicatorResult> {\n    const queueStats = await this.queueService.getStats();\n\n    const isHealthy = queueStats.failedCount < 100;\n    const result = this.getStatus(key, isHealthy, {\n      waiting: queueStats.waitingCount,\n      active: queueStats.activeCount,\n      failed: queueStats.failedCount,\n    });\n\n    if (!isHealthy) {\n      throw new HealthCheckError('Queue unhealthy', result);\n    }\n\n    return result;\n  }\n}\n\n// Redis health indicator\n@Injectable()\nexport class RedisHealthIndicator extends HealthIndicator {\n  constructor(@InjectRedis() private redis: Redis) {\n    super();\n  }\n\n  async isHealthy(key: string): Promise<HealthIndicatorResult> {\n    try {\n      const pong = await this.redis.ping();\n      return this.getStatus(key, pong === 'PONG');\n    } catch (error) {\n      throw new HealthCheckError('Redis check failed', this.getStatus(key, false));\n    }\n  }\n}\n\n// Use custom indicators\n@Get('ready')\n@HealthCheck()\nreadiness() {\n  return this.health.check([\n    () => this.db.pingCheck('database'),\n    () => this.redis.isHealthy('redis'),\n    () => this.queue.isHealthy('job-queue'),\n  ]);\n}\n\n// Graceful shutdown handling\n@Injectable()\nexport class GracefulShutdownService implements OnApplicationShutdown {\n  private isShuttingDown = false;\n\n  isShutdown(): boolean {\n    return this.isShuttingDown;\n  }\n\n  async onApplicationShutdown(signal: string): Promise<void> {\n    this.isShuttingDown = true;\n    console.log(`Shutting down on ${signal}`);\n\n    // Wait for in-flight requests\n    await new Promise((resolve) => setTimeout(resolve, 5000));\n  }\n}\n\n// Health check respects shutdown state\n@Get('ready')\n@HealthCheck()\nreadiness() {\n  if (this.shutdownService.isShutdown()) {\n    throw new ServiceUnavailableException('Shutting down');\n  }\n\n  return this.health.check([\n    () => this.db.pingCheck('database'),\n  ]);\n}\n```\n\n### Kubernetes Configuration\n\n```yaml\n# Kubernetes deployment with probes\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: api-service\nspec:\n  template:\n    spec:\n      containers:\n        - name: api\n          image: api-service:latest\n          ports:\n            - containerPort: 3000\n          livenessProbe:\n            httpGet:\n              path: /health/live\n              port: 3000\n            initialDelaySeconds: 30\n            periodSeconds: 10\n            timeoutSeconds: 5\n            failureThreshold: 3\n          readinessProbe:\n            httpGet:\n              path: /health/ready\n              port: 3000\n            initialDelaySeconds: 5\n            periodSeconds: 5\n            timeoutSeconds: 3\n            failureThreshold: 3\n          startupProbe:\n            httpGet:\n              path: /health/live\n              port: 3000\n            initialDelaySeconds: 0\n            periodSeconds: 5\n            failureThreshold: 30\n```\n\nReference: [NestJS Terminus](https://docs.nestjs.com/recipes/terminus)",".agents/skills/nestjs-best-practices/rules/micro-use-patterns.md":"---\ntitle: Use Message and Event Patterns Correctly\nimpact: MEDIUM\nimpactDescription: Proper patterns ensure reliable microservice communication\ntags: microservices, message-pattern, event-pattern, communication\n---\n\n## Use Message and Event Patterns Correctly\n\nNestJS microservices support two communication patterns: request-response (MessagePattern) and event-based (EventPattern). Use MessagePattern when you need a response, and EventPattern for fire-and-forget notifications. Understanding the difference prevents communication bugs.\n\n**Incorrect (using wrong pattern for use case):**\n\n```typescript\n// Use @MessagePattern for fire-and-forget\n@Controller()\nexport class NotificationsController {\n  @MessagePattern('user.created')\n  async handleUserCreated(data: UserCreatedEvent) {\n    // This WAITS for response, blocking the sender\n    await this.emailService.sendWelcome(data.email);\n    // If email fails, sender gets an error (coupling!)\n  }\n}\n\n// Use @EventPattern expecting a response\n@Controller()\nexport class OrdersController {\n  @EventPattern('inventory.check')\n  async checkInventory(data: CheckInventoryDto) {\n    const available = await this.inventory.check(data);\n    return available; // This return value is IGNORED with @EventPattern!\n  }\n}\n\n// Tight coupling in client\n@Injectable()\nexport class UsersService {\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Blocks until notification service responds\n    await this.client.send('user.created', user).toPromise();\n    // If notification service is down, user creation fails!\n\n    return user;\n  }\n}\n```\n\n**Correct (use MessagePattern for request-response, EventPattern for fire-and-forget):**\n\n```typescript\n// MessagePattern: Request-Response (when you NEED a response)\n@Controller()\nexport class InventoryController {\n  @MessagePattern({ cmd: 'check_inventory' })\n  async checkInventory(data: CheckInventoryDto): Promise<InventoryResult> {\n    const result = await this.inventoryService.check(data.productId, data.quantity);\n    return result; // Response sent back to caller\n  }\n}\n\n// Client expects response\n@Injectable()\nexport class OrdersService {\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    // Check inventory - we NEED this response to proceed\n    const inventory = await firstValueFrom(\n      this.inventoryClient.send<InventoryResult>(\n        { cmd: 'check_inventory' },\n        { productId: dto.productId, quantity: dto.quantity },\n      ),\n    );\n\n    if (!inventory.available) {\n      throw new BadRequestException('Insufficient inventory');\n    }\n\n    return this.repo.save(dto);\n  }\n}\n\n// EventPattern: Fire-and-Forget (for notifications, side effects)\n@Controller()\nexport class NotificationsController {\n  @EventPattern('user.created')\n  async handleUserCreated(data: UserCreatedEvent): Promise<void> {\n    // No return value needed - just process the event\n    await this.emailService.sendWelcome(data.email);\n    await this.analyticsService.track('user_signup', data);\n    // If this fails, it doesn't affect the sender\n  }\n}\n\n// Client emits event without waiting\n@Injectable()\nexport class UsersService {\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Fire and forget - doesn't block, doesn't wait\n    this.eventClient.emit('user.created', {\n      userId: user.id,\n      email: user.email,\n      timestamp: new Date(),\n    });\n\n    return user; // User creation succeeds regardless of event handling\n  }\n}\n\n// Hybrid pattern for critical events\n@Injectable()\nexport class OrdersService {\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Critical: inventory reservation (use MessagePattern)\n    const reserved = await firstValueFrom(\n      this.inventoryClient.send({ cmd: 'reserve_inventory' }, {\n        orderId: order.id,\n        items: dto.items,\n      }),\n    );\n\n    if (!reserved.success) {\n      await this.repo.delete(order.id);\n      throw new BadRequestException('Could not reserve inventory');\n    }\n\n    // Non-critical: notifications (use EventPattern)\n    this.eventClient.emit('order.created', {\n      orderId: order.id,\n      userId: dto.userId,\n      total: dto.total,\n    });\n\n    return order;\n  }\n}\n\n// Error handling patterns\n// MessagePattern errors propagate to caller\n@MessagePattern({ cmd: 'get_user' })\nasync getUser(userId: string): Promise<User> {\n  const user = await this.repo.findOne({ where: { id: userId } });\n  if (!user) {\n    throw new RpcException('User not found'); // Received by caller\n  }\n  return user;\n}\n\n// EventPattern errors should be handled locally\n@EventPattern('order.created')\nasync handleOrderCreated(data: OrderCreatedEvent): Promise<void> {\n  try {\n    await this.processOrder(data);\n  } catch (error) {\n    // Log and potentially retry - don't throw\n    this.logger.error('Failed to process order event', error);\n    await this.deadLetterQueue.add(data);\n  }\n}\n```\n\nReference: [NestJS Microservices](https://docs.nestjs.com/microservices/basics)",".agents/skills/nestjs-best-practices/rules/micro-use-queues.md":"---\ntitle: Use Message Queues for Background Jobs\nimpact: MEDIUM-HIGH\nimpactDescription: Queues enable reliable background processing\ntags: microservices, queues, bullmq, background-jobs\n---\n\n## Use Message Queues for Background Jobs\n\nUse `@nestjs/bullmq` for background job processing. Queues decouple long-running tasks from HTTP requests, enable retry logic, and distribute workload across workers. Use them for emails, file processing, notifications, and any task that shouldn't block user requests.\n\n**Incorrect (long-running tasks in HTTP handlers):**\n\n```typescript\n// Long-running tasks in HTTP handlers\n@Controller('reports')\nexport class ReportsController {\n  @Post()\n  async generate(@Body() dto: GenerateReportDto): Promise<Report> {\n    // This blocks the request for potentially minutes\n    const data = await this.fetchLargeDataset(dto);\n    const report = await this.processData(data); // Slow!\n    await this.sendEmail(dto.email, report); // Can fail!\n    return report; // Client times out\n  }\n}\n\n// Fire-and-forget without retry\n@Injectable()\nexport class EmailService {\n  async sendWelcome(email: string): Promise<void> {\n    // If this fails, email is never sent\n    await this.mailer.send({ to: email, template: 'welcome' });\n    // No retry, no tracking, no visibility\n  }\n}\n\n// Use setInterval for scheduled tasks\nsetInterval(async () => {\n  await cleanupOldRecords();\n}, 60000); // No error handling, memory leaks\n```\n\n**Correct (use BullMQ for background processing):**\n\n```typescript\n// Configure BullMQ\nimport { BullModule } from '@nestjs/bullmq';\n\n@Module({\n  imports: [\n    BullModule.forRoot({\n      connection: {\n        host: 'localhost',\n        port: 6379,\n      },\n      defaultJobOptions: {\n        removeOnComplete: 1000,\n        removeOnFail: 5000,\n        attempts: 3,\n        backoff: {\n          type: 'exponential',\n          delay: 1000,\n        },\n      },\n    }),\n    BullModule.registerQueue({ name: 'email' }, { name: 'reports' }, { name: 'notifications' }),\n  ],\n})\nexport class QueueModule {}\n\n// Producer: Add jobs to queue\n@Injectable()\nexport class ReportsService {\n  constructor(@InjectQueue('reports') private reportsQueue: Queue) {}\n\n  async requestReport(dto: GenerateReportDto): Promise<{ jobId: string }> {\n    // Return immediately, process in background\n    const job = await this.reportsQueue.add('generate', dto, {\n      priority: dto.urgent ? 1 : 10,\n      delay: dto.scheduledFor ? Date.parse(dto.scheduledFor) - Date.now() : 0,\n    });\n\n    return { jobId: job.id };\n  }\n\n  async getJobStatus(jobId: string): Promise<JobStatus> {\n    const job = await this.reportsQueue.getJob(jobId);\n    return {\n      status: await job.getState(),\n      progress: job.progress,\n      result: job.returnvalue,\n    };\n  }\n}\n\n// Consumer: Process jobs\n@Processor('reports')\nexport class ReportsProcessor {\n  private readonly logger = new Logger(ReportsProcessor.name);\n\n  @Process('generate')\n  async generateReport(job: Job<GenerateReportDto>): Promise<Report> {\n    this.logger.log(`Processing report job ${job.id}`);\n\n    // Update progress\n    await job.updateProgress(10);\n\n    const data = await this.fetchData(job.data);\n    await job.updateProgress(50);\n\n    const report = await this.processData(data);\n    await job.updateProgress(90);\n\n    await this.saveReport(report);\n    await job.updateProgress(100);\n\n    return report;\n  }\n\n  @OnQueueActive()\n  onActive(job: Job) {\n    this.logger.log(`Processing job ${job.id}`);\n  }\n\n  @OnQueueCompleted()\n  onCompleted(job: Job, result: any) {\n    this.logger.log(`Job ${job.id} completed`);\n  }\n\n  @OnQueueFailed()\n  onFailed(job: Job, error: Error) {\n    this.logger.error(`Job ${job.id} failed: ${error.message}`);\n  }\n}\n\n// Email queue with retry\n@Processor('email')\nexport class EmailProcessor {\n  @Process('send')\n  async sendEmail(job: Job<SendEmailDto>): Promise<void> {\n    const { to, template, data } = job.data;\n\n    try {\n      await this.mailer.send({\n        to,\n        template,\n        context: data,\n      });\n    } catch (error) {\n      // BullMQ will retry based on job options\n      throw error;\n    }\n  }\n}\n\n// Usage\n@Injectable()\nexport class NotificationService {\n  constructor(@InjectQueue('email') private emailQueue: Queue) {}\n\n  async sendWelcome(user: User): Promise<void> {\n    await this.emailQueue.add(\n      'send',\n      {\n        to: user.email,\n        template: 'welcome',\n        data: { name: user.name },\n      },\n      {\n        attempts: 5,\n        backoff: { type: 'exponential', delay: 5000 },\n      },\n    );\n  }\n}\n\n// Scheduled jobs\n@Injectable()\nexport class ScheduledJobsService implements OnModuleInit {\n  constructor(@InjectQueue('maintenance') private queue: Queue) {}\n\n  async onModuleInit(): Promise<void> {\n    // Clean up old reports daily at midnight\n    await this.queue.add(\n      'cleanup',\n      {},\n      {\n        repeat: { cron: '0 0 * * *' },\n        jobId: 'daily-cleanup', // Prevent duplicates\n      },\n    );\n\n    // Send digest every hour\n    await this.queue.add(\n      'digest',\n      {},\n      {\n        repeat: { every: 60 * 60 * 1000 },\n        jobId: 'hourly-digest',\n      },\n    );\n  }\n}\n\n@Processor('maintenance')\nexport class MaintenanceProcessor {\n  @Process('cleanup')\n  async cleanup(): Promise<void> {\n    await this.cleanupOldReports();\n    await this.cleanupExpiredSessions();\n  }\n\n  @Process('digest')\n  async sendDigest(): Promise<void> {\n    const users = await this.getUsersForDigest();\n    for (const user of users) {\n      await this.emailQueue.add('send', { to: user.email, template: 'digest' });\n    }\n  }\n}\n\n// Queue monitoring with Bull Board\nimport { BullBoardModule } from '@bull-board/nestjs';\nimport { BullMQAdapter } from '@bull-board/api/bullMQAdapter';\n\n@Module({\n  imports: [\n    BullBoardModule.forRoot({\n      route: '/admin/queues',\n      adapter: ExpressAdapter,\n    }),\n    BullBoardModule.forFeature({\n      name: 'email',\n      adapter: BullMQAdapter,\n    }),\n    BullBoardModule.forFeature({\n      name: 'reports',\n      adapter: BullMQAdapter,\n    }),\n  ],\n})\nexport class AdminModule {}\n```\n\nReference: [NestJS Queues](https://docs.nestjs.com/techniques/queues)",".agents/skills/nestjs-best-practices/rules/perf-async-hooks.md":"---\ntitle: Use Async Lifecycle Hooks Correctly\nimpact: HIGH\nimpactDescription: Improper async handling blocks application startup\ntags: performance, lifecycle, async, hooks\n---\n\n## Use Async Lifecycle Hooks Correctly\n\nNestJS lifecycle hooks (`onModuleInit`, `onApplicationBootstrap`, etc.) support async operations. However, misusing them can block application startup or cause race conditions. Understand the lifecycle order and use hooks appropriately.\n\n**Incorrect (fire-and-forget async without await):**\n\n```typescript\n// Fire-and-forget async without await\n@Injectable()\nexport class DatabaseService implements OnModuleInit {\n  onModuleInit() {\n    // This runs but doesn't block - app starts before DB is ready!\n    this.connect();\n  }\n\n  private async connect() {\n    await this.pool.connect();\n    console.log('Database connected');\n  }\n}\n\n// Heavy blocking operations in constructor\n@Injectable()\nexport class ConfigService {\n  private config: Config;\n\n  constructor() {\n    // BLOCKS entire module instantiation synchronously\n    this.config = fs.readFileSync('config.json');\n  }\n}\n```\n\n**Correct (return promises from async hooks):**\n\n```typescript\n// Return promise from async hooks\n@Injectable()\nexport class DatabaseService implements OnModuleInit {\n  private pool: Pool;\n\n  async onModuleInit(): Promise<void> {\n    // NestJS waits for this to complete before continuing\n    await this.pool.connect();\n    console.log('Database connected');\n  }\n\n  async onModuleDestroy(): Promise<void> {\n    // Clean up resources on shutdown\n    await this.pool.end();\n    console.log('Database disconnected');\n  }\n}\n\n// Use onApplicationBootstrap for cross-module dependencies\n@Injectable()\nexport class CacheWarmerService implements OnApplicationBootstrap {\n  constructor(\n    private cache: CacheService,\n    private products: ProductsService,\n  ) {}\n\n  async onApplicationBootstrap(): Promise<void> {\n    // All modules are initialized, safe to warm cache\n    const products = await this.products.findPopular();\n    await this.cache.warmup(products);\n  }\n}\n\n// Heavy init in async hooks, not constructor\n@Injectable()\nexport class ConfigService implements OnModuleInit {\n  private config: Config;\n\n  constructor() {\n    // Keep constructor synchronous and fast\n  }\n\n  async onModuleInit(): Promise<void> {\n    // Async loading in lifecycle hook\n    this.config = await this.loadConfig();\n  }\n\n  private async loadConfig(): Promise<Config> {\n    const file = await fs.promises.readFile('config.json');\n    return JSON.parse(file.toString());\n  }\n\n  get<T>(key: string): T {\n    return this.config[key];\n  }\n}\n\n// Enable shutdown hooks in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  app.enableShutdownHooks(); // Enable SIGTERM/SIGINT handling\n  await app.listen(3000);\n}\n```\n\nReference: [NestJS Lifecycle Events](https://docs.nestjs.com/fundamentals/lifecycle-events)",".agents/skills/nestjs-best-practices/rules/perf-lazy-loading.md":"---\ntitle: Use Lazy Loading for Large Modules\nimpact: MEDIUM\nimpactDescription: Improves startup time for large applications\ntags: performance, lazy-loading, modules, optimization\n---\n\n## Use Lazy Loading for Large Modules\n\nNestJS supports lazy-loading modules, which defers initialization until first use. This is valuable for large applications where some features are rarely used, serverless deployments where cold start time matters, or when certain modules have heavy initialization costs.\n\n**Incorrect (loading everything eagerly):**\n\n```typescript\n// Load everything eagerly in a large app\n@Module({\n  imports: [\n    UsersModule,\n    OrdersModule,\n    PaymentsModule,\n    ReportsModule, // Heavy, rarely used\n    AnalyticsModule, // Heavy, rarely used\n    AdminModule, // Only admins use this\n    LegacyModule, // Migration module, rarely used\n    BulkImportModule, // Used once a month\n  ],\n})\nexport class AppModule {}\n\n// All modules initialize at startup, even if never used\n// Slow cold starts in serverless\n// Memory wasted on unused modules\n```\n\n**Correct (lazy load rarely-used modules):**\n\n```typescript\n// Use LazyModuleLoader for optional modules\nimport { LazyModuleLoader } from '@nestjs/core';\n\n@Injectable()\nexport class ReportsService {\n  constructor(private lazyModuleLoader: LazyModuleLoader) {}\n\n  async generateReport(type: string): Promise<Report> {\n    // Load module only when needed\n    const { ReportsModule } = await import('./reports/reports.module');\n    const moduleRef = await this.lazyModuleLoader.load(() => ReportsModule);\n\n    const reportsService = moduleRef.get(ReportsGeneratorService);\n    return reportsService.generate(type);\n  }\n}\n\n// Lazy load admin features with caching\n@Injectable()\nexport class AdminService {\n  private adminModule: ModuleRef | null = null;\n\n  constructor(private lazyModuleLoader: LazyModuleLoader) {}\n\n  private async getAdminModule(): Promise<ModuleRef> {\n    if (!this.adminModule) {\n      const { AdminModule } = await import('./admin/admin.module');\n      this.adminModule = await this.lazyModuleLoader.load(() => AdminModule);\n    }\n    return this.adminModule;\n  }\n\n  async runAdminTask(task: string): Promise<void> {\n    const moduleRef = await this.getAdminModule();\n    const taskRunner = moduleRef.get(AdminTaskRunner);\n    await taskRunner.run(task);\n  }\n}\n\n// Reusable lazy loader service\n@Injectable()\nexport class ModuleLoaderService {\n  private loadedModules = new Map<string, ModuleRef>();\n\n  constructor(private lazyModuleLoader: LazyModuleLoader) {}\n\n  async load<T>(key: string, importFn: () => Promise<{ default: Type<T> } | Type<T>>): Promise<ModuleRef> {\n    if (!this.loadedModules.has(key)) {\n      const module = await importFn();\n      const moduleType = 'default' in module ? module.default : module;\n      const moduleRef = await this.lazyModuleLoader.load(() => moduleType);\n      this.loadedModules.set(key, moduleRef);\n    }\n    return this.loadedModules.get(key)!;\n  }\n}\n\n// Preload modules in background after startup\n@Injectable()\nexport class ModulePreloader implements OnApplicationBootstrap {\n  constructor(private lazyModuleLoader: LazyModuleLoader) {}\n\n  async onApplicationBootstrap(): Promise<void> {\n    setTimeout(async () => {\n      await this.preloadModule(() => import('./reports/reports.module'));\n    }, 5000); // 5 seconds after startup\n  }\n\n  private async preloadModule(importFn: () => Promise<any>): Promise<void> {\n    try {\n      const module = await importFn();\n      const moduleType = module.default || Object.values(module)[0];\n      await this.lazyModuleLoader.load(() => moduleType);\n    } catch (error) {\n      console.warn('Failed to preload module', error);\n    }\n  }\n}\n```\n\nReference: [NestJS Lazy Loading Modules](https://docs.nestjs.com/fundamentals/lazy-loading-modules)",".agents/skills/nestjs-best-practices/rules/perf-optimize-database.md":"---\ntitle: Optimize Database Queries\nimpact: HIGH\nimpactDescription: Database queries are typically the largest source of latency\ntags: performance, database, queries, optimization\n---\n\n## Optimize Database Queries\n\nSelect only needed columns, use proper indexes, avoid over-fetching relations, and consider query performance when designing your data access. Most API slowness traces back to inefficient database queries.\n\n**Incorrect (over-fetching data and missing indexes):**\n\n```typescript\n// Select everything when you need few fields\n@Injectable()\nexport class UsersService {\n  async findAllEmails(): Promise<string[]> {\n    const users = await this.repo.find();\n    // Fetches ALL columns for ALL users\n    return users.map(u => u.email);\n  }\n\n  async getUserSummary(id: string): Promise<UserSummary> {\n    const user = await this.repo.findOne({\n      where: { id },\n      relations: ['posts', 'posts.comments', 'posts.comments.author', 'followers'],\n    });\n    // Over-fetches massive relation tree\n    return { name: user.name, postCount: user.posts.length };\n  }\n}\n\n// No indexes on frequently queried columns\n@Entity()\nexport class Order {\n  @Column()\n  userId: string; // No index - full table scan on every lookup\n\n  @Column()\n  status: string; // No index - slow status filtering\n}\n```\n\n**Correct (select only needed data with proper indexes):**\n\n```typescript\n// Select only needed columns\n@Injectable()\nexport class UsersService {\n  async findAllEmails(): Promise<string[]> {\n    const users = await this.repo.find({\n      select: ['email'], // Only fetch email column\n    });\n    return users.map(u => u.email);\n  }\n\n  // Use QueryBuilder for complex selections\n  async getUserSummary(id: string): Promise<UserSummary> {\n    return this.repo\n      .createQueryBuilder('user')\n      .select('user.name', 'name')\n      .addSelect('COUNT(post.id)', 'postCount')\n      .leftJoin('user.posts', 'post')\n      .where('user.id = :id', { id })\n      .groupBy('user.id')\n      .getRawOne();\n  }\n\n  // Fetch relations only when needed\n  async getFullProfile(id: string): Promise<User> {\n    return this.repo.findOne({\n      where: { id },\n      relations: ['posts'], // Only immediate relation\n      select: {\n        id: true,\n        name: true,\n        email: true,\n        posts: {\n          id: true,\n          title: true,\n        },\n      },\n    });\n  }\n}\n\n// Add indexes on frequently queried columns\n@Entity()\n@Index(['userId'])\n@Index(['status'])\n@Index(['createdAt'])\n@Index(['userId', 'status']) // Composite index for common query pattern\nexport class Order {\n  @PrimaryGeneratedColumn('uuid')\n  id: string;\n\n  @Column()\n  userId: string;\n\n  @Column()\n  status: string;\n\n  @CreateDateColumn()\n  createdAt: Date;\n}\n\n// Always paginate large datasets\n@Injectable()\nexport class OrdersService {\n  async findAll(page = 1, limit = 20): Promise<PaginatedResult<Order>> {\n    const [items, total] = await this.repo.findAndCount({\n      skip: (page - 1) * limit,\n      take: limit,\n      order: { createdAt: 'DESC' },\n    });\n\n    return {\n      items,\n      meta: {\n        page,\n        limit,\n        total,\n        totalPages: Math.ceil(total / limit),\n      },\n    };\n  }\n}\n```\n\nReference: [TypeORM Query Builder](https://typeorm.io/select-query-builder)",".agents/skills/nestjs-best-practices/rules/perf-use-caching.md":"---\ntitle: Use Caching Strategically\nimpact: HIGH\nimpactDescription: Dramatically reduces database load and response times\ntags: performance, caching, redis, optimization\n---\n\n## Use Caching Strategically\n\nImplement caching for expensive operations, frequently accessed data, and external API calls. Use NestJS CacheModule with appropriate TTLs and cache invalidation strategies. Don't cache everything - focus on high-impact areas.\n\n**Incorrect (no caching or caching everything):**\n\n```typescript\n// No caching for expensive, repeated queries\n@Injectable()\nexport class ProductsService {\n  async getPopular(): Promise<Product[]> {\n    // Runs complex aggregation query EVERY request\n    return this.productsRepo\n      .createQueryBuilder('p')\n      .leftJoin('p.orders', 'o')\n      .select('p.*, COUNT(o.id) as orderCount')\n      .groupBy('p.id')\n      .orderBy('orderCount', 'DESC')\n      .limit(20)\n      .getMany();\n  }\n}\n\n// Cache everything without thought\n@Injectable()\nexport class UsersService {\n  @CacheKey('users')\n  @CacheTTL(3600)\n  @UseInterceptors(CacheInterceptor)\n  async findAll(): Promise<User[]> {\n    // Caching user list for 1 hour is wrong if data changes frequently\n    return this.usersRepo.find();\n  }\n}\n```\n\n**Correct (strategic caching with proper invalidation):**\n\n```typescript\n// Setup caching module\n@Module({\n  imports: [\n    CacheModule.registerAsync({\n      imports: [ConfigModule],\n      inject: [ConfigService],\n      useFactory: (config: ConfigService) => ({\n        stores: [new KeyvRedis(config.get('REDIS_URL'))],\n        ttl: 60 * 1000, // Default 60s\n      }),\n    }),\n  ],\n})\nexport class AppModule {}\n\n// Manual caching for granular control\n@Injectable()\nexport class ProductsService {\n  constructor(\n    @Inject(CACHE_MANAGER) private cache: Cache,\n    private productsRepo: ProductRepository,\n  ) {}\n\n  async getPopular(): Promise<Product[]> {\n    const cacheKey = 'products:popular';\n\n    // Try cache first\n    const cached = await this.cache.get<Product[]>(cacheKey);\n    if (cached) return cached;\n\n    // Cache miss - fetch and cache\n    const products = await this.fetchPopularProducts();\n    await this.cache.set(cacheKey, products, 5 * 60 * 1000); // 5 min TTL\n    return products;\n  }\n\n  // Invalidate cache on changes\n  async updateProduct(id: string, dto: UpdateProductDto): Promise<Product> {\n    const product = await this.productsRepo.save({ id, ...dto });\n    await this.cache.del('products:popular'); // Invalidate\n    return product;\n  }\n}\n\n// Decorator-based caching with auto-interceptor\n@Controller('categories')\n@UseInterceptors(CacheInterceptor)\nexport class CategoriesController {\n  @Get()\n  @CacheTTL(30 * 60 * 1000) // 30 minutes - categories rarely change\n  findAll(): Promise<Category[]> {\n    return this.categoriesService.findAll();\n  }\n\n  @Get(':id')\n  @CacheTTL(60 * 1000) // 1 minute\n  @CacheKey('category')\n  findOne(@Param('id') id: string): Promise<Category> {\n    return this.categoriesService.findOne(id);\n  }\n}\n\n// Event-based cache invalidation\n@Injectable()\nexport class CacheInvalidationService {\n  constructor(@Inject(CACHE_MANAGER) private cache: Cache) {}\n\n  @OnEvent('product.created')\n  @OnEvent('product.updated')\n  @OnEvent('product.deleted')\n  async invalidateProductCaches(event: ProductEvent) {\n    await Promise.all([this.cache.del('products:popular'), this.cache.del(`product:${event.productId}`)]);\n  }\n}\n```\n\nReference: [NestJS Caching](https://docs.nestjs.com/techniques/caching)",".agents/skills/nestjs-best-practices/rules/security-auth-jwt.md":"---\ntitle: Implement Secure JWT Authentication\nimpact: CRITICAL\nimpactDescription: Essential for secure APIs\ntags: security, jwt, authentication, tokens\n---\n\n## Implement Secure JWT Authentication\n\nUse `@nestjs/jwt` with `@nestjs/passport` for authentication. Store secrets securely, use appropriate token lifetimes, implement refresh tokens, and validate tokens properly. Never expose sensitive data in JWT payloads.\n\n**Incorrect (insecure JWT implementation):**\n\n```typescript\n// Hardcode secrets\n@Module({\n  imports: [\n    JwtModule.register({\n      secret: 'my-secret-key', // Exposed in code\n      signOptions: { expiresIn: '7d' }, // Too long\n    }),\n  ],\n})\nexport class AuthModule {}\n\n// Store sensitive data in JWT\nasync login(user: User): Promise<{ accessToken: string }> {\n  const payload = {\n    sub: user.id,\n    email: user.email,\n    password: user.password, // NEVER include password!\n    ssn: user.ssn, // NEVER include sensitive data!\n    isAdmin: user.isAdmin, // Can be tampered if not verified\n  };\n  return { accessToken: this.jwtService.sign(payload) };\n}\n\n// Skip token validation\n@Injectable()\nexport class JwtStrategy extends PassportStrategy(Strategy) {\n  constructor() {\n    super({\n      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),\n      secretOrKey: 'my-secret',\n    });\n  }\n\n  async validate(payload: any): Promise<any> {\n    return payload; // No validation of user existence\n  }\n}\n```\n\n**Correct (secure JWT with refresh tokens):**\n\n```typescript\n// Secure JWT configuration\n@Module({\n  imports: [\n    JwtModule.registerAsync({\n      imports: [ConfigModule],\n      inject: [ConfigService],\n      useFactory: (config: ConfigService) => ({\n        secret: config.get<string>('JWT_SECRET'),\n        signOptions: {\n          expiresIn: '15m', // Short-lived access tokens\n          issuer: config.get<string>('JWT_ISSUER'),\n          audience: config.get<string>('JWT_AUDIENCE'),\n        },\n      }),\n    }),\n    PassportModule.register({ defaultStrategy: 'jwt' }),\n  ],\n})\nexport class AuthModule {}\n\n// Minimal JWT payload\n@Injectable()\nexport class AuthService {\n  async login(user: User): Promise<TokenResponse> {\n    // Only include necessary, non-sensitive data\n    const payload: JwtPayload = {\n      sub: user.id,\n      email: user.email,\n      roles: user.roles,\n      iat: Math.floor(Date.now() / 1000),\n    };\n\n    const accessToken = this.jwtService.sign(payload);\n    const refreshToken = await this.createRefreshToken(user.id);\n\n    return { accessToken, refreshToken, expiresIn: 900 };\n  }\n\n  private async createRefreshToken(userId: string): Promise<string> {\n    const token = randomBytes(32).toString('hex');\n    const hashedToken = await bcrypt.hash(token, 10);\n\n    await this.refreshTokenRepo.save({\n      userId,\n      token: hashedToken,\n      expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days\n    });\n\n    return token;\n  }\n}\n\n// Proper JWT strategy with validation\n@Injectable()\nexport class JwtStrategy extends PassportStrategy(Strategy) {\n  constructor(\n    private config: ConfigService,\n    private usersService: UsersService,\n  ) {\n    super({\n      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),\n      secretOrKey: config.get<string>('JWT_SECRET'),\n      ignoreExpiration: false,\n      issuer: config.get<string>('JWT_ISSUER'),\n      audience: config.get<string>('JWT_AUDIENCE'),\n    });\n  }\n\n  async validate(payload: JwtPayload): Promise<User> {\n    // Verify user still exists and is active\n    const user = await this.usersService.findById(payload.sub);\n\n    if (!user || !user.isActive) {\n      throw new UnauthorizedException('User not found or inactive');\n    }\n\n    // Verify token wasn't issued before password change\n    if (user.passwordChangedAt) {\n      const tokenIssuedAt = new Date(payload.iat * 1000);\n      if (tokenIssuedAt < user.passwordChangedAt) {\n        throw new UnauthorizedException('Token invalidated by password change');\n      }\n    }\n\n    return user;\n  }\n}\n```\n\nReference: [NestJS Authentication](https://docs.nestjs.com/security/authentication)",".agents/skills/nestjs-best-practices/rules/security-rate-limiting.md":"---\ntitle: Implement Rate Limiting\nimpact: HIGH\nimpactDescription: Protects against abuse and ensures fair resource usage\ntags: security, rate-limiting, throttler, protection\n---\n\n## Implement Rate Limiting\n\nUse `@nestjs/throttler` to limit request rates per client. Apply different limits for different endpoints - stricter for auth endpoints, more relaxed for read operations. Consider using Redis for distributed rate limiting in clustered deployments.\n\n**Incorrect (no rate limiting on sensitive endpoints):**\n\n```typescript\n// No rate limiting on sensitive endpoints\n@Controller('auth')\nexport class AuthController {\n  @Post('login')\n  async login(@Body() dto: LoginDto): Promise<TokenResponse> {\n    // Attackers can brute-force credentials\n    return this.authService.login(dto);\n  }\n\n  @Post('forgot-password')\n  async forgotPassword(@Body() dto: ForgotPasswordDto): Promise<void> {\n    // Can be abused to spam users with emails\n    return this.authService.sendResetEmail(dto.email);\n  }\n}\n\n// Same limits for all endpoints\n@UseGuards(ThrottlerGuard)\n@Controller('api')\nexport class ApiController {\n  @Get('public-data')\n  async getPublic() {} // Should allow more requests\n\n  @Post('process-payment')\n  async payment() {} // Should be more restrictive\n}\n```\n\n**Correct (configured throttler with endpoint-specific limits):**\n\n```typescript\n// Configure throttler globally with multiple limits\nimport { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';\n\n@Module({\n  imports: [\n    ThrottlerModule.forRoot([\n      {\n        name: 'short',\n        ttl: 1000, // 1 second\n        limit: 3, // 3 requests per second\n      },\n      {\n        name: 'medium',\n        ttl: 10000, // 10 seconds\n        limit: 20, // 20 requests per 10 seconds\n      },\n      {\n        name: 'long',\n        ttl: 60000, // 1 minute\n        limit: 100, // 100 requests per minute\n      },\n    ]),\n  ],\n  providers: [\n    {\n      provide: APP_GUARD,\n      useClass: ThrottlerGuard,\n    },\n  ],\n})\nexport class AppModule {}\n\n// Override limits per endpoint\n@Controller('auth')\nexport class AuthController {\n  @Post('login')\n  @Throttle({ short: { limit: 5, ttl: 60000 } }) // 5 attempts per minute\n  async login(@Body() dto: LoginDto): Promise<TokenResponse> {\n    return this.authService.login(dto);\n  }\n\n  @Post('forgot-password')\n  @Throttle({ short: { limit: 3, ttl: 3600000 } }) // 3 per hour\n  async forgotPassword(@Body() dto: ForgotPasswordDto): Promise<void> {\n    return this.authService.sendResetEmail(dto.email);\n  }\n}\n\n// Skip throttling for certain routes\n@Controller('health')\nexport class HealthController {\n  @Get()\n  @SkipThrottle()\n  check(): string {\n    return 'OK';\n  }\n}\n\n// Custom throttle per user type\n@Injectable()\nexport class CustomThrottlerGuard extends ThrottlerGuard {\n  protected async getTracker(req: Request): Promise<string> {\n    // Use user ID if authenticated, IP otherwise\n    return req.user?.id || req.ip;\n  }\n\n  protected async getLimit(context: ExecutionContext): Promise<number> {\n    const request = context.switchToHttp().getRequest();\n\n    // Higher limits for authenticated users\n    if (request.user) {\n      return request.user.isPremium ? 1000 : 200;\n    }\n\n    return 50; // Anonymous users\n  }\n}\n```\n\nReference: [NestJS Throttler](https://docs.nestjs.com/security/rate-limiting)",".agents/skills/nestjs-best-practices/rules/security-sanitize-output.md":"---\ntitle: Sanitize Output to Prevent XSS\nimpact: HIGH\nimpactDescription: XSS vulnerabilities can compromise user sessions and data\ntags: security, xss, sanitization, html\n---\n\n## Sanitize Output to Prevent XSS\n\nWhile NestJS APIs typically return JSON (which browsers don't execute), XSS risks exist when rendering HTML, storing user content, or when frontend frameworks improperly handle API responses. Sanitize user-generated content before storage and use proper Content-Type headers.\n\n**Incorrect (storing raw HTML without sanitization):**\n\n```typescript\n// Store raw HTML from users\n@Injectable()\nexport class CommentsService {\n  async create(dto: CreateCommentDto): Promise<Comment> {\n    // User can inject: <script>steal(document.cookie)</script>\n    return this.repo.save({\n      content: dto.content, // Raw, unsanitized\n      authorId: dto.authorId,\n    });\n  }\n}\n\n// Return HTML without sanitization\n@Controller('pages')\nexport class PagesController {\n  @Get(':slug')\n  @Header('Content-Type', 'text/html')\n  async getPage(@Param('slug') slug: string): Promise<string> {\n    const page = await this.pagesService.findBySlug(slug);\n    // If page.content contains user input, XSS is possible\n    return `<html><body>${page.content}</body></html>`;\n  }\n}\n\n// Reflect user input in errors\n@Get(':id')\nasync findOne(@Param('id') id: string): Promise<User> {\n  const user = await this.repo.findOne({ where: { id } });\n  if (!user) {\n    // XSS if id contains malicious content and error is rendered\n    throw new NotFoundException(`User ${id} not found`);\n  }\n  return user;\n}\n```\n\n**Correct (sanitize content and use proper headers):**\n\n```typescript\n// Sanitize HTML content before storage\nimport * as sanitizeHtml from 'sanitize-html';\n\n@Injectable()\nexport class CommentsService {\n  private readonly sanitizeOptions: sanitizeHtml.IOptions = {\n    allowedTags: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],\n    allowedAttributes: {\n      a: ['href', 'title'],\n    },\n    allowedSchemes: ['http', 'https', 'mailto'],\n  };\n\n  async create(dto: CreateCommentDto): Promise<Comment> {\n    return this.repo.save({\n      content: sanitizeHtml(dto.content, this.sanitizeOptions),\n      authorId: dto.authorId,\n    });\n  }\n}\n\n// Use validation pipe to strip HTML\nimport { Transform } from 'class-transformer';\n\nexport class CreatePostDto {\n  @IsString()\n  @MaxLength(1000)\n  @Transform(({ value }) => sanitizeHtml(value, { allowedTags: [] }))\n  title: string;\n\n  @IsString()\n  @Transform(({ value }) =>\n    sanitizeHtml(value, {\n      allowedTags: ['p', 'br', 'b', 'i', 'a'],\n      allowedAttributes: { a: ['href'] },\n    }),\n  )\n  content: string;\n}\n\n// Set proper Content-Type headers\n@Controller('api')\nexport class ApiController {\n  @Get('data')\n  @Header('Content-Type', 'application/json')\n  async getData(): Promise<DataResponse> {\n    // JSON response - browser won't execute scripts\n    return this.service.getData();\n  }\n}\n\n// Sanitize error messages\n@Get(':id')\nasync findOne(@Param('id', ParseUUIDPipe) id: string): Promise<User> {\n  const user = await this.repo.findOne({ where: { id } });\n  if (!user) {\n    // UUID validation ensures safe format\n    throw new NotFoundException('User not found');\n  }\n  return user;\n}\n\n// Use Helmet for CSP headers\nimport helmet from 'helmet';\n\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n\n  app.use(\n    helmet({\n      contentSecurityPolicy: {\n        directives: {\n          defaultSrc: [\"'self'\"],\n          scriptSrc: [\"'self'\"],\n          styleSrc: [\"'self'\", \"'unsafe-inline'\"],\n          imgSrc: [\"'self'\", 'data:', 'https:'],\n        },\n      },\n    }),\n  );\n\n  await app.listen(3000);\n}\n```\n\nReference: [OWASP XSS Prevention](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)",".agents/skills/nestjs-best-practices/rules/security-use-guards.md":"---\ntitle: Use Guards for Authentication and Authorization\nimpact: HIGH\nimpactDescription: Enforces access control before handlers execute\ntags: security, guards, authentication, authorization\n---\n\n## Use Guards for Authentication and Authorization\n\nGuards determine whether a request should be handled based on authentication state, roles, permissions, or other conditions. They run after middleware but before pipes and interceptors, making them ideal for access control. Use guards instead of manual checks in controllers.\n\n**Incorrect (manual auth checks in every handler):**\n\n```typescript\n// Manual auth checks in every handler\n@Controller('admin')\nexport class AdminController {\n  @Get('users')\n  async getUsers(@Request() req) {\n    if (!req.user) {\n      throw new UnauthorizedException();\n    }\n    if (!req.user.roles.includes('admin')) {\n      throw new ForbiddenException();\n    }\n    return this.adminService.getUsers();\n  }\n\n  @Delete('users/:id')\n  async deleteUser(@Request() req, @Param('id') id: string) {\n    if (!req.user) {\n      throw new UnauthorizedException();\n    }\n    if (!req.user.roles.includes('admin')) {\n      throw new ForbiddenException();\n    }\n    return this.adminService.deleteUser(id);\n  }\n}\n```\n\n**Correct (guards with declarative decorators):**\n\n```typescript\n// JWT Auth Guard\n@Injectable()\nexport class JwtAuthGuard implements CanActivate {\n  constructor(\n    private jwtService: JwtService,\n    private reflector: Reflector,\n  ) {}\n\n  async canActivate(context: ExecutionContext): Promise<boolean> {\n    // Check for @Public() decorator\n    const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [context.getHandler(), context.getClass()]);\n    if (isPublic) return true;\n\n    const request = context.switchToHttp().getRequest();\n    const token = this.extractToken(request);\n\n    if (!token) {\n      throw new UnauthorizedException('No token provided');\n    }\n\n    try {\n      request.user = await this.jwtService.verifyAsync(token);\n      return true;\n    } catch {\n      throw new UnauthorizedException('Invalid token');\n    }\n  }\n\n  private extractToken(request: Request): string | undefined {\n    const [type, token] = request.headers.authorization?.split(' ') ?? [];\n    return type === 'Bearer' ? token : undefined;\n  }\n}\n\n// Roles Guard\n@Injectable()\nexport class RolesGuard implements CanActivate {\n  constructor(private reflector: Reflector) {}\n\n  canActivate(context: ExecutionContext): boolean {\n    const requiredRoles = this.reflector.getAllAndOverride<Role[]>('roles', [context.getHandler(), context.getClass()]);\n\n    if (!requiredRoles) return true;\n\n    const { user } = context.switchToHttp().getRequest();\n    return requiredRoles.some(role => user.roles?.includes(role));\n  }\n}\n\n// Decorators\nexport const Public = () => SetMetadata('isPublic', true);\nexport const Roles = (...roles: Role[]) => SetMetadata('roles', roles);\n\n// Register guards globally\n@Module({\n  providers: [\n    { provide: APP_GUARD, useClass: JwtAuthGuard },\n    { provide: APP_GUARD, useClass: RolesGuard },\n  ],\n})\nexport class AppModule {}\n\n// Clean controller\n@Controller('admin')\n@Roles(Role.Admin) // Applied to all routes\nexport class AdminController {\n  @Get('users')\n  getUsers(): Promise<User[]> {\n    return this.adminService.getUsers();\n  }\n\n  @Delete('users/:id')\n  deleteUser(@Param('id') id: string): Promise<void> {\n    return this.adminService.deleteUser(id);\n  }\n\n  @Public() // Override: no auth required\n  @Get('health')\n  health() {\n    return { status: 'ok' };\n  }\n}\n```\n\nReference: [NestJS Guards](https://docs.nestjs.com/guards)",".agents/skills/nestjs-best-practices/rules/security-validate-all-input.md":"---\ntitle: Validate All Input with DTOs and Pipes\nimpact: HIGH\nimpactDescription: First line of defense against attacks\ntags: security, validation, dto, pipes\n---\n\n## Validate All Input with DTOs and Pipes\n\nAlways validate incoming data using class-validator decorators on DTOs and the global ValidationPipe. Never trust user input. Validate all request bodies, query parameters, and route parameters before processing.\n\n**Incorrect (trust raw input without validation):**\n\n```typescript\n// Trust raw input without validation\n@Controller('users')\nexport class UsersController {\n  @Post()\n  create(@Body() body: any) {\n    // body could contain anything - SQL injection, XSS, etc.\n    return this.usersService.create(body);\n  }\n\n  @Get()\n  findAll(@Query() query: any) {\n    // query.limit could be \"'; DROP TABLE users; --\"\n    return this.usersService.findAll(query.limit);\n  }\n}\n\n// DTOs without validation decorators\nexport class CreateUserDto {\n  name: string; // No validation\n  email: string; // Could be \"not-an-email\"\n  age: number; // Could be \"abc\" or -999\n}\n```\n\n**Correct (validated DTOs with global ValidationPipe):**\n\n```typescript\n// Enable ValidationPipe globally in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n\n  app.useGlobalPipes(\n    new ValidationPipe({\n      whitelist: true, // Strip unknown properties\n      forbidNonWhitelisted: true, // Throw on unknown properties\n      transform: true, // Auto-transform to DTO types\n      transformOptions: {\n        enableImplicitConversion: true,\n      },\n    }),\n  );\n\n  await app.listen(3000);\n}\n\n// Create well-validated DTOs\nimport {\n  IsString,\n  IsEmail,\n  IsInt,\n  Min,\n  Max,\n  IsOptional,\n  MinLength,\n  MaxLength,\n  Matches,\n  IsNotEmpty,\n} from 'class-validator';\nimport { Transform, Type } from 'class-transformer';\n\nexport class CreateUserDto {\n  @IsString()\n  @IsNotEmpty()\n  @MinLength(2)\n  @MaxLength(100)\n  @Transform(({ value }) => value?.trim())\n  name: string;\n\n  @IsEmail()\n  @Transform(({ value }) => value?.toLowerCase().trim())\n  email: string;\n\n  @IsInt()\n  @Min(0)\n  @Max(150)\n  age: number;\n\n  @IsString()\n  @MinLength(8)\n  @MaxLength(100)\n  @Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)/, {\n    message: 'Password must contain uppercase, lowercase, and number',\n  })\n  password: string;\n}\n\n// Query DTO with defaults and transformation\nexport class FindUsersQueryDto {\n  @IsOptional()\n  @IsString()\n  @MaxLength(100)\n  search?: string;\n\n  @IsOptional()\n  @Type(() => Number)\n  @IsInt()\n  @Min(1)\n  @Max(100)\n  limit: number = 20;\n\n  @IsOptional()\n  @Type(() => Number)\n  @IsInt()\n  @Min(0)\n  offset: number = 0;\n}\n\n// Param validation\nexport class UserIdParamDto {\n  @IsUUID('4')\n  id: string;\n}\n\n@Controller('users')\nexport class UsersController {\n  @Post()\n  create(@Body() dto: CreateUserDto): Promise<User> {\n    // dto is guaranteed to be valid\n    return this.usersService.create(dto);\n  }\n\n  @Get()\n  findAll(@Query() query: FindUsersQueryDto): Promise<User[]> {\n    // query.limit is a number, query.search is sanitized\n    return this.usersService.findAll(query);\n  }\n\n  @Get(':id')\n  findOne(@Param() params: UserIdParamDto): Promise<User> {\n    // params.id is a valid UUID\n    return this.usersService.findById(params.id);\n  }\n}\n```\n\nReference: [NestJS Validation](https://docs.nestjs.com/techniques/validation)",".agents/skills/nestjs-best-practices/rules/test-e2e-supertest.md":"---\ntitle: Use Supertest for E2E Testing\nimpact: HIGH\nimpactDescription: Validates the full request/response cycle\ntags: testing, e2e, supertest, integration\n---\n\n## Use Supertest for E2E Testing\n\nEnd-to-end tests use Supertest to make real HTTP requests against your NestJS application. They test the full stack including middleware, guards, pipes, and interceptors. E2E tests catch integration issues that unit tests miss.\n\n**Incorrect (no proper E2E setup or teardown):**\n\n```typescript\n// Only unit test controllers\ndescribe('UsersController', () => {\n  it('should return users', async () => {\n    const service = { findAll: jest.fn().mockResolvedValue([]) };\n    const controller = new UsersController(service as any);\n\n    const result = await controller.findAll();\n\n    expect(result).toEqual([]);\n    // Doesn't test: routes, guards, pipes, serialization\n  });\n});\n\n// E2E tests without proper setup/teardown\ndescribe('Users API', () => {\n  it('should create user', async () => {\n    const app = await NestFactory.create(AppModule);\n    // No proper initialization\n    // No cleanup after test\n    // Hits real database\n  });\n});\n```\n\n**Correct (proper E2E setup with Supertest):**\n\n```typescript\n// Proper E2E test setup\nimport { Test, TestingModule } from '@nestjs/testing';\nimport { INestApplication, ValidationPipe } from '@nestjs/common';\nimport * as request from 'supertest';\nimport { AppModule } from '../src/app.module';\n\ndescribe('UsersController (e2e)', () => {\n  let app: INestApplication;\n\n  beforeAll(async () => {\n    const moduleFixture: TestingModule = await Test.createTestingModule({\n      imports: [AppModule],\n    }).compile();\n\n    app = moduleFixture.createNestApplication();\n\n    // Apply same config as production\n    app.useGlobalPipes(\n      new ValidationPipe({\n        whitelist: true,\n        transform: true,\n        forbidNonWhitelisted: true,\n      }),\n    );\n\n    await app.init();\n  });\n\n  afterAll(async () => {\n    await app.close();\n  });\n\n  describe('/users (POST)', () => {\n    it('should create a user', () => {\n      return request(app.getHttpServer())\n        .post('/users')\n        .send({ name: 'John', email: 'john@test.com' })\n        .expect(201)\n        .expect(res => {\n          expect(res.body).toHaveProperty('id');\n          expect(res.body.name).toBe('John');\n          expect(res.body.email).toBe('john@test.com');\n        });\n    });\n\n    it('should return 400 for invalid email', () => {\n      return request(app.getHttpServer())\n        .post('/users')\n        .send({ name: 'John', email: 'invalid-email' })\n        .expect(400)\n        .expect(res => {\n          expect(res.body.message).toContain('email');\n        });\n    });\n  });\n\n  describe('/users/:id (GET)', () => {\n    it('should return 404 for non-existent user', () => {\n      return request(app.getHttpServer()).get('/users/non-existent-id').expect(404);\n    });\n  });\n});\n\n// Testing with authentication\ndescribe('Protected Routes (e2e)', () => {\n  let app: INestApplication;\n  let authToken: string;\n\n  beforeAll(async () => {\n    const moduleFixture = await Test.createTestingModule({\n      imports: [AppModule],\n    }).compile();\n\n    app = moduleFixture.createNestApplication();\n    app.useGlobalPipes(new ValidationPipe({ whitelist: true }));\n    await app.init();\n\n    // Get auth token\n    const loginResponse = await request(app.getHttpServer())\n      .post('/auth/login')\n      .send({ email: 'test@test.com', password: 'password' });\n\n    authToken = loginResponse.body.accessToken;\n  });\n\n  it('should return 401 without token', () => {\n    return request(app.getHttpServer()).get('/users/me').expect(401);\n  });\n\n  it('should return user profile with valid token', () => {\n    return request(app.getHttpServer())\n      .get('/users/me')\n      .set('Authorization', `Bearer ${authToken}`)\n      .expect(200)\n      .expect(res => {\n        expect(res.body.email).toBe('test@test.com');\n      });\n  });\n});\n\n// Database isolation for E2E tests\ndescribe('Orders API (e2e)', () => {\n  let app: INestApplication;\n  let dataSource: DataSource;\n\n  beforeAll(async () => {\n    const moduleFixture = await Test.createTestingModule({\n      imports: [\n        ConfigModule.forRoot({\n          envFilePath: '.env.test', // Test database config\n        }),\n        AppModule,\n      ],\n    }).compile();\n\n    app = moduleFixture.createNestApplication();\n    dataSource = moduleFixture.get(DataSource);\n    await app.init();\n  });\n\n  beforeEach(async () => {\n    // Clean database between tests\n    await dataSource.synchronize(true);\n  });\n\n  afterAll(async () => {\n    await dataSource.destroy();\n    await app.close();\n  });\n});\n```\n\nReference: [NestJS E2E Testing](https://docs.nestjs.com/fundamentals/testing#end-to-end-testing)",".agents/skills/nestjs-best-practices/rules/test-mock-external-services.md":"---\ntitle: Mock External Services in Tests\nimpact: HIGH\nimpactDescription: Ensures fast, reliable, deterministic tests\ntags: testing, mocking, external-services, jest\n---\n\n## Mock External Services in Tests\n\nNever call real external services (APIs, databases, message queues) in unit tests. Mock them to ensure tests are fast, deterministic, and don't incur costs. Use realistic mock data and test edge cases like timeouts and errors.\n\n**Incorrect (calling real APIs and databases):**\n\n```typescript\n// Call real APIs in tests\ndescribe('PaymentService', () => {\n  it('should process payment', async () => {\n    const service = new PaymentService(new StripeClient(realApiKey));\n    // Hits real Stripe API!\n    const result = await service.charge('tok_visa', 1000);\n    // Slow, costs money, flaky\n  });\n});\n\n// Use real database\ndescribe('UsersService', () => {\n  beforeEach(async () => {\n    await connection.query('DELETE FROM users'); // Modifies real DB\n  });\n\n  it('should create user', async () => {\n    const user = await service.create({ email: 'test@test.com' });\n    // Side effects on shared database\n  });\n});\n\n// Incomplete mocks\nconst mockHttpService = {\n  get: jest.fn().mockResolvedValue({ data: {} }),\n  // Missing error scenarios, missing other methods\n};\n```\n\n**Correct (mock all external dependencies):**\n\n```typescript\n// Mock HTTP service properly\ndescribe('WeatherService', () => {\n  let service: WeatherService;\n  let httpService: jest.Mocked<HttpService>;\n\n  beforeEach(async () => {\n    const module = await Test.createTestingModule({\n      providers: [\n        WeatherService,\n        {\n          provide: HttpService,\n          useValue: {\n            get: jest.fn(),\n            post: jest.fn(),\n          },\n        },\n      ],\n    }).compile();\n\n    service = module.get(WeatherService);\n    httpService = module.get(HttpService);\n  });\n\n  it('should return weather data', async () => {\n    const mockResponse = {\n      data: { temperature: 72, humidity: 45 },\n      status: 200,\n      statusText: 'OK',\n      headers: {},\n      config: {},\n    };\n\n    httpService.get.mockReturnValue(of(mockResponse));\n\n    const result = await service.getWeather('NYC');\n\n    expect(result).toEqual({ temperature: 72, humidity: 45 });\n  });\n\n  it('should handle API timeout', async () => {\n    httpService.get.mockReturnValue(throwError(() => new Error('ETIMEDOUT')));\n\n    await expect(service.getWeather('NYC')).rejects.toThrow('Weather service unavailable');\n  });\n\n  it('should handle rate limiting', async () => {\n    httpService.get.mockReturnValue(\n      throwError(() => ({\n        response: { status: 429, data: { message: 'Rate limited' } },\n      })),\n    );\n\n    await expect(service.getWeather('NYC')).rejects.toThrow(TooManyRequestsException);\n  });\n});\n\n// Mock repository instead of database\ndescribe('UsersService', () => {\n  let service: UsersService;\n  let repo: jest.Mocked<Repository<User>>;\n\n  beforeEach(async () => {\n    const mockRepo = {\n      find: jest.fn(),\n      findOne: jest.fn(),\n      save: jest.fn(),\n      delete: jest.fn(),\n      createQueryBuilder: jest.fn(),\n    };\n\n    const module = await Test.createTestingModule({\n      providers: [UsersService, { provide: getRepositoryToken(User), useValue: mockRepo }],\n    }).compile();\n\n    service = module.get(UsersService);\n    repo = module.get(getRepositoryToken(User));\n  });\n\n  it('should find user by id', async () => {\n    const mockUser = { id: '1', name: 'John', email: 'john@test.com' };\n    repo.findOne.mockResolvedValue(mockUser);\n\n    const result = await service.findById('1');\n\n    expect(result).toEqual(mockUser);\n    expect(repo.findOne).toHaveBeenCalledWith({ where: { id: '1' } });\n  });\n});\n\n// Create mock factory for complex SDKs\nfunction createMockStripe(): jest.Mocked<Stripe> {\n  return {\n    paymentIntents: {\n      create: jest.fn(),\n      retrieve: jest.fn(),\n      confirm: jest.fn(),\n      cancel: jest.fn(),\n    },\n    customers: {\n      create: jest.fn(),\n      retrieve: jest.fn(),\n    },\n  } as any;\n}\n\n// Mock time for time-dependent tests\ndescribe('TokenService', () => {\n  beforeEach(() => {\n    jest.useFakeTimers();\n    jest.setSystemTime(new Date('2024-01-15'));\n  });\n\n  afterEach(() => {\n    jest.useRealTimers();\n  });\n\n  it('should expire token after 1 hour', async () => {\n    const token = await service.createToken();\n\n    // Fast-forward time\n    jest.advanceTimersByTime(61 * 60 * 1000);\n\n    expect(await service.isValid(token)).toBe(false);\n  });\n});\n```\n\nReference: [Jest Mocking](https://jestjs.io/docs/mock-functions)",".agents/skills/nestjs-best-practices/rules/test-use-testing-module.md":"---\ntitle: Use Testing Module for Unit Tests\nimpact: HIGH\nimpactDescription: Enables proper isolated testing with mocked dependencies\ntags: testing, unit-tests, mocking, jest\n---\n\n## Use Testing Module for Unit Tests\n\nUse `@nestjs/testing` module to create isolated test environments with mocked dependencies. This ensures your tests run fast, don't depend on external services, and properly test your business logic in isolation.\n\n**Incorrect (manual instantiation bypassing DI):**\n\n```typescript\n// Instantiate services manually without DI\ndescribe('UsersService', () => {\n  it('should create user', async () => {\n    // Manual instantiation bypasses DI\n    const repo = new UserRepository(); // Real repo!\n    const service = new UsersService(repo);\n\n    const user = await service.create({ name: 'Test' });\n    // This hits the real database!\n  });\n});\n\n// Test implementation details\ndescribe('UsersController', () => {\n  it('should call service', async () => {\n    const service = { create: jest.fn() };\n    const controller = new UsersController(service as any);\n\n    await controller.create({ name: 'Test' });\n\n    expect(service.create).toHaveBeenCalled(); // Tests implementation, not behavior\n  });\n});\n```\n\n**Correct (use Test.createTestingModule with mocked dependencies):**\n\n```typescript\n// Use Test.createTestingModule for proper DI\nimport { Test, TestingModule } from '@nestjs/testing';\n\ndescribe('UsersService', () => {\n  let service: UsersService;\n  let repo: jest.Mocked<UserRepository>;\n\n  beforeEach(async () => {\n    const module: TestingModule = await Test.createTestingModule({\n      providers: [\n        UsersService,\n        {\n          provide: UserRepository,\n          useValue: {\n            save: jest.fn(),\n            findOne: jest.fn(),\n            find: jest.fn(),\n          },\n        },\n      ],\n    }).compile();\n\n    service = module.get<UsersService>(UsersService);\n    repo = module.get(UserRepository);\n  });\n\n  afterEach(() => {\n    jest.clearAllMocks();\n  });\n\n  describe('create', () => {\n    it('should save and return user', async () => {\n      const dto = { name: 'John', email: 'john@test.com' };\n      const expectedUser = { id: '1', ...dto };\n\n      repo.save.mockResolvedValue(expectedUser);\n\n      const result = await service.create(dto);\n\n      expect(result).toEqual(expectedUser);\n      expect(repo.save).toHaveBeenCalledWith(dto);\n    });\n\n    it('should throw on duplicate email', async () => {\n      repo.findOne.mockResolvedValue({ id: '1', email: 'test@test.com' });\n\n      await expect(service.create({ name: 'Test', email: 'test@test.com' })).rejects.toThrow(ConflictException);\n    });\n  });\n\n  describe('findById', () => {\n    it('should return user when found', async () => {\n      const user = { id: '1', name: 'John' };\n      repo.findOne.mockResolvedValue(user);\n\n      const result = await service.findById('1');\n\n      expect(result).toEqual(user);\n    });\n\n    it('should throw NotFoundException when not found', async () => {\n      repo.findOne.mockResolvedValue(null);\n\n      await expect(service.findById('999')).rejects.toThrow(NotFoundException);\n    });\n  });\n});\n\n// Testing guards and interceptors\ndescribe('RolesGuard', () => {\n  let guard: RolesGuard;\n  let reflector: Reflector;\n\n  beforeEach(async () => {\n    const module = await Test.createTestingModule({\n      providers: [RolesGuard, Reflector],\n    }).compile();\n\n    guard = module.get<RolesGuard>(RolesGuard);\n    reflector = module.get<Reflector>(Reflector);\n  });\n\n  it('should allow when no roles required', () => {\n    const context = createMockExecutionContext({ user: { roles: [] } });\n    jest.spyOn(reflector, 'getAllAndOverride').mockReturnValue(undefined);\n\n    expect(guard.canActivate(context)).toBe(true);\n  });\n\n  it('should allow admin for admin-only route', () => {\n    const context = createMockExecutionContext({ user: { roles: ['admin'] } });\n    jest.spyOn(reflector, 'getAllAndOverride').mockReturnValue(['admin']);\n\n    expect(guard.canActivate(context)).toBe(true);\n  });\n});\n\nfunction createMockExecutionContext(request: Partial<Request>): ExecutionContext {\n  return {\n    switchToHttp: () => ({\n      getRequest: () => request,\n    }),\n    getHandler: () => jest.fn(),\n    getClass: () => jest.fn(),\n  } as ExecutionContext;\n}\n```\n\nReference: [NestJS Testing](https://docs.nestjs.com/fundamentals/testing)",".agents/skills/nestjs-best-practices/scripts/package.json":"{\n  \"name\": \"nestjs-best-practices-scripts\",\n  \"version\": \"1.0.0\",\n  \"type\": \"module\",\n  \"description\": \"Build scripts for NestJS Best Practices skillset\",\n  \"scripts\": {\n    \"build\": \"npx ts-node build-agents.ts\",\n    \"build:watch\": \"npx nodemon --watch ../rules --ext md --exec 'npx ts-node build-agents.ts'\"\n  },\n  \"devDependencies\": {\n    \"typescript\": \"^5.0.0\",\n    \"ts-node\": \"^10.9.0\",\n    \"@types/node\": \"^20.0.0\"\n  }\n}\n",".agents/skills/typeorm/SKILL.md":"---\nname: typeorm\ndescription: Guidelines for developing with TypeORM, a full-featured ORM for TypeScript and JavaScript supporting multiple databases\n---\n\n# TypeORM Development Guidelines\n\nYou are an expert in TypeORM, TypeScript, and database design with a focus on the Data Mapper pattern and enterprise application architecture.\n\n## Core Principles\n\n- TypeORM supports both Active Record and Data Mapper patterns\n- Uses TypeScript decorators for entity and column definitions\n- Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, and more\n- Works in Node.js, Browser, Ionic, Cordova, React Native, NativeScript, Expo, and Electron\n- First-class support for database migrations\n\n## TypeScript Configuration\n\nRequired settings in tsconfig.json:\n\n```json\n{\n  \"compilerOptions\": {\n    \"experimentalDecorators\": true,\n    \"emitDecoratorMetadata\": true,\n    \"strict\": true,\n    \"target\": \"ES2020\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\"\n  }\n}\n```\n\n## Entity Definition\n\n### Basic Entity\n\n```typescript\nimport { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';\n\n@Entity('users')\nexport class User {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column({ type: 'varchar', length: 255, unique: true })\n  email: string;\n\n  @Column({ type: 'varchar', length: 255, nullable: true })\n  name: string | null;\n\n  @Column({ type: 'boolean', default: true })\n  isActive: boolean;\n\n  @CreateDateColumn()\n  createdAt: Date;\n\n  @UpdateDateColumn()\n  updatedAt: Date;\n}\n```\n\n### Primary Key Options\n\n```typescript\n// Auto-increment\n@PrimaryGeneratedColumn()\nid: number;\n\n// UUID\n@PrimaryGeneratedColumn(\"uuid\")\nid: string;\n\n// Custom primary key\n@PrimaryColumn()\nid: string;\n\n// Composite primary key\n@Entity()\nexport class OrderItem {\n  @PrimaryColumn()\n  orderId: number;\n\n  @PrimaryColumn()\n  productId: number;\n}\n```\n\n### Column Decorators\n\n```typescript\n@Entity()\nexport class Product {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  // String columns\n  @Column({ type: 'varchar', length: 255 })\n  name: string;\n\n  @Column({ type: 'text', nullable: true })\n  description: string | null;\n\n  // Numeric columns\n  @Column({ type: 'decimal', precision: 10, scale: 2 })\n  price: number;\n\n  @Column({ type: 'int', default: 0 })\n  stock: number;\n\n  // Boolean\n  @Column({ type: 'boolean', default: true })\n  isAvailable: boolean;\n\n  // JSON\n  @Column({ type: 'jsonb', nullable: true })\n  metadata: Record<string, any> | null;\n\n  // Enum\n  @Column({\n    type: 'enum',\n    enum: ['active', 'inactive', 'pending'],\n    default: 'pending',\n  })\n  status: 'active' | 'inactive' | 'pending';\n\n  // Timestamps\n  @CreateDateColumn()\n  createdAt: Date;\n\n  @UpdateDateColumn()\n  updatedAt: Date;\n\n  @DeleteDateColumn()\n  deletedAt: Date | null; // For soft deletes\n\n  // Version column for optimistic locking\n  @VersionColumn()\n  version: number;\n}\n```\n\n## Relationships\n\n### One-to-One\n\n```typescript\n@Entity()\nexport class User {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @OneToOne(() => Profile, profile => profile.user, { cascade: true })\n  @JoinColumn()\n  profile: Profile;\n}\n\n@Entity()\nexport class Profile {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column()\n  bio: string;\n\n  @OneToOne(() => User, user => user.profile)\n  user: User;\n}\n```\n\n### One-to-Many / Many-to-One\n\n```typescript\n@Entity()\nexport class User {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column()\n  name: string;\n\n  @OneToMany(() => Post, post => post.author)\n  posts: Post[];\n}\n\n@Entity()\nexport class Post {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column()\n  title: string;\n\n  @ManyToOne(() => User, user => user.posts, { onDelete: 'CASCADE' })\n  @JoinColumn({ name: 'author_id' })\n  author: User;\n\n  @Column()\n  authorId: number; // Explicit foreign key column\n}\n```\n\n### Many-to-Many\n\n```typescript\n@Entity()\nexport class Post {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column()\n  title: string;\n\n  @ManyToMany(() => Tag, tag => tag.posts)\n  @JoinTable({\n    name: 'post_tags',\n    joinColumn: { name: 'post_id' },\n    inverseJoinColumn: { name: 'tag_id' },\n  })\n  tags: Tag[];\n}\n\n@Entity()\nexport class Tag {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column({ unique: true })\n  name: string;\n\n  @ManyToMany(() => Post, post => post.tags)\n  posts: Post[];\n}\n```\n\n## Repository Pattern\n\n### Basic Repository Usage\n\n```typescript\nimport { AppDataSource } from './data-source';\nimport { User } from './entities/User';\n\nconst userRepository = AppDataSource.getRepository(User);\n\n// Find all\nconst users = await userRepository.find();\n\n// Find with conditions\nconst activeUsers = await userRepository.find({\n  where: { isActive: true },\n});\n\n// Find one\nconst user = await userRepository.findOne({\n  where: { id: 1 },\n});\n\n// Find or fail\nconst user = await userRepository.findOneOrFail({\n  where: { id: 1 },\n});\n\n// Save\nconst newUser = userRepository.create({\n  email: 'user@example.com',\n  name: 'John Doe',\n});\nawait userRepository.save(newUser);\n\n// Update\nawait userRepository.update({ id: 1 }, { name: 'Jane Doe' });\n\n// Delete\nawait userRepository.delete({ id: 1 });\n\n// Soft delete (requires @DeleteDateColumn)\nawait userRepository.softDelete({ id: 1 });\n```\n\n### Custom Repository\n\n```typescript\nimport { Repository, DataSource } from 'typeorm';\nimport { User } from './entities/User';\n\nexport class UserRepository extends Repository<User> {\n  constructor(private dataSource: DataSource) {\n    super(User, dataSource.createEntityManager());\n  }\n\n  async findByEmail(email: string): Promise<User | null> {\n    return this.findOne({ where: { email } });\n  }\n\n  async findActiveUsers(): Promise<User[]> {\n    return this.find({\n      where: { isActive: true },\n      order: { createdAt: 'DESC' },\n    });\n  }\n\n  async findWithPosts(userId: number): Promise<User | null> {\n    return this.findOne({\n      where: { id: userId },\n      relations: ['posts'],\n    });\n  }\n}\n```\n\n### Query Builder\n\n```typescript\nconst users = await userRepository\n  .createQueryBuilder('user')\n  .leftJoinAndSelect('user.posts', 'post')\n  .where('user.isActive = :isActive', { isActive: true })\n  .andWhere('post.publishedAt IS NOT NULL')\n  .orderBy('user.createdAt', 'DESC')\n  .skip(0)\n  .take(10)\n  .getMany();\n\n// With raw results\nconst result = await userRepository\n  .createQueryBuilder('user')\n  .select('COUNT(*)', 'count')\n  .where('user.isActive = :isActive', { isActive: true })\n  .getRawOne();\n\n// Insert with query builder\nawait userRepository\n  .createQueryBuilder()\n  .insert()\n  .into(User)\n  .values([\n    { email: 'user1@example.com', name: 'User 1' },\n    { email: 'user2@example.com', name: 'User 2' },\n  ])\n  .execute();\n```\n\n## Data Source Configuration\n\n```typescript\n// data-source.ts\nimport { DataSource } from 'typeorm';\nimport { User } from './entities/User';\nimport { Post } from './entities/Post';\n\nexport const AppDataSource = new DataSource({\n  type: 'postgres',\n  host: process.env.DB_HOST || 'localhost',\n  port: parseInt(process.env.DB_PORT || '5432'),\n  username: process.env.DB_USERNAME,\n  password: process.env.DB_PASSWORD,\n  database: process.env.DB_NAME,\n\n  // Entity configuration\n  entities: [User, Post],\n  // Or use glob pattern: entities: [\"src/entities/**/*.ts\"]\n\n  // Migrations\n  migrations: ['src/migrations/**/*.ts'],\n\n  // Synchronize - NEVER use in production\n  synchronize: false,\n\n  // Logging\n  logging: process.env.NODE_ENV === 'development',\n\n  // Connection pool\n  poolSize: 10,\n\n  // SSL (for production)\n  ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,\n});\n\n// Initialize connection\nAppDataSource.initialize()\n  .then(() => console.log('Data Source initialized'))\n  .catch(error => console.error('Error initializing Data Source:', error));\n```\n\n## Migrations\n\n### Creating Migrations\n\n```bash\n# Generate migration from entity changes\nnpx typeorm migration:generate src/migrations/CreateUsers -d src/data-source.ts\n\n# Create empty migration\nnpx typeorm migration:create src/migrations/SeedUsers\n\n# Run migrations\nnpx typeorm migration:run -d src/data-source.ts\n\n# Revert last migration\nnpx typeorm migration:revert -d src/data-source.ts\n```\n\n### Migration File Structure\n\n```typescript\nimport { MigrationInterface, QueryRunner, Table, TableIndex } from 'typeorm';\n\nexport class CreateUsers1234567890 implements MigrationInterface {\n  public async up(queryRunner: QueryRunner): Promise<void> {\n    await queryRunner.createTable(\n      new Table({\n        name: 'users',\n        columns: [\n          {\n            name: 'id',\n            type: 'int',\n            isPrimary: true,\n            isGenerated: true,\n            generationStrategy: 'increment',\n          },\n          {\n            name: 'email',\n            type: 'varchar',\n            length: '255',\n            isUnique: true,\n          },\n          {\n            name: 'name',\n            type: 'varchar',\n            length: '255',\n            isNullable: true,\n          },\n          {\n            name: 'is_active',\n            type: 'boolean',\n            default: true,\n          },\n          {\n            name: 'created_at',\n            type: 'timestamp',\n            default: 'CURRENT_TIMESTAMP',\n          },\n          {\n            name: 'updated_at',\n            type: 'timestamp',\n            default: 'CURRENT_TIMESTAMP',\n            onUpdate: 'CURRENT_TIMESTAMP',\n          },\n        ],\n      }),\n      true,\n    );\n\n    await queryRunner.createIndex(\n      'users',\n      new TableIndex({\n        name: 'IDX_USERS_EMAIL',\n        columnNames: ['email'],\n      }),\n    );\n  }\n\n  public async down(queryRunner: QueryRunner): Promise<void> {\n    await queryRunner.dropIndex('users', 'IDX_USERS_EMAIL');\n    await queryRunner.dropTable('users');\n  }\n}\n```\n\n## Transactions\n\n```typescript\n// Using QueryRunner\nconst queryRunner = AppDataSource.createQueryRunner();\nawait queryRunner.connect();\nawait queryRunner.startTransaction();\n\ntry {\n  const user = queryRunner.manager.create(User, {\n    email: 'user@example.com',\n    name: 'User',\n  });\n  await queryRunner.manager.save(user);\n\n  const post = queryRunner.manager.create(Post, {\n    title: 'First Post',\n    author: user,\n  });\n  await queryRunner.manager.save(post);\n\n  await queryRunner.commitTransaction();\n} catch (error) {\n  await queryRunner.rollbackTransaction();\n  throw error;\n} finally {\n  await queryRunner.release();\n}\n\n// Using transaction method\nawait AppDataSource.transaction(async manager => {\n  const user = manager.create(User, {\n    email: 'user@example.com',\n    name: 'User',\n  });\n  await manager.save(user);\n\n  const post = manager.create(Post, {\n    title: 'First Post',\n    author: user,\n  });\n  await manager.save(post);\n});\n```\n\n## NestJS Integration\n\n```typescript\n// app.module.ts\nimport { Module } from '@nestjs/common';\nimport { TypeOrmModule } from '@nestjs/typeorm';\nimport { User } from './entities/user.entity';\nimport { UsersModule } from './users/users.module';\n\n@Module({\n  imports: [\n    TypeOrmModule.forRoot({\n      type: 'postgres',\n      host: 'localhost',\n      port: 5432,\n      username: 'user',\n      password: 'password',\n      database: 'db',\n      entities: [User],\n      synchronize: false,\n    }),\n    UsersModule,\n  ],\n})\nexport class AppModule {}\n\n// users/users.module.ts\n@Module({\n  imports: [TypeOrmModule.forFeature([User])],\n  providers: [UsersService],\n  controllers: [UsersController],\n})\nexport class UsersModule {}\n\n// users/users.service.ts\n@Injectable()\nexport class UsersService {\n  constructor(\n    @InjectRepository(User)\n    private usersRepository: Repository<User>,\n  ) {}\n\n  findAll(): Promise<User[]> {\n    return this.usersRepository.find();\n  }\n\n  findOne(id: number): Promise<User | null> {\n    return this.usersRepository.findOneBy({ id });\n  }\n}\n```\n\n## Best Practices\n\n### Use Migrations in Production\n\nNever use `synchronize: true` in production. Always use migrations:\n\n```typescript\n// Development: Use migrations, not sync\nsynchronize: false,\n```\n\n### Eager vs Lazy Loading\n\n```typescript\n// Eager loading - loads relations automatically\n@OneToMany(() => Post, (post) => post.author, { eager: true })\nposts: Post[];\n\n// Lazy loading - loads relations on access\n@OneToMany(() => Post, (post) => post.author)\nposts: Promise<Post[]>;\n\n// Explicit loading (recommended)\nconst user = await userRepository.findOne({\n  where: { id: 1 },\n  relations: [\"posts\"],\n});\n```\n\n### Avoid N+1 Queries\n\n```typescript\n// Bad: N+1 queries\nconst users = await userRepository.find();\nfor (const user of users) {\n  console.log(user.posts); // Separate query for each user\n}\n\n// Good: Eager load relations\nconst users = await userRepository.find({\n  relations: ['posts'],\n});\n```\n\n### Use Indexes\n\n```typescript\n@Entity()\n@Index(['email'])\n@Index(['firstName', 'lastName'])\nexport class User {\n  @Column()\n  @Index()\n  email: string;\n\n  @Column()\n  firstName: string;\n\n  @Column()\n  lastName: string;\n}\n```\n\n### Cascade Operations\n\n```typescript\n@OneToMany(() => Post, (post) => post.author, {\n  cascade: true, // Saves/removes related posts\n  onDelete: \"CASCADE\", // Database-level cascade\n})\nposts: Post[];\n```\n\n### Naming Strategies\n\nFor consistent naming between TypeScript and database:\n\n```typescript\nimport { DefaultNamingStrategy, NamingStrategyInterface } from \"typeorm\";\nimport { snakeCase } from \"typeorm/util/StringUtils\";\n\nexport class SnakeNamingStrategy extends DefaultNamingStrategy implements NamingStrategyInterface {\n  tableName(targetName: string, userSpecifiedName: string | undefined): string {\n    return userSpecifiedName ? userSpecifiedName : snakeCase(targetName);\n  }\n\n  columnName(propertyName: string, customName: string, embeddedPrefixes: string[]): string {\n    return snakeCase(embeddedPrefixes.join(\"_\")) + (customName ? customName : snakeCase(propertyName));\n  }\n}\n\n// Use in data source config\nnamingStrategy: new SnakeNamingStrategy(),\n```",".agents/skills/typescript-advanced-types/SKILL.md":"---\nname: typescript-advanced-types\ndescription: Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects.\n---\n\n# TypeScript Advanced Types\n\nComprehensive guidance for mastering TypeScript's advanced type system including generics, conditional types, mapped types, template literal types, and utility types for building robust, type-safe applications.\n\n## When to Use This Skill\n\n- Building type-safe libraries or frameworks\n- Creating reusable generic components\n- Implementing complex type inference logic\n- Designing type-safe API clients\n- Building form validation systems\n- Creating strongly-typed configuration objects\n- Implementing type-safe state management\n- Migrating JavaScript codebases to TypeScript\n\n## Core Concepts\n\n### 1. Generics\n\n**Purpose:** Create reusable, type-flexible components while maintaining type safety.\n\n**Basic Generic Function:**\n\n```typescript\nfunction identity<T>(value: T): T {\n  return value;\n}\n\nconst num = identity<number>(42); // Type: number\nconst str = identity<string>('hello'); // Type: string\nconst auto = identity(true); // Type inferred: boolean\n```\n\n**Generic Constraints:**\n\n```typescript\ninterface HasLength {\n  length: number;\n}\n\nfunction logLength<T extends HasLength>(item: T): T {\n  console.log(item.length);\n  return item;\n}\n\nlogLength('hello'); // OK: string has length\nlogLength([1, 2, 3]); // OK: array has length\nlogLength({ length: 10 }); // OK: object has length\n// logLength(42);             // Error: number has no length\n```\n\n**Multiple Type Parameters:**\n\n```typescript\nfunction merge<T, U>(obj1: T, obj2: U): T & U {\n  return { ...obj1, ...obj2 };\n}\n\nconst merged = merge({ name: 'John' }, { age: 30 });\n// Type: { name: string } & { age: number }\n```\n\n### 2. Conditional Types\n\n**Purpose:** Create types that depend on conditions, enabling sophisticated type logic.\n\n**Basic Conditional Type:**\n\n```typescript\ntype IsString<T> = T extends string ? true : false;\n\ntype A = IsString<string>; // true\ntype B = IsString<number>; // false\n```\n\n**Extracting Return Types:**\n\n```typescript\ntype ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;\n\nfunction getUser() {\n  return { id: 1, name: 'John' };\n}\n\ntype User = ReturnType<typeof getUser>;\n// Type: { id: number; name: string; }\n```\n\n**Distributive Conditional Types:**\n\n```typescript\ntype ToArray<T> = T extends any ? T[] : never;\n\ntype StrOrNumArray = ToArray<string | number>;\n// Type: string[] | number[]\n```\n\n**Nested Conditions:**\n\n```typescript\ntype TypeName<T> = T extends string\n  ? 'string'\n  : T extends number\n    ? 'number'\n    : T extends boolean\n      ? 'boolean'\n      : T extends undefined\n        ? 'undefined'\n        : T extends Function\n          ? 'function'\n          : 'object';\n\ntype T1 = TypeName<string>; // \"string\"\ntype T2 = TypeName<() => void>; // \"function\"\n```\n\n### 3. Mapped Types\n\n**Purpose:** Transform existing types by iterating over their properties.\n\n**Basic Mapped Type:**\n\n```typescript\ntype Readonly<T> = {\n  readonly [P in keyof T]: T[P];\n};\n\ninterface User {\n  id: number;\n  name: string;\n}\n\ntype ReadonlyUser = Readonly<User>;\n// Type: { readonly id: number; readonly name: string; }\n```\n\n**Optional Properties:**\n\n```typescript\ntype Partial<T> = {\n  [P in keyof T]?: T[P];\n};\n\ntype PartialUser = Partial<User>;\n// Type: { id?: number; name?: string; }\n```\n\n**Key Remapping:**\n\n```typescript\ntype Getters<T> = {\n  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];\n};\n\ninterface Person {\n  name: string;\n  age: number;\n}\n\ntype PersonGetters = Getters<Person>;\n// Type: { getName: () => string; getAge: () => number; }\n```\n\n**Filtering Properties:**\n\n```typescript\ntype PickByType<T, U> = {\n  [K in keyof T as T[K] extends U ? K : never]: T[K];\n};\n\ninterface Mixed {\n  id: number;\n  name: string;\n  age: number;\n  active: boolean;\n}\n\ntype OnlyNumbers = PickByType<Mixed, number>;\n// Type: { id: number; age: number; }\n```\n\n### 4. Template Literal Types\n\n**Purpose:** Create string-based types with pattern matching and transformation.\n\n**Basic Template Literal:**\n\n```typescript\ntype EventName = 'click' | 'focus' | 'blur';\ntype EventHandler = `on${Capitalize<EventName>}`;\n// Type: \"onClick\" | \"onFocus\" | \"onBlur\"\n```\n\n**String Manipulation:**\n\n```typescript\ntype UppercaseGreeting = Uppercase<'hello'>; // \"HELLO\"\ntype LowercaseGreeting = Lowercase<'HELLO'>; // \"hello\"\ntype CapitalizedName = Capitalize<'john'>; // \"John\"\ntype UncapitalizedName = Uncapitalize<'John'>; // \"john\"\n```\n\n**Path Building:**\n\n```typescript\ntype Path<T> = T extends object\n  ? {\n      [K in keyof T]: K extends string ? `${K}` | `${K}.${Path<T[K]>}` : never;\n    }[keyof T]\n  : never;\n\ninterface Config {\n  server: {\n    host: string;\n    port: number;\n  };\n  database: {\n    url: string;\n  };\n}\n\ntype ConfigPath = Path<Config>;\n// Type: \"server\" | \"database\" | \"server.host\" | \"server.port\" | \"database.url\"\n```\n\n### 5. Utility Types\n\n**Built-in Utility Types:**\n\n```typescript\n// Partial<T> - Make all properties optional\ntype PartialUser = Partial<User>;\n\n// Required<T> - Make all properties required\ntype RequiredUser = Required<PartialUser>;\n\n// Readonly<T> - Make all properties readonly\ntype ReadonlyUser = Readonly<User>;\n\n// Pick<T, K> - Select specific properties\ntype UserName = Pick<User, 'name' | 'email'>;\n\n// Omit<T, K> - Remove specific properties\ntype UserWithoutPassword = Omit<User, 'password'>;\n\n// Exclude<T, U> - Exclude types from union\ntype T1 = Exclude<'a' | 'b' | 'c', 'a'>; // \"b\" | \"c\"\n\n// Extract<T, U> - Extract types from union\ntype T2 = Extract<'a' | 'b' | 'c', 'a' | 'b'>; // \"a\" | \"b\"\n\n// NonNullable<T> - Exclude null and undefined\ntype T3 = NonNullable<string | null | undefined>; // string\n\n// Record<K, T> - Create object type with keys K and values T\ntype PageInfo = Record<'home' | 'about', { title: string }>;\n```\n\n## Advanced Patterns\n\n### Pattern 1: Type-Safe Event Emitter\n\n```typescript\ntype EventMap = {\n  'user:created': { id: string; name: string };\n  'user:updated': { id: string };\n  'user:deleted': { id: string };\n};\n\nclass TypedEventEmitter<T extends Record<string, any>> {\n  private listeners: {\n    [K in keyof T]?: Array<(data: T[K]) => void>;\n  } = {};\n\n  on<K extends keyof T>(event: K, callback: (data: T[K]) => void): void {\n    if (!this.listeners[event]) {\n      this.listeners[event] = [];\n    }\n    this.listeners[event]!.push(callback);\n  }\n\n  emit<K extends keyof T>(event: K, data: T[K]): void {\n    const callbacks = this.listeners[event];\n    if (callbacks) {\n      callbacks.forEach(callback => callback(data));\n    }\n  }\n}\n\nconst emitter = new TypedEventEmitter<EventMap>();\n\nemitter.on('user:created', data => {\n  console.log(data.id, data.name); // Type-safe!\n});\n\nemitter.emit('user:created', { id: '1', name: 'John' });\n// emitter.emit(\"user:created\", { id: \"1\" });  // Error: missing 'name'\n```\n\n### Pattern 2: Type-Safe API Client\n\n```typescript\ntype HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';\n\ntype EndpointConfig = {\n  '/users': {\n    GET: { response: User[] };\n    POST: { body: { name: string; email: string }; response: User };\n  };\n  '/users/:id': {\n    GET: { params: { id: string }; response: User };\n    PUT: { params: { id: string }; body: Partial<User>; response: User };\n    DELETE: { params: { id: string }; response: void };\n  };\n};\n\ntype ExtractParams<T> = T extends { params: infer P } ? P : never;\ntype ExtractBody<T> = T extends { body: infer B } ? B : never;\ntype ExtractResponse<T> = T extends { response: infer R } ? R : never;\n\nclass APIClient<Config extends Record<string, Record<HTTPMethod, any>>> {\n  async request<Path extends keyof Config, Method extends keyof Config[Path]>(\n    path: Path,\n    method: Method,\n    ...[options]: ExtractParams<Config[Path][Method]> extends never\n      ? ExtractBody<Config[Path][Method]> extends never\n        ? []\n        : [{ body: ExtractBody<Config[Path][Method]> }]\n      : [\n          {\n            params: ExtractParams<Config[Path][Method]>;\n            body?: ExtractBody<Config[Path][Method]>;\n          },\n        ]\n  ): Promise<ExtractResponse<Config[Path][Method]>> {\n    // Implementation here\n    return {} as any;\n  }\n}\n\nconst api = new APIClient<EndpointConfig>();\n\n// Type-safe API calls\nconst users = await api.request('/users', 'GET');\n// Type: User[]\n\nconst newUser = await api.request('/users', 'POST', {\n  body: { name: 'John', email: 'john@example.com' },\n});\n// Type: User\n\nconst user = await api.request('/users/:id', 'GET', {\n  params: { id: '123' },\n});\n// Type: User\n```\n\n### Pattern 3: Builder Pattern with Type Safety\n\n```typescript\ntype BuilderState<T> = {\n  [K in keyof T]: T[K] | undefined;\n};\n\ntype RequiredKeys<T> = {\n  [K in keyof T]-?: {} extends Pick<T, K> ? never : K;\n}[keyof T];\n\ntype OptionalKeys<T> = {\n  [K in keyof T]-?: {} extends Pick<T, K> ? K : never;\n}[keyof T];\n\ntype IsComplete<T, S> = RequiredKeys<T> extends keyof S ? (S[RequiredKeys<T>] extends undefined ? false : true) : false;\n\nclass Builder<T, S extends BuilderState<T> = {}> {\n  private state: S = {} as S;\n\n  set<K extends keyof T>(key: K, value: T[K]): Builder<T, S & Record<K, T[K]>> {\n    this.state[key] = value;\n    return this as any;\n  }\n\n  build(this: IsComplete<T, S> extends true ? this : never): T {\n    return this.state as T;\n  }\n}\n\ninterface User {\n  id: string;\n  name: string;\n  email: string;\n  age?: number;\n}\n\nconst builder = new Builder<User>();\n\nconst user = builder.set('id', '1').set('name', 'John').set('email', 'john@example.com').build(); // OK: all required fields set\n\n// const incomplete = builder\n//   .set(\"id\", \"1\")\n//   .build();  // Error: missing required fields\n```\n\n### Pattern 4: Deep Readonly/Partial\n\n```typescript\ntype DeepReadonly<T> = {\n  readonly [P in keyof T]: T[P] extends object ? (T[P] extends Function ? T[P] : DeepReadonly<T[P]>) : T[P];\n};\n\ntype DeepPartial<T> = {\n  [P in keyof T]?: T[P] extends object\n    ? T[P] extends Array<infer U>\n      ? Array<DeepPartial<U>>\n      : DeepPartial<T[P]>\n    : T[P];\n};\n\ninterface Config {\n  server: {\n    host: string;\n    port: number;\n    ssl: {\n      enabled: boolean;\n      cert: string;\n    };\n  };\n  database: {\n    url: string;\n    pool: {\n      min: number;\n      max: number;\n    };\n  };\n}\n\ntype ReadonlyConfig = DeepReadonly<Config>;\n// All nested properties are readonly\n\ntype PartialConfig = DeepPartial<Config>;\n// All nested properties are optional\n```\n\n### Pattern 5: Type-Safe Form Validation\n\n```typescript\ntype ValidationRule<T> = {\n  validate: (value: T) => boolean;\n  message: string;\n};\n\ntype FieldValidation<T> = {\n  [K in keyof T]?: ValidationRule<T[K]>[];\n};\n\ntype ValidationErrors<T> = {\n  [K in keyof T]?: string[];\n};\n\nclass FormValidator<T extends Record<string, any>> {\n  constructor(private rules: FieldValidation<T>) {}\n\n  validate(data: T): ValidationErrors<T> | null {\n    const errors: ValidationErrors<T> = {};\n    let hasErrors = false;\n\n    for (const key in this.rules) {\n      const fieldRules = this.rules[key];\n      const value = data[key];\n\n      if (fieldRules) {\n        const fieldErrors: string[] = [];\n\n        for (const rule of fieldRules) {\n          if (!rule.validate(value)) {\n            fieldErrors.push(rule.message);\n          }\n        }\n\n        if (fieldErrors.length > 0) {\n          errors[key] = fieldErrors;\n          hasErrors = true;\n        }\n      }\n    }\n\n    return hasErrors ? errors : null;\n  }\n}\n\ninterface LoginForm {\n  email: string;\n  password: string;\n}\n\nconst validator = new FormValidator<LoginForm>({\n  email: [\n    {\n      validate: v => v.includes('@'),\n      message: 'Email must contain @',\n    },\n    {\n      validate: v => v.length > 0,\n      message: 'Email is required',\n    },\n  ],\n  password: [\n    {\n      validate: v => v.length >= 8,\n      message: 'Password must be at least 8 characters',\n    },\n  ],\n});\n\nconst errors = validator.validate({\n  email: 'invalid',\n  password: 'short',\n});\n// Type: { email?: string[]; password?: string[]; } | null\n```\n\n### Pattern 6: Discriminated Unions\n\n```typescript\ntype Success<T> = {\n  status: 'success';\n  data: T;\n};\n\ntype Error = {\n  status: 'error';\n  error: string;\n};\n\ntype Loading = {\n  status: 'loading';\n};\n\ntype AsyncState<T> = Success<T> | Error | Loading;\n\nfunction handleState<T>(state: AsyncState<T>): void {\n  switch (state.status) {\n    case 'success':\n      console.log(state.data); // Type: T\n      break;\n    case 'error':\n      console.log(state.error); // Type: string\n      break;\n    case 'loading':\n      console.log('Loading...');\n      break;\n  }\n}\n\n// Type-safe state machine\ntype State =\n  | { type: 'idle' }\n  | { type: 'fetching'; requestId: string }\n  | { type: 'success'; data: any }\n  | { type: 'error'; error: Error };\n\ntype Event =\n  | { type: 'FETCH'; requestId: string }\n  | { type: 'SUCCESS'; data: any }\n  | { type: 'ERROR'; error: Error }\n  | { type: 'RESET' };\n\nfunction reducer(state: State, event: Event): State {\n  switch (state.type) {\n    case 'idle':\n      return event.type === 'FETCH' ? { type: 'fetching', requestId: event.requestId } : state;\n    case 'fetching':\n      if (event.type === 'SUCCESS') {\n        return { type: 'success', data: event.data };\n      }\n      if (event.type === 'ERROR') {\n        return { type: 'error', error: event.error };\n      }\n      return state;\n    case 'success':\n    case 'error':\n      return event.type === 'RESET' ? { type: 'idle' } : state;\n  }\n}\n```\n\n## Type Inference Techniques\n\n### 1. Infer Keyword\n\n```typescript\n// Extract array element type\ntype ElementType<T> = T extends (infer U)[] ? U : never;\n\ntype NumArray = number[];\ntype Num = ElementType<NumArray>; // number\n\n// Extract promise type\ntype PromiseType<T> = T extends Promise<infer U> ? U : never;\n\ntype AsyncNum = PromiseType<Promise<number>>; // number\n\n// Extract function parameters\ntype Parameters<T> = T extends (...args: infer P) => any ? P : never;\n\nfunction foo(a: string, b: number) {}\ntype FooParams = Parameters<typeof foo>; // [string, number]\n```\n\n### 2. Type Guards\n\n```typescript\nfunction isString(value: unknown): value is string {\n  return typeof value === 'string';\n}\n\nfunction isArrayOf<T>(value: unknown, guard: (item: unknown) => item is T): value is T[] {\n  return Array.isArray(value) && value.every(guard);\n}\n\nconst data: unknown = ['a', 'b', 'c'];\n\nif (isArrayOf(data, isString)) {\n  data.forEach(s => s.toUpperCase()); // Type: string[]\n}\n```\n\n### 3. Assertion Functions\n\n```typescript\nfunction assertIsString(value: unknown): asserts value is string {\n  if (typeof value !== 'string') {\n    throw new Error('Not a string');\n  }\n}\n\nfunction processValue(value: unknown) {\n  assertIsString(value);\n  // value is now typed as string\n  console.log(value.toUpperCase());\n}\n```\n\n## Best Practices\n\n1. **Use `unknown` over `any`**: Enforce type checking\n2. **Prefer `interface` for object shapes**: Better error messages\n3. **Use `type` for unions and complex types**: More flexible\n4. **Leverage type inference**: Let TypeScript infer when possible\n5. **Create helper types**: Build reusable type utilities\n6. **Use const assertions**: Preserve literal types\n7. **Avoid type assertions**: Use type guards instead\n8. **Document complex types**: Add JSDoc comments\n9. **Use strict mode**: Enable all strict compiler options\n10. **Test your types**: Use type tests to verify type behavior\n\n## Type Testing\n\n```typescript\n// Type assertion tests\ntype AssertEqual<T, U> = [T] extends [U] ? ([U] extends [T] ? true : false) : false;\n\ntype Test1 = AssertEqual<string, string>; // true\ntype Test2 = AssertEqual<string, number>; // false\ntype Test3 = AssertEqual<string | number, string>; // false\n\n// Expect error helper\ntype ExpectError<T extends never> = T;\n\n// Example usage\ntype ShouldError = ExpectError<AssertEqual<string, number>>;\n```\n\n## Common Pitfalls\n\n1. **Over-using `any`**: Defeats the purpose of TypeScript\n2. **Ignoring strict null checks**: Can lead to runtime errors\n3. **Too complex types**: Can slow down compilation\n4. **Not using discriminated unions**: Misses type narrowing opportunities\n5. **Forgetting readonly modifiers**: Allows unintended mutations\n6. **Circular type references**: Can cause compiler errors\n7. **Not handling edge cases**: Like empty arrays or null values\n\n## Performance Considerations\n\n- Avoid deeply nested conditional types\n- Use simple types when possible\n- Cache complex type computations\n- Limit recursion depth in recursive types\n- Use build tools to skip type checking in production\n\n## Resources\n\n- **TypeScript Handbook**: https://www.typescriptlang.org/docs/handbook/\n- **Type Challenges**: https://github.com/type-challenges/type-challenges\n- **TypeScript Deep Dive**: https://basarat.gitbook.io/typescript/\n- **Effective TypeScript**: Book by Dan Vanderkam",".github/copilot-instructions.md":"Use instructions from `AGENTS.md` to guide your work.","AGENTS.md":"# LLM Agents Instructions\n\n## General\n\n- Read `README.md` first for project overview and architecture\n- Read `CONTRIBUTING.md` for local development setup, commands, commit messages, and PR conventions\n- Read `GUIDELINES.md` for code style and testing patterns\n- Read `DOMAIN.md` for application domain and data models\n- Read all `*/README.md` files for workspace specific architecture and development guidelines\n\n## Instructions\n\n- Use the `tmp` folder in the repository to store temporary files during execution and planning if needed\n- Never run deploy commands\n- Run `npm run lint`, `npm run test`, `npm run compile`, and `npm run format` at the end of each completed task if you modified any files. If any of these steps fail, fix the issues and run the commands again.\n- Prefer telegraph style for communications and reasoning. Sacrifice grammar and punctuation for clarity and conciseness. Save tokens.\n\n## Documentation Style\n\nUse the following writing style for documentation, comments and README files.\n\nAudience: Software Engineers. Expect half to be native speakers and half non-native speakers.\n\n- Keep the reading level at or below 9th grade (Flesch-Kincaid)\n- Write in clear, concise, active voice; don't assume passive voice reads well\n- Keep noun phrases short and avoid stacked modifiers\n- Limit embedded clauses to one level\n- Use conditionals sparingly and stick to simple if/then forms; avoid mixed or inverted versions\n- Use transition words (however, therefore, because) to show logic instead of expecting readers to infer it\n- Spread new ideas across sentences; avoid packing many concepts into one\n- Do not end list items with a period\n- Do not use emojis\n- Do not write obvious comments; explain why, not how or what\n\n## Commands\n\n- `npm run lint`: Run linting\n- `npm run test`: Run tests\n- `npm run compile`: Compile the project\n- `npm run format`: Format the code","tools/sloths/README.md":"# rs-sloths\n\nRS SLOTHS is a depository of sloths. View all existing stickers, download the necessary ones or chill a bit - why not? Feel a sloth!\n\n## Stack\n\nVue3, Pinia, Vue Router, TypeScript, Vite\n\n## Project Setup\n\n```sh\nnpm install\n```\n\n### Compile and Hot-Reload for Development\n\n```sh\nnpm run dev\n```\n\n### Type-Check, Compile and Minify for Production\n\n```sh\nnpm run build\n```\n\n### Lint with [ESLint](https://eslint.org/)\n\n```sh\nnpm run lint\n```"},"files":{".agents/skills/nestjs-best-practices/AGENTS.md":"# NestJS Best Practices\n\n**Version 1.1.0**\nNestJS Best Practices\nJanuary 2026\n\n> **Note:**\n> This document is mainly for agents and LLMs to follow when maintaining,\n> generating, or refactoring NestJS codebases. Humans may also find it\n> useful, but guidance here is optimized for automation and consistency\n> by AI-assisted workflows.\n\n---\n\n## Abstract\n\nComprehensive best practices and architecture guide for NestJS applications, designed for AI agents and LLMs. Contains 40 rules across 10 categories, prioritized by impact from critical (architecture, dependency injection) to incremental (DevOps patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.\n\n---\n\n## Table of Contents\n\n1. [Architecture](#1-architecture) — **CRITICAL**\n   - 1.1 [Avoid Circular Dependencies](#11-avoid-circular-dependencies)\n   - 1.2 [Organize by Feature Modules](#12-organize-by-feature-modules)\n   - 1.3 [Use Proper Module Sharing Patterns](#13-use-proper-module-sharing-patterns)\n   - 1.4 [Single Responsibility for Services](#14-single-responsibility-for-services)\n   - 1.5 [Use Event-Driven Architecture for Decoupling](#15-use-event-driven-architecture-for-decoupling)\n   - 1.6 [Use Repository Pattern for Data Access](#16-use-repository-pattern-for-data-access)\n2. [Dependency Injection](#2-dependency-injection) — **CRITICAL**\n   - 2.1 [Avoid Service Locator Anti-Pattern](#21-avoid-service-locator-anti-pattern)\n   - 2.2 [Apply Interface Segregation Principle](#22-apply-interface-segregation-principle)\n   - 2.3 [Honor Liskov Substitution Principle](#23-honor-liskov-substitution-principle)\n   - 2.4 [Prefer Constructor Injection](#24-prefer-constructor-injection)\n   - 2.5 [Understand Provider Scopes](#25-understand-provider-scopes)\n   - 2.6 [Use Injection Tokens for Interfaces](#26-use-injection-tokens-for-interfaces)\n3. [Error Handling](#3-error-handling) — **HIGH**\n   - 3.1 [Handle Async Errors Properly](#31-handle-async-errors-properly)\n   - 3.2 [Throw HTTP Exceptions from Services](#32-throw-http-exceptions-from-services)\n   - 3.3 [Use Exception Filters for Error Handling](#33-use-exception-filters-for-error-handling)\n4. [Security](#4-security) — **HIGH**\n   - 4.1 [Implement Secure JWT Authentication](#41-implement-secure-jwt-authentication)\n   - 4.2 [Implement Rate Limiting](#42-implement-rate-limiting)\n   - 4.3 [Sanitize Output to Prevent XSS](#43-sanitize-output-to-prevent-xss)\n   - 4.4 [Use Guards for Authentication and Authorization](#44-use-guards-for-authentication-and-authorization)\n   - 4.5 [Validate All Input with DTOs and Pipes](#45-validate-all-input-with-dtos-and-pipes)\n5. [Performance](#5-performance) — **HIGH**\n   - 5.1 [Use Async Lifecycle Hooks Correctly](#51-use-async-lifecycle-hooks-correctly)\n   - 5.2 [Use Lazy Loading for Large Modules](#52-use-lazy-loading-for-large-modules)\n   - 5.3 [Optimize Database Queries](#53-optimize-database-queries)\n   - 5.4 [Use Caching Strategically](#54-use-caching-strategically)\n6. [Testing](#6-testing) — **MEDIUM-HIGH**\n   - 6.1 [Use Supertest for E2E Testing](#61-use-supertest-for-e2e-testing)\n   - 6.2 [Mock External Services in Tests](#62-mock-external-services-in-tests)\n   - 6.3 [Use Testing Module for Unit Tests](#63-use-testing-module-for-unit-tests)\n7. [Database & ORM](#7-database-orm) — **MEDIUM-HIGH**\n   - 7.1 [Avoid N+1 Query Problems](#71-avoid-n-1-query-problems)\n   - 7.2 [Use Database Migrations](#72-use-database-migrations)\n   - 7.3 [Use Transactions for Multi-Step Operations](#73-use-transactions-for-multi-step-operations)\n8. [API Design](#8-api-design) — **MEDIUM**\n   - 8.1 [Use DTOs and Serialization for API Responses](#81-use-dtos-and-serialization-for-api-responses)\n   - 8.2 [Use Interceptors for Cross-Cutting Concerns](#82-use-interceptors-for-cross-cutting-concerns)\n   - 8.3 [Use Pipes for Input Transformation](#83-use-pipes-for-input-transformation)\n   - 8.4 [Use API Versioning for Breaking Changes](#84-use-api-versioning-for-breaking-changes)\n9. [Microservices](#9-microservices) — **MEDIUM**\n   - 9.1 [Implement Health Checks for Microservices](#91-implement-health-checks-for-microservices)\n   - 9.2 [Use Message and Event Patterns Correctly](#92-use-message-and-event-patterns-correctly)\n   - 9.3 [Use Message Queues for Background Jobs](#93-use-message-queues-for-background-jobs)\n10. [DevOps & Deployment](#10-devops-deployment) — **LOW-MEDIUM**\n\n- 10.1 [Implement Graceful Shutdown](#101-implement-graceful-shutdown)\n- 10.2 [Use ConfigModule for Environment Configuration](#102-use-configmodule-for-environment-configuration)\n- 10.3 [Use Structured Logging](#103-use-structured-logging)\n\n---\n\n## 1. Architecture\n\n**Section Impact: CRITICAL**\n\n### 1.1 Avoid Circular Dependencies\n\n**Impact: CRITICAL** — \"#1 cause of runtime crashes\"\n\nCircular dependencies occur when Module A imports Module B, and Module B imports Module A (directly or transitively). NestJS can sometimes resolve these through forward references, but they indicate architectural problems and should be avoided. This is the #1 cause of runtime crashes in NestJS applications.\n\n**Incorrect (circular module imports):**\n\n```typescript\n// users.module.ts\n@Module({\n  imports: [OrdersModule], // Orders needs Users, Users needs Orders = circular\n  providers: [UsersService],\n  exports: [UsersService],\n})\nexport class UsersModule {}\n\n// orders.module.ts\n@Module({\n  imports: [UsersModule], // Circular dependency!\n  providers: [OrdersService],\n  exports: [OrdersService],\n})\nexport class OrdersModule {}\n```\n\n**Correct (extract shared logic or use events):**\n\n```typescript\n// Option 1: Extract shared logic to a third module\n// shared.module.ts\n@Module({\n  providers: [SharedService],\n  exports: [SharedService],\n})\nexport class SharedModule {}\n\n// users.module.ts\n@Module({\n  imports: [SharedModule],\n  providers: [UsersService],\n})\nexport class UsersModule {}\n\n// orders.module.ts\n@Module({\n  imports: [SharedModule],\n  providers: [OrdersService],\n})\nexport class OrdersModule {}\n\n// Option 2: Use events for decoupled communication\n// users.service.ts\n@Injectable()\nexport class UsersService {\n  constructor(private eventEmitter: EventEmitter2) {}\n\n  async createUser(data: CreateUserDto) {\n    const user = await this.userRepo.save(data);\n    this.eventEmitter.emit('user.created', user);\n    return user;\n  }\n}\n\n// orders.service.ts\n@Injectable()\nexport class OrdersService {\n  @OnEvent('user.created')\n  handleUserCreated(user: User) {\n    // React to user creation without direct dependency\n  }\n}\n```\n\nReference: [NestJS Circular Dependency](https://docs.nestjs.com/fundamentals/circular-dependency)\n\n---\n\n### 1.2 Organize by Feature Modules\n\n**Impact: CRITICAL** — \"3-5x faster onboarding and development\"\n\nOrganize your application into feature modules that encapsulate related functionality. Each feature module should be self-contained with its own controllers, services, entities, and DTOs. Avoid organizing by technical layer (all controllers together, all services together). This enables 3-5x faster onboarding and feature development.\n\n**Incorrect (technical layer organization):**\n\n```typescript\n// Technical layer organization (anti-pattern)\nsrc/\n├── controllers/\n│   ├── users.controller.ts\n│   ├── orders.controller.ts\n│   └── products.controller.ts\n├── services/\n│   ├── users.service.ts\n│   ├── orders.service.ts\n│   └── products.service.ts\n├── entities/\n│   ├── user.entity.ts\n│   ├── order.entity.ts\n│   └── product.entity.ts\n└── app.module.ts  // Imports everything directly\n```\n\n**Correct (feature module organization):**\n\n```typescript\n// Feature module organization\nsrc/\n├── users/\n│   ├── dto/\n│   │   ├── create-user.dto.ts\n│   │   └── update-user.dto.ts\n│   ├── entities/\n│   │   └── user.entity.ts\n│   ├── users.controller.ts\n│   ├── users.service.ts\n│   ├── users.repository.ts\n│   └── users.module.ts\n├── orders/\n│   ├── dto/\n│   ├── entities/\n│   ├── orders.controller.ts\n│   ├── orders.service.ts\n│   └── orders.module.ts\n├── shared/\n│   ├── guards/\n│   ├── interceptors/\n│   ├── filters/\n│   └── shared.module.ts\n└── app.module.ts\n\n// users.module.ts\n@Module({\n  imports: [TypeOrmModule.forFeature([User])],\n  controllers: [UsersController],\n  providers: [UsersService, UsersRepository],\n  exports: [UsersService], // Only export what others need\n})\nexport class UsersModule {}\n\n// app.module.ts\n@Module({\n  imports: [\n    ConfigModule.forRoot(),\n    TypeOrmModule.forRoot(),\n    UsersModule,\n    OrdersModule,\n    SharedModule,\n  ],\n})\nexport class AppModule {}\n```\n\nReference: [NestJS Modules](https://docs.nestjs.com/modules)\n\n---\n\n### 1.3 Use Proper Module Sharing Patterns\n\n**Impact: CRITICAL** — Prevents duplicate instances, memory leaks, and state inconsistency\n\nNestJS modules are singletons by default. When a service is properly exported from a module and that module is imported elsewhere, the same instance is shared. However, providing a service in multiple modules creates separate instances, leading to memory waste, state inconsistency, and confusing behavior. Always encapsulate services in dedicated modules, export them explicitly, and import the module where needed.\n\n**Incorrect (service provided in multiple modules):**\n\n```typescript\n// StorageService provided directly in multiple modules - WRONG\n// storage.service.ts\n@Injectable()\nexport class StorageService {\n  private cache = new Map(); // Each instance has separate state!\n\n  store(key: string, value: any) {\n    this.cache.set(key, value);\n  }\n}\n\n// app.module.ts\n@Module({\n  providers: [StorageService], // Instance #1\n  controllers: [AppController],\n})\nexport class AppModule {}\n\n// videos.module.ts\n@Module({\n  providers: [StorageService], // Instance #2 - different from AppModule!\n  controllers: [VideosController],\n})\nexport class VideosModule {}\n\n// Problems:\n// 1. Two separate StorageService instances exist\n// 2. cache.set() in VideosModule doesn't affect AppModule's cache\n// 3. Memory wasted on duplicate instances\n// 4. Debugging nightmares when state doesn't sync\n```\n\n**Correct (dedicated module with exports):**\n\n```typescript\n// storage/storage.module.ts\n@Module({\n  providers: [StorageService],\n  exports: [StorageService], // Make available to importers\n})\nexport class StorageModule {}\n\n// videos/videos.module.ts\n@Module({\n  imports: [StorageModule], // Import the module, not the service\n  controllers: [VideosController],\n  providers: [VideosService],\n})\nexport class VideosModule {}\n\n// channels/channels.module.ts\n@Module({\n  imports: [StorageModule], // Same instance shared\n  controllers: [ChannelsController],\n  providers: [ChannelsService],\n})\nexport class ChannelsModule {}\n\n// app.module.ts\n@Module({\n  imports: [\n    StorageModule, // Only if AppModule itself needs StorageService\n    VideosModule,\n    ChannelsModule,\n  ],\n})\nexport class AppModule {}\n\n// Now all modules share the SAME StorageService instance\n```\n\n**When to use @Global() (sparingly):**\n\n```typescript\n// ONLY for truly cross-cutting concerns\n@Global()\n@Module({\n  providers: [ConfigService, LoggerService],\n  exports: [ConfigService, LoggerService],\n})\nexport class CoreModule {}\n\n// Import once in AppModule\n@Module({\n  imports: [CoreModule], // Registered globally, available everywhere\n})\nexport class AppModule {}\n\n// Other modules don't need to import CoreModule\n@Module({\n  controllers: [UsersController],\n  providers: [UsersService], // Can inject ConfigService without importing\n})\nexport class UsersModule {}\n\n// WARNING: Don't make everything global!\n// - Hides dependencies (can't see what a module needs from imports)\n// - Makes testing harder\n// - Reserve for: config, logging, database connections\n```\n\n**Module re-exporting pattern:**\n\n```typescript\n// common.module.ts - shared utilities\n@Module({\n  providers: [DateService, ValidationService],\n  exports: [DateService, ValidationService],\n})\nexport class CommonModule {}\n\n// core.module.ts - re-exports common for convenience\n@Module({\n  imports: [CommonModule, DatabaseModule],\n  exports: [CommonModule, DatabaseModule], // Re-export for consumers\n})\nexport class CoreModule {}\n\n// feature.module.ts - imports CoreModule, gets both\n@Module({\n  imports: [CoreModule], // Gets CommonModule + DatabaseModule\n  controllers: [FeatureController],\n})\nexport class FeatureModule {}\n```\n\nReference: [NestJS Modules](https://docs.nestjs.com/modules#shared-modules)\n\n---\n\n### 1.4 Single Responsibility for Services\n\n**Impact: CRITICAL** — \"40%+ improvement in testability\"\n\nEach service should have a single, well-defined responsibility. Avoid \"god services\" that handle multiple unrelated concerns. If a service name includes \"And\" or handles more than one domain concept, it likely violates single responsibility. This reduces complexity and improves testability by 40%+.\n\n**Incorrect (god service anti-pattern):**\n\n```typescript\n// God service anti-pattern\n@Injectable()\nexport class UserAndOrderService {\n  constructor(\n    private userRepo: UserRepository,\n    private orderRepo: OrderRepository,\n    private mailer: MailService,\n    private payment: PaymentService,\n  ) {}\n\n  async createUser(dto: CreateUserDto) {\n    const user = await this.userRepo.save(dto);\n    await this.mailer.sendWelcome(user);\n    return user;\n  }\n\n  async createOrder(userId: string, dto: CreateOrderDto) {\n    const order = await this.orderRepo.save({ userId, ...dto });\n    await this.payment.charge(order);\n    await this.mailer.sendOrderConfirmation(order);\n    return order;\n  }\n\n  async calculateOrderStats(userId: string) {\n    // Stats logic mixed in\n  }\n\n  async validatePayment(orderId: string) {\n    // Payment logic mixed in\n  }\n}\n```\n\n**Correct (focused services with single responsibility):**\n\n```typescript\n// Focused services with single responsibility\n@Injectable()\nexport class UsersService {\n  constructor(private userRepo: UserRepository) {}\n\n  async create(dto: CreateUserDto): Promise<User> {\n    return this.userRepo.save(dto);\n  }\n\n  async findById(id: string): Promise<User> {\n    return this.userRepo.findOneOrFail({ where: { id } });\n  }\n}\n\n@Injectable()\nexport class OrdersService {\n  constructor(private orderRepo: OrderRepository) {}\n\n  async create(userId: string, dto: CreateOrderDto): Promise<Order> {\n    return this.orderRepo.save({ userId, ...dto });\n  }\n\n  async findByUser(userId: string): Promise<Order[]> {\n    return this.orderRepo.find({ where: { userId } });\n  }\n}\n\n@Injectable()\nexport class OrderStatsService {\n  constructor(private orderRepo: OrderRepository) {}\n\n  async calculateForUser(userId: string): Promise<OrderStats> {\n    // Focused stats calculation\n  }\n}\n\n// Orchestration in controller or dedicated orchestrator\n@Controller('orders')\nexport class OrdersController {\n  constructor(\n    private orders: OrdersService,\n    private payment: PaymentService,\n    private notifications: NotificationService,\n  ) {}\n\n  @Post()\n  async create(@CurrentUser() user: User, @Body() dto: CreateOrderDto) {\n    const order = await this.orders.create(user.id, dto);\n    await this.payment.charge(order);\n    await this.notifications.sendOrderConfirmation(order);\n    return order;\n  }\n}\n```\n\nReference: [NestJS Providers](https://docs.nestjs.com/providers)\n\n---\n\n### 1.5 Use Event-Driven Architecture for Decoupling\n\n**Impact: MEDIUM-HIGH** — Enables async processing and modularity\n\nUse `@nestjs/event-emitter` for intra-service events and message brokers for inter-service communication. Events allow modules to react to changes without direct dependencies, improving modularity and enabling async processing.\n\n**Incorrect (direct service coupling):**\n\n```typescript\n// Direct service coupling\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private inventoryService: InventoryService,\n    private emailService: EmailService,\n    private analyticsService: AnalyticsService,\n    private notificationService: NotificationService,\n    private loyaltyService: LoyaltyService,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Tight coupling - OrdersService knows about all consumers\n    await this.inventoryService.reserve(order.items);\n    await this.emailService.sendConfirmation(order);\n    await this.analyticsService.track('order_created', order);\n    await this.notificationService.push(order.userId, 'Order placed');\n    await this.loyaltyService.addPoints(order.userId, order.total);\n\n    // Adding new behavior requires modifying this service\n    return order;\n  }\n}\n```\n\n**Correct (event-driven decoupling):**\n\n```typescript\n// Use EventEmitter for decoupling\nimport { EventEmitter2 } from '@nestjs/event-emitter';\n\n// Define event\nexport class OrderCreatedEvent {\n  constructor(\n    public readonly orderId: string,\n    public readonly userId: string,\n    public readonly items: OrderItem[],\n    public readonly total: number,\n  ) {}\n}\n\n// Service emits events\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private eventEmitter: EventEmitter2,\n    private repo: Repository<Order>,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Emit event - no knowledge of consumers\n    this.eventEmitter.emit('order.created', new OrderCreatedEvent(order.id, order.userId, order.items, order.total));\n\n    return order;\n  }\n}\n\n// Listeners in separate modules\n@Injectable()\nexport class InventoryListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.inventoryService.reserve(event.items);\n  }\n}\n\n@Injectable()\nexport class EmailListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.emailService.sendConfirmation(event.orderId);\n  }\n}\n\n@Injectable()\nexport class AnalyticsListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.analyticsService.track('order_created', {\n      orderId: event.orderId,\n      total: event.total,\n    });\n  }\n}\n```\n\nReference: [NestJS Events](https://docs.nestjs.com/techniques/events)\n\n---\n\n### 1.6 Use Repository Pattern for Data Access\n\n**Impact: HIGH** — Decouples business logic from database\n\nCreate custom repositories to encapsulate complex queries and database logic. This keeps services focused on business logic, makes testing easier with mock repositories, and allows changing database implementations without affecting business code.\n\n**Incorrect (complex queries in services):**\n\n```typescript\n// Complex queries in services\n@Injectable()\nexport class UsersService {\n  constructor(@InjectRepository(User) private repo: Repository<User>) {}\n\n  async findActiveWithOrders(minOrders: number): Promise<User[]> {\n    // Complex query logic mixed with business logic\n    return this.repo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.orders', 'order')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('user.deletedAt IS NULL')\n      .groupBy('user.id')\n      .having('COUNT(order.id) >= :min', { min: minOrders })\n      .orderBy('user.createdAt', 'DESC')\n      .getMany();\n  }\n\n  // Service becomes bloated with query logic\n}\n```\n\n**Correct (custom repository with encapsulated queries):**\n\n```typescript\n// Custom repository with encapsulated queries\n@Injectable()\nexport class UsersRepository {\n  constructor(@InjectRepository(User) private repo: Repository<User>) {}\n\n  async findById(id: string): Promise<User | null> {\n    return this.repo.findOne({ where: { id } });\n  }\n\n  async findByEmail(email: string): Promise<User | null> {\n    return this.repo.findOne({ where: { email } });\n  }\n\n  async findActiveWithMinOrders(minOrders: number): Promise<User[]> {\n    return this.repo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.orders', 'order')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('user.deletedAt IS NULL')\n      .groupBy('user.id')\n      .having('COUNT(order.id) >= :min', { min: minOrders })\n      .orderBy('user.createdAt', 'DESC')\n      .getMany();\n  }\n\n  async save(user: User): Promise<User> {\n    return this.repo.save(user);\n  }\n}\n\n// Clean service with business logic only\n@Injectable()\nexport class UsersService {\n  constructor(private usersRepo: UsersRepository) {}\n\n  async getActiveUsersWithOrders(): Promise<User[]> {\n    return this.usersRepo.findActiveWithMinOrders(1);\n  }\n\n  async create(dto: CreateUserDto): Promise<User> {\n    const existing = await this.usersRepo.findByEmail(dto.email);\n    if (existing) {\n      throw new ConflictException('Email already registered');\n    }\n\n    const user = new User();\n    user.email = dto.email;\n    user.name = dto.name;\n    return this.usersRepo.save(user);\n  }\n}\n```\n\nReference: [Repository Pattern](https://martinfowler.com/eaaCatalog/repository.html)\n\n---\n\n## 2. Dependency Injection\n\n**Section Impact: CRITICAL**\n\n### 2.1 Avoid Service Locator Anti-Pattern\n\n**Impact: HIGH** — Hides dependencies and breaks testability\n\nAvoid using `ModuleRef.get()` or global containers to resolve dependencies at runtime. This hides dependencies, makes code harder to test, and breaks the benefits of dependency injection. Use constructor injection instead.\n\n**Incorrect (service locator anti-pattern):**\n\n```typescript\n// Use ModuleRef to get dependencies dynamically\n@Injectable()\nexport class OrdersService {\n  constructor(private moduleRef: ModuleRef) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    // Dependencies are hidden - not visible in constructor\n    const usersService = this.moduleRef.get(UsersService);\n    const inventoryService = this.moduleRef.get(InventoryService);\n    const paymentService = this.moduleRef.get(PaymentService);\n\n    const user = await usersService.findOne(dto.userId);\n    // ... rest of logic\n  }\n}\n\n// Global singleton container\nclass ServiceContainer {\n  private static instance: ServiceContainer;\n  private services = new Map<string, any>();\n\n  static getInstance(): ServiceContainer {\n    if (!this.instance) {\n      this.instance = new ServiceContainer();\n    }\n    return this.instance;\n  }\n\n  get<T>(key: string): T {\n    return this.services.get(key);\n  }\n}\n```\n\n**Correct (constructor injection with explicit dependencies):**\n\n```typescript\n// Use constructor injection - dependencies are explicit\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private usersService: UsersService,\n    private inventoryService: InventoryService,\n    private paymentService: PaymentService,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const user = await this.usersService.findOne(dto.userId);\n    const inventory = await this.inventoryService.check(dto.items);\n    // Dependencies are clear and testable\n  }\n}\n\n// Easy to test with mocks\ndescribe('OrdersService', () => {\n  let service: OrdersService;\n\n  beforeEach(async () => {\n    const module = await Test.createTestingModule({\n      providers: [\n        OrdersService,\n        { provide: UsersService, useValue: mockUsersService },\n        { provide: InventoryService, useValue: mockInventoryService },\n        { provide: PaymentService, useValue: mockPaymentService },\n      ],\n    }).compile();\n\n    service = module.get(OrdersService);\n  });\n});\n\n// VALID: Factory pattern for dynamic instantiation\n@Injectable()\nexport class HandlerFactory {\n  constructor(private moduleRef: ModuleRef) {}\n\n  getHandler(type: string): Handler {\n    switch (type) {\n      case 'email':\n        return this.moduleRef.get(EmailHandler);\n      case 'sms':\n        return this.moduleRef.get(SmsHandler);\n      default:\n        return this.moduleRef.get(DefaultHandler);\n    }\n  }\n}\n```\n\nReference: [NestJS Module Reference](https://docs.nestjs.com/fundamentals/module-ref)\n\n---\n\n### 2.2 Apply Interface Segregation Principle\n\n**Impact: HIGH** — Reduces coupling and improves testability by 30-50%\n\nClients should not be forced to depend on interfaces they don't use. In NestJS, this means keeping interfaces small and focused on specific capabilities rather than creating \"fat\" interfaces that bundle unrelated methods. When a service only needs to send emails, it shouldn't depend on an interface that also includes SMS, push notifications, and logging. Split large interfaces into role-based ones.\n\n**Incorrect (fat interface forcing unused dependencies):**\n\n```typescript\n// Fat interface - forces all consumers to depend on everything\ninterface NotificationService {\n  sendEmail(to: string, subject: string, body: string): Promise<void>;\n  sendSms(phone: string, message: string): Promise<void>;\n  sendPush(userId: string, notification: PushPayload): Promise<void>;\n  sendSlack(channel: string, message: string): Promise<void>;\n  logNotification(type: string, payload: any): Promise<void>;\n  getDeliveryStatus(id: string): Promise<DeliveryStatus>;\n  retryFailed(id: string): Promise<void>;\n  scheduleNotification(dto: ScheduleDto): Promise<string>;\n}\n\n// Consumer only needs email, but must mock everything for tests\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private notifications: NotificationService, // Depends on 8 methods, uses 1\n  ) {}\n\n  async confirmOrder(order: Order): Promise<void> {\n    await this.notifications.sendEmail(\n      order.customer.email,\n      'Order Confirmed',\n      `Your order ${order.id} has been confirmed.`,\n    );\n  }\n}\n\n// Testing is painful - must mock unused methods\nconst mockNotificationService = {\n  sendEmail: jest.fn(),\n  sendSms: jest.fn(), // Never used, but required\n  sendPush: jest.fn(), // Never used, but required\n  sendSlack: jest.fn(), // Never used, but required\n  logNotification: jest.fn(), // Never used, but required\n  getDeliveryStatus: jest.fn(), // Never used, but required\n  retryFailed: jest.fn(), // Never used, but required\n  scheduleNotification: jest.fn(), // Never used, but required\n};\n```\n\n**Correct (segregated interfaces by capability):**\n\n```typescript\n// Segregated interfaces - each focused on one capability\ninterface EmailSender {\n  sendEmail(to: string, subject: string, body: string): Promise<void>;\n}\n\ninterface SmsSender {\n  sendSms(phone: string, message: string): Promise<void>;\n}\n\ninterface PushSender {\n  sendPush(userId: string, notification: PushPayload): Promise<void>;\n}\n\ninterface NotificationLogger {\n  logNotification(type: string, payload: any): Promise<void>;\n}\n\ninterface NotificationScheduler {\n  scheduleNotification(dto: ScheduleDto): Promise<string>;\n}\n\n// Implementation can implement multiple interfaces\n@Injectable()\nexport class NotificationService implements EmailSender, SmsSender, PushSender {\n  async sendEmail(to: string, subject: string, body: string): Promise<void> {\n    // Email implementation\n  }\n\n  async sendSms(phone: string, message: string): Promise<void> {\n    // SMS implementation\n  }\n\n  async sendPush(userId: string, notification: PushPayload): Promise<void> {\n    // Push implementation\n  }\n}\n\n// Or separate implementations\n@Injectable()\nexport class SendGridEmailService implements EmailSender {\n  async sendEmail(to: string, subject: string, body: string): Promise<void> {\n    // SendGrid-specific implementation\n  }\n}\n\n// Consumer depends only on what it needs\n@Injectable()\nexport class OrdersService {\n  constructor(\n    @Inject(EMAIL_SENDER) private emailSender: EmailSender, // Minimal dependency\n  ) {}\n\n  async confirmOrder(order: Order): Promise<void> {\n    await this.emailSender.sendEmail(\n      order.customer.email,\n      'Order Confirmed',\n      `Your order ${order.id} has been confirmed.`,\n    );\n  }\n}\n\n// Testing is simple - only mock what's used\nconst mockEmailSender: EmailSender = {\n  sendEmail: jest.fn(),\n};\n\n// Module registration with tokens\nexport const EMAIL_SENDER = Symbol('EMAIL_SENDER');\nexport const SMS_SENDER = Symbol('SMS_SENDER');\n\n@Module({\n  providers: [\n    { provide: EMAIL_SENDER, useClass: SendGridEmailService },\n    { provide: SMS_SENDER, useClass: TwilioSmsService },\n  ],\n  exports: [EMAIL_SENDER, SMS_SENDER],\n})\nexport class NotificationModule {}\n```\n\n**Combining interfaces when needed:**\n\n```typescript\n// Sometimes a consumer legitimately needs multiple capabilities\ninterface EmailAndSmsSender extends EmailSender, SmsSender {}\n\n// Or use intersection types\ntype MultiChannelSender = EmailSender & SmsSender & PushSender;\n\n// Consumer that genuinely needs multiple channels\n@Injectable()\nexport class AlertService {\n  constructor(\n    @Inject(MULTI_CHANNEL_SENDER)\n    private sender: EmailSender & SmsSender,\n  ) {}\n\n  async sendCriticalAlert(user: User, message: string): Promise<void> {\n    await Promise.all([\n      this.sender.sendEmail(user.email, 'Critical Alert', message),\n      this.sender.sendSms(user.phone, message),\n    ]);\n  }\n}\n```\n\nReference: [Interface Segregation Principle](https://en.wikipedia.org/wiki/Interface_segregation_principle)\n\n---\n\n### 2.3 Honor Liskov Substitution Principle\n\n**Impact: HIGH** — Ensures implementations are truly interchangeable without breaking callers\n\nSubtypes must be substitutable for their base types without altering program correctness. In NestJS with dependency injection, this means any implementation of an interface or abstract class must honor the contract completely. A mock payment service used in tests must behave like a real payment service (return similar shapes, handle errors the same way). Violating LSP causes subtle bugs when swapping implementations.\n\n**Incorrect (implementation violates the contract):**\n\n```typescript\n// Base interface with clear contract\ninterface PaymentGateway {\n  /**\n   * Charges the specified amount.\n   * @returns PaymentResult on success\n   * @throws PaymentFailedException on payment failure\n   */\n  charge(amount: number, currency: string): Promise<PaymentResult>;\n}\n\n// Production implementation - follows the contract\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    const response = await this.stripe.charges.create({ amount, currency });\n    return { success: true, transactionId: response.id, amount };\n  }\n}\n\n// Mock that violates LSP - different behavior!\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    // VIOLATION 1: Throws for valid input (contract says return PaymentResult)\n    if (amount > 1000) {\n      throw new Error('Mock does not support large amounts');\n    }\n\n    // VIOLATION 2: Returns null instead of PaymentResult\n    if (currency !== 'USD') {\n      return null as any; // Real service would convert or reject properly\n    }\n\n    // VIOLATION 3: Missing required field\n    return { success: true } as PaymentResult; // Missing transactionId!\n  }\n}\n\n// Consumer trusts the contract\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async checkout(order: Order): Promise<void> {\n    const result = await this.payment.charge(order.total, order.currency);\n    // These fail with MockPaymentService:\n    await this.saveTransaction(result.transactionId); // undefined!\n    await this.sendReceipt(result); // might be null!\n  }\n}\n```\n\n**Correct (implementations honor the contract):**\n\n```typescript\n// Well-defined interface with documented behavior\ninterface PaymentGateway {\n  /**\n   * Charges the specified amount.\n   * @param amount - Amount in smallest currency unit (cents)\n   * @param currency - ISO 4217 currency code\n   * @returns PaymentResult with transactionId, success status, and amount\n   * @throws PaymentFailedException if charge is declined\n   * @throws InvalidCurrencyException if currency is not supported\n   */\n  charge(amount: number, currency: string): Promise<PaymentResult>;\n\n  /**\n   * Refunds a previous charge.\n   * @throws TransactionNotFoundException if transactionId is invalid\n   */\n  refund(transactionId: string, amount?: number): Promise<RefundResult>;\n}\n\n// Production implementation\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    try {\n      const response = await this.stripe.charges.create({ amount, currency });\n      return {\n        success: true,\n        transactionId: response.id,\n        amount: response.amount,\n      };\n    } catch (error) {\n      if (error.type === 'card_error') {\n        throw new PaymentFailedException(error.message);\n      }\n      throw error;\n    }\n  }\n\n  async refund(transactionId: string, amount?: number): Promise<RefundResult> {\n    // Implementation...\n  }\n}\n\n// Mock that honors LSP - same contract, same behavior shape\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  private transactions = new Map<string, PaymentResult>();\n\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    // Honor the contract: validate currency like real service would\n    if (!['USD', 'EUR', 'GBP'].includes(currency)) {\n      throw new InvalidCurrencyException(`Unsupported currency: ${currency}`);\n    }\n\n    // Simulate decline for specific test scenarios\n    if (amount === 99999) {\n      throw new PaymentFailedException('Card declined (test scenario)');\n    }\n\n    // Return same shape as production\n    const result: PaymentResult = {\n      success: true,\n      transactionId: `mock_${Date.now()}_${Math.random().toString(36)}`,\n      amount,\n    };\n\n    this.transactions.set(result.transactionId, result);\n    return result;\n  }\n\n  async refund(transactionId: string, amount?: number): Promise<RefundResult> {\n    // Honor the contract: throw if transaction not found\n    if (!this.transactions.has(transactionId)) {\n      throw new TransactionNotFoundException(transactionId);\n    }\n\n    return {\n      success: true,\n      refundId: `refund_${transactionId}`,\n      amount: amount ?? this.transactions.get(transactionId)!.amount,\n    };\n  }\n}\n\n// Consumer can swap implementations safely\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async checkout(order: Order): Promise<Order> {\n    try {\n      const result = await this.payment.charge(order.total, order.currency);\n      // Works with both StripeService and MockPaymentService\n      order.transactionId = result.transactionId;\n      order.status = 'paid';\n      return order;\n    } catch (error) {\n      if (error instanceof PaymentFailedException) {\n        order.status = 'payment_failed';\n        return order;\n      }\n      throw error;\n    }\n  }\n}\n```\n\n**Testing LSP compliance:**\n\n```typescript\n// Shared test suite that any implementation must pass\nfunction testPaymentGatewayContract(createGateway: () => PaymentGateway) {\n  describe('PaymentGateway contract', () => {\n    let gateway: PaymentGateway;\n\n    beforeEach(() => {\n      gateway = createGateway();\n    });\n\n    it('returns PaymentResult with all required fields', async () => {\n      const result = await gateway.charge(1000, 'USD');\n      expect(result).toHaveProperty('success');\n      expect(result).toHaveProperty('transactionId');\n      expect(result).toHaveProperty('amount');\n      expect(typeof result.transactionId).toBe('string');\n    });\n\n    it('throws InvalidCurrencyException for unsupported currency', async () => {\n      await expect(gateway.charge(1000, 'INVALID')).rejects.toThrow(InvalidCurrencyException);\n    });\n\n    it('throws TransactionNotFoundException for invalid refund', async () => {\n      await expect(gateway.refund('nonexistent')).rejects.toThrow(TransactionNotFoundException);\n    });\n  });\n}\n\n// Run against all implementations\ndescribe('StripeService', () => {\n  testPaymentGatewayContract(() => new StripeService(mockStripeClient));\n});\n\ndescribe('MockPaymentService', () => {\n  testPaymentGatewayContract(() => new MockPaymentService());\n});\n```\n\nReference: [Liskov Substitution Principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle)\n\n---\n\n### 2.4 Prefer Constructor Injection\n\n**Impact: CRITICAL** — Required for proper DI and testing\n\nAlways use constructor injection over property injection. Constructor injection makes dependencies explicit, enables TypeScript type checking, ensures dependencies are available when the class is instantiated, and improves testability. This is required for proper DI, testing, and TypeScript support.\n\n**Incorrect (property injection with hidden dependencies):**\n\n```typescript\n// Property injection - avoid unless necessary\n@Injectable()\nexport class UsersService {\n  @Inject()\n  private userRepo: UserRepository; // Hidden dependency\n\n  @Inject('CONFIG')\n  private config: ConfigType; // Also hidden\n\n  async findAll() {\n    return this.userRepo.find();\n  }\n}\n\n// Problems:\n// 1. Dependencies not visible in constructor\n// 2. Service can be instantiated without dependencies in tests\n// 3. TypeScript can't enforce dependency types at instantiation\n```\n\n**Correct (constructor injection with explicit dependencies):**\n\n```typescript\n// Constructor injection - explicit and testable\n@Injectable()\nexport class UsersService {\n  constructor(\n    private readonly userRepo: UserRepository,\n    @Inject('CONFIG') private readonly config: ConfigType,\n  ) {}\n\n  async findAll(): Promise<User[]> {\n    return this.userRepo.find();\n  }\n}\n\n// Testing is straightforward\ndescribe('UsersService', () => {\n  let service: UsersService;\n  let mockRepo: jest.Mocked<UserRepository>;\n\n  beforeEach(() => {\n    mockRepo = {\n      find: jest.fn(),\n      save: jest.fn(),\n    } as any;\n\n    service = new UsersService(mockRepo, { dbUrl: 'test' });\n  });\n\n  it('should find all users', async () => {\n    mockRepo.find.mockResolvedValue([{ id: '1', name: 'Test' }]);\n    const result = await service.findAll();\n    expect(result).toHaveLength(1);\n  });\n});\n\n// Only use property injection for optional dependencies\n@Injectable()\nexport class LoggingService {\n  @Optional()\n  @Inject('ANALYTICS')\n  private analytics?: AnalyticsService;\n\n  log(message: string) {\n    console.log(message);\n    this.analytics?.track('log', message); // Optional enhancement\n  }\n}\n```\n\nReference: [NestJS Providers](https://docs.nestjs.com/providers)\n\n---\n\n### 2.5 Understand Provider Scopes\n\n**Impact: CRITICAL** — Prevents data leaks and performance issues\n\nNestJS has three provider scopes: DEFAULT (singleton), REQUEST (per-request instance), and TRANSIENT (new instance for each injection). Most providers should be singletons. Request-scoped providers have performance implications as they bubble up through the dependency tree. Understanding scopes prevents memory leaks and incorrect data sharing.\n\n**Incorrect (wrong scope usage):**\n\n```typescript\n// Request-scoped when not needed (performance hit)\n@Injectable({ scope: Scope.REQUEST })\nexport class UsersService {\n  // This creates a new instance for EVERY request\n  // All dependencies also become request-scoped\n  async findAll() {\n    return this.userRepo.find();\n  }\n}\n\n// Singleton with mutable request state\n@Injectable() // Default: singleton\nexport class RequestContextService {\n  private userId: string; // DANGER: Shared across all requests!\n\n  setUser(userId: string) {\n    this.userId = userId; // Overwrites for all concurrent requests\n  }\n\n  getUser() {\n    return this.userId; // Returns wrong user!\n  }\n}\n```\n\n**Correct (appropriate scope for each use case):**\n\n```typescript\n// Singleton for stateless services (default, most common)\n@Injectable()\nexport class UsersService {\n  constructor(private readonly userRepo: UserRepository) {}\n\n  async findById(id: string): Promise<User> {\n    return this.userRepo.findOne({ where: { id } });\n  }\n}\n\n// Request-scoped ONLY when you need request context\n@Injectable({ scope: Scope.REQUEST })\nexport class RequestContextService {\n  private userId: string;\n\n  setUser(userId: string) {\n    this.userId = userId;\n  }\n\n  getUser(): string {\n    return this.userId;\n  }\n}\n\n// Better: Use NestJS built-in request context\nimport { REQUEST } from '@nestjs/core';\nimport { Request } from 'express';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class AuditService {\n  constructor(@Inject(REQUEST) private request: Request) {}\n\n  log(action: string) {\n    console.log(`User ${this.request.user?.id} performed ${action}`);\n  }\n}\n\n// Best: Use ClsModule for async context (no scope bubble-up)\nimport { ClsService } from 'nestjs-cls';\n\n@Injectable() // Stays singleton!\nexport class AuditService {\n  constructor(private cls: ClsService) {}\n\n  log(action: string) {\n    const userId = this.cls.get('userId');\n    console.log(`User ${userId} performed ${action}`);\n  }\n}\n```\n\nReference: [NestJS Injection Scopes](https://docs.nestjs.com/fundamentals/injection-scopes)\n\n---\n\n### 2.6 Use Injection Tokens for Interfaces\n\n**Impact: HIGH** — Enables interface-based DI at runtime\n\nTypeScript interfaces are erased at compile time and can't be used as injection tokens. Use string tokens, symbols, or abstract classes when you want to inject implementations of interfaces. This enables swapping implementations for testing or different environments.\n\n**Incorrect (interface can't be used as token):**\n\n```typescript\n// Interface can't be used as injection token\ninterface PaymentGateway {\n  charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  charge(amount: number) {\n    /* ... */\n  }\n}\n\n@Injectable()\nexport class OrdersService {\n  // This WON'T work - PaymentGateway doesn't exist at runtime\n  constructor(private payment: PaymentGateway) {}\n}\n```\n\n**Correct (symbol tokens or abstract classes):**\n\n```typescript\n// Option 1: String/Symbol tokens (most flexible)\nexport const PAYMENT_GATEWAY = Symbol('PAYMENT_GATEWAY');\n\nexport interface PaymentGateway {\n  charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    // Stripe implementation\n  }\n}\n\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    return { success: true, id: 'mock-id' };\n  }\n}\n\n// Module registration\n@Module({\n  providers: [\n    {\n      provide: PAYMENT_GATEWAY,\n      useClass: process.env.NODE_ENV === 'test' ? MockPaymentService : StripeService,\n    },\n  ],\n  exports: [PAYMENT_GATEWAY],\n})\nexport class PaymentModule {}\n\n// Injection\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async createOrder(dto: CreateOrderDto) {\n    await this.payment.charge(dto.amount);\n  }\n}\n\n// Option 2: Abstract class (carries runtime type info)\nexport abstract class PaymentGateway {\n  abstract charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService extends PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    // Implementation\n  }\n}\n\n// No @Inject needed with abstract class\n@Injectable()\nexport class OrdersService {\n  constructor(private payment: PaymentGateway) {}\n}\n```\n\nReference: [NestJS Custom Providers](https://docs.nestjs.com/fundamentals/custom-providers)\n\n---\n\n## 3. Error Handling\n\n**Section Impact: HIGH**\n\n### 3.1 Handle Async Errors Properly\n\n**Impact: HIGH** — Prevents process crashes from unhandled rejections\n\nNestJS automatically catches errors from async route handlers, but errors from background tasks, event handlers, and manually created promises can crash your application. Always handle async errors explicitly and use global handlers as a safety net.\n\n**Incorrect (fire-and-forget without error handling):**\n\n```typescript\n// Fire-and-forget without error handling\n@Injectable()\nexport class UsersService {\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Fire and forget - if this fails, error is unhandled!\n    this.emailService.sendWelcome(user.email);\n\n    return user;\n  }\n}\n\n// Unhandled promise in event handler\n@Injectable()\nexport class OrdersService {\n  @OnEvent('order.created')\n  handleOrderCreated(event: OrderCreatedEvent) {\n    // This returns a promise but it's not awaited!\n    this.processOrder(event);\n    // Errors will crash the process\n  }\n\n  private async processOrder(event: OrderCreatedEvent): Promise<void> {\n    await this.inventoryService.reserve(event.items);\n    await this.notificationService.send(event.userId);\n  }\n}\n\n// Missing try-catch in scheduled tasks\n@Cron('0 0 * * *')\nasync dailyCleanup(): Promise<void> {\n  await this.cleanupService.run();\n  // If this throws, no error handling\n}\n```\n\n**Correct (explicit async error handling):**\n\n```typescript\n// Handle fire-and-forget with explicit catch\n@Injectable()\nexport class UsersService {\n  private readonly logger = new Logger(UsersService.name);\n\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Explicitly catch and log errors\n    this.emailService.sendWelcome(user.email).catch(error => {\n      this.logger.error('Failed to send welcome email', error.stack);\n      // Optionally queue for retry\n    });\n\n    return user;\n  }\n}\n\n// Properly handle async event handlers\n@Injectable()\nexport class OrdersService {\n  private readonly logger = new Logger(OrdersService.name);\n\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    try {\n      await this.processOrder(event);\n    } catch (error) {\n      this.logger.error('Failed to process order', { event, error });\n      // Don't rethrow - would crash the process\n      await this.deadLetterQueue.add('order.created', event);\n    }\n  }\n}\n\n// Safe scheduled tasks\n@Injectable()\nexport class CleanupService {\n  private readonly logger = new Logger(CleanupService.name);\n\n  @Cron('0 0 * * *')\n  async dailyCleanup(): Promise<void> {\n    try {\n      await this.cleanupService.run();\n      this.logger.log('Daily cleanup completed');\n    } catch (error) {\n      this.logger.error('Daily cleanup failed', error.stack);\n      // Alert or retry logic\n    }\n  }\n}\n\n// Global unhandled rejection handler in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  const logger = new Logger('Bootstrap');\n\n  process.on('unhandledRejection', (reason, promise) => {\n    logger.error('Unhandled Rejection at:', promise, 'reason:', reason);\n  });\n\n  process.on('uncaughtException', error => {\n    logger.error('Uncaught Exception:', error);\n    process.exit(1);\n  });\n\n  await app.listen(3000);\n}\n```\n\nReference: [Node.js Unhandled Rejections](https://nodejs.org/api/process.html#event-unhandledrejection)\n\n---\n\n### 3.2 Throw HTTP Exceptions from Services\n\n**Impact: HIGH** — Keeps controllers thin and simplifies error handling\n\nIt's acceptable (and often preferable) to throw `HttpException` subclasses from services in HTTP applications. This keeps controllers thin and allows services to communicate appropriate error states. For truly layer-agnostic services, use domain exceptions that map to HTTP status codes.\n\n**Incorrect (return error objects instead of throwing):**\n\n```typescript\n// Return error objects instead of throwing\n@Injectable()\nexport class UsersService {\n  async findById(id: string): Promise<{ user?: User; error?: string }> {\n    const user = await this.repo.findOne({ where: { id } });\n    if (!user) {\n      return { error: 'User not found' }; // Controller must check this\n    }\n    return { user };\n  }\n}\n\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string) {\n    const result = await this.usersService.findById(id);\n    if (result.error) {\n      throw new NotFoundException(result.error);\n    }\n    return result.user;\n  }\n}\n```\n\n**Correct (throw exceptions directly from service):**\n\n```typescript\n// Throw exceptions directly from service\n@Injectable()\nexport class UsersService {\n  constructor(private readonly repo: UserRepository) {}\n\n  async findById(id: string): Promise<User> {\n    const user = await this.repo.findOne({ where: { id } });\n    if (!user) {\n      throw new NotFoundException(`User #${id} not found`);\n    }\n    return user;\n  }\n\n  async create(dto: CreateUserDto): Promise<User> {\n    const existing = await this.repo.findOne({\n      where: { email: dto.email },\n    });\n    if (existing) {\n      throw new ConflictException('Email already registered');\n    }\n    return this.repo.save(dto);\n  }\n\n  async update(id: string, dto: UpdateUserDto): Promise<User> {\n    const user = await this.findById(id); // Throws if not found\n    Object.assign(user, dto);\n    return this.repo.save(user);\n  }\n}\n\n// Controller stays thin\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findById(id);\n  }\n\n  @Post()\n  create(@Body() dto: CreateUserDto): Promise<User> {\n    return this.usersService.create(dto);\n  }\n}\n\n// For layer-agnostic services, use domain exceptions\nexport class EntityNotFoundException extends Error {\n  constructor(\n    public readonly entity: string,\n    public readonly id: string,\n  ) {\n    super(`${entity} with ID \"${id}\" not found`);\n  }\n}\n\n// Map to HTTP in exception filter\n@Catch(EntityNotFoundException)\nexport class EntityNotFoundFilter implements ExceptionFilter {\n  catch(exception: EntityNotFoundException, host: ArgumentsHost) {\n    const ctx = host.switchToHttp();\n    const response = ctx.getResponse<Response>();\n\n    response.status(404).json({\n      statusCode: 404,\n      message: exception.message,\n      entity: exception.entity,\n      id: exception.id,\n    });\n  }\n}\n```\n\nReference: [NestJS Exception Filters](https://docs.nestjs.com/exception-filters)\n\n---\n\n### 3.3 Use Exception Filters for Error Handling\n\n**Impact: HIGH** — Consistent\n\n<!-- [TRUNCATED AT 50KB] -->",".agents/skills/nestjs-best-practices/README.md":"# NestJS Best Practices\n\n📖 [For Humans <3](https://kadajett.github.io/agent-nestjs-skills/)\n\nA structured repository for creating and maintaining NestJS Best Practices optimized for agents and LLMs.\n\n## Installation\n\nInstall this skill using [skills](https://github.com/vercel-labs/skills):\n\n```bash\n# GitHub shorthand\nnpx skills add Kadajett/agent-nestjs-skills\n\n# Install globally (available across all projects)\nnpx skills add Kadajett/agent-nestjs-skills --global\n\n# Install for specific agents\nnpx skills add Kadajett/agent-nestjs-skills -a claude-code -a cursor\n```\n\n### Supported Agents\n\n- Claude Code\n- OpenCode\n- Codex\n- Cursor\n- Antigravity\n- Roo Code\n\n## Structure\n\n- `rules/` - Individual rule files (one per rule)\n  - `_sections.md` - Section metadata (titles, impacts, descriptions)\n  - `_template.md` - Template for creating new rules\n  - `area-description.md` - Individual rule files\n- `scripts/` - Build scripts and utilities\n- `metadata.json` - Document metadata (version, organization, abstract)\n- **`AGENTS.md`** - Compiled output (generated)\n\n## Getting Started\n\n1. Install dependencies:\n\n   ```bash\n   cd scripts && npm install\n   ```\n\n2. Build AGENTS.md from rules:\n   ```bash\n   npm run build\n   # or\n   ./scripts/build.sh\n   ```\n\n## Creating a New Rule\n\n1. Copy `rules/_template.md` to `rules/area-description.md`\n2. Choose the appropriate area prefix:\n   - `arch-` for Architecture (Section 1)\n   - `di-` for Dependency Injection (Section 2)\n   - `error-` for Error Handling (Section 3)\n   - `security-` for Security (Section 4)\n   - `perf-` for Performance (Section 5)\n   - `test-` for Testing (Section 6)\n   - `db-` for Database & ORM (Section 7)\n   - `api-` for API Design (Section 8)\n   - `micro-` for Microservices (Section 9)\n   - `devops-` for DevOps & Deployment (Section 10)\n3. Fill in the frontmatter and content\n4. Ensure you have clear examples with explanations\n5. Run the build script to regenerate AGENTS.md\n\n## Rule File Structure\n\nEach rule file should follow this structure:\n\n````markdown\n---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: Optional description\ntags: tag1, tag2, tag3\n---\n\n## Rule Title Here\n\nBrief explanation of the rule and why it matters.\n\n**Incorrect (description of what's wrong):**\n\n```typescript\n// Bad code example\n```\n````\n\n**Correct (description of what's right):**\n\n```typescript\n// Good code example\n```\n\nOptional explanatory text after examples.\n\nReference: [NestJS Documentation](https://docs.nestjs.com)\n\n## File Naming Convention\n\n- Files starting with `_` are special (excluded from build)\n- Rule files: `area-description.md` (e.g., `arch-avoid-circular-deps.md`)\n- Section is automatically inferred from filename prefix\n- Rules are sorted alphabetically by title within each section\n- IDs (e.g., 1.1, 1.2) are auto-generated during build\n\n## Impact Levels\n\n| Level       | Description                                                                           |\n| ----------- | ------------------------------------------------------------------------------------- |\n| CRITICAL    | Violations cause runtime errors, security vulnerabilities, or architectural breakdown |\n| HIGH        | Significant impact on reliability, security, or maintainability                       |\n| MEDIUM-HIGH | Notable impact on quality and developer experience                                    |\n| MEDIUM      | Moderate impact on code quality and best practices                                    |\n| LOW-MEDIUM  | Minor improvements for consistency and maintainability                                |\n\n## Scripts\n\n- `npm run build` (in scripts/) - Compile rules into AGENTS.md\n\n## Contributing\n\nWhen adding or modifying rules:\n\n1. Use the correct filename prefix for your section\n2. Follow the `_template.md` structure\n3. Include clear bad/good examples with explanations\n4. Add appropriate tags\n5. Run the build script to regenerate AGENTS.md\n6. Rules are automatically sorted by title - no need to manage numbers!\n\n## Documentation Website\n\nThe documentation website source code lives on the [`docs` branch](https://github.com/Kadajett/agent-nestjs-skills/tree/docs/website). This separation keeps the skill installation lightweight while maintaining the full documentation site.\n\nTo contribute to the website:\n\n```bash\ngit checkout docs\ncd website\nnpm install\nnpm run dev\n```\n\n## Acknowledgments\n\n- Inspired by the [Vercel React Best Practices](https://github.com/vercel-labs/agent-skills) skill structure\n- Compatible with [skills](https://github.com/vercel-labs/skills) for easy installation across coding agents\n\n## Compatible Agents\n\nThese NestJS skills work with:\n\n- [Claude Code](https://claude.ai/code) - Anthropic's official CLI\n- [AdaL](https://sylph.ai/adal) - Self-evolving AI coding agent with MCP support",".agents/skills/nestjs-best-practices/SKILL.md":"---\nname: nestjs-best-practices\ndescription: NestJS best practices and architecture patterns for building production-ready applications. This skill should be used when writing, reviewing, or refactoring NestJS code to ensure proper patterns for modules, dependency injection, security, and performance.\nlicense: MIT\nmetadata:\n  author: Kadajett\n  version: '1.1.0'\n---\n\n# NestJS Best Practices\n\nComprehensive best practices guide for NestJS applications. Contains 40 rules across 10 categories, prioritized by impact to guide automated refactoring and code generation.\n\n## When to Apply\n\nReference these guidelines when:\n\n- Writing new NestJS modules, controllers, or services\n- Implementing authentication and authorization\n- Reviewing code for architecture and security issues\n- Refactoring existing NestJS codebases\n- Optimizing performance or database queries\n- Building microservices architectures\n\n## Rule Categories by Priority\n\n| Priority | Category             | Impact      | Prefix      |\n| -------- | -------------------- | ----------- | ----------- |\n| 1        | Architecture         | CRITICAL    | `arch-`     |\n| 2        | Dependency Injection | CRITICAL    | `di-`       |\n| 3        | Error Handling       | HIGH        | `error-`    |\n| 4        | Security             | HIGH        | `security-` |\n| 5        | Performance          | HIGH        | `perf-`     |\n| 6        | Testing              | MEDIUM-HIGH | `test-`     |\n| 7        | Database & ORM       | MEDIUM-HIGH | `db-`       |\n| 8        | API Design           | MEDIUM      | `api-`      |\n| 9        | Microservices        | MEDIUM      | `micro-`    |\n| 10       | DevOps & Deployment  | LOW-MEDIUM  | `devops-`   |\n\n## Quick Reference\n\n### 1. Architecture (CRITICAL)\n\n- `arch-avoid-circular-deps` - Avoid circular module dependencies\n- `arch-feature-modules` - Organize by feature, not technical layer\n- `arch-module-sharing` - Proper module exports/imports, avoid duplicate providers\n- `arch-single-responsibility` - Focused services over \"god services\"\n- `arch-use-repository-pattern` - Abstract database logic for testability\n- `arch-use-events` - Event-driven architecture for decoupling\n\n### 2. Dependency Injection (CRITICAL)\n\n- `di-avoid-service-locator` - Avoid service locator anti-pattern\n- `di-interface-segregation` - Interface Segregation Principle (ISP)\n- `di-liskov-substitution` - Liskov Substitution Principle (LSP)\n- `di-prefer-constructor-injection` - Constructor over property injection\n- `di-scope-awareness` - Understand singleton/request/transient scopes\n- `di-use-interfaces-tokens` - Use injection tokens for interfaces\n\n### 3. Error Handling (HIGH)\n\n- `error-use-exception-filters` - Centralized exception handling\n- `error-throw-http-exceptions` - Use NestJS HTTP exceptions\n- `error-handle-async-errors` - Handle async errors properly\n\n### 4. Security (HIGH)\n\n- `security-auth-jwt` - Secure JWT authentication\n- `security-validate-all-input` - Validate with class-validator\n- `security-use-guards` - Authentication and authorization guards\n- `security-sanitize-output` - Prevent XSS attacks\n- `security-rate-limiting` - Implement rate limiting\n\n### 5. Performance (HIGH)\n\n- `perf-async-hooks` - Proper async lifecycle hooks\n- `perf-use-caching` - Implement caching strategies\n- `perf-optimize-database` - Optimize database queries\n- `perf-lazy-loading` - Lazy load modules for faster startup\n\n### 6. Testing (MEDIUM-HIGH)\n\n- `test-use-testing-module` - Use NestJS testing utilities\n- `test-e2e-supertest` - E2E testing with Supertest\n- `test-mock-external-services` - Mock external dependencies\n\n### 7. Database & ORM (MEDIUM-HIGH)\n\n- `db-use-transactions` - Transaction management\n- `db-avoid-n-plus-one` - Avoid N+1 query problems\n- `db-use-migrations` - Use migrations for schema changes\n\n### 8. API Design (MEDIUM)\n\n- `api-use-dto-serialization` - DTO and response serialization\n- `api-use-interceptors` - Cross-cutting concerns\n- `api-versioning` - API versioning strategies\n- `api-use-pipes` - Input transformation with pipes\n\n### 9. Microservices (MEDIUM)\n\n- `micro-use-patterns` - Message and event patterns\n- `micro-use-health-checks` - Health checks for orchestration\n- `micro-use-queues` - Background job processing\n\n### 10. DevOps & Deployment (LOW-MEDIUM)\n\n- `devops-use-config-module` - Environment configuration\n- `devops-use-logging` - Structured logging\n- `devops-graceful-shutdown` - Zero-downtime deployments\n\n## How to Use\n\nRead individual rule files for detailed explanations and code examples:\n\n```\nrules/arch-avoid-circular-deps.md\nrules/security-validate-all-input.md\nrules/_sections.md\n```\n\nEach rule file contains:\n\n- Brief explanation of why it matters\n- Incorrect code example with explanation\n- Correct code example with explanation\n- Additional context and references\n\n## Full Compiled Document\n\nFor the complete guide with all rules expanded: `AGENTS.md`",".agents/skills/nestjs-best-practices/rules/api-use-dto-serialization.md":"---\ntitle: Use DTOs and Serialization for API Responses\nimpact: MEDIUM\nimpactDescription: Response DTOs prevent accidental data exposure and ensure consistency\ntags: api, dto, serialization, class-transformer\n---\n\n## Use DTOs and Serialization for API Responses\n\nNever return entity objects directly from controllers. Use response DTOs with class-transformer's `@Exclude()` and `@Expose()` decorators to control exactly what data is sent to clients. This prevents accidental exposure of sensitive fields and provides a stable API contract.\n\n**Incorrect (returning entities directly or manual spreading):**\n\n```typescript\n// Return entities directly\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findById(id);\n    // Returns: { id, email, passwordHash, ssn, internalNotes, ... }\n    // Exposes sensitive data!\n  }\n}\n\n// Manual object spreading (error-prone)\n@Get(':id')\nasync findOne(@Param('id') id: string) {\n  const user = await this.usersService.findById(id);\n  return {\n    id: user.id,\n    email: user.email,\n    name: user.name,\n    // Easy to forget to exclude sensitive fields\n    // Hard to maintain across endpoints\n  };\n}\n```\n\n**Correct (use class-transformer with @Exclude and response DTOs):**\n\n```typescript\n// Enable class-transformer globally\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));\n  await app.listen(3000);\n}\n\n// Entity with serialization control\n@Entity()\nexport class User {\n  @PrimaryGeneratedColumn('uuid')\n  id: string;\n\n  @Column()\n  email: string;\n\n  @Column()\n  name: string;\n\n  @Column()\n  @Exclude() // Never include in responses\n  passwordHash: string;\n\n  @Column({ nullable: true })\n  @Exclude()\n  ssn: string;\n\n  @Column({ default: false })\n  @Exclude({ toPlainOnly: true }) // Exclude from response, allow in requests\n  isAdmin: boolean;\n\n  @CreateDateColumn()\n  createdAt: Date;\n\n  @Column()\n  @Exclude()\n  internalNotes: string;\n}\n\n// Now returning entity is safe\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findById(id);\n    // Returns: { id, email, name, createdAt }\n    // Sensitive fields excluded automatically\n  }\n}\n\n// For different response shapes, use explicit DTOs\nexport class UserResponseDto {\n  @Expose()\n  id: string;\n\n  @Expose()\n  email: string;\n\n  @Expose()\n  name: string;\n\n  @Expose()\n  @Transform(({ obj }) => obj.posts?.length || 0)\n  postCount: number;\n\n  constructor(partial: Partial<User>) {\n    Object.assign(this, partial);\n  }\n}\n\nexport class UserDetailResponseDto extends UserResponseDto {\n  @Expose()\n  createdAt: Date;\n\n  @Expose()\n  @Type(() => PostResponseDto)\n  posts: PostResponseDto[];\n}\n\n// Controller with explicit DTOs\n@Controller('users')\nexport class UsersController {\n  @Get()\n  @SerializeOptions({ type: UserResponseDto })\n  async findAll(): Promise<UserResponseDto[]> {\n    const users = await this.usersService.findAll();\n    return users.map(u => plainToInstance(UserResponseDto, u));\n  }\n\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<UserDetailResponseDto> {\n    const user = await this.usersService.findByIdWithPosts(id);\n    return plainToInstance(UserDetailResponseDto, user, {\n      excludeExtraneousValues: true,\n    });\n  }\n}\n\n// Groups for conditional serialization\nexport class UserDto {\n  @Expose()\n  id: string;\n\n  @Expose()\n  name: string;\n\n  @Expose({ groups: ['admin'] })\n  email: string;\n\n  @Expose({ groups: ['admin'] })\n  createdAt: Date;\n\n  @Expose({ groups: ['admin', 'owner'] })\n  settings: UserSettings;\n}\n\n@Controller('users')\nexport class UsersController {\n  @Get()\n  @SerializeOptions({ groups: ['public'] })\n  async findAllPublic(): Promise<UserDto[]> {\n    // Returns: { id, name }\n  }\n\n  @Get('admin')\n  @UseGuards(AdminGuard)\n  @SerializeOptions({ groups: ['admin'] })\n  async findAllAdmin(): Promise<UserDto[]> {\n    // Returns: { id, name, email, createdAt }\n  }\n\n  @Get('me')\n  @SerializeOptions({ groups: ['owner'] })\n  async getProfile(@CurrentUser() user: User): Promise<UserDto> {\n    // Returns: { id, name, settings }\n  }\n}\n```\n\nReference: [NestJS Serialization](https://docs.nestjs.com/techniques/serialization)",".agents/skills/nestjs-best-practices/rules/api-use-interceptors.md":"---\ntitle: Use Interceptors for Cross-Cutting Concerns\nimpact: MEDIUM-HIGH\nimpactDescription: Interceptors provide clean separation for cross-cutting logic\ntags: api, interceptors, logging, caching\n---\n\n## Use Interceptors for Cross-Cutting Concerns\n\nInterceptors can transform responses, add logging, handle caching, and measure performance without polluting your business logic. They wrap the route handler execution, giving you access to both the request and response streams.\n\n**Incorrect (logging and transformation in every method):**\n\n```typescript\n// Logging in every controller method\n@Controller('users')\nexport class UsersController {\n  @Get()\n  async findAll(): Promise<User[]> {\n    const start = Date.now();\n    this.logger.log('findAll called');\n\n    const users = await this.usersService.findAll();\n\n    this.logger.log(`findAll completed in ${Date.now() - start}ms`);\n    return users;\n  }\n\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    const start = Date.now();\n    this.logger.log(`findOne called with id: ${id}`);\n\n    const user = await this.usersService.findOne(id);\n\n    this.logger.log(`findOne completed in ${Date.now() - start}ms`);\n    return user;\n  }\n  // Repeated in every method!\n}\n\n// Manual response wrapping\n@Get()\nasync findAll(): Promise<{ data: User[]; meta: Meta }> {\n  const users = await this.usersService.findAll();\n  return {\n    data: users,\n    meta: { timestamp: new Date(), count: users.length },\n  };\n}\n```\n\n**Correct (use interceptors for cross-cutting concerns):**\n\n```typescript\n// Logging interceptor\n@Injectable()\nexport class LoggingInterceptor implements NestInterceptor {\n  private readonly logger = new Logger('HTTP');\n\n  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n    const request = context.switchToHttp().getRequest();\n    const { method, url, body } = request;\n    const now = Date.now();\n\n    return next.handle().pipe(\n      tap({\n        next: (data) => {\n          const response = context.switchToHttp().getResponse();\n          this.logger.log(\n            `${method} ${url} ${response.statusCode} - ${Date.now() - now}ms`,\n          );\n        },\n        error: (error) => {\n          this.logger.error(\n            `${method} ${url} ${error.status || 500} - ${Date.now() - now}ms`,\n            error.stack,\n          );\n        },\n      }),\n    );\n  }\n}\n\n// Response transformation interceptor\n@Injectable()\nexport class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {\n  intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {\n    return next.handle().pipe(\n      map((data) => ({\n        data,\n        meta: {\n          timestamp: new Date().toISOString(),\n          path: context.switchToHttp().getRequest().url,\n        },\n      })),\n    );\n  }\n}\n\n// Timeout interceptor\n@Injectable()\nexport class TimeoutInterceptor implements NestInterceptor {\n  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n    return next.handle().pipe(\n      timeout(5000),\n      catchError((err) => {\n        if (err instanceof TimeoutError) {\n          throw new RequestTimeoutException('Request timed out');\n        }\n        throw err;\n      }),\n    );\n  }\n}\n\n// Apply globally or per-controller\n@Module({\n  providers: [\n    { provide: APP_INTERCEPTOR, useClass: LoggingInterceptor },\n    { provide: APP_INTERCEPTOR, useClass: TransformInterceptor },\n  ],\n})\nexport class AppModule {}\n\n// Or per-controller\n@Controller('users')\n@UseInterceptors(LoggingInterceptor)\nexport class UsersController {\n  @Get()\n  async findAll(): Promise<User[]> {\n    // Clean business logic only\n    return this.usersService.findAll();\n  }\n}\n\n// Custom cache interceptor with TTL\n@Injectable()\nexport class HttpCacheInterceptor implements NestInterceptor {\n  constructor(\n    private cacheManager: Cache,\n    private reflector: Reflector,\n  ) {}\n\n  async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {\n    const request = context.switchToHttp().getRequest();\n\n    // Only cache GET requests\n    if (request.method !== 'GET') {\n      return next.handle();\n    }\n\n    const cacheKey = this.generateKey(request);\n    const ttl = this.reflector.get<number>('cacheTTL', context.getHandler()) || 300;\n\n    const cached = await this.cacheManager.get(cacheKey);\n    if (cached) {\n      return of(cached);\n    }\n\n    return next.handle().pipe(\n      tap((response) => {\n        this.cacheManager.set(cacheKey, response, ttl);\n      }),\n    );\n  }\n\n  private generateKey(request: Request): string {\n    return `cache:${request.url}:${JSON.stringify(request.query)}`;\n  }\n}\n\n// Usage with custom TTL\n@Get()\n@SetMetadata('cacheTTL', 600)\n@UseInterceptors(HttpCacheInterceptor)\nasync findAll(): Promise<User[]> {\n  return this.usersService.findAll();\n}\n\n// Error mapping interceptor\n@Injectable()\nexport class ErrorMappingInterceptor implements NestInterceptor {\n  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n    return next.handle().pipe(\n      catchError((error) => {\n        if (error instanceof EntityNotFoundError) {\n          throw new NotFoundException(error.message);\n        }\n        if (error instanceof QueryFailedError) {\n          if (error.message.includes('duplicate')) {\n            throw new ConflictException('Resource already exists');\n          }\n        }\n        throw error;\n      }),\n    );\n  }\n}\n```\n\nReference: [NestJS Interceptors](https://docs.nestjs.com/interceptors)",".agents/skills/nestjs-best-practices/rules/api-use-pipes.md":"---\ntitle: Use Pipes for Input Transformation\nimpact: MEDIUM\nimpactDescription: Pipes ensure clean, validated data reaches your handlers\ntags: api, pipes, validation, transformation\n---\n\n## Use Pipes for Input Transformation\n\nUse built-in pipes like `ParseIntPipe`, `ParseUUIDPipe`, and `DefaultValuePipe` for common transformations. Create custom pipes for business-specific transformations. Pipes separate validation/transformation logic from controllers.\n\n**Incorrect (manual type parsing in handlers):**\n\n```typescript\n// Manual type parsing in handlers\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    // Manual validation in every handler\n    const uuid = id.trim();\n    if (!isUUID(uuid)) {\n      throw new BadRequestException('Invalid UUID');\n    }\n    return this.usersService.findOne(uuid);\n  }\n\n  @Get()\n  async findAll(\n    @Query('page') page: string,\n    @Query('limit') limit: string,\n  ): Promise<User[]> {\n    // Manual parsing and defaults\n    const pageNum = parseInt(page) || 1;\n    const limitNum = parseInt(limit) || 10;\n    return this.usersService.findAll(pageNum, limitNum);\n  }\n}\n\n// Type coercion without validation\n@Get()\nasync search(@Query('price') price: string): Promise<Product[]> {\n  const priceNum = +price; // NaN if invalid, no error\n  return this.productsService.findByPrice(priceNum);\n}\n```\n\n**Correct (use built-in and custom pipes):**\n\n```typescript\n// Use built-in pipes for common transformations\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id', ParseUUIDPipe) id: string): Promise<User> {\n    // id is guaranteed to be a valid UUID\n    return this.usersService.findOne(id);\n  }\n\n  @Get()\n  async findAll(\n    @Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number,\n    @Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number,\n  ): Promise<User[]> {\n    // Automatic defaults and type conversion\n    return this.usersService.findAll(page, limit);\n  }\n\n  @Get('by-status/:status')\n  async findByStatus(\n    @Param('status', new ParseEnumPipe(UserStatus)) status: UserStatus,\n  ): Promise<User[]> {\n    return this.usersService.findByStatus(status);\n  }\n}\n\n// Custom pipe for business logic\n@Injectable()\nexport class ParseDatePipe implements PipeTransform<string, Date> {\n  transform(value: string): Date {\n    const date = new Date(value);\n    if (isNaN(date.getTime())) {\n      throw new BadRequestException('Invalid date format');\n    }\n    return date;\n  }\n}\n\n@Get('reports')\nasync getReports(\n  @Query('from', ParseDatePipe) from: Date,\n  @Query('to', ParseDatePipe) to: Date,\n): Promise<Report[]> {\n  return this.reportsService.findBetween(from, to);\n}\n\n// Custom transformation pipes\n@Injectable()\nexport class NormalizeEmailPipe implements PipeTransform<string, string> {\n  transform(value: string): string {\n    if (!value) return value;\n    return value.trim().toLowerCase();\n  }\n}\n\n// Parse comma-separated values\n@Injectable()\nexport class ParseArrayPipe implements PipeTransform<string, string[]> {\n  transform(value: string): string[] {\n    if (!value) return [];\n    return value.split(',').map((v) => v.trim()).filter(Boolean);\n  }\n}\n\n@Get('products')\nasync findProducts(\n  @Query('ids', ParseArrayPipe) ids: string[],\n  @Query('email', NormalizeEmailPipe) email: string,\n): Promise<Product[]> {\n  // ids is already an array, email is normalized\n  return this.productsService.findByIds(ids);\n}\n\n// Sanitize HTML input\n@Injectable()\nexport class SanitizeHtmlPipe implements PipeTransform<string, string> {\n  transform(value: string): string {\n    if (!value) return value;\n    return sanitizeHtml(value, { allowedTags: [] });\n  }\n}\n\n// Global validation pipe with transformation\napp.useGlobalPipes(\n  new ValidationPipe({\n    whitelist: true, // Strip non-DTO properties\n    transform: true, // Auto-transform to DTO types\n    transformOptions: {\n      enableImplicitConversion: true, // Convert query strings to numbers\n    },\n    forbidNonWhitelisted: true, // Throw on extra properties\n  }),\n);\n\n// DTO with transformation decorators\nexport class FindProductsDto {\n  @IsOptional()\n  @Type(() => Number)\n  @IsInt()\n  @Min(1)\n  page?: number = 1;\n\n  @IsOptional()\n  @Type(() => Number)\n  @IsInt()\n  @Min(1)\n  @Max(100)\n  limit?: number = 10;\n\n  @IsOptional()\n  @Transform(({ value }) => value?.toLowerCase())\n  @IsString()\n  search?: string;\n\n  @IsOptional()\n  @Transform(({ value }) => value?.split(','))\n  @IsArray()\n  @IsString({ each: true })\n  categories?: string[];\n}\n\n@Get()\nasync findAll(@Query() dto: FindProductsDto): Promise<Product[]> {\n  // dto is already transformed and validated\n  return this.productsService.findAll(dto);\n}\n\n// Pipe error customization\n@Injectable()\nexport class CustomParseIntPipe extends ParseIntPipe {\n  constructor() {\n    super({\n      exceptionFactory: (error) =>\n        new BadRequestException(`${error} must be a valid integer`),\n    });\n  }\n}\n\n// Or use options on built-in pipes\n@Get(':id')\nasync findOne(\n  @Param(\n    'id',\n    new ParseIntPipe({\n      errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE,\n      exceptionFactory: () => new NotAcceptableException('ID must be numeric'),\n    }),\n  )\n  id: number,\n): Promise<Item> {\n  return this.itemsService.findOne(id);\n}\n```\n\nReference: [NestJS Pipes](https://docs.nestjs.com/pipes)",".agents/skills/nestjs-best-practices/rules/api-versioning.md":"---\ntitle: Use API Versioning for Breaking Changes\nimpact: MEDIUM\nimpactDescription: Versioning allows you to evolve APIs without breaking existing clients\ntags: api, versioning, breaking-changes, compatibility\n---\n\n## Use API Versioning for Breaking Changes\n\nUse NestJS built-in versioning when making breaking changes to your API. Choose a versioning strategy (URI, header, or media type) and apply it consistently. This allows old clients to continue working while new clients use updated endpoints.\n\n**Incorrect (breaking changes without versioning):**\n\n```typescript\n// Breaking changes without versioning\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    // Original response: { id, name, email }\n    // Later changed to: { id, firstName, lastName, emailAddress }\n    // Old clients break!\n    return this.usersService.findOne(id);\n  }\n}\n\n// Manual versioning in routes\n@Controller('v1/users')\nexport class UsersV1Controller {}\n\n@Controller('v2/users')\nexport class UsersV2Controller {}\n// Inconsistent, error-prone, hard to maintain\n```\n\n**Correct (use NestJS built-in versioning):**\n\n```typescript\n// Enable versioning in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n\n  // URI versioning: /v1/users, /v2/users\n  app.enableVersioning({\n    type: VersioningType.URI,\n    defaultVersion: '1',\n  });\n\n  // Or header versioning: X-API-Version: 1\n  app.enableVersioning({\n    type: VersioningType.HEADER,\n    header: 'X-API-Version',\n    defaultVersion: '1',\n  });\n\n  // Or media type: Accept: application/json;v=1\n  app.enableVersioning({\n    type: VersioningType.MEDIA_TYPE,\n    key: 'v=',\n    defaultVersion: '1',\n  });\n\n  await app.listen(3000);\n}\n\n// Version-specific controllers\n@Controller('users')\n@Version('1')\nexport class UsersV1Controller {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<UserV1Response> {\n    const user = await this.usersService.findOne(id);\n    // V1 response format\n    return {\n      id: user.id,\n      name: user.name,\n      email: user.email,\n    };\n  }\n}\n\n@Controller('users')\n@Version('2')\nexport class UsersV2Controller {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<UserV2Response> {\n    const user = await this.usersService.findOne(id);\n    // V2 response format with breaking changes\n    return {\n      id: user.id,\n      firstName: user.firstName,\n      lastName: user.lastName,\n      emailAddress: user.email,\n      createdAt: user.createdAt,\n    };\n  }\n}\n\n// Per-route versioning - different versions for different routes\n@Controller('users')\nexport class UsersController {\n  @Get()\n  @Version('1')\n  findAllV1(): Promise<UserV1Response[]> {\n    return this.usersService.findAllV1();\n  }\n\n  @Get()\n  @Version('2')\n  findAllV2(): Promise<UserV2Response[]> {\n    return this.usersService.findAllV2();\n  }\n\n  @Get(':id')\n  @Version(['1', '2']) // Same handler for multiple versions\n  findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findOne(id);\n  }\n\n  @Post()\n  @Version(VERSION_NEUTRAL) // Available in all versions\n  create(@Body() dto: CreateUserDto): Promise<User> {\n    return this.usersService.create(dto);\n  }\n}\n\n// Shared service with version-specific logic\n@Injectable()\nexport class UsersService {\n  async findOne(id: string, version: string): Promise<any> {\n    const user = await this.repo.findOne({ where: { id } });\n\n    if (version === '1') {\n      return this.toV1Response(user);\n    }\n    return this.toV2Response(user);\n  }\n\n  private toV1Response(user: User): UserV1Response {\n    return {\n      id: user.id,\n      name: `${user.firstName} ${user.lastName}`,\n      email: user.email,\n    };\n  }\n\n  private toV2Response(user: User): UserV2Response {\n    return {\n      id: user.id,\n      firstName: user.firstName,\n      lastName: user.lastName,\n      emailAddress: user.email,\n      createdAt: user.createdAt,\n    };\n  }\n}\n\n// Controller extracts version\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string, @Headers('X-API-Version') version: string = '1'): Promise<any> {\n    return this.usersService.findOne(id, version);\n  }\n}\n\n// Deprecation strategy - mark old versions as deprecated\n@Controller('users')\n@Version('1')\n@UseInterceptors(DeprecationInterceptor)\nexport class UsersV1Controller {\n  // All V1 routes will include deprecation warning\n}\n\n@Injectable()\nexport class DeprecationInterceptor implements NestInterceptor {\n  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n    const response = context.switchToHttp().getResponse();\n    response.setHeader('Deprecation', 'true');\n    response.setHeader('Sunset', 'Sat, 1 Jan 2025 00:00:00 GMT');\n    response.setHeader('Link', '</v2/users>; rel=\"successor-version\"');\n\n    return next.handle();\n  }\n}\n```\n\nReference: [NestJS Versioning](https://docs.nestjs.com/techniques/versioning)",".agents/skills/nestjs-best-practices/rules/arch-avoid-circular-deps.md":"---\ntitle: Avoid Circular Dependencies\nimpact: CRITICAL\nimpactDescription: '#1 cause of runtime crashes'\ntags: architecture, modules, dependencies\n---\n\n## Avoid Circular Dependencies\n\nCircular dependencies occur when Module A imports Module B, and Module B imports Module A (directly or transitively). NestJS can sometimes resolve these through forward references, but they indicate architectural problems and should be avoided. This is the #1 cause of runtime crashes in NestJS applications.\n\n**Incorrect (circular module imports):**\n\n```typescript\n// users.module.ts\n@Module({\n  imports: [OrdersModule], // Orders needs Users, Users needs Orders = circular\n  providers: [UsersService],\n  exports: [UsersService],\n})\nexport class UsersModule {}\n\n// orders.module.ts\n@Module({\n  imports: [UsersModule], // Circular dependency!\n  providers: [OrdersService],\n  exports: [OrdersService],\n})\nexport class OrdersModule {}\n```\n\n**Correct (extract shared logic or use events):**\n\n```typescript\n// Option 1: Extract shared logic to a third module\n// shared.module.ts\n@Module({\n  providers: [SharedService],\n  exports: [SharedService],\n})\nexport class SharedModule {}\n\n// users.module.ts\n@Module({\n  imports: [SharedModule],\n  providers: [UsersService],\n})\nexport class UsersModule {}\n\n// orders.module.ts\n@Module({\n  imports: [SharedModule],\n  providers: [OrdersService],\n})\nexport class OrdersModule {}\n\n// Option 2: Use events for decoupled communication\n// users.service.ts\n@Injectable()\nexport class UsersService {\n  constructor(private eventEmitter: EventEmitter2) {}\n\n  async createUser(data: CreateUserDto) {\n    const user = await this.userRepo.save(data);\n    this.eventEmitter.emit('user.created', user);\n    return user;\n  }\n}\n\n// orders.service.ts\n@Injectable()\nexport class OrdersService {\n  @OnEvent('user.created')\n  handleUserCreated(user: User) {\n    // React to user creation without direct dependency\n  }\n}\n```\n\nReference: [NestJS Circular Dependency](https://docs.nestjs.com/fundamentals/circular-dependency)",".agents/skills/nestjs-best-practices/rules/arch-feature-modules.md":"---\ntitle: Organize by Feature Modules\nimpact: CRITICAL\nimpactDescription: '3-5x faster onboarding and development'\ntags: architecture, modules, organization\n---\n\n## Organize by Feature Modules\n\nOrganize your application into feature modules that encapsulate related functionality. Each feature module should be self-contained with its own controllers, services, entities, and DTOs. Avoid organizing by technical layer (all controllers together, all services together). This enables 3-5x faster onboarding and feature development.\n\n**Incorrect (technical layer organization):**\n\n```typescript\n// Technical layer organization (anti-pattern)\nsrc/\n├── controllers/\n│   ├── users.controller.ts\n│   ├── orders.controller.ts\n│   └── products.controller.ts\n├── services/\n│   ├── users.service.ts\n│   ├── orders.service.ts\n│   └── products.service.ts\n├── entities/\n│   ├── user.entity.ts\n│   ├── order.entity.ts\n│   └── product.entity.ts\n└── app.module.ts  // Imports everything directly\n```\n\n**Correct (feature module organization):**\n\n```typescript\n// Feature module organization\nsrc/\n├── users/\n│   ├── dto/\n│   │   ├── create-user.dto.ts\n│   │   └── update-user.dto.ts\n│   ├── entities/\n│   │   └── user.entity.ts\n│   ├── users.controller.ts\n│   ├── users.service.ts\n│   ├── users.repository.ts\n│   └── users.module.ts\n├── orders/\n│   ├── dto/\n│   ├── entities/\n│   ├── orders.controller.ts\n│   ├── orders.service.ts\n│   └── orders.module.ts\n├── shared/\n│   ├── guards/\n│   ├── interceptors/\n│   ├── filters/\n│   └── shared.module.ts\n└── app.module.ts\n\n// users.module.ts\n@Module({\n  imports: [TypeOrmModule.forFeature([User])],\n  controllers: [UsersController],\n  providers: [UsersService, UsersRepository],\n  exports: [UsersService], // Only export what others need\n})\nexport class UsersModule {}\n\n// app.module.ts\n@Module({\n  imports: [\n    ConfigModule.forRoot(),\n    TypeOrmModule.forRoot(),\n    UsersModule,\n    OrdersModule,\n    SharedModule,\n  ],\n})\nexport class AppModule {}\n```\n\nReference: [NestJS Modules](https://docs.nestjs.com/modules)",".agents/skills/nestjs-best-practices/rules/arch-module-sharing.md":"---\ntitle: Use Proper Module Sharing Patterns\nimpact: CRITICAL\nimpactDescription: Prevents duplicate instances, memory leaks, and state inconsistency\ntags: architecture, modules, sharing, exports\n---\n\n## Use Proper Module Sharing Patterns\n\nNestJS modules are singletons by default. When a service is properly exported from a module and that module is imported elsewhere, the same instance is shared. However, providing a service in multiple modules creates separate instances, leading to memory waste, state inconsistency, and confusing behavior. Always encapsulate services in dedicated modules, export them explicitly, and import the module where needed.\n\n**Incorrect (service provided in multiple modules):**\n\n```typescript\n// StorageService provided directly in multiple modules - WRONG\n// storage.service.ts\n@Injectable()\nexport class StorageService {\n  private cache = new Map(); // Each instance has separate state!\n\n  store(key: string, value: any) {\n    this.cache.set(key, value);\n  }\n}\n\n// app.module.ts\n@Module({\n  providers: [StorageService], // Instance #1\n  controllers: [AppController],\n})\nexport class AppModule {}\n\n// videos.module.ts\n@Module({\n  providers: [StorageService], // Instance #2 - different from AppModule!\n  controllers: [VideosController],\n})\nexport class VideosModule {}\n\n// Problems:\n// 1. Two separate StorageService instances exist\n// 2. cache.set() in VideosModule doesn't affect AppModule's cache\n// 3. Memory wasted on duplicate instances\n// 4. Debugging nightmares when state doesn't sync\n```\n\n**Correct (dedicated module with exports):**\n\n```typescript\n// storage/storage.module.ts\n@Module({\n  providers: [StorageService],\n  exports: [StorageService], // Make available to importers\n})\nexport class StorageModule {}\n\n// videos/videos.module.ts\n@Module({\n  imports: [StorageModule], // Import the module, not the service\n  controllers: [VideosController],\n  providers: [VideosService],\n})\nexport class VideosModule {}\n\n// channels/channels.module.ts\n@Module({\n  imports: [StorageModule], // Same instance shared\n  controllers: [ChannelsController],\n  providers: [ChannelsService],\n})\nexport class ChannelsModule {}\n\n// app.module.ts\n@Module({\n  imports: [\n    StorageModule, // Only if AppModule itself needs StorageService\n    VideosModule,\n    ChannelsModule,\n  ],\n})\nexport class AppModule {}\n\n// Now all modules share the SAME StorageService instance\n```\n\n**When to use @Global() (sparingly):**\n\n```typescript\n// ONLY for truly cross-cutting concerns\n@Global()\n@Module({\n  providers: [ConfigService, LoggerService],\n  exports: [ConfigService, LoggerService],\n})\nexport class CoreModule {}\n\n// Import once in AppModule\n@Module({\n  imports: [CoreModule], // Registered globally, available everywhere\n})\nexport class AppModule {}\n\n// Other modules don't need to import CoreModule\n@Module({\n  controllers: [UsersController],\n  providers: [UsersService], // Can inject ConfigService without importing\n})\nexport class UsersModule {}\n\n// WARNING: Don't make everything global!\n// - Hides dependencies (can't see what a module needs from imports)\n// - Makes testing harder\n// - Reserve for: config, logging, database connections\n```\n\n**Module re-exporting pattern:**\n\n```typescript\n// common.module.ts - shared utilities\n@Module({\n  providers: [DateService, ValidationService],\n  exports: [DateService, ValidationService],\n})\nexport class CommonModule {}\n\n// core.module.ts - re-exports common for convenience\n@Module({\n  imports: [CommonModule, DatabaseModule],\n  exports: [CommonModule, DatabaseModule], // Re-export for consumers\n})\nexport class CoreModule {}\n\n// feature.module.ts - imports CoreModule, gets both\n@Module({\n  imports: [CoreModule], // Gets CommonModule + DatabaseModule\n  controllers: [FeatureController],\n})\nexport class FeatureModule {}\n```\n\nReference: [NestJS Modules](https://docs.nestjs.com/modules#shared-modules)",".agents/skills/nestjs-best-practices/rules/arch-single-responsibility.md":"---\ntitle: Single Responsibility for Services\nimpact: CRITICAL\nimpactDescription: '40%+ improvement in testability'\ntags: architecture, services, single-responsibility\n---\n\n## Single Responsibility for Services\n\nEach service should have a single, well-defined responsibility. Avoid \"god services\" that handle multiple unrelated concerns. If a service name includes \"And\" or handles more than one domain concept, it likely violates single responsibility. This reduces complexity and improves testability by 40%+.\n\n**Incorrect (god service anti-pattern):**\n\n```typescript\n// God service anti-pattern\n@Injectable()\nexport class UserAndOrderService {\n  constructor(\n    private userRepo: UserRepository,\n    private orderRepo: OrderRepository,\n    private mailer: MailService,\n    private payment: PaymentService,\n  ) {}\n\n  async createUser(dto: CreateUserDto) {\n    const user = await this.userRepo.save(dto);\n    await this.mailer.sendWelcome(user);\n    return user;\n  }\n\n  async createOrder(userId: string, dto: CreateOrderDto) {\n    const order = await this.orderRepo.save({ userId, ...dto });\n    await this.payment.charge(order);\n    await this.mailer.sendOrderConfirmation(order);\n    return order;\n  }\n\n  async calculateOrderStats(userId: string) {\n    // Stats logic mixed in\n  }\n\n  async validatePayment(orderId: string) {\n    // Payment logic mixed in\n  }\n}\n```\n\n**Correct (focused services with single responsibility):**\n\n```typescript\n// Focused services with single responsibility\n@Injectable()\nexport class UsersService {\n  constructor(private userRepo: UserRepository) {}\n\n  async create(dto: CreateUserDto): Promise<User> {\n    return this.userRepo.save(dto);\n  }\n\n  async findById(id: string): Promise<User> {\n    return this.userRepo.findOneOrFail({ where: { id } });\n  }\n}\n\n@Injectable()\nexport class OrdersService {\n  constructor(private orderRepo: OrderRepository) {}\n\n  async create(userId: string, dto: CreateOrderDto): Promise<Order> {\n    return this.orderRepo.save({ userId, ...dto });\n  }\n\n  async findByUser(userId: string): Promise<Order[]> {\n    return this.orderRepo.find({ where: { userId } });\n  }\n}\n\n@Injectable()\nexport class OrderStatsService {\n  constructor(private orderRepo: OrderRepository) {}\n\n  async calculateForUser(userId: string): Promise<OrderStats> {\n    // Focused stats calculation\n  }\n}\n\n// Orchestration in controller or dedicated orchestrator\n@Controller('orders')\nexport class OrdersController {\n  constructor(\n    private orders: OrdersService,\n    private payment: PaymentService,\n    private notifications: NotificationService,\n  ) {}\n\n  @Post()\n  async create(@CurrentUser() user: User, @Body() dto: CreateOrderDto) {\n    const order = await this.orders.create(user.id, dto);\n    await this.payment.charge(order);\n    await this.notifications.sendOrderConfirmation(order);\n    return order;\n  }\n}\n```\n\nReference: [NestJS Providers](https://docs.nestjs.com/providers)",".agents/skills/nestjs-best-practices/rules/arch-use-events.md":"---\ntitle: Use Event-Driven Architecture for Decoupling\nimpact: MEDIUM-HIGH\nimpactDescription: Enables async processing and modularity\ntags: architecture, events, decoupling\n---\n\n## Use Event-Driven Architecture for Decoupling\n\nUse `@nestjs/event-emitter` for intra-service events and message brokers for inter-service communication. Events allow modules to react to changes without direct dependencies, improving modularity and enabling async processing.\n\n**Incorrect (direct service coupling):**\n\n```typescript\n// Direct service coupling\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private inventoryService: InventoryService,\n    private emailService: EmailService,\n    private analyticsService: AnalyticsService,\n    private notificationService: NotificationService,\n    private loyaltyService: LoyaltyService,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Tight coupling - OrdersService knows about all consumers\n    await this.inventoryService.reserve(order.items);\n    await this.emailService.sendConfirmation(order);\n    await this.analyticsService.track('order_created', order);\n    await this.notificationService.push(order.userId, 'Order placed');\n    await this.loyaltyService.addPoints(order.userId, order.total);\n\n    // Adding new behavior requires modifying this service\n    return order;\n  }\n}\n```\n\n**Correct (event-driven decoupling):**\n\n```typescript\n// Use EventEmitter for decoupling\nimport { EventEmitter2 } from '@nestjs/event-emitter';\n\n// Define event\nexport class OrderCreatedEvent {\n  constructor(\n    public readonly orderId: string,\n    public readonly userId: string,\n    public readonly items: OrderItem[],\n    public readonly total: number,\n  ) {}\n}\n\n// Service emits events\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private eventEmitter: EventEmitter2,\n    private repo: Repository<Order>,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Emit event - no knowledge of consumers\n    this.eventEmitter.emit('order.created', new OrderCreatedEvent(order.id, order.userId, order.items, order.total));\n\n    return order;\n  }\n}\n\n// Listeners in separate modules\n@Injectable()\nexport class InventoryListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.inventoryService.reserve(event.items);\n  }\n}\n\n@Injectable()\nexport class EmailListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.emailService.sendConfirmation(event.orderId);\n  }\n}\n\n@Injectable()\nexport class AnalyticsListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.analyticsService.track('order_created', {\n      orderId: event.orderId,\n      total: event.total,\n    });\n  }\n}\n```\n\nReference: [NestJS Events](https://docs.nestjs.com/techniques/events)",".agents/skills/nestjs-best-practices/rules/arch-use-repository-pattern.md":"---\ntitle: Use Repository Pattern for Data Access\nimpact: HIGH\nimpactDescription: Decouples business logic from database\ntags: architecture, repository, data-access\n---\n\n## Use Repository Pattern for Data Access\n\nCreate custom repositories to encapsulate complex queries and database logic. This keeps services focused on business logic, makes testing easier with mock repositories, and allows changing database implementations without affecting business code.\n\n**Incorrect (complex queries in services):**\n\n```typescript\n// Complex queries in services\n@Injectable()\nexport class UsersService {\n  constructor(@InjectRepository(User) private repo: Repository<User>) {}\n\n  async findActiveWithOrders(minOrders: number): Promise<User[]> {\n    // Complex query logic mixed with business logic\n    return this.repo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.orders', 'order')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('user.deletedAt IS NULL')\n      .groupBy('user.id')\n      .having('COUNT(order.id) >= :min', { min: minOrders })\n      .orderBy('user.createdAt', 'DESC')\n      .getMany();\n  }\n\n  // Service becomes bloated with query logic\n}\n```\n\n**Correct (custom repository with encapsulated queries):**\n\n```typescript\n// Custom repository with encapsulated queries\n@Injectable()\nexport class UsersRepository {\n  constructor(@InjectRepository(User) private repo: Repository<User>) {}\n\n  async findById(id: string): Promise<User | null> {\n    return this.repo.findOne({ where: { id } });\n  }\n\n  async findByEmail(email: string): Promise<User | null> {\n    return this.repo.findOne({ where: { email } });\n  }\n\n  async findActiveWithMinOrders(minOrders: number): Promise<User[]> {\n    return this.repo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.orders', 'order')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('user.deletedAt IS NULL')\n      .groupBy('user.id')\n      .having('COUNT(order.id) >= :min', { min: minOrders })\n      .orderBy('user.createdAt', 'DESC')\n      .getMany();\n  }\n\n  async save(user: User): Promise<User> {\n    return this.repo.save(user);\n  }\n}\n\n// Clean service with business logic only\n@Injectable()\nexport class UsersService {\n  constructor(private usersRepo: UsersRepository) {}\n\n  async getActiveUsersWithOrders(): Promise<User[]> {\n    return this.usersRepo.findActiveWithMinOrders(1);\n  }\n\n  async create(dto: CreateUserDto): Promise<User> {\n    const existing = await this.usersRepo.findByEmail(dto.email);\n    if (existing) {\n      throw new ConflictException('Email already registered');\n    }\n\n    const user = new User();\n    user.email = dto.email;\n    user.name = dto.name;\n    return this.usersRepo.save(user);\n  }\n}\n```\n\nReference: [Repository Pattern](https://martinfowler.com/eaaCatalog/repository.html)",".agents/skills/nestjs-best-practices/rules/db-avoid-n-plus-one.md":"---\ntitle: Avoid N+1 Query Problems\nimpact: HIGH\nimpactDescription: N+1 queries are one of the most common performance killers\ntags: database, n-plus-one, queries, performance\n---\n\n## Avoid N+1 Query Problems\n\nN+1 queries occur when you fetch a list of entities, then make an additional query for each entity to load related data. Use eager loading with `relations`, query builder joins, or DataLoader to batch queries efficiently.\n\n**Incorrect (lazy loading in loops causes N+1):**\n\n```typescript\n// Lazy loading in loops causes N+1\n@Injectable()\nexport class OrdersService {\n  async getOrdersWithItems(userId: string): Promise<Order[]> {\n    const orders = await this.orderRepo.find({ where: { userId } });\n    // 1 query for orders\n\n    for (const order of orders) {\n      // N additional queries - one per order!\n      order.items = await this.itemRepo.find({ where: { orderId: order.id } });\n    }\n\n    return orders;\n  }\n}\n\n// Accessing lazy relations without loading\n@Controller('users')\nexport class UsersController {\n  @Get()\n  async findAll(): Promise<User[]> {\n    const users = await this.userRepo.find();\n    // If User.posts is lazy-loaded, serializing triggers N queries\n    return users; // Each user.posts access = 1 query\n  }\n}\n```\n\n**Correct (use relations for eager loading):**\n\n```typescript\n// Use relations option for eager loading\n@Injectable()\nexport class OrdersService {\n  async getOrdersWithItems(userId: string): Promise<Order[]> {\n    // Single query with JOIN\n    return this.orderRepo.find({\n      where: { userId },\n      relations: ['items', 'items.product'],\n    });\n  }\n}\n\n// Use QueryBuilder for complex joins\n@Injectable()\nexport class UsersService {\n  async getUsersWithPostCounts(): Promise<UserWithPostCount[]> {\n    return this.userRepo\n      .createQueryBuilder('user')\n      .leftJoin('user.posts', 'post')\n      .select('user.id', 'id')\n      .addSelect('user.name', 'name')\n      .addSelect('COUNT(post.id)', 'postCount')\n      .groupBy('user.id')\n      .getRawMany();\n  }\n\n  async getActiveUsersWithPosts(): Promise<User[]> {\n    return this.userRepo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.posts', 'post')\n      .leftJoinAndSelect('post.comments', 'comment')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('post.status = :status', { status: 'published' })\n      .getMany();\n  }\n}\n\n// Use find options for specific fields\nasync getOrderSummaries(userId: string): Promise<OrderSummary[]> {\n  return this.orderRepo.find({\n    where: { userId },\n    relations: ['items'],\n    select: {\n      id: true,\n      total: true,\n      status: true,\n      items: {\n        id: true,\n        quantity: true,\n        price: true,\n      },\n    },\n  });\n}\n\n// Use DataLoader for GraphQL to batch and cache queries\nimport DataLoader from 'dataloader';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class PostsLoader {\n  constructor(private postsService: PostsService) {}\n\n  readonly batchPosts = new DataLoader<string, Post[]>(async (userIds) => {\n    // Single query for all users' posts\n    const posts = await this.postsService.findByUserIds([...userIds]);\n\n    // Group by userId\n    const postsMap = new Map<string, Post[]>();\n    for (const post of posts) {\n      const userPosts = postsMap.get(post.userId) || [];\n      userPosts.push(post);\n      postsMap.set(post.userId, userPosts);\n    }\n\n    // Return in same order as input\n    return userIds.map((id) => postsMap.get(id) || []);\n  });\n}\n\n// In resolver\n@ResolveField()\nasync posts(@Parent() user: User): Promise<Post[]> {\n  // DataLoader batches multiple calls into single query\n  return this.postsLoader.batchPosts.load(user.id);\n}\n\n// Enable query logging in development to detect N+1\nTypeOrmModule.forRoot({\n  logging: ['query', 'error'],\n  logger: 'advanced-console',\n});\n```\n\nReference: [TypeORM Relations](https://typeorm.io/relations)",".agents/skills/nestjs-best-practices/rules/db-use-migrations.md":"---\ntitle: Use Database Migrations\nimpact: HIGH\nimpactDescription: Enables safe, repeatable database schema changes\ntags: database, migrations, typeorm, schema\n---\n\n## Use Database Migrations\n\nNever use `synchronize: true` in production. Use migrations for all schema changes. Migrations provide version control for your database, enable safe rollbacks, and ensure consistency across all environments.\n\n**Incorrect (using synchronize or manual SQL):**\n\n```typescript\n// Use synchronize in production\nTypeOrmModule.forRoot({\n  type: 'postgres',\n  synchronize: true, // DANGEROUS in production!\n  // Can drop columns, tables, or data\n});\n\n// Manual SQL in production\n@Injectable()\nexport class DatabaseService {\n  async addColumn(): Promise<void> {\n    await this.dataSource.query('ALTER TABLE users ADD COLUMN age INT');\n    // No version control, no rollback, inconsistent across envs\n  }\n}\n\n// Modify entities without migration\n@Entity()\nexport class User {\n  @Column()\n  email: string;\n\n  @Column() // Added without migration\n  newField: string; // Will crash in production if synchronize is false\n}\n```\n\n**Correct (use migrations for all schema changes):**\n\n```typescript\n// Configure TypeORM for migrations\n// data-source.ts\nexport const dataSource = new DataSource({\n  type: 'postgres',\n  host: process.env.DB_HOST,\n  port: parseInt(process.env.DB_PORT),\n  username: process.env.DB_USERNAME,\n  password: process.env.DB_PASSWORD,\n  database: process.env.DB_NAME,\n  entities: ['dist/**/*.entity.js'],\n  migrations: ['dist/migrations/*.js'],\n  synchronize: false, // Always false in production\n  migrationsRun: true, // Run migrations on startup\n});\n\n// app.module.ts\nTypeOrmModule.forRootAsync({\n  inject: [ConfigService],\n  useFactory: (config: ConfigService) => ({\n    type: 'postgres',\n    host: config.get('DB_HOST'),\n    synchronize: config.get('NODE_ENV') === 'development', // Only in dev\n    migrations: ['dist/migrations/*.js'],\n    migrationsRun: true,\n  }),\n});\n\n// migrations/1705312800000-AddUserAge.ts\nimport { MigrationInterface, QueryRunner } from 'typeorm';\n\nexport class AddUserAge1705312800000 implements MigrationInterface {\n  name = 'AddUserAge1705312800000';\n\n  public async up(queryRunner: QueryRunner): Promise<void> {\n    // Add column with default to handle existing rows\n    await queryRunner.query(`\n      ALTER TABLE \"users\" ADD \"age\" integer DEFAULT 0\n    `);\n\n    // Add index for frequently queried columns\n    await queryRunner.query(`\n      CREATE INDEX \"IDX_users_age\" ON \"users\" (\"age\")\n    `);\n  }\n\n  public async down(queryRunner: QueryRunner): Promise<void> {\n    // Always implement down for rollback\n    await queryRunner.query(`DROP INDEX \"IDX_users_age\"`);\n    await queryRunner.query(`ALTER TABLE \"users\" DROP COLUMN \"age\"`);\n  }\n}\n\n// Safe column rename (two-step)\nexport class RenameNameToFullName1705312900000 implements MigrationInterface {\n  public async up(queryRunner: QueryRunner): Promise<void> {\n    // Step 1: Add new column\n    await queryRunner.query(`\n      ALTER TABLE \"users\" ADD \"full_name\" varchar(255)\n    `);\n\n    // Step 2: Copy data\n    await queryRunner.query(`\n      UPDATE \"users\" SET \"full_name\" = \"name\"\n    `);\n\n    // Step 3: Add NOT NULL constraint\n    await queryRunner.query(`\n      ALTER TABLE \"users\" ALTER COLUMN \"full_name\" SET NOT NULL\n    `);\n\n    // Step 4: Drop old column (after verifying app works)\n    await queryRunner.query(`\n      ALTER TABLE \"users\" DROP COLUMN \"name\"\n    `);\n  }\n\n  public async down(queryRunner: QueryRunner): Promise<void> {\n    await queryRunner.query(`ALTER TABLE \"users\" ADD \"name\" varchar(255)`);\n    await queryRunner.query(`UPDATE \"users\" SET \"name\" = \"full_name\"`);\n    await queryRunner.query(`ALTER TABLE \"users\" DROP COLUMN \"full_name\"`);\n  }\n}\n```\n\nReference: [TypeORM Migrations](https://typeorm.io/migrations)",".agents/skills/nestjs-best-practices/rules/db-use-transactions.md":"---\ntitle: Use Transactions for Multi-Step Operations\nimpact: HIGH\nimpactDescription: Ensures data consistency in multi-step operations\ntags: database, transactions, typeorm, consistency\n---\n\n## Use Transactions for Multi-Step Operations\n\nWhen multiple database operations must succeed or fail together, wrap them in a transaction. This prevents partial updates that leave your data in an inconsistent state. Use TypeORM's transaction APIs or the DataSource query runner for complex scenarios.\n\n**Incorrect (multiple saves without transaction):**\n\n```typescript\n// Multiple saves without transaction\n@Injectable()\nexport class OrdersService {\n  async createOrder(userId: string, items: OrderItem[]): Promise<Order> {\n    // If any step fails, data is inconsistent\n    const order = await this.orderRepo.save({ userId, status: 'pending' });\n\n    for (const item of items) {\n      await this.orderItemRepo.save({ orderId: order.id, ...item });\n      await this.inventoryRepo.decrement({ productId: item.productId }, 'stock', item.quantity);\n    }\n\n    await this.paymentService.charge(order.id);\n    // If payment fails, order and inventory are already modified!\n\n    return order;\n  }\n}\n```\n\n**Correct (use DataSource.transaction for automatic rollback):**\n\n```typescript\n// Use DataSource.transaction() for automatic rollback\n@Injectable()\nexport class OrdersService {\n  constructor(private dataSource: DataSource) {}\n\n  async createOrder(userId: string, items: OrderItem[]): Promise<Order> {\n    return this.dataSource.transaction(async manager => {\n      // All operations use the same transactional manager\n      const order = await manager.save(Order, { userId, status: 'pending' });\n\n      for (const item of items) {\n        await manager.save(OrderItem, { orderId: order.id, ...item });\n        await manager.decrement(Inventory, { productId: item.productId }, 'stock', item.quantity);\n      }\n\n      // If this throws, everything rolls back\n      await this.paymentService.chargeWithManager(manager, order.id);\n\n      return order;\n    });\n  }\n}\n\n// QueryRunner for manual transaction control\n@Injectable()\nexport class TransferService {\n  constructor(private dataSource: DataSource) {}\n\n  async transfer(fromId: string, toId: string, amount: number): Promise<void> {\n    const queryRunner = this.dataSource.createQueryRunner();\n    await queryRunner.connect();\n    await queryRunner.startTransaction();\n\n    try {\n      // Debit source account\n      await queryRunner.manager.decrement(Account, { id: fromId }, 'balance', amount);\n\n      // Verify sufficient funds\n      const source = await queryRunner.manager.findOne(Account, {\n        where: { id: fromId },\n      });\n      if (source.balance < 0) {\n        throw new BadRequestException('Insufficient funds');\n      }\n\n      // Credit destination account\n      await queryRunner.manager.increment(Account, { id: toId }, 'balance', amount);\n\n      // Log the transaction\n      await queryRunner.manager.save(TransactionLog, {\n        fromId,\n        toId,\n        amount,\n        timestamp: new Date(),\n      });\n\n      await queryRunner.commitTransaction();\n    } catch (error) {\n      await queryRunner.rollbackTransaction();\n      throw error;\n    } finally {\n      await queryRunner.release();\n    }\n  }\n}\n\n// Repository method with transaction support\n@Injectable()\nexport class UsersRepository {\n  constructor(\n    @InjectRepository(User) private repo: Repository<User>,\n    private dataSource: DataSource,\n  ) {}\n\n  async createWithProfile(userData: CreateUserDto, profileData: CreateProfileDto): Promise<User> {\n    return this.dataSource.transaction(async manager => {\n      const user = await manager.save(User, userData);\n      await manager.save(Profile, { ...profileData, userId: user.id });\n      return user;\n    });\n  }\n}\n```\n\nReference: [TypeORM Transactions](https://typeorm.io/transactions)",".agents/skills/nestjs-best-practices/rules/devops-graceful-shutdown.md":"---\ntitle: Implement Graceful Shutdown\nimpact: MEDIUM-HIGH\nimpactDescription: Proper shutdown handling ensures zero-downtime deployments\ntags: devops, graceful-shutdown, lifecycle, kubernetes\n---\n\n## Implement Graceful Shutdown\n\nHandle SIGTERM and SIGINT signals to gracefully shutdown your NestJS application. Stop accepting new requests, wait for in-flight requests to complete, close database connections, and clean up resources. This prevents data loss and connection errors during deployments.\n\n**Incorrect (ignoring shutdown signals):**\n\n```typescript\n// Ignore shutdown signals\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  await app.listen(3000);\n  // App crashes immediately on SIGTERM\n  // In-flight requests fail\n  // Database connections are abruptly closed\n}\n\n// Long-running tasks without cancellation\n@Injectable()\nexport class ProcessingService {\n  async processLargeFile(file: File): Promise<void> {\n    // No way to interrupt this during shutdown\n    for (let i = 0; i < file.chunks.length; i++) {\n      await this.processChunk(file.chunks[i]);\n      // May run for minutes, blocking shutdown\n    }\n  }\n}\n```\n\n**Correct (enable shutdown hooks and handle cleanup):**\n\n```typescript\n// Enable shutdown hooks in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n\n  // Enable shutdown hooks\n  app.enableShutdownHooks();\n\n  // Optional: Add timeout for forced shutdown\n  const server = await app.listen(3000);\n  server.setTimeout(30000); // 30 second timeout\n\n  // Handle graceful shutdown\n  const signals = ['SIGTERM', 'SIGINT'];\n  signals.forEach(signal => {\n    process.on(signal, async () => {\n      console.log(`Received ${signal}, starting graceful shutdown...`);\n\n      // Stop accepting new connections\n      server.close(async () => {\n        console.log('HTTP server closed');\n        await app.close();\n        process.exit(0);\n      });\n\n      // Force exit after timeout\n      setTimeout(() => {\n        console.error('Forced shutdown after timeout');\n        process.exit(1);\n      }, 30000);\n    });\n  });\n}\n\n// Lifecycle hooks for cleanup\n@Injectable()\nexport class DatabaseService implements OnApplicationShutdown {\n  private readonly connections: Connection[] = [];\n\n  async onApplicationShutdown(signal?: string): Promise<void> {\n    console.log(`Database service shutting down on ${signal}`);\n\n    // Close all connections gracefully\n    await Promise.all(this.connections.map(conn => conn.close()));\n\n    console.log('All database connections closed');\n  }\n}\n\n// Queue processor with graceful shutdown\n@Injectable()\nexport class QueueService implements OnApplicationShutdown, OnModuleDestroy {\n  private isShuttingDown = false;\n\n  onModuleDestroy(): void {\n    this.isShuttingDown = true;\n  }\n\n  async onApplicationShutdown(): Promise<void> {\n    // Wait for current jobs to complete\n    await this.queue.close();\n  }\n\n  async processJob(job: Job): Promise<void> {\n    if (this.isShuttingDown) {\n      throw new Error('Service is shutting down');\n    }\n    await this.doWork(job);\n  }\n}\n\n// WebSocket gateway cleanup\n@WebSocketGateway()\nexport class EventsGateway implements OnApplicationShutdown {\n  @WebSocketServer()\n  server: Server;\n\n  async onApplicationShutdown(): Promise<void> {\n    // Notify all connected clients\n    this.server.emit('shutdown', { message: 'Server is shutting down' });\n\n    // Close all connections\n    this.server.disconnectSockets();\n  }\n}\n\n// Health check integration\n@Injectable()\nexport class ShutdownService {\n  private isShuttingDown = false;\n\n  startShutdown(): void {\n    this.isShuttingDown = true;\n  }\n\n  isShutdown(): boolean {\n    return this.isShuttingDown;\n  }\n}\n\n@Controller('health')\nexport class HealthController {\n  constructor(private shutdownService: ShutdownService) {}\n\n  @Get('ready')\n  @HealthCheck()\n  readiness(): Promise<HealthCheckResult> {\n    // Return 503 during shutdown - k8s stops sending traffic\n    if (this.shutdownService.isShutdown()) {\n      throw new ServiceUnavailableException('Shutting down');\n    }\n\n    return this.health.check([() => this.db.pingCheck('database')]);\n  }\n}\n\n// Integrate with shutdown\n@Injectable()\nexport class AppShutdownService implements OnApplicationShutdown {\n  constructor(private shutdownService: ShutdownService) {}\n\n  async onApplicationShutdown(): Promise<void> {\n    // Mark as unhealthy first\n    this.shutdownService.startShutdown();\n\n    // Wait for k8s to update endpoints\n    await this.sleep(5000);\n\n    // Then proceed with cleanup\n  }\n}\n\n// Request tracking for in-flight requests\n@Injectable()\nexport class RequestTracker implements NestMiddleware, OnApplicationShutdown {\n  private activeRequests = 0;\n  private isShuttingDown = false;\n  private shutdownPromise: Promise<void> | null = null;\n  private resolveShutdown: (() => void) | null = null;\n\n  use(req: Request, res: Response, next: NextFunction): void {\n    if (this.isShuttingDown) {\n      res.status(503).send('Service Unavailable');\n      return;\n    }\n\n    this.activeRequests++;\n\n    res.on('finish', () => {\n      this.activeRequests--;\n      if (this.isShuttingDown && this.activeRequests === 0 && this.resolveShutdown) {\n        this.resolveShutdown();\n      }\n    });\n\n    next();\n  }\n\n  async onApplicationShutdown(): Promise<void> {\n    this.isShuttingDown = true;\n\n    if (this.activeRequests > 0) {\n      console.log(`Waiting for ${this.activeRequests} requests to complete`);\n      this.shutdownPromise = new Promise(resolve => {\n        this.resolveShutdown = resolve;\n      });\n\n      // Wait with timeout\n      await Promise.race([this.shutdownPromise, new Promise(resolve => setTimeout(resolve, 30000))]);\n    }\n\n    console.log('All requests completed');\n  }\n}\n```\n\nReference: [NestJS Lifecycle Events](https://docs.nestjs.com/fundamentals/lifecycle-events)",".agents/skills/nestjs-best-practices/rules/devops-use-config-module.md":"---\ntitle: Use ConfigModule for Environment Configuration\nimpact: LOW-MEDIUM\nimpactDescription: Proper configuration prevents deployment failures\ntags: devops, configuration, environment, validation\n---\n\n## Use ConfigModule for Environment Configuration\n\nUse `@nestjs/config` for environment-based configuration. Validate configuration at startup to fail fast on misconfigurations. Use namespaced configuration for organization and type safety.\n\n**Incorrect (accessing process.env directly):**\n\n```typescript\n// Access process.env directly\n@Injectable()\nexport class DatabaseService {\n  constructor() {\n    // No validation, can fail at runtime\n    this.connection = new Pool({\n      host: process.env.DB_HOST,\n      port: parseInt(process.env.DB_PORT), // NaN if missing\n      password: process.env.DB_PASSWORD, // undefined if missing\n    });\n  }\n}\n\n// Scattered env access\n@Injectable()\nexport class EmailService {\n  sendEmail() {\n    // Different services access env differently\n    const apiKey = process.env.SENDGRID_API_KEY || 'default';\n    // Typos go unnoticed: process.env.SENDGRID_API_KY\n  }\n}\n```\n\n**Correct (use @nestjs/config with validation):**\n\n```typescript\n// Setup validated configuration\nimport { ConfigModule, ConfigService, registerAs } from '@nestjs/config';\nimport * as Joi from 'joi';\n\n// config/database.config.ts\nexport const databaseConfig = registerAs('database', () => ({\n  host: process.env.DB_HOST,\n  port: parseInt(process.env.DB_PORT, 10),\n  username: process.env.DB_USERNAME,\n  password: process.env.DB_PASSWORD,\n  database: process.env.DB_NAME,\n}));\n\n// config/app.config.ts\nexport const appConfig = registerAs('app', () => ({\n  port: parseInt(process.env.PORT, 10) || 3000,\n  environment: process.env.NODE_ENV || 'development',\n  apiPrefix: process.env.API_PREFIX || 'api',\n}));\n\n// config/validation.schema.ts\nexport const validationSchema = Joi.object({\n  NODE_ENV: Joi.string().valid('development', 'production', 'test').default('development'),\n  PORT: Joi.number().default(3000),\n  DB_HOST: Joi.string().required(),\n  DB_PORT: Joi.number().default(5432),\n  DB_USERNAME: Joi.string().required(),\n  DB_PASSWORD: Joi.string().required(),\n  DB_NAME: Joi.string().required(),\n  JWT_SECRET: Joi.string().min(32).required(),\n  REDIS_URL: Joi.string().uri().required(),\n});\n\n// app.module.ts\n@Module({\n  imports: [\n    ConfigModule.forRoot({\n      isGlobal: true, // Available everywhere without importing\n      load: [databaseConfig, appConfig],\n      validationSchema,\n      validationOptions: {\n        abortEarly: true, // Stop on first error\n        allowUnknown: true, // Allow other env vars\n      },\n    }),\n    TypeOrmModule.forRootAsync({\n      inject: [ConfigService],\n      useFactory: (config: ConfigService) => ({\n        type: 'postgres',\n        host: config.get('database.host'),\n        port: config.get('database.port'),\n        username: config.get('database.username'),\n        password: config.get('database.password'),\n        database: config.get('database.database'),\n        autoLoadEntities: true,\n      }),\n    }),\n  ],\n})\nexport class AppModule {}\n\n// Type-safe configuration access\nexport interface AppConfig {\n  port: number;\n  environment: 'development' | 'production' | 'test';\n  apiPrefix: string;\n}\n\nexport interface DatabaseConfig {\n  host: string;\n  port: number;\n  username: string;\n  password: string;\n  database: string;\n}\n\n// Type-safe access\n@Injectable()\nexport class AppService {\n  constructor(private config: ConfigService) {}\n\n  getPort(): number {\n    // Type-safe with generic\n    return this.config.get<number>('app.port');\n  }\n\n  getDatabaseConfig(): DatabaseConfig {\n    return this.config.get<DatabaseConfig>('database');\n  }\n}\n\n// Inject namespaced config directly\n@Injectable()\nexport class DatabaseService {\n  constructor(\n    @Inject(databaseConfig.KEY)\n    private dbConfig: ConfigType<typeof databaseConfig>,\n  ) {\n    // Full type inference!\n    const host = this.dbConfig.host; // string\n    const port = this.dbConfig.port; // number\n  }\n}\n\n// Environment files support\nConfigModule.forRoot({\n  envFilePath: [`.env.${process.env.NODE_ENV}.local`, `.env.${process.env.NODE_ENV}`, '.env.local', '.env'],\n});\n\n// .env.development\n// DB_HOST=localhost\n// DB_PORT=5432\n\n// .env.production\n// DB_HOST=prod-db.example.com\n// DB_PORT=5432\n```\n\nReference: [NestJS Configuration](https://docs.nestjs.com/techniques/configuration)",".agents/skills/nestjs-best-practices/rules/devops-use-logging.md":"---\ntitle: Use Structured Logging\nimpact: MEDIUM-HIGH\nimpactDescription: Structured logging enables effective debugging and monitoring\ntags: devops, logging, structured-logs, pino\n---\n\n## Use Structured Logging\n\nUse NestJS Logger with structured JSON output in production. Include contextual information (request ID, user ID, operation) to trace requests across services. Avoid console.log and implement proper log levels.\n\n**Incorrect (using console.log in production):**\n\n```typescript\n// Use console.log in production\n@Injectable()\nexport class UsersService {\n  async createUser(dto: CreateUserDto): Promise<User> {\n    console.log('Creating user:', dto);\n    // Not structured, no levels, lost in production logs\n\n    try {\n      const user = await this.repo.save(dto);\n      console.log('User created:', user.id);\n      return user;\n    } catch (error) {\n      console.log('Error:', error); // Using log for errors\n      throw error;\n    }\n  }\n}\n\n// Log sensitive data\nconsole.log('Login attempt:', { email, password }); // SECURITY RISK!\n\n// Inconsistent log format\nlogger.log('User ' + userId + ' created at ' + new Date());\n// Hard to parse, no structure\n```\n\n**Correct (use structured logging with context):**\n\n```typescript\n// Configure logger in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule, {\n    logger:\n      process.env.NODE_ENV === 'production' ? ['error', 'warn', 'log'] : ['error', 'warn', 'log', 'debug', 'verbose'],\n  });\n}\n\n// Use NestJS Logger with context\n@Injectable()\nexport class UsersService {\n  private readonly logger = new Logger(UsersService.name);\n\n  async createUser(dto: CreateUserDto): Promise<User> {\n    this.logger.log('Creating user', { email: dto.email });\n\n    try {\n      const user = await this.repo.save(dto);\n      this.logger.log('User created', { userId: user.id });\n      return user;\n    } catch (error) {\n      this.logger.error('Failed to create user', error.stack, {\n        email: dto.email,\n      });\n      throw error;\n    }\n  }\n}\n\n// Custom logger for JSON output\n@Injectable()\nexport class JsonLogger implements LoggerService {\n  log(message: string, context?: object): void {\n    console.log(\n      JSON.stringify({\n        level: 'info',\n        timestamp: new Date().toISOString(),\n        message,\n        ...context,\n      }),\n    );\n  }\n\n  error(message: string, trace?: string, context?: object): void {\n    console.error(\n      JSON.stringify({\n        level: 'error',\n        timestamp: new Date().toISOString(),\n        message,\n        trace,\n        ...context,\n      }),\n    );\n  }\n\n  warn(message: string, context?: object): void {\n    console.warn(\n      JSON.stringify({\n        level: 'warn',\n        timestamp: new Date().toISOString(),\n        message,\n        ...context,\n      }),\n    );\n  }\n\n  debug(message: string, context?: object): void {\n    console.debug(\n      JSON.stringify({\n        level: 'debug',\n        timestamp: new Date().toISOString(),\n        message,\n        ...context,\n      }),\n    );\n  }\n}\n\n// Request context logging with ClsModule\nimport { ClsModule, ClsService } from 'nestjs-cls';\n\n@Module({\n  imports: [\n    ClsModule.forRoot({\n      global: true,\n      middleware: {\n        mount: true,\n        generateId: true,\n      },\n    }),\n  ],\n})\nexport class AppModule {}\n\n// Middleware to set request context\n@Injectable()\nexport class RequestContextMiddleware implements NestMiddleware {\n  constructor(private cls: ClsService) {}\n\n  use(req: Request, res: Response, next: NextFunction): void {\n    const requestId = req.headers['x-request-id'] || randomUUID();\n    this.cls.set('requestId', requestId);\n    this.cls.set('userId', req.user?.id);\n\n    res.setHeader('x-request-id', requestId);\n    next();\n  }\n}\n\n// Logger that includes request context\n@Injectable()\nexport class ContextLogger {\n  constructor(private cls: ClsService) {}\n\n  log(message: string, data?: object): void {\n    console.log(\n      JSON.stringify({\n        level: 'info',\n        timestamp: new Date().toISOString(),\n        requestId: this.cls.get('requestId'),\n        userId: this.cls.get('userId'),\n        message,\n        ...data,\n      }),\n    );\n  }\n\n  error(message: string, error: Error, data?: object): void {\n    console.error(\n      JSON.stringify({\n        level: 'error',\n        timestamp: new Date().toISOString(),\n        requestId: this.cls.get('requestId'),\n        userId: this.cls.get('userId'),\n        message,\n        error: error.message,\n        stack: error.stack,\n        ...data,\n      }),\n    );\n  }\n}\n\n// Pino integration for high-performance logging\nimport { LoggerModule } from 'nestjs-pino';\n\n@Module({\n  imports: [\n    LoggerModule.forRoot({\n      pinoHttp: {\n        level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',\n        transport: process.env.NODE_ENV !== 'production' ? { target: 'pino-pretty' } : undefined,\n        redact: ['req.headers.authorization', 'req.body.password'],\n        serializers: {\n          req: req => ({\n            method: req.method,\n            url: req.url,\n            query: req.query,\n          }),\n          res: res => ({\n            statusCode: res.statusCode,\n          }),\n        },\n      },\n    }),\n  ],\n})\nexport class AppModule {}\n\n// Usage with Pino\n@Injectable()\nexport class UsersService {\n  constructor(private logger: PinoLogger) {\n    this.logger.setContext(UsersService.name);\n  }\n\n  async findOne(id: string): Promise<User> {\n    this.logger.info({ userId: id }, 'Finding user');\n    // Pino uses first arg for data, second for message\n  }\n}\n```\n\nReference: [NestJS Logger](https://docs.nestjs.com/techniques/logger)",".agents/skills/nestjs-best-practices/rules/di-avoid-service-locator.md":"---\ntitle: Avoid Service Locator Anti-Pattern\nimpact: HIGH\nimpactDescription: Hides dependencies and breaks testability\ntags: dependency-injection, anti-patterns, testing\n---\n\n## Avoid Service Locator Anti-Pattern\n\nAvoid using `ModuleRef.get()` or global containers to resolve dependencies at runtime. This hides dependencies, makes code harder to test, and breaks the benefits of dependency injection. Use constructor injection instead.\n\n**Incorrect (service locator anti-pattern):**\n\n```typescript\n// Use ModuleRef to get dependencies dynamically\n@Injectable()\nexport class OrdersService {\n  constructor(private moduleRef: ModuleRef) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    // Dependencies are hidden - not visible in constructor\n    const usersService = this.moduleRef.get(UsersService);\n    const inventoryService = this.moduleRef.get(InventoryService);\n    const paymentService = this.moduleRef.get(PaymentService);\n\n    const user = await usersService.findOne(dto.userId);\n    // ... rest of logic\n  }\n}\n\n// Global singleton container\nclass ServiceContainer {\n  private static instance: ServiceContainer;\n  private services = new Map<string, any>();\n\n  static getInstance(): ServiceContainer {\n    if (!this.instance) {\n      this.instance = new ServiceContainer();\n    }\n    return this.instance;\n  }\n\n  get<T>(key: string): T {\n    return this.services.get(key);\n  }\n}\n```\n\n**Correct (constructor injection with explicit dependencies):**\n\n```typescript\n// Use constructor injection - dependencies are explicit\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private usersService: UsersService,\n    private inventoryService: InventoryService,\n    private paymentService: PaymentService,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const user = await this.usersService.findOne(dto.userId);\n    const inventory = await this.inventoryService.check(dto.items);\n    // Dependencies are clear and testable\n  }\n}\n\n// Easy to test with mocks\ndescribe('OrdersService', () => {\n  let service: OrdersService;\n\n  beforeEach(async () => {\n    const module = await Test.createTestingModule({\n      providers: [\n        OrdersService,\n        { provide: UsersService, useValue: mockUsersService },\n        { provide: InventoryService, useValue: mockInventoryService },\n        { provide: PaymentService, useValue: mockPaymentService },\n      ],\n    }).compile();\n\n    service = module.get(OrdersService);\n  });\n});\n\n// VALID: Factory pattern for dynamic instantiation\n@Injectable()\nexport class HandlerFactory {\n  constructor(private moduleRef: ModuleRef) {}\n\n  getHandler(type: string): Handler {\n    switch (type) {\n      case 'email':\n        return this.moduleRef.get(EmailHandler);\n      case 'sms':\n        return this.moduleRef.get(SmsHandler);\n      default:\n        return this.moduleRef.get(DefaultHandler);\n    }\n  }\n}\n```\n\nReference: [NestJS Module Reference](https://docs.nestjs.com/fundamentals/module-ref)",".agents/skills/nestjs-best-practices/rules/di-interface-segregation.md":"---\ntitle: Apply Interface Segregation Principle\nimpact: HIGH\nimpactDescription: Reduces coupling and improves testability by 30-50%\ntags: dependency-injection, interfaces, solid, isp\n---\n\n## Apply Interface Segregation Principle\n\nClients should not be forced to depend on interfaces they don't use. In NestJS, this means keeping interfaces small and focused on specific capabilities rather than creating \"fat\" interfaces that bundle unrelated methods. When a service only needs to send emails, it shouldn't depend on an interface that also includes SMS, push notifications, and logging. Split large interfaces into role-based ones.\n\n**Incorrect (fat interface forcing unused dependencies):**\n\n```typescript\n// Fat interface - forces all consumers to depend on everything\ninterface NotificationService {\n  sendEmail(to: string, subject: string, body: string): Promise<void>;\n  sendSms(phone: string, message: string): Promise<void>;\n  sendPush(userId: string, notification: PushPayload): Promise<void>;\n  sendSlack(channel: string, message: string): Promise<void>;\n  logNotification(type: string, payload: any): Promise<void>;\n  getDeliveryStatus(id: string): Promise<DeliveryStatus>;\n  retryFailed(id: string): Promise<void>;\n  scheduleNotification(dto: ScheduleDto): Promise<string>;\n}\n\n// Consumer only needs email, but must mock everything for tests\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private notifications: NotificationService, // Depends on 8 methods, uses 1\n  ) {}\n\n  async confirmOrder(order: Order): Promise<void> {\n    await this.notifications.sendEmail(\n      order.customer.email,\n      'Order Confirmed',\n      `Your order ${order.id} has been confirmed.`,\n    );\n  }\n}\n\n// Testing is painful - must mock unused methods\nconst mockNotificationService = {\n  sendEmail: jest.fn(),\n  sendSms: jest.fn(), // Never used, but required\n  sendPush: jest.fn(), // Never used, but required\n  sendSlack: jest.fn(), // Never used, but required\n  logNotification: jest.fn(), // Never used, but required\n  getDeliveryStatus: jest.fn(), // Never used, but required\n  retryFailed: jest.fn(), // Never used, but required\n  scheduleNotification: jest.fn(), // Never used, but required\n};\n```\n\n**Correct (segregated interfaces by capability):**\n\n```typescript\n// Segregated interfaces - each focused on one capability\ninterface EmailSender {\n  sendEmail(to: string, subject: string, body: string): Promise<void>;\n}\n\ninterface SmsSender {\n  sendSms(phone: string, message: string): Promise<void>;\n}\n\ninterface PushSender {\n  sendPush(userId: string, notification: PushPayload): Promise<void>;\n}\n\ninterface NotificationLogger {\n  logNotification(type: string, payload: any): Promise<void>;\n}\n\ninterface NotificationScheduler {\n  scheduleNotification(dto: ScheduleDto): Promise<string>;\n}\n\n// Implementation can implement multiple interfaces\n@Injectable()\nexport class NotificationService implements EmailSender, SmsSender, PushSender {\n  async sendEmail(to: string, subject: string, body: string): Promise<void> {\n    // Email implementation\n  }\n\n  async sendSms(phone: string, message: string): Promise<void> {\n    // SMS implementation\n  }\n\n  async sendPush(userId: string, notification: PushPayload): Promise<void> {\n    // Push implementation\n  }\n}\n\n// Or separate implementations\n@Injectable()\nexport class SendGridEmailService implements EmailSender {\n  async sendEmail(to: string, subject: string, body: string): Promise<void> {\n    // SendGrid-specific implementation\n  }\n}\n\n// Consumer depends only on what it needs\n@Injectable()\nexport class OrdersService {\n  constructor(\n    @Inject(EMAIL_SENDER) private emailSender: EmailSender, // Minimal dependency\n  ) {}\n\n  async confirmOrder(order: Order): Promise<void> {\n    await this.emailSender.sendEmail(\n      order.customer.email,\n      'Order Confirmed',\n      `Your order ${order.id} has been confirmed.`,\n    );\n  }\n}\n\n// Testing is simple - only mock what's used\nconst mockEmailSender: EmailSender = {\n  sendEmail: jest.fn(),\n};\n\n// Module registration with tokens\nexport const EMAIL_SENDER = Symbol('EMAIL_SENDER');\nexport const SMS_SENDER = Symbol('SMS_SENDER');\n\n@Module({\n  providers: [\n    { provide: EMAIL_SENDER, useClass: SendGridEmailService },\n    { provide: SMS_SENDER, useClass: TwilioSmsService },\n  ],\n  exports: [EMAIL_SENDER, SMS_SENDER],\n})\nexport class NotificationModule {}\n```\n\n**Combining interfaces when needed:**\n\n```typescript\n// Sometimes a consumer legitimately needs multiple capabilities\ninterface EmailAndSmsSender extends EmailSender, SmsSender {}\n\n// Or use intersection types\ntype MultiChannelSender = EmailSender & SmsSender & PushSender;\n\n// Consumer that genuinely needs multiple channels\n@Injectable()\nexport class AlertService {\n  constructor(\n    @Inject(MULTI_CHANNEL_SENDER)\n    private sender: EmailSender & SmsSender,\n  ) {}\n\n  async sendCriticalAlert(user: User, message: string): Promise<void> {\n    await Promise.all([\n      this.sender.sendEmail(user.email, 'Critical Alert', message),\n      this.sender.sendSms(user.phone, message),\n    ]);\n  }\n}\n```\n\nReference: [Interface Segregation Principle](https://en.wikipedia.org/wiki/Interface_segregation_principle)",".agents/skills/nestjs-best-practices/rules/di-liskov-substitution.md":"---\ntitle: Honor Liskov Substitution Principle\nimpact: HIGH\nimpactDescription: Ensures implementations are truly interchangeable without breaking callers\ntags: dependency-injection, inheritance, solid, lsp\n---\n\n## Honor Liskov Substitution Principle\n\nSubtypes must be substitutable for their base types without altering program correctness. In NestJS with dependency injection, this means any implementation of an interface or abstract class must honor the contract completely. A mock payment service used in tests must behave like a real payment service (return similar shapes, handle errors the same way). Violating LSP causes subtle bugs when swapping implementations.\n\n**Incorrect (implementation violates the contract):**\n\n```typescript\n// Base interface with clear contract\ninterface PaymentGateway {\n  /**\n   * Charges the specified amount.\n   * @returns PaymentResult on success\n   * @throws PaymentFailedException on payment failure\n   */\n  charge(amount: number, currency: string): Promise<PaymentResult>;\n}\n\n// Production implementation - follows the contract\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    const response = await this.stripe.charges.create({ amount, currency });\n    return { success: true, transactionId: response.id, amount };\n  }\n}\n\n// Mock that violates LSP - different behavior!\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    // VIOLATION 1: Throws for valid input (contract says return PaymentResult)\n    if (amount > 1000) {\n      throw new Error('Mock does not support large amounts');\n    }\n\n    // VIOLATION 2: Returns null instead of PaymentResult\n    if (currency !== 'USD') {\n      return null as any; // Real service would convert or reject properly\n    }\n\n    // VIOLATION 3: Missing required field\n    return { success: true } as PaymentResult; // Missing transactionId!\n  }\n}\n\n// Consumer trusts the contract\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async checkout(order: Order): Promise<void> {\n    const result = await this.payment.charge(order.total, order.currency);\n    // These fail with MockPaymentService:\n    await this.saveTransaction(result.transactionId); // undefined!\n    await this.sendReceipt(result); // might be null!\n  }\n}\n```\n\n**Correct (implementations honor the contract):**\n\n```typescript\n// Well-defined interface with documented behavior\ninterface PaymentGateway {\n  /**\n   * Charges the specified amount.\n   * @param amount - Amount in smallest currency unit (cents)\n   * @param currency - ISO 4217 currency code\n   * @returns PaymentResult with transactionId, success status, and amount\n   * @throws PaymentFailedException if charge is declined\n   * @throws InvalidCurrencyException if currency is not supported\n   */\n  charge(amount: number, currency: string): Promise<PaymentResult>;\n\n  /**\n   * Refunds a previous charge.\n   * @throws TransactionNotFoundException if transactionId is invalid\n   */\n  refund(transactionId: string, amount?: number): Promise<RefundResult>;\n}\n\n// Production implementation\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    try {\n      const response = await this.stripe.charges.create({ amount, currency });\n      return {\n        success: true,\n        transactionId: response.id,\n        amount: response.amount,\n      };\n    } catch (error) {\n      if (error.type === 'card_error') {\n        throw new PaymentFailedException(error.message);\n      }\n      throw error;\n    }\n  }\n\n  async refund(transactionId: string, amount?: number): Promise<RefundResult> {\n    // Implementation...\n  }\n}\n\n// Mock that honors LSP - same contract, same behavior shape\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  private transactions = new Map<string, PaymentResult>();\n\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    // Honor the contract: validate currency like real service would\n    if (!['USD', 'EUR', 'GBP'].includes(currency)) {\n      throw new InvalidCurrencyException(`Unsupported currency: ${currency}`);\n    }\n\n    // Simulate decline for specific test scenarios\n    if (amount === 99999) {\n      throw new PaymentFailedException('Card declined (test scenario)');\n    }\n\n    // Return same shape as production\n    const result: PaymentResult = {\n      success: true,\n      transactionId: `mock_${Date.now()}_${Math.random().toString(36)}`,\n      amount,\n    };\n\n    this.transactions.set(result.transactionId, result);\n    return result;\n  }\n\n  async refund(transactionId: string, amount?: number): Promise<RefundResult> {\n    // Honor the contract: throw if transaction not found\n    if (!this.transactions.has(transactionId)) {\n      throw new TransactionNotFoundException(transactionId);\n    }\n\n    return {\n      success: true,\n      refundId: `refund_${transactionId}`,\n      amount: amount ?? this.transactions.get(transactionId)!.amount,\n    };\n  }\n}\n\n// Consumer can swap implementations safely\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async checkout(order: Order): Promise<Order> {\n    try {\n      const result = await this.payment.charge(order.total, order.currency);\n      // Works with both StripeService and MockPaymentService\n      order.transactionId = result.transactionId;\n      order.status = 'paid';\n      return order;\n    } catch (error) {\n      if (error instanceof PaymentFailedException) {\n        order.status = 'payment_failed';\n        return order;\n      }\n      throw error;\n    }\n  }\n}\n```\n\n**Testing LSP compliance:**\n\n```typescript\n// Shared test suite that any implementation must pass\nfunction testPaymentGatewayContract(createGateway: () => PaymentGateway) {\n  describe('PaymentGateway contract', () => {\n    let gateway: PaymentGateway;\n\n    beforeEach(() => {\n      gateway = createGateway();\n    });\n\n    it('returns PaymentResult with all required fields', async () => {\n      const result = await gateway.charge(1000, 'USD');\n      expect(result).toHaveProperty('success');\n      expect(result).toHaveProperty('transactionId');\n      expect(result).toHaveProperty('amount');\n      expect(typeof result.transactionId).toBe('string');\n    });\n\n    it('throws InvalidCurrencyException for unsupported currency', async () => {\n      await expect(gateway.charge(1000, 'INVALID')).rejects.toThrow(InvalidCurrencyException);\n    });\n\n    it('throws TransactionNotFoundException for invalid refund', async () => {\n      await expect(gateway.refund('nonexistent')).rejects.toThrow(TransactionNotFoundException);\n    });\n  });\n}\n\n// Run against all implementations\ndescribe('StripeService', () => {\n  testPaymentGatewayContract(() => new StripeService(mockStripeClient));\n});\n\ndescribe('MockPaymentService', () => {\n  testPaymentGatewayContract(() => new MockPaymentService());\n});\n```\n\nReference: [Liskov Substitution Principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle)",".agents/skills/nestjs-best-practices/rules/di-prefer-constructor-injection.md":"---\ntitle: Prefer Constructor Injection\nimpact: CRITICAL\nimpactDescription: Required for proper DI and testing\ntags: dependency-injection, constructor, testing\n---\n\n## Prefer Constructor Injection\n\nAlways use constructor injection over property injection. Constructor injection makes dependencies explicit, enables TypeScript type checking, ensures dependencies are available when the class is instantiated, and improves testability. This is required for proper DI, testing, and TypeScript support.\n\n**Incorrect (property injection with hidden dependencies):**\n\n```typescript\n// Property injection - avoid unless necessary\n@Injectable()\nexport class UsersService {\n  @Inject()\n  private userRepo: UserRepository; // Hidden dependency\n\n  @Inject('CONFIG')\n  private config: ConfigType; // Also hidden\n\n  async findAll() {\n    return this.userRepo.find();\n  }\n}\n\n// Problems:\n// 1. Dependencies not visible in constructor\n// 2. Service can be instantiated without dependencies in tests\n// 3. TypeScript can't enforce dependency types at instantiation\n```\n\n**Correct (constructor injection with explicit dependencies):**\n\n```typescript\n// Constructor injection - explicit and testable\n@Injectable()\nexport class UsersService {\n  constructor(\n    private readonly userRepo: UserRepository,\n    @Inject('CONFIG') private readonly config: ConfigType,\n  ) {}\n\n  async findAll(): Promise<User[]> {\n    return this.userRepo.find();\n  }\n}\n\n// Testing is straightforward\ndescribe('UsersService', () => {\n  let service: UsersService;\n  let mockRepo: jest.Mocked<UserRepository>;\n\n  beforeEach(() => {\n    mockRepo = {\n      find: jest.fn(),\n      save: jest.fn(),\n    } as any;\n\n    service = new UsersService(mockRepo, { dbUrl: 'test' });\n  });\n\n  it('should find all users', async () => {\n    mockRepo.find.mockResolvedValue([{ id: '1', name: 'Test' }]);\n    const result = await service.findAll();\n    expect(result).toHaveLength(1);\n  });\n});\n\n// Only use property injection for optional dependencies\n@Injectable()\nexport class LoggingService {\n  @Optional()\n  @Inject('ANALYTICS')\n  private analytics?: AnalyticsService;\n\n  log(message: string) {\n    console.log(message);\n    this.analytics?.track('log', message); // Optional enhancement\n  }\n}\n```\n\nReference: [NestJS Providers](https://docs.nestjs.com/providers)",".agents/skills/nestjs-best-practices/rules/di-scope-awareness.md":"---\ntitle: Understand Provider Scopes\nimpact: CRITICAL\nimpactDescription: Prevents data leaks and performance issues\ntags: dependency-injection, scopes, request-context\n---\n\n## Understand Provider Scopes\n\nNestJS has three provider scopes: DEFAULT (singleton), REQUEST (per-request instance), and TRANSIENT (new instance for each injection). Most providers should be singletons. Request-scoped providers have performance implications as they bubble up through the dependency tree. Understanding scopes prevents memory leaks and incorrect data sharing.\n\n**Incorrect (wrong scope usage):**\n\n```typescript\n// Request-scoped when not needed (performance hit)\n@Injectable({ scope: Scope.REQUEST })\nexport class UsersService {\n  // This creates a new instance for EVERY request\n  // All dependencies also become request-scoped\n  async findAll() {\n    return this.userRepo.find();\n  }\n}\n\n// Singleton with mutable request state\n@Injectable() // Default: singleton\nexport class RequestContextService {\n  private userId: string; // DANGER: Shared across all requests!\n\n  setUser(userId: string) {\n    this.userId = userId; // Overwrites for all concurrent requests\n  }\n\n  getUser() {\n    return this.userId; // Returns wrong user!\n  }\n}\n```\n\n**Correct (appropriate scope for each use case):**\n\n```typescript\n// Singleton for stateless services (default, most common)\n@Injectable()\nexport class UsersService {\n  constructor(private readonly userRepo: UserRepository) {}\n\n  async findById(id: string): Promise<User> {\n    return this.userRepo.findOne({ where: { id } });\n  }\n}\n\n// Request-scoped ONLY when you need request context\n@Injectable({ scope: Scope.REQUEST })\nexport class RequestContextService {\n  private userId: string;\n\n  setUser(userId: string) {\n    this.userId = userId;\n  }\n\n  getUser(): string {\n    return this.userId;\n  }\n}\n\n// Better: Use NestJS built-in request context\nimport { REQUEST } from '@nestjs/core';\nimport { Request } from 'express';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class AuditService {\n  constructor(@Inject(REQUEST) private request: Request) {}\n\n  log(action: string) {\n    console.log(`User ${this.request.user?.id} performed ${action}`);\n  }\n}\n\n// Best: Use ClsModule for async context (no scope bubble-up)\nimport { ClsService } from 'nestjs-cls';\n\n@Injectable() // Stays singleton!\nexport class AuditService {\n  constructor(private cls: ClsService) {}\n\n  log(action: string) {\n    const userId = this.cls.get('userId');\n    console.log(`User ${userId} performed ${action}`);\n  }\n}\n```\n\nReference: [NestJS Injection Scopes](https://docs.nestjs.com/fundamentals/injection-scopes)",".agents/skills/nestjs-best-practices/rules/di-use-interfaces-tokens.md":"---\ntitle: Use Injection Tokens for Interfaces\nimpact: HIGH\nimpactDescription: Enables interface-based DI at runtime\ntags: dependency-injection, tokens, interfaces\n---\n\n## Use Injection Tokens for Interfaces\n\nTypeScript interfaces are erased at compile time and can't be used as injection tokens. Use string tokens, symbols, or abstract classes when you want to inject implementations of interfaces. This enables swapping implementations for testing or different environments.\n\n**Incorrect (interface can't be used as token):**\n\n```typescript\n// Interface can't be used as injection token\ninterface PaymentGateway {\n  charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  charge(amount: number) {\n    /* ... */\n  }\n}\n\n@Injectable()\nexport class OrdersService {\n  // This WON'T work - PaymentGateway doesn't exist at runtime\n  constructor(private payment: PaymentGateway) {}\n}\n```\n\n**Correct (symbol tokens or abstract classes):**\n\n```typescript\n// Option 1: String/Symbol tokens (most flexible)\nexport const PAYMENT_GATEWAY = Symbol('PAYMENT_GATEWAY');\n\nexport interface PaymentGateway {\n  charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    // Stripe implementation\n  }\n}\n\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    return { success: true, id: 'mock-id' };\n  }\n}\n\n// Module registration\n@Module({\n  providers: [\n    {\n      provide: PAYMENT_GATEWAY,\n      useClass: process.env.NODE_ENV === 'test' ? MockPaymentService : StripeService,\n    },\n  ],\n  exports: [PAYMENT_GATEWAY],\n})\nexport class PaymentModule {}\n\n// Injection\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async createOrder(dto: CreateOrderDto) {\n    await this.payment.charge(dto.amount);\n  }\n}\n\n// Option 2: Abstract class (carries runtime type info)\nexport abstract class PaymentGateway {\n  abstract charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService extends PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    // Implementation\n  }\n}\n\n// No @Inject needed with abstract class\n@Injectable()\nexport class OrdersService {\n  constructor(private payment: PaymentGateway) {}\n}\n```\n\nReference: [NestJS Custom Providers](https://docs.nestjs.com/fundamentals/custom-providers)",".agents/skills/nestjs-best-practices/rules/error-handle-async-errors.md":"---\ntitle: Handle Async Errors Properly\nimpact: HIGH\nimpactDescription: Prevents process crashes from unhandled rejections\ntags: error-handling, async, promises\n---\n\n## Handle Async Errors Properly\n\nNestJS automatically catches errors from async route handlers, but errors from background tasks, event handlers, and manually created promises can crash your application. Always handle async errors explicitly and use global handlers as a safety net.\n\n**Incorrect (fire-and-forget without error handling):**\n\n```typescript\n// Fire-and-forget without error handling\n@Injectable()\nexport class UsersService {\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Fire and forget - if this fails, error is unhandled!\n    this.emailService.sendWelcome(user.email);\n\n    return user;\n  }\n}\n\n// Unhandled promise in event handler\n@Injectable()\nexport class OrdersService {\n  @OnEvent('order.created')\n  handleOrderCreated(event: OrderCreatedEvent) {\n    // This returns a promise but it's not awaited!\n    this.processOrder(event);\n    // Errors will crash the process\n  }\n\n  private async processOrder(event: OrderCreatedEvent): Promise<void> {\n    await this.inventoryService.reserve(event.items);\n    await this.notificationService.send(event.userId);\n  }\n}\n\n// Missing try-catch in scheduled tasks\n@Cron('0 0 * * *')\nasync dailyCleanup(): Promise<void> {\n  await this.cleanupService.run();\n  // If this throws, no error handling\n}\n```\n\n**Correct (explicit async error handling):**\n\n```typescript\n// Handle fire-and-forget with explicit catch\n@Injectable()\nexport class UsersService {\n  private readonly logger = new Logger(UsersService.name);\n\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Explicitly catch and log errors\n    this.emailService.sendWelcome(user.email).catch(error => {\n      this.logger.error('Failed to send welcome email', error.stack);\n      // Optionally queue for retry\n    });\n\n    return user;\n  }\n}\n\n// Properly handle async event handlers\n@Injectable()\nexport class OrdersService {\n  private readonly logger = new Logger(OrdersService.name);\n\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    try {\n      await this.processOrder(event);\n    } catch (error) {\n      this.logger.error('Failed to process order', { event, error });\n      // Don't rethrow - would crash the process\n      await this.deadLetterQueue.add('order.created', event);\n    }\n  }\n}\n\n// Safe scheduled tasks\n@Injectable()\nexport class CleanupService {\n  private readonly logger = new Logger(CleanupService.name);\n\n  @Cron('0 0 * * *')\n  async dailyCleanup(): Promise<void> {\n    try {\n      await this.cleanupService.run();\n      this.logger.log('Daily cleanup completed');\n    } catch (error) {\n      this.logger.error('Daily cleanup failed', error.stack);\n      // Alert or retry logic\n    }\n  }\n}\n\n// Global unhandled rejection handler in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  const logger = new Logger('Bootstrap');\n\n  process.on('unhandledRejection', (reason, promise) => {\n    logger.error('Unhandled Rejection at:', promise, 'reason:', reason);\n  });\n\n  process.on('uncaughtException', error => {\n    logger.error('Uncaught Exception:', error);\n    process.exit(1);\n  });\n\n  await app.listen(3000);\n}\n```\n\nReference: [Node.js Unhandled Rejections](https://nodejs.org/api/process.html#event-unhandledrejection)",".agents/skills/nestjs-best-practices/rules/error-throw-http-exceptions.md":"---\ntitle: Throw HTTP Exceptions from Services\nimpact: HIGH\nimpactDescription: Keeps controllers thin and simplifies error handling\ntags: error-handling, exceptions, services\n---\n\n## Throw HTTP Exceptions from Services\n\nIt's acceptable (and often preferable) to throw `HttpException` subclasses from services in HTTP applications. This keeps controllers thin and allows services to communicate appropriate error states. For truly layer-agnostic services, use domain exceptions that map to HTTP status codes.\n\n**Incorrect (return error objects instead of throwing):**\n\n```typescript\n// Return error objects instead of throwing\n@Injectable()\nexport class UsersService {\n  async findById(id: string): Promise<{ user?: User; error?: string }> {\n    const user = await this.repo.findOne({ where: { id } });\n    if (!user) {\n      return { error: 'User not found' }; // Controller must check this\n    }\n    return { user };\n  }\n}\n\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string) {\n    const result = await this.usersService.findById(id);\n    if (result.error) {\n      throw new NotFoundException(result.error);\n    }\n    return result.user;\n  }\n}\n```\n\n**Correct (throw exceptions directly from service):**\n\n```typescript\n// Throw exceptions directly from service\n@Injectable()\nexport class UsersService {\n  constructor(private readonly repo: UserRepository) {}\n\n  async findById(id: string): Promise<User> {\n    const user = await this.repo.findOne({ where: { id } });\n    if (!user) {\n      throw new NotFoundException(`User #${id} not found`);\n    }\n    return user;\n  }\n\n  async create(dto: CreateUserDto): Promise<User> {\n    const existing = await this.repo.findOne({\n      where: { email: dto.email },\n    });\n    if (existing) {\n      throw new ConflictException('Email already registered');\n    }\n    return this.repo.save(dto);\n  }\n\n  async update(id: string, dto: UpdateUserDto): Promise<User> {\n    const user = await this.findById(id); // Throws if not found\n    Object.assign(user, dto);\n    return this.repo.save(user);\n  }\n}\n\n// Controller stays thin\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findById(id);\n  }\n\n  @Post()\n  create(@Body() dto: CreateUserDto): Promise<User> {\n    return this.usersService.create(dto);\n  }\n}\n\n// For layer-agnostic services, use domain exceptions\nexport class EntityNotFoundException extends Error {\n  constructor(\n    public readonly entity: string,\n    public readonly id: string,\n  ) {\n    super(`${entity} with ID \"${id}\" not found`);\n  }\n}\n\n// Map to HTTP in exception filter\n@Catch(EntityNotFoundException)\nexport class EntityNotFoundFilter implements ExceptionFilter {\n  catch(exception: EntityNotFoundException, host: ArgumentsHost) {\n    const ctx = host.switchToHttp();\n    const response = ctx.getResponse<Response>();\n\n    response.status(404).json({\n      statusCode: 404,\n      message: exception.message,\n      entity: exception.entity,\n      id: exception.id,\n    });\n  }\n}\n```\n\nReference: [NestJS Exception Filters](https://docs.nestjs.com/exception-filters)",".agents/skills/nestjs-best-practices/rules/error-use-exception-filters.md":"---\ntitle: Use Exception Filters for Error Handling\nimpact: HIGH\nimpactDescription: Consistent, centralized error handling\ntags: error-handling, exception-filters, consistency\n---\n\n## Use Exception Filters for Error Handling\n\nNever catch exceptions and manually format error responses in controllers. Use NestJS exception filters to handle errors consistently across your application. Create custom exception filters for specific error types and a global filter for unhandled exceptions.\n\n**Incorrect (manual error handling in controllers):**\n\n```typescript\n// Manual error handling in controllers\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string, @Res() res: Response) {\n    try {\n      const user = await this.usersService.findById(id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found',\n        });\n      }\n      return res.json(user);\n    } catch (error) {\n      console.error(error);\n      return res.status(500).json({\n        statusCode: 500,\n        message: 'Internal server error',\n      });\n    }\n  }\n}\n```\n\n**Correct (exception filters with consistent handling):**\n\n```typescript\n// Use built-in and custom exceptions\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    const user = await this.usersService.findById(id);\n    if (!user) {\n      throw new NotFoundException(`User #${id} not found`);\n    }\n    return user;\n  }\n}\n\n// Custom domain exception\nexport class UserNotFoundException extends NotFoundException {\n  constructor(userId: string) {\n    super({\n      statusCode: 404,\n      error: 'Not Found',\n      message: `User with ID \"${userId}\" not found`,\n      code: 'USER_NOT_FOUND',\n    });\n  }\n}\n\n// Custom exception filter for domain errors\n@Catch(DomainException)\nexport class DomainExceptionFilter implements ExceptionFilter {\n  catch(exception: DomainException, host: ArgumentsHost) {\n    const ctx = host.switchToHttp();\n    const response = ctx.getResponse<Response>();\n    const request = ctx.getRequest<Request>();\n\n    const status = exception.getStatus?.() || 400;\n\n    response.status(status).json({\n      statusCode: status,\n      code: exception.code,\n      message: exception.message,\n      timestamp: new Date().toISOString(),\n      path: request.url,\n    });\n  }\n}\n\n// Global exception filter for unhandled errors\n@Catch()\nexport class AllExceptionsFilter implements ExceptionFilter {\n  constructor(private readonly logger: Logger) {}\n\n  catch(exception: unknown, host: ArgumentsHost) {\n    const ctx = host.switchToHttp();\n    const response = ctx.getResponse<Response>();\n    const request = ctx.getRequest<Request>();\n\n    const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;\n\n    const message = exception instanceof HttpException ? exception.message : 'Internal server error';\n\n    this.logger.error(`${request.method} ${request.url}`, exception instanceof Error ? exception.stack : exception);\n\n    response.status(status).json({\n      statusCode: status,\n      message,\n      timestamp: new Date().toISOString(),\n      path: request.url,\n    });\n  }\n}\n\n// Register globally in main.ts\napp.useGlobalFilters(new AllExceptionsFilter(app.get(Logger)), new DomainExceptionFilter());\n\n// Or via module\n@Module({\n  providers: [\n    {\n      provide: APP_FILTER,\n      useClass: AllExceptionsFilter,\n    },\n  ],\n})\nexport class AppModule {}\n```\n\nReference: [NestJS Exception Filters](https://docs.nestjs.com/exception-filters)",".agents/skills/nestjs-best-practices/rules/micro-use-health-checks.md":"---\ntitle: Implement Health Checks for Microservices\nimpact: MEDIUM-HIGH\nimpactDescription: Health checks enable orchestrators to manage service lifecycle\ntags: microservices, health-checks, terminus, kubernetes\n---\n\n## Implement Health Checks for Microservices\n\nImplement liveness and readiness probes using `@nestjs/terminus`. Liveness checks determine if the service should be restarted. Readiness checks determine if the service can accept traffic. Proper health checks enable Kubernetes and load balancers to route traffic correctly.\n\n**Incorrect (simple ping that doesn't check dependencies):**\n\n```typescript\n// Simple ping that doesn't check dependencies\n@Controller('health')\nexport class HealthController {\n  @Get()\n  check(): string {\n    return 'OK'; // Service might be unhealthy but returns OK\n  }\n}\n\n// Health check that blocks on slow dependencies\n@Controller('health')\nexport class HealthController {\n  @Get()\n  async check(): Promise<string> {\n    // If database is slow, health check times out\n    await this.userRepo.findOne({ where: { id: '1' } });\n    await this.redis.ping();\n    await this.externalApi.healthCheck();\n    return 'OK';\n  }\n}\n```\n\n**Correct (use @nestjs/terminus for comprehensive health checks):**\n\n```typescript\n// Use @nestjs/terminus for comprehensive health checks\nimport {\n  HealthCheckService,\n  HttpHealthIndicator,\n  TypeOrmHealthIndicator,\n  HealthCheck,\n  DiskHealthIndicator,\n  MemoryHealthIndicator,\n} from '@nestjs/terminus';\n\n@Controller('health')\nexport class HealthController {\n  constructor(\n    private health: HealthCheckService,\n    private http: HttpHealthIndicator,\n    private db: TypeOrmHealthIndicator,\n    private disk: DiskHealthIndicator,\n    private memory: MemoryHealthIndicator,\n  ) {}\n\n  // Liveness probe - is the service alive?\n  @Get('live')\n  @HealthCheck()\n  liveness() {\n    return this.health.check([\n      // Basic checks only\n      () => this.memory.checkHeap('memory_heap', 200 * 1024 * 1024), // 200MB\n    ]);\n  }\n\n  // Readiness probe - can the service handle traffic?\n  @Get('ready')\n  @HealthCheck()\n  readiness() {\n    return this.health.check([\n      () => this.db.pingCheck('database'),\n      () =>\n        this.http.pingCheck('redis', 'http://redis:6379', { timeout: 1000 }),\n      () =>\n        this.disk.checkStorage('disk', { path: '/', thresholdPercent: 0.9 }),\n    ]);\n  }\n\n  // Deep health check for debugging\n  @Get('deep')\n  @HealthCheck()\n  deepCheck() {\n    return this.health.check([\n      () => this.db.pingCheck('database'),\n      () => this.memory.checkHeap('memory_heap', 200 * 1024 * 1024),\n      () => this.memory.checkRSS('memory_rss', 300 * 1024 * 1024),\n      () =>\n        this.disk.checkStorage('disk', { path: '/', thresholdPercent: 0.9 }),\n      () =>\n        this.http.pingCheck('external-api', 'https://api.example.com/health'),\n    ]);\n  }\n}\n\n// Custom indicator for business-specific health\n@Injectable()\nexport class QueueHealthIndicator extends HealthIndicator {\n  constructor(private queueService: QueueService) {\n    super();\n  }\n\n  async isHealthy(key: string): Promise<HealthIndicatorResult> {\n    const queueStats = await this.queueService.getStats();\n\n    const isHealthy = queueStats.failedCount < 100;\n    const result = this.getStatus(key, isHealthy, {\n      waiting: queueStats.waitingCount,\n      active: queueStats.activeCount,\n      failed: queueStats.failedCount,\n    });\n\n    if (!isHealthy) {\n      throw new HealthCheckError('Queue unhealthy', result);\n    }\n\n    return result;\n  }\n}\n\n// Redis health indicator\n@Injectable()\nexport class RedisHealthIndicator extends HealthIndicator {\n  constructor(@InjectRedis() private redis: Redis) {\n    super();\n  }\n\n  async isHealthy(key: string): Promise<HealthIndicatorResult> {\n    try {\n      const pong = await this.redis.ping();\n      return this.getStatus(key, pong === 'PONG');\n    } catch (error) {\n      throw new HealthCheckError('Redis check failed', this.getStatus(key, false));\n    }\n  }\n}\n\n// Use custom indicators\n@Get('ready')\n@HealthCheck()\nreadiness() {\n  return this.health.check([\n    () => this.db.pingCheck('database'),\n    () => this.redis.isHealthy('redis'),\n    () => this.queue.isHealthy('job-queue'),\n  ]);\n}\n\n// Graceful shutdown handling\n@Injectable()\nexport class GracefulShutdownService implements OnApplicationShutdown {\n  private isShuttingDown = false;\n\n  isShutdown(): boolean {\n    return this.isShuttingDown;\n  }\n\n  async onApplicationShutdown(signal: string): Promise<void> {\n    this.isShuttingDown = true;\n    console.log(`Shutting down on ${signal}`);\n\n    // Wait for in-flight requests\n    await new Promise((resolve) => setTimeout(resolve, 5000));\n  }\n}\n\n// Health check respects shutdown state\n@Get('ready')\n@HealthCheck()\nreadiness() {\n  if (this.shutdownService.isShutdown()) {\n    throw new ServiceUnavailableException('Shutting down');\n  }\n\n  return this.health.check([\n    () => this.db.pingCheck('database'),\n  ]);\n}\n```\n\n### Kubernetes Configuration\n\n```yaml\n# Kubernetes deployment with probes\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: api-service\nspec:\n  template:\n    spec:\n      containers:\n        - name: api\n          image: api-service:latest\n          ports:\n            - containerPort: 3000\n          livenessProbe:\n            httpGet:\n              path: /health/live\n              port: 3000\n            initialDelaySeconds: 30\n            periodSeconds: 10\n            timeoutSeconds: 5\n            failureThreshold: 3\n          readinessProbe:\n            httpGet:\n              path: /health/ready\n              port: 3000\n            initialDelaySeconds: 5\n            periodSeconds: 5\n            timeoutSeconds: 3\n            failureThreshold: 3\n          startupProbe:\n            httpGet:\n              path: /health/live\n              port: 3000\n            initialDelaySeconds: 0\n            periodSeconds: 5\n            failureThreshold: 30\n```\n\nReference: [NestJS Terminus](https://docs.nestjs.com/recipes/terminus)",".agents/skills/nestjs-best-practices/rules/micro-use-patterns.md":"---\ntitle: Use Message and Event Patterns Correctly\nimpact: MEDIUM\nimpactDescription: Proper patterns ensure reliable microservice communication\ntags: microservices, message-pattern, event-pattern, communication\n---\n\n## Use Message and Event Patterns Correctly\n\nNestJS microservices support two communication patterns: request-response (MessagePattern) and event-based (EventPattern). Use MessagePattern when you need a response, and EventPattern for fire-and-forget notifications. Understanding the difference prevents communication bugs.\n\n**Incorrect (using wrong pattern for use case):**\n\n```typescript\n// Use @MessagePattern for fire-and-forget\n@Controller()\nexport class NotificationsController {\n  @MessagePattern('user.created')\n  async handleUserCreated(data: UserCreatedEvent) {\n    // This WAITS for response, blocking the sender\n    await this.emailService.sendWelcome(data.email);\n    // If email fails, sender gets an error (coupling!)\n  }\n}\n\n// Use @EventPattern expecting a response\n@Controller()\nexport class OrdersController {\n  @EventPattern('inventory.check')\n  async checkInventory(data: CheckInventoryDto) {\n    const available = await this.inventory.check(data);\n    return available; // This return value is IGNORED with @EventPattern!\n  }\n}\n\n// Tight coupling in client\n@Injectable()\nexport class UsersService {\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Blocks until notification service responds\n    await this.client.send('user.created', user).toPromise();\n    // If notification service is down, user creation fails!\n\n    return user;\n  }\n}\n```\n\n**Correct (use MessagePattern for request-response, EventPattern for fire-and-forget):**\n\n```typescript\n// MessagePattern: Request-Response (when you NEED a response)\n@Controller()\nexport class InventoryController {\n  @MessagePattern({ cmd: 'check_inventory' })\n  async checkInventory(data: CheckInventoryDto): Promise<InventoryResult> {\n    const result = await this.inventoryService.check(data.productId, data.quantity);\n    return result; // Response sent back to caller\n  }\n}\n\n// Client expects response\n@Injectable()\nexport class OrdersService {\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    // Check inventory - we NEED this response to proceed\n    const inventory = await firstValueFrom(\n      this.inventoryClient.send<InventoryResult>(\n        { cmd: 'check_inventory' },\n        { productId: dto.productId, quantity: dto.quantity },\n      ),\n    );\n\n    if (!inventory.available) {\n      throw new BadRequestException('Insufficient inventory');\n    }\n\n    return this.repo.save(dto);\n  }\n}\n\n// EventPattern: Fire-and-Forget (for notifications, side effects)\n@Controller()\nexport class NotificationsController {\n  @EventPattern('user.created')\n  async handleUserCreated(data: UserCreatedEvent): Promise<void> {\n    // No return value needed - just process the event\n    await this.emailService.sendWelcome(data.email);\n    await this.analyticsService.track('user_signup', data);\n    // If this fails, it doesn't affect the sender\n  }\n}\n\n// Client emits event without waiting\n@Injectable()\nexport class UsersService {\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Fire and forget - doesn't block, doesn't wait\n    this.eventClient.emit('user.created', {\n      userId: user.id,\n      email: user.email,\n      timestamp: new Date(),\n    });\n\n    return user; // User creation succeeds regardless of event handling\n  }\n}\n\n// Hybrid pattern for critical events\n@Injectable()\nexport class OrdersService {\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Critical: inventory reservation (use MessagePattern)\n    const reserved = await firstValueFrom(\n      this.inventoryClient.send({ cmd: 'reserve_inventory' }, {\n        orderId: order.id,\n        items: dto.items,\n      }),\n    );\n\n    if (!reserved.success) {\n      await this.repo.delete(order.id);\n      throw new BadRequestException('Could not reserve inventory');\n    }\n\n    // Non-critical: notifications (use EventPattern)\n    this.eventClient.emit('order.created', {\n      orderId: order.id,\n      userId: dto.userId,\n      total: dto.total,\n    });\n\n    return order;\n  }\n}\n\n// Error handling patterns\n// MessagePattern errors propagate to caller\n@MessagePattern({ cmd: 'get_user' })\nasync getUser(userId: string): Promise<User> {\n  const user = await this.repo.findOne({ where: { id: userId } });\n  if (!user) {\n    throw new RpcException('User not found'); // Received by caller\n  }\n  return user;\n}\n\n// EventPattern errors should be handled locally\n@EventPattern('order.created')\nasync handleOrderCreated(data: OrderCreatedEvent): Promise<void> {\n  try {\n    await this.processOrder(data);\n  } catch (error) {\n    // Log and potentially retry - don't throw\n    this.logger.error('Failed to process order event', error);\n    await this.deadLetterQueue.add(data);\n  }\n}\n```\n\nReference: [NestJS Microservices](https://docs.nestjs.com/microservices/basics)",".agents/skills/nestjs-best-practices/rules/micro-use-queues.md":"---\ntitle: Use Message Queues for Background Jobs\nimpact: MEDIUM-HIGH\nimpactDescription: Queues enable reliable background processing\ntags: microservices, queues, bullmq, background-jobs\n---\n\n## Use Message Queues for Background Jobs\n\nUse `@nestjs/bullmq` for background job processing. Queues decouple long-running tasks from HTTP requests, enable retry logic, and distribute workload across workers. Use them for emails, file processing, notifications, and any task that shouldn't block user requests.\n\n**Incorrect (long-running tasks in HTTP handlers):**\n\n```typescript\n// Long-running tasks in HTTP handlers\n@Controller('reports')\nexport class ReportsController {\n  @Post()\n  async generate(@Body() dto: GenerateReportDto): Promise<Report> {\n    // This blocks the request for potentially minutes\n    const data = await this.fetchLargeDataset(dto);\n    const report = await this.processData(data); // Slow!\n    await this.sendEmail(dto.email, report); // Can fail!\n    return report; // Client times out\n  }\n}\n\n// Fire-and-forget without retry\n@Injectable()\nexport class EmailService {\n  async sendWelcome(email: string): Promise<void> {\n    // If this fails, email is never sent\n    await this.mailer.send({ to: email, template: 'welcome' });\n    // No retry, no tracking, no visibility\n  }\n}\n\n// Use setInterval for scheduled tasks\nsetInterval(async () => {\n  await cleanupOldRecords();\n}, 60000); // No error handling, memory leaks\n```\n\n**Correct (use BullMQ for background processing):**\n\n```typescript\n// Configure BullMQ\nimport { BullModule } from '@nestjs/bullmq';\n\n@Module({\n  imports: [\n    BullModule.forRoot({\n      connection: {\n        host: 'localhost',\n        port: 6379,\n      },\n      defaultJobOptions: {\n        removeOnComplete: 1000,\n        removeOnFail: 5000,\n        attempts: 3,\n        backoff: {\n          type: 'exponential',\n          delay: 1000,\n        },\n      },\n    }),\n    BullModule.registerQueue({ name: 'email' }, { name: 'reports' }, { name: 'notifications' }),\n  ],\n})\nexport class QueueModule {}\n\n// Producer: Add jobs to queue\n@Injectable()\nexport class ReportsService {\n  constructor(@InjectQueue('reports') private reportsQueue: Queue) {}\n\n  async requestReport(dto: GenerateReportDto): Promise<{ jobId: string }> {\n    // Return immediately, process in background\n    const job = await this.reportsQueue.add('generate', dto, {\n      priority: dto.urgent ? 1 : 10,\n      delay: dto.scheduledFor ? Date.parse(dto.scheduledFor) - Date.now() : 0,\n    });\n\n    return { jobId: job.id };\n  }\n\n  async getJobStatus(jobId: string): Promise<JobStatus> {\n    const job = await this.reportsQueue.getJob(jobId);\n    return {\n      status: await job.getState(),\n      progress: job.progress,\n      result: job.returnvalue,\n    };\n  }\n}\n\n// Consumer: Process jobs\n@Processor('reports')\nexport class ReportsProcessor {\n  private readonly logger = new Logger(ReportsProcessor.name);\n\n  @Process('generate')\n  async generateReport(job: Job<GenerateReportDto>): Promise<Report> {\n    this.logger.log(`Processing report job ${job.id}`);\n\n    // Update progress\n    await job.updateProgress(10);\n\n    const data = await this.fetchData(job.data);\n    await job.updateProgress(50);\n\n    const report = await this.processData(data);\n    await job.updateProgress(90);\n\n    await this.saveReport(report);\n    await job.updateProgress(100);\n\n    return report;\n  }\n\n  @OnQueueActive()\n  onActive(job: Job) {\n    this.logger.log(`Processing job ${job.id}`);\n  }\n\n  @OnQueueCompleted()\n  onCompleted(job: Job, result: any) {\n    this.logger.log(`Job ${job.id} completed`);\n  }\n\n  @OnQueueFailed()\n  onFailed(job: Job, error: Error) {\n    this.logger.error(`Job ${job.id} failed: ${error.message}`);\n  }\n}\n\n// Email queue with retry\n@Processor('email')\nexport class EmailProcessor {\n  @Process('send')\n  async sendEmail(job: Job<SendEmailDto>): Promise<void> {\n    const { to, template, data } = job.data;\n\n    try {\n      await this.mailer.send({\n        to,\n        template,\n        context: data,\n      });\n    } catch (error) {\n      // BullMQ will retry based on job options\n      throw error;\n    }\n  }\n}\n\n// Usage\n@Injectable()\nexport class NotificationService {\n  constructor(@InjectQueue('email') private emailQueue: Queue) {}\n\n  async sendWelcome(user: User): Promise<void> {\n    await this.emailQueue.add(\n      'send',\n      {\n        to: user.email,\n        template: 'welcome',\n        data: { name: user.name },\n      },\n      {\n        attempts: 5,\n        backoff: { type: 'exponential', delay: 5000 },\n      },\n    );\n  }\n}\n\n// Scheduled jobs\n@Injectable()\nexport class ScheduledJobsService implements OnModuleInit {\n  constructor(@InjectQueue('maintenance') private queue: Queue) {}\n\n  async onModuleInit(): Promise<void> {\n    // Clean up old reports daily at midnight\n    await this.queue.add(\n      'cleanup',\n      {},\n      {\n        repeat: { cron: '0 0 * * *' },\n        jobId: 'daily-cleanup', // Prevent duplicates\n      },\n    );\n\n    // Send digest every hour\n    await this.queue.add(\n      'digest',\n      {},\n      {\n        repeat: { every: 60 * 60 * 1000 },\n        jobId: 'hourly-digest',\n      },\n    );\n  }\n}\n\n@Processor('maintenance')\nexport class MaintenanceProcessor {\n  @Process('cleanup')\n  async cleanup(): Promise<void> {\n    await this.cleanupOldReports();\n    await this.cleanupExpiredSessions();\n  }\n\n  @Process('digest')\n  async sendDigest(): Promise<void> {\n    const users = await this.getUsersForDigest();\n    for (const user of users) {\n      await this.emailQueue.add('send', { to: user.email, template: 'digest' });\n    }\n  }\n}\n\n// Queue monitoring with Bull Board\nimport { BullBoardModule } from '@bull-board/nestjs';\nimport { BullMQAdapter } from '@bull-board/api/bullMQAdapter';\n\n@Module({\n  imports: [\n    BullBoardModule.forRoot({\n      route: '/admin/queues',\n      adapter: ExpressAdapter,\n    }),\n    BullBoardModule.forFeature({\n      name: 'email',\n      adapter: BullMQAdapter,\n    }),\n    BullBoardModule.forFeature({\n      name: 'reports',\n      adapter: BullMQAdapter,\n    }),\n  ],\n})\nexport class AdminModule {}\n```\n\nReference: [NestJS Queues](https://docs.nestjs.com/techniques/queues)",".agents/skills/nestjs-best-practices/rules/perf-async-hooks.md":"---\ntitle: Use Async Lifecycle Hooks Correctly\nimpact: HIGH\nimpactDescription: Improper async handling blocks application startup\ntags: performance, lifecycle, async, hooks\n---\n\n## Use Async Lifecycle Hooks Correctly\n\nNestJS lifecycle hooks (`onModuleInit`, `onApplicationBootstrap`, etc.) support async operations. However, misusing them can block application startup or cause race conditions. Understand the lifecycle order and use hooks appropriately.\n\n**Incorrect (fire-and-forget async without await):**\n\n```typescript\n// Fire-and-forget async without await\n@Injectable()\nexport class DatabaseService implements OnModuleInit {\n  onModuleInit() {\n    // This runs but doesn't block - app starts before DB is ready!\n    this.connect();\n  }\n\n  private async connect() {\n    await this.pool.connect();\n    console.log('Database connected');\n  }\n}\n\n// Heavy blocking operations in constructor\n@Injectable()\nexport class ConfigService {\n  private config: Config;\n\n  constructor() {\n    // BLOCKS entire module instantiation synchronously\n    this.config = fs.readFileSync('config.json');\n  }\n}\n```\n\n**Correct (return promises from async hooks):**\n\n```typescript\n// Return promise from async hooks\n@Injectable()\nexport class DatabaseService implements OnModuleInit {\n  private pool: Pool;\n\n  async onModuleInit(): Promise<void> {\n    // NestJS waits for this to complete before continuing\n    await this.pool.connect();\n    console.log('Database connected');\n  }\n\n  async onModuleDestroy(): Promise<void> {\n    // Clean up resources on shutdown\n    await this.pool.end();\n    console.log('Database disconnected');\n  }\n}\n\n// Use onApplicationBootstrap for cross-module dependencies\n@Injectable()\nexport class CacheWarmerService implements OnApplicationBootstrap {\n  constructor(\n    private cache: CacheService,\n    private products: ProductsService,\n  ) {}\n\n  async onApplicationBootstrap(): Promise<void> {\n    // All modules are initialized, safe to warm cache\n    const products = await this.products.findPopular();\n    await this.cache.warmup(products);\n  }\n}\n\n// Heavy init in async hooks, not constructor\n@Injectable()\nexport class ConfigService implements OnModuleInit {\n  private config: Config;\n\n  constructor() {\n    // Keep constructor synchronous and fast\n  }\n\n  async onModuleInit(): Promise<void> {\n    // Async loading in lifecycle hook\n    this.config = await this.loadConfig();\n  }\n\n  private async loadConfig(): Promise<Config> {\n    const file = await fs.promises.readFile('config.json');\n    return JSON.parse(file.toString());\n  }\n\n  get<T>(key: string): T {\n    return this.config[key];\n  }\n}\n\n// Enable shutdown hooks in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  app.enableShutdownHooks(); // Enable SIGTERM/SIGINT handling\n  await app.listen(3000);\n}\n```\n\nReference: [NestJS Lifecycle Events](https://docs.nestjs.com/fundamentals/lifecycle-events)",".agents/skills/nestjs-best-practices/rules/perf-lazy-loading.md":"---\ntitle: Use Lazy Loading for Large Modules\nimpact: MEDIUM\nimpactDescription: Improves startup time for large applications\ntags: performance, lazy-loading, modules, optimization\n---\n\n## Use Lazy Loading for Large Modules\n\nNestJS supports lazy-loading modules, which defers initialization until first use. This is valuable for large applications where some features are rarely used, serverless deployments where cold start time matters, or when certain modules have heavy initialization costs.\n\n**Incorrect (loading everything eagerly):**\n\n```typescript\n// Load everything eagerly in a large app\n@Module({\n  imports: [\n    UsersModule,\n    OrdersModule,\n    PaymentsModule,\n    ReportsModule, // Heavy, rarely used\n    AnalyticsModule, // Heavy, rarely used\n    AdminModule, // Only admins use this\n    LegacyModule, // Migration module, rarely used\n    BulkImportModule, // Used once a month\n  ],\n})\nexport class AppModule {}\n\n// All modules initialize at startup, even if never used\n// Slow cold starts in serverless\n// Memory wasted on unused modules\n```\n\n**Correct (lazy load rarely-used modules):**\n\n```typescript\n// Use LazyModuleLoader for optional modules\nimport { LazyModuleLoader } from '@nestjs/core';\n\n@Injectable()\nexport class ReportsService {\n  constructor(private lazyModuleLoader: LazyModuleLoader) {}\n\n  async generateReport(type: string): Promise<Report> {\n    // Load module only when needed\n    const { ReportsModule } = await import('./reports/reports.module');\n    const moduleRef = await this.lazyModuleLoader.load(() => ReportsModule);\n\n    const reportsService = moduleRef.get(ReportsGeneratorService);\n    return reportsService.generate(type);\n  }\n}\n\n// Lazy load admin features with caching\n@Injectable()\nexport class AdminService {\n  private adminModule: ModuleRef | null = null;\n\n  constructor(private lazyModuleLoader: LazyModuleLoader) {}\n\n  private async getAdminModule(): Promise<ModuleRef> {\n    if (!this.adminModule) {\n      const { AdminModule } = await import('./admin/admin.module');\n      this.adminModule = await this.lazyModuleLoader.load(() => AdminModule);\n    }\n    return this.adminModule;\n  }\n\n  async runAdminTask(task: string): Promise<void> {\n    const moduleRef = await this.getAdminModule();\n    const taskRunner = moduleRef.get(AdminTaskRunner);\n    await taskRunner.run(task);\n  }\n}\n\n// Reusable lazy loader service\n@Injectable()\nexport class ModuleLoaderService {\n  private loadedModules = new Map<string, ModuleRef>();\n\n  constructor(private lazyModuleLoader: LazyModuleLoader) {}\n\n  async load<T>(key: string, importFn: () => Promise<{ default: Type<T> } | Type<T>>): Promise<ModuleRef> {\n    if (!this.loadedModules.has(key)) {\n      const module = await importFn();\n      const moduleType = 'default' in module ? module.default : module;\n      const moduleRef = await this.lazyModuleLoader.load(() => moduleType);\n      this.loadedModules.set(key, moduleRef);\n    }\n    return this.loadedModules.get(key)!;\n  }\n}\n\n// Preload modules in background after startup\n@Injectable()\nexport class ModulePreloader implements OnApplicationBootstrap {\n  constructor(private lazyModuleLoader: LazyModuleLoader) {}\n\n  async onApplicationBootstrap(): Promise<void> {\n    setTimeout(async () => {\n      await this.preloadModule(() => import('./reports/reports.module'));\n    }, 5000); // 5 seconds after startup\n  }\n\n  private async preloadModule(importFn: () => Promise<any>): Promise<void> {\n    try {\n      const module = await importFn();\n      const moduleType = module.default || Object.values(module)[0];\n      await this.lazyModuleLoader.load(() => moduleType);\n    } catch (error) {\n      console.warn('Failed to preload module', error);\n    }\n  }\n}\n```\n\nReference: [NestJS Lazy Loading Modules](https://docs.nestjs.com/fundamentals/lazy-loading-modules)",".agents/skills/nestjs-best-practices/rules/perf-optimize-database.md":"---\ntitle: Optimize Database Queries\nimpact: HIGH\nimpactDescription: Database queries are typically the largest source of latency\ntags: performance, database, queries, optimization\n---\n\n## Optimize Database Queries\n\nSelect only needed columns, use proper indexes, avoid over-fetching relations, and consider query performance when designing your data access. Most API slowness traces back to inefficient database queries.\n\n**Incorrect (over-fetching data and missing indexes):**\n\n```typescript\n// Select everything when you need few fields\n@Injectable()\nexport class UsersService {\n  async findAllEmails(): Promise<string[]> {\n    const users = await this.repo.find();\n    // Fetches ALL columns for ALL users\n    return users.map(u => u.email);\n  }\n\n  async getUserSummary(id: string): Promise<UserSummary> {\n    const user = await this.repo.findOne({\n      where: { id },\n      relations: ['posts', 'posts.comments', 'posts.comments.author', 'followers'],\n    });\n    // Over-fetches massive relation tree\n    return { name: user.name, postCount: user.posts.length };\n  }\n}\n\n// No indexes on frequently queried columns\n@Entity()\nexport class Order {\n  @Column()\n  userId: string; // No index - full table scan on every lookup\n\n  @Column()\n  status: string; // No index - slow status filtering\n}\n```\n\n**Correct (select only needed data with proper indexes):**\n\n```typescript\n// Select only needed columns\n@Injectable()\nexport class UsersService {\n  async findAllEmails(): Promise<string[]> {\n    const users = await this.repo.find({\n      select: ['email'], // Only fetch email column\n    });\n    return users.map(u => u.email);\n  }\n\n  // Use QueryBuilder for complex selections\n  async getUserSummary(id: string): Promise<UserSummary> {\n    return this.repo\n      .createQueryBuilder('user')\n      .select('user.name', 'name')\n      .addSelect('COUNT(post.id)', 'postCount')\n      .leftJoin('user.posts', 'post')\n      .where('user.id = :id', { id })\n      .groupBy('user.id')\n      .getRawOne();\n  }\n\n  // Fetch relations only when needed\n  async getFullProfile(id: string): Promise<User> {\n    return this.repo.findOne({\n      where: { id },\n      relations: ['posts'], // Only immediate relation\n      select: {\n        id: true,\n        name: true,\n        email: true,\n        posts: {\n          id: true,\n          title: true,\n        },\n      },\n    });\n  }\n}\n\n// Add indexes on frequently queried columns\n@Entity()\n@Index(['userId'])\n@Index(['status'])\n@Index(['createdAt'])\n@Index(['userId', 'status']) // Composite index for common query pattern\nexport class Order {\n  @PrimaryGeneratedColumn('uuid')\n  id: string;\n\n  @Column()\n  userId: string;\n\n  @Column()\n  status: string;\n\n  @CreateDateColumn()\n  createdAt: Date;\n}\n\n// Always paginate large datasets\n@Injectable()\nexport class OrdersService {\n  async findAll(page = 1, limit = 20): Promise<PaginatedResult<Order>> {\n    const [items, total] = await this.repo.findAndCount({\n      skip: (page - 1) * limit,\n      take: limit,\n      order: { createdAt: 'DESC' },\n    });\n\n    return {\n      items,\n      meta: {\n        page,\n        limit,\n        total,\n        totalPages: Math.ceil(total / limit),\n      },\n    };\n  }\n}\n```\n\nReference: [TypeORM Query Builder](https://typeorm.io/select-query-builder)",".agents/skills/nestjs-best-practices/rules/perf-use-caching.md":"---\ntitle: Use Caching Strategically\nimpact: HIGH\nimpactDescription: Dramatically reduces database load and response times\ntags: performance, caching, redis, optimization\n---\n\n## Use Caching Strategically\n\nImplement caching for expensive operations, frequently accessed data, and external API calls. Use NestJS CacheModule with appropriate TTLs and cache invalidation strategies. Don't cache everything - focus on high-impact areas.\n\n**Incorrect (no caching or caching everything):**\n\n```typescript\n// No caching for expensive, repeated queries\n@Injectable()\nexport class ProductsService {\n  async getPopular(): Promise<Product[]> {\n    // Runs complex aggregation query EVERY request\n    return this.productsRepo\n      .createQueryBuilder('p')\n      .leftJoin('p.orders', 'o')\n      .select('p.*, COUNT(o.id) as orderCount')\n      .groupBy('p.id')\n      .orderBy('orderCount', 'DESC')\n      .limit(20)\n      .getMany();\n  }\n}\n\n// Cache everything without thought\n@Injectable()\nexport class UsersService {\n  @CacheKey('users')\n  @CacheTTL(3600)\n  @UseInterceptors(CacheInterceptor)\n  async findAll(): Promise<User[]> {\n    // Caching user list for 1 hour is wrong if data changes frequently\n    return this.usersRepo.find();\n  }\n}\n```\n\n**Correct (strategic caching with proper invalidation):**\n\n```typescript\n// Setup caching module\n@Module({\n  imports: [\n    CacheModule.registerAsync({\n      imports: [ConfigModule],\n      inject: [ConfigService],\n      useFactory: (config: ConfigService) => ({\n        stores: [new KeyvRedis(config.get('REDIS_URL'))],\n        ttl: 60 * 1000, // Default 60s\n      }),\n    }),\n  ],\n})\nexport class AppModule {}\n\n// Manual caching for granular control\n@Injectable()\nexport class ProductsService {\n  constructor(\n    @Inject(CACHE_MANAGER) private cache: Cache,\n    private productsRepo: ProductRepository,\n  ) {}\n\n  async getPopular(): Promise<Product[]> {\n    const cacheKey = 'products:popular';\n\n    // Try cache first\n    const cached = await this.cache.get<Product[]>(cacheKey);\n    if (cached) return cached;\n\n    // Cache miss - fetch and cache\n    const products = await this.fetchPopularProducts();\n    await this.cache.set(cacheKey, products, 5 * 60 * 1000); // 5 min TTL\n    return products;\n  }\n\n  // Invalidate cache on changes\n  async updateProduct(id: string, dto: UpdateProductDto): Promise<Product> {\n    const product = await this.productsRepo.save({ id, ...dto });\n    await this.cache.del('products:popular'); // Invalidate\n    return product;\n  }\n}\n\n// Decorator-based caching with auto-interceptor\n@Controller('categories')\n@UseInterceptors(CacheInterceptor)\nexport class CategoriesController {\n  @Get()\n  @CacheTTL(30 * 60 * 1000) // 30 minutes - categories rarely change\n  findAll(): Promise<Category[]> {\n    return this.categoriesService.findAll();\n  }\n\n  @Get(':id')\n  @CacheTTL(60 * 1000) // 1 minute\n  @CacheKey('category')\n  findOne(@Param('id') id: string): Promise<Category> {\n    return this.categoriesService.findOne(id);\n  }\n}\n\n// Event-based cache invalidation\n@Injectable()\nexport class CacheInvalidationService {\n  constructor(@Inject(CACHE_MANAGER) private cache: Cache) {}\n\n  @OnEvent('product.created')\n  @OnEvent('product.updated')\n  @OnEvent('product.deleted')\n  async invalidateProductCaches(event: ProductEvent) {\n    await Promise.all([this.cache.del('products:popular'), this.cache.del(`product:${event.productId}`)]);\n  }\n}\n```\n\nReference: [NestJS Caching](https://docs.nestjs.com/techniques/caching)",".agents/skills/nestjs-best-practices/rules/security-auth-jwt.md":"---\ntitle: Implement Secure JWT Authentication\nimpact: CRITICAL\nimpactDescription: Essential for secure APIs\ntags: security, jwt, authentication, tokens\n---\n\n## Implement Secure JWT Authentication\n\nUse `@nestjs/jwt` with `@nestjs/passport` for authentication. Store secrets securely, use appropriate token lifetimes, implement refresh tokens, and validate tokens properly. Never expose sensitive data in JWT payloads.\n\n**Incorrect (insecure JWT implementation):**\n\n```typescript\n// Hardcode secrets\n@Module({\n  imports: [\n    JwtModule.register({\n      secret: 'my-secret-key', // Exposed in code\n      signOptions: { expiresIn: '7d' }, // Too long\n    }),\n  ],\n})\nexport class AuthModule {}\n\n// Store sensitive data in JWT\nasync login(user: User): Promise<{ accessToken: string }> {\n  const payload = {\n    sub: user.id,\n    email: user.email,\n    password: user.password, // NEVER include password!\n    ssn: user.ssn, // NEVER include sensitive data!\n    isAdmin: user.isAdmin, // Can be tampered if not verified\n  };\n  return { accessToken: this.jwtService.sign(payload) };\n}\n\n// Skip token validation\n@Injectable()\nexport class JwtStrategy extends PassportStrategy(Strategy) {\n  constructor() {\n    super({\n      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),\n      secretOrKey: 'my-secret',\n    });\n  }\n\n  async validate(payload: any): Promise<any> {\n    return payload; // No validation of user existence\n  }\n}\n```\n\n**Correct (secure JWT with refresh tokens):**\n\n```typescript\n// Secure JWT configuration\n@Module({\n  imports: [\n    JwtModule.registerAsync({\n      imports: [ConfigModule],\n      inject: [ConfigService],\n      useFactory: (config: ConfigService) => ({\n        secret: config.get<string>('JWT_SECRET'),\n        signOptions: {\n          expiresIn: '15m', // Short-lived access tokens\n          issuer: config.get<string>('JWT_ISSUER'),\n          audience: config.get<string>('JWT_AUDIENCE'),\n        },\n      }),\n    }),\n    PassportModule.register({ defaultStrategy: 'jwt' }),\n  ],\n})\nexport class AuthModule {}\n\n// Minimal JWT payload\n@Injectable()\nexport class AuthService {\n  async login(user: User): Promise<TokenResponse> {\n    // Only include necessary, non-sensitive data\n    const payload: JwtPayload = {\n      sub: user.id,\n      email: user.email,\n      roles: user.roles,\n      iat: Math.floor(Date.now() / 1000),\n    };\n\n    const accessToken = this.jwtService.sign(payload);\n    const refreshToken = await this.createRefreshToken(user.id);\n\n    return { accessToken, refreshToken, expiresIn: 900 };\n  }\n\n  private async createRefreshToken(userId: string): Promise<string> {\n    const token = randomBytes(32).toString('hex');\n    const hashedToken = await bcrypt.hash(token, 10);\n\n    await this.refreshTokenRepo.save({\n      userId,\n      token: hashedToken,\n      expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days\n    });\n\n    return token;\n  }\n}\n\n// Proper JWT strategy with validation\n@Injectable()\nexport class JwtStrategy extends PassportStrategy(Strategy) {\n  constructor(\n    private config: ConfigService,\n    private usersService: UsersService,\n  ) {\n    super({\n      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),\n      secretOrKey: config.get<string>('JWT_SECRET'),\n      ignoreExpiration: false,\n      issuer: config.get<string>('JWT_ISSUER'),\n      audience: config.get<string>('JWT_AUDIENCE'),\n    });\n  }\n\n  async validate(payload: JwtPayload): Promise<User> {\n    // Verify user still exists and is active\n    const user = await this.usersService.findById(payload.sub);\n\n    if (!user || !user.isActive) {\n      throw new UnauthorizedException('User not found or inactive');\n    }\n\n    // Verify token wasn't issued before password change\n    if (user.passwordChangedAt) {\n      const tokenIssuedAt = new Date(payload.iat * 1000);\n      if (tokenIssuedAt < user.passwordChangedAt) {\n        throw new UnauthorizedException('Token invalidated by password change');\n      }\n    }\n\n    return user;\n  }\n}\n```\n\nReference: [NestJS Authentication](https://docs.nestjs.com/security/authentication)",".agents/skills/nestjs-best-practices/rules/security-rate-limiting.md":"---\ntitle: Implement Rate Limiting\nimpact: HIGH\nimpactDescription: Protects against abuse and ensures fair resource usage\ntags: security, rate-limiting, throttler, protection\n---\n\n## Implement Rate Limiting\n\nUse `@nestjs/throttler` to limit request rates per client. Apply different limits for different endpoints - stricter for auth endpoints, more relaxed for read operations. Consider using Redis for distributed rate limiting in clustered deployments.\n\n**Incorrect (no rate limiting on sensitive endpoints):**\n\n```typescript\n// No rate limiting on sensitive endpoints\n@Controller('auth')\nexport class AuthController {\n  @Post('login')\n  async login(@Body() dto: LoginDto): Promise<TokenResponse> {\n    // Attackers can brute-force credentials\n    return this.authService.login(dto);\n  }\n\n  @Post('forgot-password')\n  async forgotPassword(@Body() dto: ForgotPasswordDto): Promise<void> {\n    // Can be abused to spam users with emails\n    return this.authService.sendResetEmail(dto.email);\n  }\n}\n\n// Same limits for all endpoints\n@UseGuards(ThrottlerGuard)\n@Controller('api')\nexport class ApiController {\n  @Get('public-data')\n  async getPublic() {} // Should allow more requests\n\n  @Post('process-payment')\n  async payment() {} // Should be more restrictive\n}\n```\n\n**Correct (configured throttler with endpoint-specific limits):**\n\n```typescript\n// Configure throttler globally with multiple limits\nimport { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';\n\n@Module({\n  imports: [\n    ThrottlerModule.forRoot([\n      {\n        name: 'short',\n        ttl: 1000, // 1 second\n        limit: 3, // 3 requests per second\n      },\n      {\n        name: 'medium',\n        ttl: 10000, // 10 seconds\n        limit: 20, // 20 requests per 10 seconds\n      },\n      {\n        name: 'long',\n        ttl: 60000, // 1 minute\n        limit: 100, // 100 requests per minute\n      },\n    ]),\n  ],\n  providers: [\n    {\n      provide: APP_GUARD,\n      useClass: ThrottlerGuard,\n    },\n  ],\n})\nexport class AppModule {}\n\n// Override limits per endpoint\n@Controller('auth')\nexport class AuthController {\n  @Post('login')\n  @Throttle({ short: { limit: 5, ttl: 60000 } }) // 5 attempts per minute\n  async login(@Body() dto: LoginDto): Promise<TokenResponse> {\n    return this.authService.login(dto);\n  }\n\n  @Post('forgot-password')\n  @Throttle({ short: { limit: 3, ttl: 3600000 } }) // 3 per hour\n  async forgotPassword(@Body() dto: ForgotPasswordDto): Promise<void> {\n    return this.authService.sendResetEmail(dto.email);\n  }\n}\n\n// Skip throttling for certain routes\n@Controller('health')\nexport class HealthController {\n  @Get()\n  @SkipThrottle()\n  check(): string {\n    return 'OK';\n  }\n}\n\n// Custom throttle per user type\n@Injectable()\nexport class CustomThrottlerGuard extends ThrottlerGuard {\n  protected async getTracker(req: Request): Promise<string> {\n    // Use user ID if authenticated, IP otherwise\n    return req.user?.id || req.ip;\n  }\n\n  protected async getLimit(context: ExecutionContext): Promise<number> {\n    const request = context.switchToHttp().getRequest();\n\n    // Higher limits for authenticated users\n    if (request.user) {\n      return request.user.isPremium ? 1000 : 200;\n    }\n\n    return 50; // Anonymous users\n  }\n}\n```\n\nReference: [NestJS Throttler](https://docs.nestjs.com/security/rate-limiting)",".agents/skills/nestjs-best-practices/rules/security-sanitize-output.md":"---\ntitle: Sanitize Output to Prevent XSS\nimpact: HIGH\nimpactDescription: XSS vulnerabilities can compromise user sessions and data\ntags: security, xss, sanitization, html\n---\n\n## Sanitize Output to Prevent XSS\n\nWhile NestJS APIs typically return JSON (which browsers don't execute), XSS risks exist when rendering HTML, storing user content, or when frontend frameworks improperly handle API responses. Sanitize user-generated content before storage and use proper Content-Type headers.\n\n**Incorrect (storing raw HTML without sanitization):**\n\n```typescript\n// Store raw HTML from users\n@Injectable()\nexport class CommentsService {\n  async create(dto: CreateCommentDto): Promise<Comment> {\n    // User can inject: <script>steal(document.cookie)</script>\n    return this.repo.save({\n      content: dto.content, // Raw, unsanitized\n      authorId: dto.authorId,\n    });\n  }\n}\n\n// Return HTML without sanitization\n@Controller('pages')\nexport class PagesController {\n  @Get(':slug')\n  @Header('Content-Type', 'text/html')\n  async getPage(@Param('slug') slug: string): Promise<string> {\n    const page = await this.pagesService.findBySlug(slug);\n    // If page.content contains user input, XSS is possible\n    return `<html><body>${page.content}</body></html>`;\n  }\n}\n\n// Reflect user input in errors\n@Get(':id')\nasync findOne(@Param('id') id: string): Promise<User> {\n  const user = await this.repo.findOne({ where: { id } });\n  if (!user) {\n    // XSS if id contains malicious content and error is rendered\n    throw new NotFoundException(`User ${id} not found`);\n  }\n  return user;\n}\n```\n\n**Correct (sanitize content and use proper headers):**\n\n```typescript\n// Sanitize HTML content before storage\nimport * as sanitizeHtml from 'sanitize-html';\n\n@Injectable()\nexport class CommentsService {\n  private readonly sanitizeOptions: sanitizeHtml.IOptions = {\n    allowedTags: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],\n    allowedAttributes: {\n      a: ['href', 'title'],\n    },\n    allowedSchemes: ['http', 'https', 'mailto'],\n  };\n\n  async create(dto: CreateCommentDto): Promise<Comment> {\n    return this.repo.save({\n      content: sanitizeHtml(dto.content, this.sanitizeOptions),\n      authorId: dto.authorId,\n    });\n  }\n}\n\n// Use validation pipe to strip HTML\nimport { Transform } from 'class-transformer';\n\nexport class CreatePostDto {\n  @IsString()\n  @MaxLength(1000)\n  @Transform(({ value }) => sanitizeHtml(value, { allowedTags: [] }))\n  title: string;\n\n  @IsString()\n  @Transform(({ value }) =>\n    sanitizeHtml(value, {\n      allowedTags: ['p', 'br', 'b', 'i', 'a'],\n      allowedAttributes: { a: ['href'] },\n    }),\n  )\n  content: string;\n}\n\n// Set proper Content-Type headers\n@Controller('api')\nexport class ApiController {\n  @Get('data')\n  @Header('Content-Type', 'application/json')\n  async getData(): Promise<DataResponse> {\n    // JSON response - browser won't execute scripts\n    return this.service.getData();\n  }\n}\n\n// Sanitize error messages\n@Get(':id')\nasync findOne(@Param('id', ParseUUIDPipe) id: string): Promise<User> {\n  const user = await this.repo.findOne({ where: { id } });\n  if (!user) {\n    // UUID validation ensures safe format\n    throw new NotFoundException('User not found');\n  }\n  return user;\n}\n\n// Use Helmet for CSP headers\nimport helmet from 'helmet';\n\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n\n  app.use(\n    helmet({\n      contentSecurityPolicy: {\n        directives: {\n          defaultSrc: [\"'self'\"],\n          scriptSrc: [\"'self'\"],\n          styleSrc: [\"'self'\", \"'unsafe-inline'\"],\n          imgSrc: [\"'self'\", 'data:', 'https:'],\n        },\n      },\n    }),\n  );\n\n  await app.listen(3000);\n}\n```\n\nReference: [OWASP XSS Prevention](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)",".agents/skills/nestjs-best-practices/rules/security-use-guards.md":"---\ntitle: Use Guards for Authentication and Authorization\nimpact: HIGH\nimpactDescription: Enforces access control before handlers execute\ntags: security, guards, authentication, authorization\n---\n\n## Use Guards for Authentication and Authorization\n\nGuards determine whether a request should be handled based on authentication state, roles, permissions, or other conditions. They run after middleware but before pipes and interceptors, making them ideal for access control. Use guards instead of manual checks in controllers.\n\n**Incorrect (manual auth checks in every handler):**\n\n```typescript\n// Manual auth checks in every handler\n@Controller('admin')\nexport class AdminController {\n  @Get('users')\n  async getUsers(@Request() req) {\n    if (!req.user) {\n      throw new UnauthorizedException();\n    }\n    if (!req.user.roles.includes('admin')) {\n      throw new ForbiddenException();\n    }\n    return this.adminService.getUsers();\n  }\n\n  @Delete('users/:id')\n  async deleteUser(@Request() req, @Param('id') id: string) {\n    if (!req.user) {\n      throw new UnauthorizedException();\n    }\n    if (!req.user.roles.includes('admin')) {\n      throw new ForbiddenException();\n    }\n    return this.adminService.deleteUser(id);\n  }\n}\n```\n\n**Correct (guards with declarative decorators):**\n\n```typescript\n// JWT Auth Guard\n@Injectable()\nexport class JwtAuthGuard implements CanActivate {\n  constructor(\n    private jwtService: JwtService,\n    private reflector: Reflector,\n  ) {}\n\n  async canActivate(context: ExecutionContext): Promise<boolean> {\n    // Check for @Public() decorator\n    const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [context.getHandler(), context.getClass()]);\n    if (isPublic) return true;\n\n    const request = context.switchToHttp().getRequest();\n    const token = this.extractToken(request);\n\n    if (!token) {\n      throw new UnauthorizedException('No token provided');\n    }\n\n    try {\n      request.user = await this.jwtService.verifyAsync(token);\n      return true;\n    } catch {\n      throw new UnauthorizedException('Invalid token');\n    }\n  }\n\n  private extractToken(request: Request): string | undefined {\n    const [type, token] = request.headers.authorization?.split(' ') ?? [];\n    return type === 'Bearer' ? token : undefined;\n  }\n}\n\n// Roles Guard\n@Injectable()\nexport class RolesGuard implements CanActivate {\n  constructor(private reflector: Reflector) {}\n\n  canActivate(context: ExecutionContext): boolean {\n    const requiredRoles = this.reflector.getAllAndOverride<Role[]>('roles', [context.getHandler(), context.getClass()]);\n\n    if (!requiredRoles) return true;\n\n    const { user } = context.switchToHttp().getRequest();\n    return requiredRoles.some(role => user.roles?.includes(role));\n  }\n}\n\n// Decorators\nexport const Public = () => SetMetadata('isPublic', true);\nexport const Roles = (...roles: Role[]) => SetMetadata('roles', roles);\n\n// Register guards globally\n@Module({\n  providers: [\n    { provide: APP_GUARD, useClass: JwtAuthGuard },\n    { provide: APP_GUARD, useClass: RolesGuard },\n  ],\n})\nexport class AppModule {}\n\n// Clean controller\n@Controller('admin')\n@Roles(Role.Admin) // Applied to all routes\nexport class AdminController {\n  @Get('users')\n  getUsers(): Promise<User[]> {\n    return this.adminService.getUsers();\n  }\n\n  @Delete('users/:id')\n  deleteUser(@Param('id') id: string): Promise<void> {\n    return this.adminService.deleteUser(id);\n  }\n\n  @Public() // Override: no auth required\n  @Get('health')\n  health() {\n    return { status: 'ok' };\n  }\n}\n```\n\nReference: [NestJS Guards](https://docs.nestjs.com/guards)",".agents/skills/nestjs-best-practices/rules/security-validate-all-input.md":"---\ntitle: Validate All Input with DTOs and Pipes\nimpact: HIGH\nimpactDescription: First line of defense against attacks\ntags: security, validation, dto, pipes\n---\n\n## Validate All Input with DTOs and Pipes\n\nAlways validate incoming data using class-validator decorators on DTOs and the global ValidationPipe. Never trust user input. Validate all request bodies, query parameters, and route parameters before processing.\n\n**Incorrect (trust raw input without validation):**\n\n```typescript\n// Trust raw input without validation\n@Controller('users')\nexport class UsersController {\n  @Post()\n  create(@Body() body: any) {\n    // body could contain anything - SQL injection, XSS, etc.\n    return this.usersService.create(body);\n  }\n\n  @Get()\n  findAll(@Query() query: any) {\n    // query.limit could be \"'; DROP TABLE users; --\"\n    return this.usersService.findAll(query.limit);\n  }\n}\n\n// DTOs without validation decorators\nexport class CreateUserDto {\n  name: string; // No validation\n  email: string; // Could be \"not-an-email\"\n  age: number; // Could be \"abc\" or -999\n}\n```\n\n**Correct (validated DTOs with global ValidationPipe):**\n\n```typescript\n// Enable ValidationPipe globally in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n\n  app.useGlobalPipes(\n    new ValidationPipe({\n      whitelist: true, // Strip unknown properties\n      forbidNonWhitelisted: true, // Throw on unknown properties\n      transform: true, // Auto-transform to DTO types\n      transformOptions: {\n        enableImplicitConversion: true,\n      },\n    }),\n  );\n\n  await app.listen(3000);\n}\n\n// Create well-validated DTOs\nimport {\n  IsString,\n  IsEmail,\n  IsInt,\n  Min,\n  Max,\n  IsOptional,\n  MinLength,\n  MaxLength,\n  Matches,\n  IsNotEmpty,\n} from 'class-validator';\nimport { Transform, Type } from 'class-transformer';\n\nexport class CreateUserDto {\n  @IsString()\n  @IsNotEmpty()\n  @MinLength(2)\n  @MaxLength(100)\n  @Transform(({ value }) => value?.trim())\n  name: string;\n\n  @IsEmail()\n  @Transform(({ value }) => value?.toLowerCase().trim())\n  email: string;\n\n  @IsInt()\n  @Min(0)\n  @Max(150)\n  age: number;\n\n  @IsString()\n  @MinLength(8)\n  @MaxLength(100)\n  @Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)/, {\n    message: 'Password must contain uppercase, lowercase, and number',\n  })\n  password: string;\n}\n\n// Query DTO with defaults and transformation\nexport class FindUsersQueryDto {\n  @IsOptional()\n  @IsString()\n  @MaxLength(100)\n  search?: string;\n\n  @IsOptional()\n  @Type(() => Number)\n  @IsInt()\n  @Min(1)\n  @Max(100)\n  limit: number = 20;\n\n  @IsOptional()\n  @Type(() => Number)\n  @IsInt()\n  @Min(0)\n  offset: number = 0;\n}\n\n// Param validation\nexport class UserIdParamDto {\n  @IsUUID('4')\n  id: string;\n}\n\n@Controller('users')\nexport class UsersController {\n  @Post()\n  create(@Body() dto: CreateUserDto): Promise<User> {\n    // dto is guaranteed to be valid\n    return this.usersService.create(dto);\n  }\n\n  @Get()\n  findAll(@Query() query: FindUsersQueryDto): Promise<User[]> {\n    // query.limit is a number, query.search is sanitized\n    return this.usersService.findAll(query);\n  }\n\n  @Get(':id')\n  findOne(@Param() params: UserIdParamDto): Promise<User> {\n    // params.id is a valid UUID\n    return this.usersService.findById(params.id);\n  }\n}\n```\n\nReference: [NestJS Validation](https://docs.nestjs.com/techniques/validation)",".agents/skills/nestjs-best-practices/rules/test-e2e-supertest.md":"---\ntitle: Use Supertest for E2E Testing\nimpact: HIGH\nimpactDescription: Validates the full request/response cycle\ntags: testing, e2e, supertest, integration\n---\n\n## Use Supertest for E2E Testing\n\nEnd-to-end tests use Supertest to make real HTTP requests against your NestJS application. They test the full stack including middleware, guards, pipes, and interceptors. E2E tests catch integration issues that unit tests miss.\n\n**Incorrect (no proper E2E setup or teardown):**\n\n```typescript\n// Only unit test controllers\ndescribe('UsersController', () => {\n  it('should return users', async () => {\n    const service = { findAll: jest.fn().mockResolvedValue([]) };\n    const controller = new UsersController(service as any);\n\n    const result = await controller.findAll();\n\n    expect(result).toEqual([]);\n    // Doesn't test: routes, guards, pipes, serialization\n  });\n});\n\n// E2E tests without proper setup/teardown\ndescribe('Users API', () => {\n  it('should create user', async () => {\n    const app = await NestFactory.create(AppModule);\n    // No proper initialization\n    // No cleanup after test\n    // Hits real database\n  });\n});\n```\n\n**Correct (proper E2E setup with Supertest):**\n\n```typescript\n// Proper E2E test setup\nimport { Test, TestingModule } from '@nestjs/testing';\nimport { INestApplication, ValidationPipe } from '@nestjs/common';\nimport * as request from 'supertest';\nimport { AppModule } from '../src/app.module';\n\ndescribe('UsersController (e2e)', () => {\n  let app: INestApplication;\n\n  beforeAll(async () => {\n    const moduleFixture: TestingModule = await Test.createTestingModule({\n      imports: [AppModule],\n    }).compile();\n\n    app = moduleFixture.createNestApplication();\n\n    // Apply same config as production\n    app.useGlobalPipes(\n      new ValidationPipe({\n        whitelist: true,\n        transform: true,\n        forbidNonWhitelisted: true,\n      }),\n    );\n\n    await app.init();\n  });\n\n  afterAll(async () => {\n    await app.close();\n  });\n\n  describe('/users (POST)', () => {\n    it('should create a user', () => {\n      return request(app.getHttpServer())\n        .post('/users')\n        .send({ name: 'John', email: 'john@test.com' })\n        .expect(201)\n        .expect(res => {\n          expect(res.body).toHaveProperty('id');\n          expect(res.body.name).toBe('John');\n          expect(res.body.email).toBe('john@test.com');\n        });\n    });\n\n    it('should return 400 for invalid email', () => {\n      return request(app.getHttpServer())\n        .post('/users')\n        .send({ name: 'John', email: 'invalid-email' })\n        .expect(400)\n        .expect(res => {\n          expect(res.body.message).toContain('email');\n        });\n    });\n  });\n\n  describe('/users/:id (GET)', () => {\n    it('should return 404 for non-existent user', () => {\n      return request(app.getHttpServer()).get('/users/non-existent-id').expect(404);\n    });\n  });\n});\n\n// Testing with authentication\ndescribe('Protected Routes (e2e)', () => {\n  let app: INestApplication;\n  let authToken: string;\n\n  beforeAll(async () => {\n    const moduleFixture = await Test.createTestingModule({\n      imports: [AppModule],\n    }).compile();\n\n    app = moduleFixture.createNestApplication();\n    app.useGlobalPipes(new ValidationPipe({ whitelist: true }));\n    await app.init();\n\n    // Get auth token\n    const loginResponse = await request(app.getHttpServer())\n      .post('/auth/login')\n      .send({ email: 'test@test.com', password: 'password' });\n\n    authToken = loginResponse.body.accessToken;\n  });\n\n  it('should return 401 without token', () => {\n    return request(app.getHttpServer()).get('/users/me').expect(401);\n  });\n\n  it('should return user profile with valid token', () => {\n    return request(app.getHttpServer())\n      .get('/users/me')\n      .set('Authorization', `Bearer ${authToken}`)\n      .expect(200)\n      .expect(res => {\n        expect(res.body.email).toBe('test@test.com');\n      });\n  });\n});\n\n// Database isolation for E2E tests\ndescribe('Orders API (e2e)', () => {\n  let app: INestApplication;\n  let dataSource: DataSource;\n\n  beforeAll(async () => {\n    const moduleFixture = await Test.createTestingModule({\n      imports: [\n        ConfigModule.forRoot({\n          envFilePath: '.env.test', // Test database config\n        }),\n        AppModule,\n      ],\n    }).compile();\n\n    app = moduleFixture.createNestApplication();\n    dataSource = moduleFixture.get(DataSource);\n    await app.init();\n  });\n\n  beforeEach(async () => {\n    // Clean database between tests\n    await dataSource.synchronize(true);\n  });\n\n  afterAll(async () => {\n    await dataSource.destroy();\n    await app.close();\n  });\n});\n```\n\nReference: [NestJS E2E Testing](https://docs.nestjs.com/fundamentals/testing#end-to-end-testing)",".agents/skills/nestjs-best-practices/rules/test-mock-external-services.md":"---\ntitle: Mock External Services in Tests\nimpact: HIGH\nimpactDescription: Ensures fast, reliable, deterministic tests\ntags: testing, mocking, external-services, jest\n---\n\n## Mock External Services in Tests\n\nNever call real external services (APIs, databases, message queues) in unit tests. Mock them to ensure tests are fast, deterministic, and don't incur costs. Use realistic mock data and test edge cases like timeouts and errors.\n\n**Incorrect (calling real APIs and databases):**\n\n```typescript\n// Call real APIs in tests\ndescribe('PaymentService', () => {\n  it('should process payment', async () => {\n    const service = new PaymentService(new StripeClient(realApiKey));\n    // Hits real Stripe API!\n    const result = await service.charge('tok_visa', 1000);\n    // Slow, costs money, flaky\n  });\n});\n\n// Use real database\ndescribe('UsersService', () => {\n  beforeEach(async () => {\n    await connection.query('DELETE FROM users'); // Modifies real DB\n  });\n\n  it('should create user', async () => {\n    const user = await service.create({ email: 'test@test.com' });\n    // Side effects on shared database\n  });\n});\n\n// Incomplete mocks\nconst mockHttpService = {\n  get: jest.fn().mockResolvedValue({ data: {} }),\n  // Missing error scenarios, missing other methods\n};\n```\n\n**Correct (mock all external dependencies):**\n\n```typescript\n// Mock HTTP service properly\ndescribe('WeatherService', () => {\n  let service: WeatherService;\n  let httpService: jest.Mocked<HttpService>;\n\n  beforeEach(async () => {\n    const module = await Test.createTestingModule({\n      providers: [\n        WeatherService,\n        {\n          provide: HttpService,\n          useValue: {\n            get: jest.fn(),\n            post: jest.fn(),\n          },\n        },\n      ],\n    }).compile();\n\n    service = module.get(WeatherService);\n    httpService = module.get(HttpService);\n  });\n\n  it('should return weather data', async () => {\n    const mockResponse = {\n      data: { temperature: 72, humidity: 45 },\n      status: 200,\n      statusText: 'OK',\n      headers: {},\n      config: {},\n    };\n\n    httpService.get.mockReturnValue(of(mockResponse));\n\n    const result = await service.getWeather('NYC');\n\n    expect(result).toEqual({ temperature: 72, humidity: 45 });\n  });\n\n  it('should handle API timeout', async () => {\n    httpService.get.mockReturnValue(throwError(() => new Error('ETIMEDOUT')));\n\n    await expect(service.getWeather('NYC')).rejects.toThrow('Weather service unavailable');\n  });\n\n  it('should handle rate limiting', async () => {\n    httpService.get.mockReturnValue(\n      throwError(() => ({\n        response: { status: 429, data: { message: 'Rate limited' } },\n      })),\n    );\n\n    await expect(service.getWeather('NYC')).rejects.toThrow(TooManyRequestsException);\n  });\n});\n\n// Mock repository instead of database\ndescribe('UsersService', () => {\n  let service: UsersService;\n  let repo: jest.Mocked<Repository<User>>;\n\n  beforeEach(async () => {\n    const mockRepo = {\n      find: jest.fn(),\n      findOne: jest.fn(),\n      save: jest.fn(),\n      delete: jest.fn(),\n      createQueryBuilder: jest.fn(),\n    };\n\n    const module = await Test.createTestingModule({\n      providers: [UsersService, { provide: getRepositoryToken(User), useValue: mockRepo }],\n    }).compile();\n\n    service = module.get(UsersService);\n    repo = module.get(getRepositoryToken(User));\n  });\n\n  it('should find user by id', async () => {\n    const mockUser = { id: '1', name: 'John', email: 'john@test.com' };\n    repo.findOne.mockResolvedValue(mockUser);\n\n    const result = await service.findById('1');\n\n    expect(result).toEqual(mockUser);\n    expect(repo.findOne).toHaveBeenCalledWith({ where: { id: '1' } });\n  });\n});\n\n// Create mock factory for complex SDKs\nfunction createMockStripe(): jest.Mocked<Stripe> {\n  return {\n    paymentIntents: {\n      create: jest.fn(),\n      retrieve: jest.fn(),\n      confirm: jest.fn(),\n      cancel: jest.fn(),\n    },\n    customers: {\n      create: jest.fn(),\n      retrieve: jest.fn(),\n    },\n  } as any;\n}\n\n// Mock time for time-dependent tests\ndescribe('TokenService', () => {\n  beforeEach(() => {\n    jest.useFakeTimers();\n    jest.setSystemTime(new Date('2024-01-15'));\n  });\n\n  afterEach(() => {\n    jest.useRealTimers();\n  });\n\n  it('should expire token after 1 hour', async () => {\n    const token = await service.createToken();\n\n    // Fast-forward time\n    jest.advanceTimersByTime(61 * 60 * 1000);\n\n    expect(await service.isValid(token)).toBe(false);\n  });\n});\n```\n\nReference: [Jest Mocking](https://jestjs.io/docs/mock-functions)",".agents/skills/nestjs-best-practices/rules/test-use-testing-module.md":"---\ntitle: Use Testing Module for Unit Tests\nimpact: HIGH\nimpactDescription: Enables proper isolated testing with mocked dependencies\ntags: testing, unit-tests, mocking, jest\n---\n\n## Use Testing Module for Unit Tests\n\nUse `@nestjs/testing` module to create isolated test environments with mocked dependencies. This ensures your tests run fast, don't depend on external services, and properly test your business logic in isolation.\n\n**Incorrect (manual instantiation bypassing DI):**\n\n```typescript\n// Instantiate services manually without DI\ndescribe('UsersService', () => {\n  it('should create user', async () => {\n    // Manual instantiation bypasses DI\n    const repo = new UserRepository(); // Real repo!\n    const service = new UsersService(repo);\n\n    const user = await service.create({ name: 'Test' });\n    // This hits the real database!\n  });\n});\n\n// Test implementation details\ndescribe('UsersController', () => {\n  it('should call service', async () => {\n    const service = { create: jest.fn() };\n    const controller = new UsersController(service as any);\n\n    await controller.create({ name: 'Test' });\n\n    expect(service.create).toHaveBeenCalled(); // Tests implementation, not behavior\n  });\n});\n```\n\n**Correct (use Test.createTestingModule with mocked dependencies):**\n\n```typescript\n// Use Test.createTestingModule for proper DI\nimport { Test, TestingModule } from '@nestjs/testing';\n\ndescribe('UsersService', () => {\n  let service: UsersService;\n  let repo: jest.Mocked<UserRepository>;\n\n  beforeEach(async () => {\n    const module: TestingModule = await Test.createTestingModule({\n      providers: [\n        UsersService,\n        {\n          provide: UserRepository,\n          useValue: {\n            save: jest.fn(),\n            findOne: jest.fn(),\n            find: jest.fn(),\n          },\n        },\n      ],\n    }).compile();\n\n    service = module.get<UsersService>(UsersService);\n    repo = module.get(UserRepository);\n  });\n\n  afterEach(() => {\n    jest.clearAllMocks();\n  });\n\n  describe('create', () => {\n    it('should save and return user', async () => {\n      const dto = { name: 'John', email: 'john@test.com' };\n      const expectedUser = { id: '1', ...dto };\n\n      repo.save.mockResolvedValue(expectedUser);\n\n      const result = await service.create(dto);\n\n      expect(result).toEqual(expectedUser);\n      expect(repo.save).toHaveBeenCalledWith(dto);\n    });\n\n    it('should throw on duplicate email', async () => {\n      repo.findOne.mockResolvedValue({ id: '1', email: 'test@test.com' });\n\n      await expect(service.create({ name: 'Test', email: 'test@test.com' })).rejects.toThrow(ConflictException);\n    });\n  });\n\n  describe('findById', () => {\n    it('should return user when found', async () => {\n      const user = { id: '1', name: 'John' };\n      repo.findOne.mockResolvedValue(user);\n\n      const result = await service.findById('1');\n\n      expect(result).toEqual(user);\n    });\n\n    it('should throw NotFoundException when not found', async () => {\n      repo.findOne.mockResolvedValue(null);\n\n      await expect(service.findById('999')).rejects.toThrow(NotFoundException);\n    });\n  });\n});\n\n// Testing guards and interceptors\ndescribe('RolesGuard', () => {\n  let guard: RolesGuard;\n  let reflector: Reflector;\n\n  beforeEach(async () => {\n    const module = await Test.createTestingModule({\n      providers: [RolesGuard, Reflector],\n    }).compile();\n\n    guard = module.get<RolesGuard>(RolesGuard);\n    reflector = module.get<Reflector>(Reflector);\n  });\n\n  it('should allow when no roles required', () => {\n    const context = createMockExecutionContext({ user: { roles: [] } });\n    jest.spyOn(reflector, 'getAllAndOverride').mockReturnValue(undefined);\n\n    expect(guard.canActivate(context)).toBe(true);\n  });\n\n  it('should allow admin for admin-only route', () => {\n    const context = createMockExecutionContext({ user: { roles: ['admin'] } });\n    jest.spyOn(reflector, 'getAllAndOverride').mockReturnValue(['admin']);\n\n    expect(guard.canActivate(context)).toBe(true);\n  });\n});\n\nfunction createMockExecutionContext(request: Partial<Request>): ExecutionContext {\n  return {\n    switchToHttp: () => ({\n      getRequest: () => request,\n    }),\n    getHandler: () => jest.fn(),\n    getClass: () => jest.fn(),\n  } as ExecutionContext;\n}\n```\n\nReference: [NestJS Testing](https://docs.nestjs.com/fundamentals/testing)",".agents/skills/nestjs-best-practices/scripts/package.json":"{\n  \"name\": \"nestjs-best-practices-scripts\",\n  \"version\": \"1.0.0\",\n  \"type\": \"module\",\n  \"description\": \"Build scripts for NestJS Best Practices skillset\",\n  \"scripts\": {\n    \"build\": \"npx ts-node build-agents.ts\",\n    \"build:watch\": \"npx nodemon --watch ../rules --ext md --exec 'npx ts-node build-agents.ts'\"\n  },\n  \"devDependencies\": {\n    \"typescript\": \"^5.0.0\",\n    \"ts-node\": \"^10.9.0\",\n    \"@types/node\": \"^20.0.0\"\n  }\n}\n",".agents/skills/typeorm/SKILL.md":"---\nname: typeorm\ndescription: Guidelines for developing with TypeORM, a full-featured ORM for TypeScript and JavaScript supporting multiple databases\n---\n\n# TypeORM Development Guidelines\n\nYou are an expert in TypeORM, TypeScript, and database design with a focus on the Data Mapper pattern and enterprise application architecture.\n\n## Core Principles\n\n- TypeORM supports both Active Record and Data Mapper patterns\n- Uses TypeScript decorators for entity and column definitions\n- Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, and more\n- Works in Node.js, Browser, Ionic, Cordova, React Native, NativeScript, Expo, and Electron\n- First-class support for database migrations\n\n## TypeScript Configuration\n\nRequired settings in tsconfig.json:\n\n```json\n{\n  \"compilerOptions\": {\n    \"experimentalDecorators\": true,\n    \"emitDecoratorMetadata\": true,\n    \"strict\": true,\n    \"target\": \"ES2020\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\"\n  }\n}\n```\n\n## Entity Definition\n\n### Basic Entity\n\n```typescript\nimport { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';\n\n@Entity('users')\nexport class User {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column({ type: 'varchar', length: 255, unique: true })\n  email: string;\n\n  @Column({ type: 'varchar', length: 255, nullable: true })\n  name: string | null;\n\n  @Column({ type: 'boolean', default: true })\n  isActive: boolean;\n\n  @CreateDateColumn()\n  createdAt: Date;\n\n  @UpdateDateColumn()\n  updatedAt: Date;\n}\n```\n\n### Primary Key Options\n\n```typescript\n// Auto-increment\n@PrimaryGeneratedColumn()\nid: number;\n\n// UUID\n@PrimaryGeneratedColumn(\"uuid\")\nid: string;\n\n// Custom primary key\n@PrimaryColumn()\nid: string;\n\n// Composite primary key\n@Entity()\nexport class OrderItem {\n  @PrimaryColumn()\n  orderId: number;\n\n  @PrimaryColumn()\n  productId: number;\n}\n```\n\n### Column Decorators\n\n```typescript\n@Entity()\nexport class Product {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  // String columns\n  @Column({ type: 'varchar', length: 255 })\n  name: string;\n\n  @Column({ type: 'text', nullable: true })\n  description: string | null;\n\n  // Numeric columns\n  @Column({ type: 'decimal', precision: 10, scale: 2 })\n  price: number;\n\n  @Column({ type: 'int', default: 0 })\n  stock: number;\n\n  // Boolean\n  @Column({ type: 'boolean', default: true })\n  isAvailable: boolean;\n\n  // JSON\n  @Column({ type: 'jsonb', nullable: true })\n  metadata: Record<string, any> | null;\n\n  // Enum\n  @Column({\n    type: 'enum',\n    enum: ['active', 'inactive', 'pending'],\n    default: 'pending',\n  })\n  status: 'active' | 'inactive' | 'pending';\n\n  // Timestamps\n  @CreateDateColumn()\n  createdAt: Date;\n\n  @UpdateDateColumn()\n  updatedAt: Date;\n\n  @DeleteDateColumn()\n  deletedAt: Date | null; // For soft deletes\n\n  // Version column for optimistic locking\n  @VersionColumn()\n  version: number;\n}\n```\n\n## Relationships\n\n### One-to-One\n\n```typescript\n@Entity()\nexport class User {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @OneToOne(() => Profile, profile => profile.user, { cascade: true })\n  @JoinColumn()\n  profile: Profile;\n}\n\n@Entity()\nexport class Profile {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column()\n  bio: string;\n\n  @OneToOne(() => User, user => user.profile)\n  user: User;\n}\n```\n\n### One-to-Many / Many-to-One\n\n```typescript\n@Entity()\nexport class User {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column()\n  name: string;\n\n  @OneToMany(() => Post, post => post.author)\n  posts: Post[];\n}\n\n@Entity()\nexport class Post {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column()\n  title: string;\n\n  @ManyToOne(() => User, user => user.posts, { onDelete: 'CASCADE' })\n  @JoinColumn({ name: 'author_id' })\n  author: User;\n\n  @Column()\n  authorId: number; // Explicit foreign key column\n}\n```\n\n### Many-to-Many\n\n```typescript\n@Entity()\nexport class Post {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column()\n  title: string;\n\n  @ManyToMany(() => Tag, tag => tag.posts)\n  @JoinTable({\n    name: 'post_tags',\n    joinColumn: { name: 'post_id' },\n    inverseJoinColumn: { name: 'tag_id' },\n  })\n  tags: Tag[];\n}\n\n@Entity()\nexport class Tag {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column({ unique: true })\n  name: string;\n\n  @ManyToMany(() => Post, post => post.tags)\n  posts: Post[];\n}\n```\n\n## Repository Pattern\n\n### Basic Repository Usage\n\n```typescript\nimport { AppDataSource } from './data-source';\nimport { User } from './entities/User';\n\nconst userRepository = AppDataSource.getRepository(User);\n\n// Find all\nconst users = await userRepository.find();\n\n// Find with conditions\nconst activeUsers = await userRepository.find({\n  where: { isActive: true },\n});\n\n// Find one\nconst user = await userRepository.findOne({\n  where: { id: 1 },\n});\n\n// Find or fail\nconst user = await userRepository.findOneOrFail({\n  where: { id: 1 },\n});\n\n// Save\nconst newUser = userRepository.create({\n  email: 'user@example.com',\n  name: 'John Doe',\n});\nawait userRepository.save(newUser);\n\n// Update\nawait userRepository.update({ id: 1 }, { name: 'Jane Doe' });\n\n// Delete\nawait userRepository.delete({ id: 1 });\n\n// Soft delete (requires @DeleteDateColumn)\nawait userRepository.softDelete({ id: 1 });\n```\n\n### Custom Repository\n\n```typescript\nimport { Repository, DataSource } from 'typeorm';\nimport { User } from './entities/User';\n\nexport class UserRepository extends Repository<User> {\n  constructor(private dataSource: DataSource) {\n    super(User, dataSource.createEntityManager());\n  }\n\n  async findByEmail(email: string): Promise<User | null> {\n    return this.findOne({ where: { email } });\n  }\n\n  async findActiveUsers(): Promise<User[]> {\n    return this.find({\n      where: { isActive: true },\n      order: { createdAt: 'DESC' },\n    });\n  }\n\n  async findWithPosts(userId: number): Promise<User | null> {\n    return this.findOne({\n      where: { id: userId },\n      relations: ['posts'],\n    });\n  }\n}\n```\n\n### Query Builder\n\n```typescript\nconst users = await userRepository\n  .createQueryBuilder('user')\n  .leftJoinAndSelect('user.posts', 'post')\n  .where('user.isActive = :isActive', { isActive: true })\n  .andWhere('post.publishedAt IS NOT NULL')\n  .orderBy('user.createdAt', 'DESC')\n  .skip(0)\n  .take(10)\n  .getMany();\n\n// With raw results\nconst result = await userRepository\n  .createQueryBuilder('user')\n  .select('COUNT(*)', 'count')\n  .where('user.isActive = :isActive', { isActive: true })\n  .getRawOne();\n\n// Insert with query builder\nawait userRepository\n  .createQueryBuilder()\n  .insert()\n  .into(User)\n  .values([\n    { email: 'user1@example.com', name: 'User 1' },\n    { email: 'user2@example.com', name: 'User 2' },\n  ])\n  .execute();\n```\n\n## Data Source Configuration\n\n```typescript\n// data-source.ts\nimport { DataSource } from 'typeorm';\nimport { User } from './entities/User';\nimport { Post } from './entities/Post';\n\nexport const AppDataSource = new DataSource({\n  type: 'postgres',\n  host: process.env.DB_HOST || 'localhost',\n  port: parseInt(process.env.DB_PORT || '5432'),\n  username: process.env.DB_USERNAME,\n  password: process.env.DB_PASSWORD,\n  database: process.env.DB_NAME,\n\n  // Entity configuration\n  entities: [User, Post],\n  // Or use glob pattern: entities: [\"src/entities/**/*.ts\"]\n\n  // Migrations\n  migrations: ['src/migrations/**/*.ts'],\n\n  // Synchronize - NEVER use in production\n  synchronize: false,\n\n  // Logging\n  logging: process.env.NODE_ENV === 'development',\n\n  // Connection pool\n  poolSize: 10,\n\n  // SSL (for production)\n  ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,\n});\n\n// Initialize connection\nAppDataSource.initialize()\n  .then(() => console.log('Data Source initialized'))\n  .catch(error => console.error('Error initializing Data Source:', error));\n```\n\n## Migrations\n\n### Creating Migrations\n\n```bash\n# Generate migration from entity changes\nnpx typeorm migration:generate src/migrations/CreateUsers -d src/data-source.ts\n\n# Create empty migration\nnpx typeorm migration:create src/migrations/SeedUsers\n\n# Run migrations\nnpx typeorm migration:run -d src/data-source.ts\n\n# Revert last migration\nnpx typeorm migration:revert -d src/data-source.ts\n```\n\n### Migration File Structure\n\n```typescript\nimport { MigrationInterface, QueryRunner, Table, TableIndex } from 'typeorm';\n\nexport class CreateUsers1234567890 implements MigrationInterface {\n  public async up(queryRunner: QueryRunner): Promise<void> {\n    await queryRunner.createTable(\n      new Table({\n        name: 'users',\n        columns: [\n          {\n            name: 'id',\n            type: 'int',\n            isPrimary: true,\n            isGenerated: true,\n            generationStrategy: 'increment',\n          },\n          {\n            name: 'email',\n            type: 'varchar',\n            length: '255',\n            isUnique: true,\n          },\n          {\n            name: 'name',\n            type: 'varchar',\n            length: '255',\n            isNullable: true,\n          },\n          {\n            name: 'is_active',\n            type: 'boolean',\n            default: true,\n          },\n          {\n            name: 'created_at',\n            type: 'timestamp',\n            default: 'CURRENT_TIMESTAMP',\n          },\n          {\n            name: 'updated_at',\n            type: 'timestamp',\n            default: 'CURRENT_TIMESTAMP',\n            onUpdate: 'CURRENT_TIMESTAMP',\n          },\n        ],\n      }),\n      true,\n    );\n\n    await queryRunner.createIndex(\n      'users',\n      new TableIndex({\n        name: 'IDX_USERS_EMAIL',\n        columnNames: ['email'],\n      }),\n    );\n  }\n\n  public async down(queryRunner: QueryRunner): Promise<void> {\n    await queryRunner.dropIndex('users', 'IDX_USERS_EMAIL');\n    await queryRunner.dropTable('users');\n  }\n}\n```\n\n## Transactions\n\n```typescript\n// Using QueryRunner\nconst queryRunner = AppDataSource.createQueryRunner();\nawait queryRunner.connect();\nawait queryRunner.startTransaction();\n\ntry {\n  const user = queryRunner.manager.create(User, {\n    email: 'user@example.com',\n    name: 'User',\n  });\n  await queryRunner.manager.save(user);\n\n  const post = queryRunner.manager.create(Post, {\n    title: 'First Post',\n    author: user,\n  });\n  await queryRunner.manager.save(post);\n\n  await queryRunner.commitTransaction();\n} catch (error) {\n  await queryRunner.rollbackTransaction();\n  throw error;\n} finally {\n  await queryRunner.release();\n}\n\n// Using transaction method\nawait AppDataSource.transaction(async manager => {\n  const user = manager.create(User, {\n    email: 'user@example.com',\n    name: 'User',\n  });\n  await manager.save(user);\n\n  const post = manager.create(Post, {\n    title: 'First Post',\n    author: user,\n  });\n  await manager.save(post);\n});\n```\n\n## NestJS Integration\n\n```typescript\n// app.module.ts\nimport { Module } from '@nestjs/common';\nimport { TypeOrmModule } from '@nestjs/typeorm';\nimport { User } from './entities/user.entity';\nimport { UsersModule } from './users/users.module';\n\n@Module({\n  imports: [\n    TypeOrmModule.forRoot({\n      type: 'postgres',\n      host: 'localhost',\n      port: 5432,\n      username: 'user',\n      password: 'password',\n      database: 'db',\n      entities: [User],\n      synchronize: false,\n    }),\n    UsersModule,\n  ],\n})\nexport class AppModule {}\n\n// users/users.module.ts\n@Module({\n  imports: [TypeOrmModule.forFeature([User])],\n  providers: [UsersService],\n  controllers: [UsersController],\n})\nexport class UsersModule {}\n\n// users/users.service.ts\n@Injectable()\nexport class UsersService {\n  constructor(\n    @InjectRepository(User)\n    private usersRepository: Repository<User>,\n  ) {}\n\n  findAll(): Promise<User[]> {\n    return this.usersRepository.find();\n  }\n\n  findOne(id: number): Promise<User | null> {\n    return this.usersRepository.findOneBy({ id });\n  }\n}\n```\n\n## Best Practices\n\n### Use Migrations in Production\n\nNever use `synchronize: true` in production. Always use migrations:\n\n```typescript\n// Development: Use migrations, not sync\nsynchronize: false,\n```\n\n### Eager vs Lazy Loading\n\n```typescript\n// Eager loading - loads relations automatically\n@OneToMany(() => Post, (post) => post.author, { eager: true })\nposts: Post[];\n\n// Lazy loading - loads relations on access\n@OneToMany(() => Post, (post) => post.author)\nposts: Promise<Post[]>;\n\n// Explicit loading (recommended)\nconst user = await userRepository.findOne({\n  where: { id: 1 },\n  relations: [\"posts\"],\n});\n```\n\n### Avoid N+1 Queries\n\n```typescript\n// Bad: N+1 queries\nconst users = await userRepository.find();\nfor (const user of users) {\n  console.log(user.posts); // Separate query for each user\n}\n\n// Good: Eager load relations\nconst users = await userRepository.find({\n  relations: ['posts'],\n});\n```\n\n### Use Indexes\n\n```typescript\n@Entity()\n@Index(['email'])\n@Index(['firstName', 'lastName'])\nexport class User {\n  @Column()\n  @Index()\n  email: string;\n\n  @Column()\n  firstName: string;\n\n  @Column()\n  lastName: string;\n}\n```\n\n### Cascade Operations\n\n```typescript\n@OneToMany(() => Post, (post) => post.author, {\n  cascade: true, // Saves/removes related posts\n  onDelete: \"CASCADE\", // Database-level cascade\n})\nposts: Post[];\n```\n\n### Naming Strategies\n\nFor consistent naming between TypeScript and database:\n\n```typescript\nimport { DefaultNamingStrategy, NamingStrategyInterface } from \"typeorm\";\nimport { snakeCase } from \"typeorm/util/StringUtils\";\n\nexport class SnakeNamingStrategy extends DefaultNamingStrategy implements NamingStrategyInterface {\n  tableName(targetName: string, userSpecifiedName: string | undefined): string {\n    return userSpecifiedName ? userSpecifiedName : snakeCase(targetName);\n  }\n\n  columnName(propertyName: string, customName: string, embeddedPrefixes: string[]): string {\n    return snakeCase(embeddedPrefixes.join(\"_\")) + (customName ? customName : snakeCase(propertyName));\n  }\n}\n\n// Use in data source config\nnamingStrategy: new SnakeNamingStrategy(),\n```",".agents/skills/typescript-advanced-types/SKILL.md":"---\nname: typescript-advanced-types\ndescription: Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects.\n---\n\n# TypeScript Advanced Types\n\nComprehensive guidance for mastering TypeScript's advanced type system including generics, conditional types, mapped types, template literal types, and utility types for building robust, type-safe applications.\n\n## When to Use This Skill\n\n- Building type-safe libraries or frameworks\n- Creating reusable generic components\n- Implementing complex type inference logic\n- Designing type-safe API clients\n- Building form validation systems\n- Creating strongly-typed configuration objects\n- Implementing type-safe state management\n- Migrating JavaScript codebases to TypeScript\n\n## Core Concepts\n\n### 1. Generics\n\n**Purpose:** Create reusable, type-flexible components while maintaining type safety.\n\n**Basic Generic Function:**\n\n```typescript\nfunction identity<T>(value: T): T {\n  return value;\n}\n\nconst num = identity<number>(42); // Type: number\nconst str = identity<string>('hello'); // Type: string\nconst auto = identity(true); // Type inferred: boolean\n```\n\n**Generic Constraints:**\n\n```typescript\ninterface HasLength {\n  length: number;\n}\n\nfunction logLength<T extends HasLength>(item: T): T {\n  console.log(item.length);\n  return item;\n}\n\nlogLength('hello'); // OK: string has length\nlogLength([1, 2, 3]); // OK: array has length\nlogLength({ length: 10 }); // OK: object has length\n// logLength(42);             // Error: number has no length\n```\n\n**Multiple Type Parameters:**\n\n```typescript\nfunction merge<T, U>(obj1: T, obj2: U): T & U {\n  return { ...obj1, ...obj2 };\n}\n\nconst merged = merge({ name: 'John' }, { age: 30 });\n// Type: { name: string } & { age: number }\n```\n\n### 2. Conditional Types\n\n**Purpose:** Create types that depend on conditions, enabling sophisticated type logic.\n\n**Basic Conditional Type:**\n\n```typescript\ntype IsString<T> = T extends string ? true : false;\n\ntype A = IsString<string>; // true\ntype B = IsString<number>; // false\n```\n\n**Extracting Return Types:**\n\n```typescript\ntype ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;\n\nfunction getUser() {\n  return { id: 1, name: 'John' };\n}\n\ntype User = ReturnType<typeof getUser>;\n// Type: { id: number; name: string; }\n```\n\n**Distributive Conditional Types:**\n\n```typescript\ntype ToArray<T> = T extends any ? T[] : never;\n\ntype StrOrNumArray = ToArray<string | number>;\n// Type: string[] | number[]\n```\n\n**Nested Conditions:**\n\n```typescript\ntype TypeName<T> = T extends string\n  ? 'string'\n  : T extends number\n    ? 'number'\n    : T extends boolean\n      ? 'boolean'\n      : T extends undefined\n        ? 'undefined'\n        : T extends Function\n          ? 'function'\n          : 'object';\n\ntype T1 = TypeName<string>; // \"string\"\ntype T2 = TypeName<() => void>; // \"function\"\n```\n\n### 3. Mapped Types\n\n**Purpose:** Transform existing types by iterating over their properties.\n\n**Basic Mapped Type:**\n\n```typescript\ntype Readonly<T> = {\n  readonly [P in keyof T]: T[P];\n};\n\ninterface User {\n  id: number;\n  name: string;\n}\n\ntype ReadonlyUser = Readonly<User>;\n// Type: { readonly id: number; readonly name: string; }\n```\n\n**Optional Properties:**\n\n```typescript\ntype Partial<T> = {\n  [P in keyof T]?: T[P];\n};\n\ntype PartialUser = Partial<User>;\n// Type: { id?: number; name?: string; }\n```\n\n**Key Remapping:**\n\n```typescript\ntype Getters<T> = {\n  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];\n};\n\ninterface Person {\n  name: string;\n  age: number;\n}\n\ntype PersonGetters = Getters<Person>;\n// Type: { getName: () => string; getAge: () => number; }\n```\n\n**Filtering Properties:**\n\n```typescript\ntype PickByType<T, U> = {\n  [K in keyof T as T[K] extends U ? K : never]: T[K];\n};\n\ninterface Mixed {\n  id: number;\n  name: string;\n  age: number;\n  active: boolean;\n}\n\ntype OnlyNumbers = PickByType<Mixed, number>;\n// Type: { id: number; age: number; }\n```\n\n### 4. Template Literal Types\n\n**Purpose:** Create string-based types with pattern matching and transformation.\n\n**Basic Template Literal:**\n\n```typescript\ntype EventName = 'click' | 'focus' | 'blur';\ntype EventHandler = `on${Capitalize<EventName>}`;\n// Type: \"onClick\" | \"onFocus\" | \"onBlur\"\n```\n\n**String Manipulation:**\n\n```typescript\ntype UppercaseGreeting = Uppercase<'hello'>; // \"HELLO\"\ntype LowercaseGreeting = Lowercase<'HELLO'>; // \"hello\"\ntype CapitalizedName = Capitalize<'john'>; // \"John\"\ntype UncapitalizedName = Uncapitalize<'John'>; // \"john\"\n```\n\n**Path Building:**\n\n```typescript\ntype Path<T> = T extends object\n  ? {\n      [K in keyof T]: K extends string ? `${K}` | `${K}.${Path<T[K]>}` : never;\n    }[keyof T]\n  : never;\n\ninterface Config {\n  server: {\n    host: string;\n    port: number;\n  };\n  database: {\n    url: string;\n  };\n}\n\ntype ConfigPath = Path<Config>;\n// Type: \"server\" | \"database\" | \"server.host\" | \"server.port\" | \"database.url\"\n```\n\n### 5. Utility Types\n\n**Built-in Utility Types:**\n\n```typescript\n// Partial<T> - Make all properties optional\ntype PartialUser = Partial<User>;\n\n// Required<T> - Make all properties required\ntype RequiredUser = Required<PartialUser>;\n\n// Readonly<T> - Make all properties readonly\ntype ReadonlyUser = Readonly<User>;\n\n// Pick<T, K> - Select specific properties\ntype UserName = Pick<User, 'name' | 'email'>;\n\n// Omit<T, K> - Remove specific properties\ntype UserWithoutPassword = Omit<User, 'password'>;\n\n// Exclude<T, U> - Exclude types from union\ntype T1 = Exclude<'a' | 'b' | 'c', 'a'>; // \"b\" | \"c\"\n\n// Extract<T, U> - Extract types from union\ntype T2 = Extract<'a' | 'b' | 'c', 'a' | 'b'>; // \"a\" | \"b\"\n\n// NonNullable<T> - Exclude null and undefined\ntype T3 = NonNullable<string | null | undefined>; // string\n\n// Record<K, T> - Create object type with keys K and values T\ntype PageInfo = Record<'home' | 'about', { title: string }>;\n```\n\n## Advanced Patterns\n\n### Pattern 1: Type-Safe Event Emitter\n\n```typescript\ntype EventMap = {\n  'user:created': { id: string; name: string };\n  'user:updated': { id: string };\n  'user:deleted': { id: string };\n};\n\nclass TypedEventEmitter<T extends Record<string, any>> {\n  private listeners: {\n    [K in keyof T]?: Array<(data: T[K]) => void>;\n  } = {};\n\n  on<K extends keyof T>(event: K, callback: (data: T[K]) => void): void {\n    if (!this.listeners[event]) {\n      this.listeners[event] = [];\n    }\n    this.listeners[event]!.push(callback);\n  }\n\n  emit<K extends keyof T>(event: K, data: T[K]): void {\n    const callbacks = this.listeners[event];\n    if (callbacks) {\n      callbacks.forEach(callback => callback(data));\n    }\n  }\n}\n\nconst emitter = new TypedEventEmitter<EventMap>();\n\nemitter.on('user:created', data => {\n  console.log(data.id, data.name); // Type-safe!\n});\n\nemitter.emit('user:created', { id: '1', name: 'John' });\n// emitter.emit(\"user:created\", { id: \"1\" });  // Error: missing 'name'\n```\n\n### Pattern 2: Type-Safe API Client\n\n```typescript\ntype HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';\n\ntype EndpointConfig = {\n  '/users': {\n    GET: { response: User[] };\n    POST: { body: { name: string; email: string }; response: User };\n  };\n  '/users/:id': {\n    GET: { params: { id: string }; response: User };\n    PUT: { params: { id: string }; body: Partial<User>; response: User };\n    DELETE: { params: { id: string }; response: void };\n  };\n};\n\ntype ExtractParams<T> = T extends { params: infer P } ? P : never;\ntype ExtractBody<T> = T extends { body: infer B } ? B : never;\ntype ExtractResponse<T> = T extends { response: infer R } ? R : never;\n\nclass APIClient<Config extends Record<string, Record<HTTPMethod, any>>> {\n  async request<Path extends keyof Config, Method extends keyof Config[Path]>(\n    path: Path,\n    method: Method,\n    ...[options]: ExtractParams<Config[Path][Method]> extends never\n      ? ExtractBody<Config[Path][Method]> extends never\n        ? []\n        : [{ body: ExtractBody<Config[Path][Method]> }]\n      : [\n          {\n            params: ExtractParams<Config[Path][Method]>;\n            body?: ExtractBody<Config[Path][Method]>;\n          },\n        ]\n  ): Promise<ExtractResponse<Config[Path][Method]>> {\n    // Implementation here\n    return {} as any;\n  }\n}\n\nconst api = new APIClient<EndpointConfig>();\n\n// Type-safe API calls\nconst users = await api.request('/users', 'GET');\n// Type: User[]\n\nconst newUser = await api.request('/users', 'POST', {\n  body: { name: 'John', email: 'john@example.com' },\n});\n// Type: User\n\nconst user = await api.request('/users/:id', 'GET', {\n  params: { id: '123' },\n});\n// Type: User\n```\n\n### Pattern 3: Builder Pattern with Type Safety\n\n```typescript\ntype BuilderState<T> = {\n  [K in keyof T]: T[K] | undefined;\n};\n\ntype RequiredKeys<T> = {\n  [K in keyof T]-?: {} extends Pick<T, K> ? never : K;\n}[keyof T];\n\ntype OptionalKeys<T> = {\n  [K in keyof T]-?: {} extends Pick<T, K> ? K : never;\n}[keyof T];\n\ntype IsComplete<T, S> = RequiredKeys<T> extends keyof S ? (S[RequiredKeys<T>] extends undefined ? false : true) : false;\n\nclass Builder<T, S extends BuilderState<T> = {}> {\n  private state: S = {} as S;\n\n  set<K extends keyof T>(key: K, value: T[K]): Builder<T, S & Record<K, T[K]>> {\n    this.state[key] = value;\n    return this as any;\n  }\n\n  build(this: IsComplete<T, S> extends true ? this : never): T {\n    return this.state as T;\n  }\n}\n\ninterface User {\n  id: string;\n  name: string;\n  email: string;\n  age?: number;\n}\n\nconst builder = new Builder<User>();\n\nconst user = builder.set('id', '1').set('name', 'John').set('email', 'john@example.com').build(); // OK: all required fields set\n\n// const incomplete = builder\n//   .set(\"id\", \"1\")\n//   .build();  // Error: missing required fields\n```\n\n### Pattern 4: Deep Readonly/Partial\n\n```typescript\ntype DeepReadonly<T> = {\n  readonly [P in keyof T]: T[P] extends object ? (T[P] extends Function ? T[P] : DeepReadonly<T[P]>) : T[P];\n};\n\ntype DeepPartial<T> = {\n  [P in keyof T]?: T[P] extends object\n    ? T[P] extends Array<infer U>\n      ? Array<DeepPartial<U>>\n      : DeepPartial<T[P]>\n    : T[P];\n};\n\ninterface Config {\n  server: {\n    host: string;\n    port: number;\n    ssl: {\n      enabled: boolean;\n      cert: string;\n    };\n  };\n  database: {\n    url: string;\n    pool: {\n      min: number;\n      max: number;\n    };\n  };\n}\n\ntype ReadonlyConfig = DeepReadonly<Config>;\n// All nested properties are readonly\n\ntype PartialConfig = DeepPartial<Config>;\n// All nested properties are optional\n```\n\n### Pattern 5: Type-Safe Form Validation\n\n```typescript\ntype ValidationRule<T> = {\n  validate: (value: T) => boolean;\n  message: string;\n};\n\ntype FieldValidation<T> = {\n  [K in keyof T]?: ValidationRule<T[K]>[];\n};\n\ntype ValidationErrors<T> = {\n  [K in keyof T]?: string[];\n};\n\nclass FormValidator<T extends Record<string, any>> {\n  constructor(private rules: FieldValidation<T>) {}\n\n  validate(data: T): ValidationErrors<T> | null {\n    const errors: ValidationErrors<T> = {};\n    let hasErrors = false;\n\n    for (const key in this.rules) {\n      const fieldRules = this.rules[key];\n      const value = data[key];\n\n      if (fieldRules) {\n        const fieldErrors: string[] = [];\n\n        for (const rule of fieldRules) {\n          if (!rule.validate(value)) {\n            fieldErrors.push(rule.message);\n          }\n        }\n\n        if (fieldErrors.length > 0) {\n          errors[key] = fieldErrors;\n          hasErrors = true;\n        }\n      }\n    }\n\n    return hasErrors ? errors : null;\n  }\n}\n\ninterface LoginForm {\n  email: string;\n  password: string;\n}\n\nconst validator = new FormValidator<LoginForm>({\n  email: [\n    {\n      validate: v => v.includes('@'),\n      message: 'Email must contain @',\n    },\n    {\n      validate: v => v.length > 0,\n      message: 'Email is required',\n    },\n  ],\n  password: [\n    {\n      validate: v => v.length >= 8,\n      message: 'Password must be at least 8 characters',\n    },\n  ],\n});\n\nconst errors = validator.validate({\n  email: 'invalid',\n  password: 'short',\n});\n// Type: { email?: string[]; password?: string[]; } | null\n```\n\n### Pattern 6: Discriminated Unions\n\n```typescript\ntype Success<T> = {\n  status: 'success';\n  data: T;\n};\n\ntype Error = {\n  status: 'error';\n  error: string;\n};\n\ntype Loading = {\n  status: 'loading';\n};\n\ntype AsyncState<T> = Success<T> | Error | Loading;\n\nfunction handleState<T>(state: AsyncState<T>): void {\n  switch (state.status) {\n    case 'success':\n      console.log(state.data); // Type: T\n      break;\n    case 'error':\n      console.log(state.error); // Type: string\n      break;\n    case 'loading':\n      console.log('Loading...');\n      break;\n  }\n}\n\n// Type-safe state machine\ntype State =\n  | { type: 'idle' }\n  | { type: 'fetching'; requestId: string }\n  | { type: 'success'; data: any }\n  | { type: 'error'; error: Error };\n\ntype Event =\n  | { type: 'FETCH'; requestId: string }\n  | { type: 'SUCCESS'; data: any }\n  | { type: 'ERROR'; error: Error }\n  | { type: 'RESET' };\n\nfunction reducer(state: State, event: Event): State {\n  switch (state.type) {\n    case 'idle':\n      return event.type === 'FETCH' ? { type: 'fetching', requestId: event.requestId } : state;\n    case 'fetching':\n      if (event.type === 'SUCCESS') {\n        return { type: 'success', data: event.data };\n      }\n      if (event.type === 'ERROR') {\n        return { type: 'error', error: event.error };\n      }\n      return state;\n    case 'success':\n    case 'error':\n      return event.type === 'RESET' ? { type: 'idle' } : state;\n  }\n}\n```\n\n## Type Inference Techniques\n\n### 1. Infer Keyword\n\n```typescript\n// Extract array element type\ntype ElementType<T> = T extends (infer U)[] ? U : never;\n\ntype NumArray = number[];\ntype Num = ElementType<NumArray>; // number\n\n// Extract promise type\ntype PromiseType<T> = T extends Promise<infer U> ? U : never;\n\ntype AsyncNum = PromiseType<Promise<number>>; // number\n\n// Extract function parameters\ntype Parameters<T> = T extends (...args: infer P) => any ? P : never;\n\nfunction foo(a: string, b: number) {}\ntype FooParams = Parameters<typeof foo>; // [string, number]\n```\n\n### 2. Type Guards\n\n```typescript\nfunction isString(value: unknown): value is string {\n  return typeof value === 'string';\n}\n\nfunction isArrayOf<T>(value: unknown, guard: (item: unknown) => item is T): value is T[] {\n  return Array.isArray(value) && value.every(guard);\n}\n\nconst data: unknown = ['a', 'b', 'c'];\n\nif (isArrayOf(data, isString)) {\n  data.forEach(s => s.toUpperCase()); // Type: string[]\n}\n```\n\n### 3. Assertion Functions\n\n```typescript\nfunction assertIsString(value: unknown): asserts value is string {\n  if (typeof value !== 'string') {\n    throw new Error('Not a string');\n  }\n}\n\nfunction processValue(value: unknown) {\n  assertIsString(value);\n  // value is now typed as string\n  console.log(value.toUpperCase());\n}\n```\n\n## Best Practices\n\n1. **Use `unknown` over `any`**: Enforce type checking\n2. **Prefer `interface` for object shapes**: Better error messages\n3. **Use `type` for unions and complex types**: More flexible\n4. **Leverage type inference**: Let TypeScript infer when possible\n5. **Create helper types**: Build reusable type utilities\n6. **Use const assertions**: Preserve literal types\n7. **Avoid type assertions**: Use type guards instead\n8. **Document complex types**: Add JSDoc comments\n9. **Use strict mode**: Enable all strict compiler options\n10. **Test your types**: Use type tests to verify type behavior\n\n## Type Testing\n\n```typescript\n// Type assertion tests\ntype AssertEqual<T, U> = [T] extends [U] ? ([U] extends [T] ? true : false) : false;\n\ntype Test1 = AssertEqual<string, string>; // true\ntype Test2 = AssertEqual<string, number>; // false\ntype Test3 = AssertEqual<string | number, string>; // false\n\n// Expect error helper\ntype ExpectError<T extends never> = T;\n\n// Example usage\ntype ShouldError = ExpectError<AssertEqual<string, number>>;\n```\n\n## Common Pitfalls\n\n1. **Over-using `any`**: Defeats the purpose of TypeScript\n2. **Ignoring strict null checks**: Can lead to runtime errors\n3. **Too complex types**: Can slow down compilation\n4. **Not using discriminated unions**: Misses type narrowing opportunities\n5. **Forgetting readonly modifiers**: Allows unintended mutations\n6. **Circular type references**: Can cause compiler errors\n7. **Not handling edge cases**: Like empty arrays or null values\n\n## Performance Considerations\n\n- Avoid deeply nested conditional types\n- Use simple types when possible\n- Cache complex type computations\n- Limit recursion depth in recursive types\n- Use build tools to skip type checking in production\n\n## Resources\n\n- **TypeScript Handbook**: https://www.typescriptlang.org/docs/handbook/\n- **Type Challenges**: https://github.com/type-challenges/type-challenges\n- **TypeScript Deep Dive**: https://basarat.gitbook.io/typescript/\n- **Effective TypeScript**: Book by Dan Vanderkam",".github/copilot-instructions.md":"Use instructions from `AGENTS.md` to guide your work.","AGENTS.md":"# LLM Agents Instructions\n\n## General\n\n- Read `README.md` first for project overview and architecture\n- Read `CONTRIBUTING.md` for local development setup, commands, commit messages, and PR conventions\n- Read `GUIDELINES.md` for code style and testing patterns\n- Read `DOMAIN.md` for application domain and data models\n- Read all `*/README.md` files for workspace specific architecture and development guidelines\n\n## Instructions\n\n- Use the `tmp` folder in the repository to store temporary files during execution and planning if needed\n- Never run deploy commands\n- Run `npm run lint`, `npm run test`, `npm run compile`, and `npm run format` at the end of each completed task if you modified any files. If any of these steps fail, fix the issues and run the commands again.\n- Prefer telegraph style for communications and reasoning. Sacrifice grammar and punctuation for clarity and conciseness. Save tokens.\n\n## Documentation Style\n\nUse the following writing style for documentation, comments and README files.\n\nAudience: Software Engineers. Expect half to be native speakers and half non-native speakers.\n\n- Keep the reading level at or below 9th grade (Flesch-Kincaid)\n- Write in clear, concise, active voice; don't assume passive voice reads well\n- Keep noun phrases short and avoid stacked modifiers\n- Limit embedded clauses to one level\n- Use conditionals sparingly and stick to simple if/then forms; avoid mixed or inverted versions\n- Use transition words (however, therefore, because) to show logic instead of expecting readers to infer it\n- Spread new ideas across sentences; avoid packing many concepts into one\n- Do not end list items with a period\n- Do not use emojis\n- Do not write obvious comments; explain why, not how or what\n\n## Commands\n\n- `npm run lint`: Run linting\n- `npm run test`: Run tests\n- `npm run compile`: Compile the project\n- `npm run format`: Format the code","tools/sloths/README.md":"# rs-sloths\n\nRS SLOTHS is a depository of sloths. View all existing stickers, download the necessary ones or chill a bit - why not? Feel a sloth!\n\n## Stack\n\nVue3, Pinia, Vue Router, TypeScript, Vite\n\n## Project Setup\n\n```sh\nnpm install\n```\n\n### Compile and Hot-Reload for Development\n\n```sh\nnpm run dev\n```\n\n### Type-Check, Compile and Minify for Production\n\n```sh\nnpm run build\n```\n\n### Lint with [ESLint](https://eslint.org/)\n\n```sh\nnpm run lint\n```"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# LLM Agents Instructions\n\n## General\n\n- Read `README.md` first for project overview and architecture\n- Read `CONTRIBUTING.md` for local development setup, commands, commit messages, and PR conventions\n- Read `GUIDELINES.md` for code style and testing patterns\n- Read `DOMAIN.md` for application domain and data models\n- Read all `*/README.md` files for workspace specific architecture and development guidelines\n\n## Instructions\n\n- Use the `tmp` folder in the repository to store temporary files during execution and planning if needed\n- Never run deploy commands\n- Run `npm run lint`, `npm run test`, `npm run compile`, and `npm run format` at the end of each completed task if you modified any files. If any of these steps fail, fix the issues and run the commands again.\n- Prefer telegraph style for communications and reasoning. Sacrifice grammar and punctuation for clarity and conciseness. Save tokens.\n\n## Documentation Style\n\nUse the following writing style for documentation, comments and README files.\n\nAudience: Software Engineers. Expect half to be native speakers and half non-native speakers.\n\n- Keep the reading level at or below 9th grade (Flesch-Kincaid)\n- Write in clear, concise, active voice; don't assume passive voice reads well\n- Keep noun phrases short and avoid stacked modifiers\n- Limit embedded clauses to one level\n- Use conditionals sparingly and stick to simple if/then forms; avoid mixed or inverted versions\n- Use transition words (however, therefore, because) to show logic instead of expecting readers to infer it\n- Spread new ideas across sentences; avoid packing many concepts into one\n- Do not end list items with a period\n- Do not use emojis\n- Do not write obvious comments; explain why, not how or what\n\n## Commands\n\n- `npm run lint`: Run linting\n- `npm run test`: Run tests\n- `npm run compile`: Compile the project\n- `npm run format`: Format the code","isInternal":false,"tokens":418,"sizeBytes":1891},{"name":"copilot-instructions.md","path":".github/copilot-instructions.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.github/copilot-instructions.md","title":"GitHub Copilot Instructions","category":"copilot-instructions","format":"markdown","content":"Use instructions from `AGENTS.md` to guide your work.","isInternal":false,"tokens":13,"sizeBytes":53},{"name":"AGENTS.md","path":".agents/skills/nestjs-best-practices/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/AGENTS.md","title":"Nestjs-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# NestJS Best Practices\n\n**Version 1.1.0**\nNestJS Best Practices\nJanuary 2026\n\n> **Note:**\n> This document is mainly for agents and LLMs to follow when maintaining,\n> generating, or refactoring NestJS codebases. Humans may also find it\n> useful, but guidance here is optimized for automation and consistency\n> by AI-assisted workflows.\n\n---\n\n## Abstract\n\nComprehensive best practices and architecture guide for NestJS applications, designed for AI agents and LLMs. Contains 40 rules across 10 categories, prioritized by impact from critical (architecture, dependency injection) to incremental (DevOps patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.\n\n---\n\n## Table of Contents\n\n1. [Architecture](#1-architecture) — **CRITICAL**\n   - 1.1 [Avoid Circular Dependencies](#11-avoid-circular-dependencies)\n   - 1.2 [Organize by Feature Modules](#12-organize-by-feature-modules)\n   - 1.3 [Use Proper Module Sharing Patterns](#13-use-proper-module-sharing-patterns)\n   - 1.4 [Single Responsibility for Services](#14-single-responsibility-for-services)\n   - 1.5 [Use Event-Driven Architecture for Decoupling](#15-use-event-driven-architecture-for-decoupling)\n   - 1.6 [Use Repository Pattern for Data Access](#16-use-repository-pattern-for-data-access)\n2. [Dependency Injection](#2-dependency-injection) — **CRITICAL**\n   - 2.1 [Avoid Service Locator Anti-Pattern](#21-avoid-service-locator-anti-pattern)\n   - 2.2 [Apply Interface Segregation Principle](#22-apply-interface-segregation-principle)\n   - 2.3 [Honor Liskov Substitution Principle](#23-honor-liskov-substitution-principle)\n   - 2.4 [Prefer Constructor Injection](#24-prefer-constructor-injection)\n   - 2.5 [Understand Provider Scopes](#25-understand-provider-scopes)\n   - 2.6 [Use Injection Tokens for Interfaces](#26-use-injection-tokens-for-interfaces)\n3. [Error Handling](#3-error-handling) — **HIGH**\n   - 3.1 [Handle Async Errors Properly](#31-handle-async-errors-properly)\n   - 3.2 [Throw HTTP Exceptions from Services](#32-throw-http-exceptions-from-services)\n   - 3.3 [Use Exception Filters for Error Handling](#33-use-exception-filters-for-error-handling)\n4. [Security](#4-security) — **HIGH**\n   - 4.1 [Implement Secure JWT Authentication](#41-implement-secure-jwt-authentication)\n   - 4.2 [Implement Rate Limiting](#42-implement-rate-limiting)\n   - 4.3 [Sanitize Output to Prevent XSS](#43-sanitize-output-to-prevent-xss)\n   - 4.4 [Use Guards for Authentication and Authorization](#44-use-guards-for-authentication-and-authorization)\n   - 4.5 [Validate All Input with DTOs and Pipes](#45-validate-all-input-with-dtos-and-pipes)\n5. [Performance](#5-performance) — **HIGH**\n   - 5.1 [Use Async Lifecycle Hooks Correctly](#51-use-async-lifecycle-hooks-correctly)\n   - 5.2 [Use Lazy Loading for Large Modules](#52-use-lazy-loading-for-large-modules)\n   - 5.3 [Optimize Database Queries](#53-optimize-database-queries)\n   - 5.4 [Use Caching Strategically](#54-use-caching-strategically)\n6. [Testing](#6-testing) — **MEDIUM-HIGH**\n   - 6.1 [Use Supertest for E2E Testing](#61-use-supertest-for-e2e-testing)\n   - 6.2 [Mock External Services in Tests](#62-mock-external-services-in-tests)\n   - 6.3 [Use Testing Module for Unit Tests](#63-use-testing-module-for-unit-tests)\n7. [Database & ORM](#7-database-orm) — **MEDIUM-HIGH**\n   - 7.1 [Avoid N+1 Query Problems](#71-avoid-n-1-query-problems)\n   - 7.2 [Use Database Migrations](#72-use-database-migrations)\n   - 7.3 [Use Transactions for Multi-Step Operations](#73-use-transactions-for-multi-step-operations)\n8. [API Design](#8-api-design) — **MEDIUM**\n   - 8.1 [Use DTOs and Serialization for API Responses](#81-use-dtos-and-serialization-for-api-responses)\n   - 8.2 [Use Interceptors for Cross-Cutting Concerns](#82-use-interceptors-for-cross-cutting-concerns)\n   - 8.3 [Use Pipes for Input Transformation](#83-use-pipes-for-input-transformation)\n   - 8.4 [Use API Versioning for Breaking Changes](#84-use-api-versioning-for-breaking-changes)\n9. [Microservices](#9-microservices) — **MEDIUM**\n   - 9.1 [Implement Health Checks for Microservices](#91-implement-health-checks-for-microservices)\n   - 9.2 [Use Message and Event Patterns Correctly](#92-use-message-and-event-patterns-correctly)\n   - 9.3 [Use Message Queues for Background Jobs](#93-use-message-queues-for-background-jobs)\n10. [DevOps & Deployment](#10-devops-deployment) — **LOW-MEDIUM**\n\n- 10.1 [Implement Graceful Shutdown](#101-implement-graceful-shutdown)\n- 10.2 [Use ConfigModule for Environment Configuration](#102-use-configmodule-for-environment-configuration)\n- 10.3 [Use Structured Logging](#103-use-structured-logging)\n\n---\n\n## 1. Architecture\n\n**Section Impact: CRITICAL**\n\n### 1.1 Avoid Circular Dependencies\n\n**Impact: CRITICAL** — \"#1 cause of runtime crashes\"\n\nCircular dependencies occur when Module A imports Module B, and Module B imports Module A (directly or transitively). NestJS can sometimes resolve these through forward references, but they indicate architectural problems and should be avoided. This is the #1 cause of runtime crashes in NestJS applications.\n\n**Incorrect (circular module imports):**\n\n```typescript\n// users.module.ts\n@Module({\n  imports: [OrdersModule], // Orders needs Users, Users needs Orders = circular\n  providers: [UsersService],\n  exports: [UsersService],\n})\nexport class UsersModule {}\n\n// orders.module.ts\n@Module({\n  imports: [UsersModule], // Circular dependency!\n  providers: [OrdersService],\n  exports: [OrdersService],\n})\nexport class OrdersModule {}\n```\n\n**Correct (extract shared logic or use events):**\n\n```typescript\n// Option 1: Extract shared logic to a third module\n// shared.module.ts\n@Module({\n  providers: [SharedService],\n  exports: [SharedService],\n})\nexport class SharedModule {}\n\n// users.module.ts\n@Module({\n  imports: [SharedModule],\n  providers: [UsersService],\n})\nexport class UsersModule {}\n\n// orders.module.ts\n@Module({\n  imports: [SharedModule],\n  providers: [OrdersService],\n})\nexport class OrdersModule {}\n\n// Option 2: Use events for decoupled communication\n// users.service.ts\n@Injectable()\nexport class UsersService {\n  constructor(private eventEmitter: EventEmitter2) {}\n\n  async createUser(data: CreateUserDto) {\n    const user = await this.userRepo.save(data);\n    this.eventEmitter.emit('user.created', user);\n    return user;\n  }\n}\n\n// orders.service.ts\n@Injectable()\nexport class OrdersService {\n  @OnEvent('user.created')\n  handleUserCreated(user: User) {\n    // React to user creation without direct dependency\n  }\n}\n```\n\nReference: [NestJS Circular Dependency](https://docs.nestjs.com/fundamentals/circular-dependency)\n\n---\n\n### 1.2 Organize by Feature Modules\n\n**Impact: CRITICAL** — \"3-5x faster onboarding and development\"\n\nOrganize your application into feature modules that encapsulate related functionality. Each feature module should be self-contained with its own controllers, services, entities, and DTOs. Avoid organizing by technical layer (all controllers together, all services together). This enables 3-5x faster onboarding and feature development.\n\n**Incorrect (technical layer organization):**\n\n```typescript\n// Technical layer organization (anti-pattern)\nsrc/\n├── controllers/\n│   ├── users.controller.ts\n│   ├── orders.controller.ts\n│   └── products.controller.ts\n├── services/\n│   ├── users.service.ts\n│   ├── orders.service.ts\n│   └── products.service.ts\n├── entities/\n│   ├── user.entity.ts\n│   ├── order.entity.ts\n│   └── product.entity.ts\n└── app.module.ts  // Imports everything directly\n```\n\n**Correct (feature module organization):**\n\n```typescript\n// Feature module organization\nsrc/\n├── users/\n│   ├── dto/\n│   │   ├── create-user.dto.ts\n│   │   └── update-user.dto.ts\n│   ├── entities/\n│   │   └── user.entity.ts\n│   ├── users.controller.ts\n│   ├── users.service.ts\n│   ├── users.repository.ts\n│   └── users.module.ts\n├── orders/\n│   ├── dto/\n│   ├── entities/\n│   ├── orders.controller.ts\n│   ├── orders.service.ts\n│   └── orders.module.ts\n├── shared/\n│   ├── guards/\n│   ├── interceptors/\n│   ├── filters/\n│   └── shared.module.ts\n└── app.module.ts\n\n// users.module.ts\n@Module({\n  imports: [TypeOrmModule.forFeature([User])],\n  controllers: [UsersController],\n  providers: [UsersService, UsersRepository],\n  exports: [UsersService], // Only export what others need\n})\nexport class UsersModule {}\n\n// app.module.ts\n@Module({\n  imports: [\n    ConfigModule.forRoot(),\n    TypeOrmModule.forRoot(),\n    UsersModule,\n    OrdersModule,\n    SharedModule,\n  ],\n})\nexport class AppModule {}\n```\n\nReference: [NestJS Modules](https://docs.nestjs.com/modules)\n\n---\n\n### 1.3 Use Proper Module Sharing Patterns\n\n**Impact: CRITICAL** — Prevents duplicate instances, memory leaks, and state inconsistency\n\nNestJS modules are singletons by default. When a service is properly exported from a module and that module is imported elsewhere, the same instance is shared. However, providing a service in multiple modules creates separate instances, leading to memory waste, state inconsistency, and confusing behavior. Always encapsulate services in dedicated modules, export them explicitly, and import the module where needed.\n\n**Incorrect (service provided in multiple modules):**\n\n```typescript\n// StorageService provided directly in multiple modules - WRONG\n// storage.service.ts\n@Injectable()\nexport class StorageService {\n  private cache = new Map(); // Each instance has separate state!\n\n  store(key: string, value: any) {\n    this.cache.set(key, value);\n  }\n}\n\n// app.module.ts\n@Module({\n  providers: [StorageService], // Instance #1\n  controllers: [AppController],\n})\nexport class AppModule {}\n\n// videos.module.ts\n@Module({\n  providers: [StorageService], // Instance #2 - different from AppModule!\n  controllers: [VideosController],\n})\nexport class VideosModule {}\n\n// Problems:\n// 1. Two separate StorageService instances exist\n// 2. cache.set() in VideosModule doesn't affect AppModule's cache\n// 3. Memory wasted on duplicate instances\n// 4. Debugging nightmares when state doesn't sync\n```\n\n**Correct (dedicated module with exports):**\n\n```typescript\n// storage/storage.module.ts\n@Module({\n  providers: [StorageService],\n  exports: [StorageService], // Make available to importers\n})\nexport class StorageModule {}\n\n// videos/videos.module.ts\n@Module({\n  imports: [StorageModule], // Import the module, not the service\n  controllers: [VideosController],\n  providers: [VideosService],\n})\nexport class VideosModule {}\n\n// channels/channels.module.ts\n@Module({\n  imports: [StorageModule], // Same instance shared\n  controllers: [ChannelsController],\n  providers: [ChannelsService],\n})\nexport class ChannelsModule {}\n\n// app.module.ts\n@Module({\n  imports: [\n    StorageModule, // Only if AppModule itself needs StorageService\n    VideosModule,\n    ChannelsModule,\n  ],\n})\nexport class AppModule {}\n\n// Now all modules share the SAME StorageService instance\n```\n\n**When to use @Global() (sparingly):**\n\n```typescript\n// ONLY for truly cross-cutting concerns\n@Global()\n@Module({\n  providers: [ConfigService, LoggerService],\n  exports: [ConfigService, LoggerService],\n})\nexport class CoreModule {}\n\n// Import once in AppModule\n@Module({\n  imports: [CoreModule], // Registered globally, available everywhere\n})\nexport class AppModule {}\n\n// Other modules don't need to import CoreModule\n@Module({\n  controllers: [UsersController],\n  providers: [UsersService], // Can inject ConfigService without importing\n})\nexport class UsersModule {}\n\n// WARNING: Don't make everything global!\n// - Hides dependencies (can't see what a module needs from imports)\n// - Makes testing harder\n// - Reserve for: config, logging, database connections\n```\n\n**Module re-exporting pattern:**\n\n```typescript\n// common.module.ts - shared utilities\n@Module({\n  providers: [DateService, ValidationService],\n  exports: [DateService, ValidationService],\n})\nexport class CommonModule {}\n\n// core.module.ts - re-exports common for convenience\n@Module({\n  imports: [CommonModule, DatabaseModule],\n  exports: [CommonModule, DatabaseModule], // Re-export for consumers\n})\nexport class CoreModule {}\n\n// feature.module.ts - imports CoreModule, gets both\n@Module({\n  imports: [CoreModule], // Gets CommonModule + DatabaseModule\n  controllers: [FeatureController],\n})\nexport class FeatureModule {}\n```\n\nReference: [NestJS Modules](https://docs.nestjs.com/modules#shared-modules)\n\n---\n\n### 1.4 Single Responsibility for Services\n\n**Impact: CRITICAL** — \"40%+ improvement in testability\"\n\nEach service should have a single, well-defined responsibility. Avoid \"god services\" that handle multiple unrelated concerns. If a service name includes \"And\" or handles more than one domain concept, it likely violates single responsibility. This reduces complexity and improves testability by 40%+.\n\n**Incorrect (god service anti-pattern):**\n\n```typescript\n// God service anti-pattern\n@Injectable()\nexport class UserAndOrderService {\n  constructor(\n    private userRepo: UserRepository,\n    private orderRepo: OrderRepository,\n    private mailer: MailService,\n    private payment: PaymentService,\n  ) {}\n\n  async createUser(dto: CreateUserDto) {\n    const user = await this.userRepo.save(dto);\n    await this.mailer.sendWelcome(user);\n    return user;\n  }\n\n  async createOrder(userId: string, dto: CreateOrderDto) {\n    const order = await this.orderRepo.save({ userId, ...dto });\n    await this.payment.charge(order);\n    await this.mailer.sendOrderConfirmation(order);\n    return order;\n  }\n\n  async calculateOrderStats(userId: string) {\n    // Stats logic mixed in\n  }\n\n  async validatePayment(orderId: string) {\n    // Payment logic mixed in\n  }\n}\n```\n\n**Correct (focused services with single responsibility):**\n\n```typescript\n// Focused services with single responsibility\n@Injectable()\nexport class UsersService {\n  constructor(private userRepo: UserRepository) {}\n\n  async create(dto: CreateUserDto): Promise<User> {\n    return this.userRepo.save(dto);\n  }\n\n  async findById(id: string): Promise<User> {\n    return this.userRepo.findOneOrFail({ where: { id } });\n  }\n}\n\n@Injectable()\nexport class OrdersService {\n  constructor(private orderRepo: OrderRepository) {}\n\n  async create(userId: string, dto: CreateOrderDto): Promise<Order> {\n    return this.orderRepo.save({ userId, ...dto });\n  }\n\n  async findByUser(userId: string): Promise<Order[]> {\n    return this.orderRepo.find({ where: { userId } });\n  }\n}\n\n@Injectable()\nexport class OrderStatsService {\n  constructor(private orderRepo: OrderRepository) {}\n\n  async calculateForUser(userId: string): Promise<OrderStats> {\n    // Focused stats calculation\n  }\n}\n\n// Orchestration in controller or dedicated orchestrator\n@Controller('orders')\nexport class OrdersController {\n  constructor(\n    private orders: OrdersService,\n    private payment: PaymentService,\n    private notifications: NotificationService,\n  ) {}\n\n  @Post()\n  async create(@CurrentUser() user: User, @Body() dto: CreateOrderDto) {\n    const order = await this.orders.create(user.id, dto);\n    await this.payment.charge(order);\n    await this.notifications.sendOrderConfirmation(order);\n    return order;\n  }\n}\n```\n\nReference: [NestJS Providers](https://docs.nestjs.com/providers)\n\n---\n\n### 1.5 Use Event-Driven Architecture for Decoupling\n\n**Impact: MEDIUM-HIGH** — Enables async processing and modularity\n\nUse `@nestjs/event-emitter` for intra-service events and message brokers for inter-service communication. Events allow modules to react to changes without direct dependencies, improving modularity and enabling async processing.\n\n**Incorrect (direct service coupling):**\n\n```typescript\n// Direct service coupling\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private inventoryService: InventoryService,\n    private emailService: EmailService,\n    private analyticsService: AnalyticsService,\n    private notificationService: NotificationService,\n    private loyaltyService: LoyaltyService,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Tight coupling - OrdersService knows about all consumers\n    await this.inventoryService.reserve(order.items);\n    await this.emailService.sendConfirmation(order);\n    await this.analyticsService.track('order_created', order);\n    await this.notificationService.push(order.userId, 'Order placed');\n    await this.loyaltyService.addPoints(order.userId, order.total);\n\n    // Adding new behavior requires modifying this service\n    return order;\n  }\n}\n```\n\n**Correct (event-driven decoupling):**\n\n```typescript\n// Use EventEmitter for decoupling\nimport { EventEmitter2 } from '@nestjs/event-emitter';\n\n// Define event\nexport class OrderCreatedEvent {\n  constructor(\n    public readonly orderId: string,\n    public readonly userId: string,\n    public readonly items: OrderItem[],\n    public readonly total: number,\n  ) {}\n}\n\n// Service emits events\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private eventEmitter: EventEmitter2,\n    private repo: Repository<Order>,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Emit event - no knowledge of consumers\n    this.eventEmitter.emit('order.created', new OrderCreatedEvent(order.id, order.userId, order.items, order.total));\n\n    return order;\n  }\n}\n\n// Listeners in separate modules\n@Injectable()\nexport class InventoryListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.inventoryService.reserve(event.items);\n  }\n}\n\n@Injectable()\nexport class EmailListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.emailService.sendConfirmation(event.orderId);\n  }\n}\n\n@Injectable()\nexport class AnalyticsListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.analyticsService.track('order_created', {\n      orderId: event.orderId,\n      total: event.total,\n    });\n  }\n}\n```\n\nReference: [NestJS Events](https://docs.nestjs.com/techniques/events)\n\n---\n\n### 1.6 Use Repository Pattern for Data Access\n\n**Impact: HIGH** — Decouples business logic from database\n\nCreate custom repositories to encapsulate complex queries and database logic. This keeps services focused on business logic, makes testing easier with mock repositories, and allows changing database implementations without affecting business code.\n\n**Incorrect (complex queries in services):**\n\n```typescript\n// Complex queries in services\n@Injectable()\nexport class UsersService {\n  constructor(@InjectRepository(User) private repo: Repository<User>) {}\n\n  async findActiveWithOrders(minOrders: number): Promise<User[]> {\n    // Complex query logic mixed with business logic\n    return this.repo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.orders', 'order')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('user.deletedAt IS NULL')\n      .groupBy('user.id')\n      .having('COUNT(order.id) >= :min', { min: minOrders })\n      .orderBy('user.createdAt', 'DESC')\n      .getMany();\n  }\n\n  // Service becomes bloated with query logic\n}\n```\n\n**Correct (custom repository with encapsulated queries):**\n\n```typescript\n// Custom repository with encapsulated queries\n@Injectable()\nexport class UsersRepository {\n  constructor(@InjectRepository(User) private repo: Repository<User>) {}\n\n  async findById(id: string): Promise<User | null> {\n    return this.repo.findOne({ where: { id } });\n  }\n\n  async findByEmail(email: string): Promise<User | null> {\n    return this.repo.findOne({ where: { email } });\n  }\n\n  async findActiveWithMinOrders(minOrders: number): Promise<User[]> {\n    return this.repo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.orders', 'order')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('user.deletedAt IS NULL')\n      .groupBy('user.id')\n      .having('COUNT(order.id) >= :min', { min: minOrders })\n      .orderBy('user.createdAt', 'DESC')\n      .getMany();\n  }\n\n  async save(user: User): Promise<User> {\n    return this.repo.save(user);\n  }\n}\n\n// Clean service with business logic only\n@Injectable()\nexport class UsersService {\n  constructor(private usersRepo: UsersRepository) {}\n\n  async getActiveUsersWithOrders(): Promise<User[]> {\n    return this.usersRepo.findActiveWithMinOrders(1);\n  }\n\n  async create(dto: CreateUserDto): Promise<User> {\n    const existing = await this.usersRepo.findByEmail(dto.email);\n    if (existing) {\n      throw new ConflictException('Email already registered');\n    }\n\n    const user = new User();\n    user.email = dto.email;\n    user.name = dto.name;\n    return this.usersRepo.save(user);\n  }\n}\n```\n\nReference: [Repository Pattern](https://martinfowler.com/eaaCatalog/repository.html)\n\n---\n\n## 2. Dependency Injection\n\n**Section Impact: CRITICAL**\n\n### 2.1 Avoid Service Locator Anti-Pattern\n\n**Impact: HIGH** — Hides dependencies and breaks testability\n\nAvoid using `ModuleRef.get()` or global containers to resolve dependencies at runtime. This hides dependencies, makes code harder to test, and breaks the benefits of dependency injection. Use constructor injection instead.\n\n**Incorrect (service locator anti-pattern):**\n\n```typescript\n// Use ModuleRef to get dependencies dynamically\n@Injectable()\nexport class OrdersService {\n  constructor(private moduleRef: ModuleRef) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    // Dependencies are hidden - not visible in constructor\n    const usersService = this.moduleRef.get(UsersService);\n    const inventoryService = this.moduleRef.get(InventoryService);\n    const paymentService = this.moduleRef.get(PaymentService);\n\n    const user = await usersService.findOne(dto.userId);\n    // ... rest of logic\n  }\n}\n\n// Global singleton container\nclass ServiceContainer {\n  private static instance: ServiceContainer;\n  private services = new Map<string, any>();\n\n  static getInstance(): ServiceContainer {\n    if (!this.instance) {\n      this.instance = new ServiceContainer();\n    }\n    return this.instance;\n  }\n\n  get<T>(key: string): T {\n    return this.services.get(key);\n  }\n}\n```\n\n**Correct (constructor injection with explicit dependencies):**\n\n```typescript\n// Use constructor injection - dependencies are explicit\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private usersService: UsersService,\n    private inventoryService: InventoryService,\n    private paymentService: PaymentService,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const user = await this.usersService.findOne(dto.userId);\n    const inventory = await this.inventoryService.check(dto.items);\n    // Dependencies are clear and testable\n  }\n}\n\n// Easy to test with mocks\ndescribe('OrdersService', () => {\n  let service: OrdersService;\n\n  beforeEach(async () => {\n    const module = await Test.createTestingModule({\n      providers: [\n        OrdersService,\n        { provide: UsersService, useValue: mockUsersService },\n        { provide: InventoryService, useValue: mockInventoryService },\n        { provide: PaymentService, useValue: mockPaymentService },\n      ],\n    }).compile();\n\n    service = module.get(OrdersService);\n  });\n});\n\n// VALID: Factory pattern for dynamic instantiation\n@Injectable()\nexport class HandlerFactory {\n  constructor(private moduleRef: ModuleRef) {}\n\n  getHandler(type: string): Handler {\n    switch (type) {\n      case 'email':\n        return this.moduleRef.get(EmailHandler);\n      case 'sms':\n        return this.moduleRef.get(SmsHandler);\n      default:\n        return this.moduleRef.get(DefaultHandler);\n    }\n  }\n}\n```\n\nReference: [NestJS Module Reference](https://docs.nestjs.com/fundamentals/module-ref)\n\n---\n\n### 2.2 Apply Interface Segregation Principle\n\n**Impact: HIGH** — Reduces coupling and improves testability by 30-50%\n\nClients should not be forced to depend on interfaces they don't use. In NestJS, this means keeping interfaces small and focused on specific capabilities rather than creating \"fat\" interfaces that bundle unrelated methods. When a service only needs to send emails, it shouldn't depend on an interface that also includes SMS, push notifications, and logging. Split large interfaces into role-based ones.\n\n**Incorrect (fat interface forcing unused dependencies):**\n\n```typescript\n// Fat interface - forces all consumers to depend on everything\ninterface NotificationService {\n  sendEmail(to: string, subject: string, body: string): Promise<void>;\n  sendSms(phone: string, message: string): Promise<void>;\n  sendPush(userId: string, notification: PushPayload): Promise<void>;\n  sendSlack(channel: string, message: string): Promise<void>;\n  logNotification(type: string, payload: any): Promise<void>;\n  getDeliveryStatus(id: string): Promise<DeliveryStatus>;\n  retryFailed(id: string): Promise<void>;\n  scheduleNotification(dto: ScheduleDto): Promise<string>;\n}\n\n// Consumer only needs email, but must mock everything for tests\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private notifications: NotificationService, // Depends on 8 methods, uses 1\n  ) {}\n\n  async confirmOrder(order: Order): Promise<void> {\n    await this.notifications.sendEmail(\n      order.customer.email,\n      'Order Confirmed',\n      `Your order ${order.id} has been confirmed.`,\n    );\n  }\n}\n\n// Testing is painful - must mock unused methods\nconst mockNotificationService = {\n  sendEmail: jest.fn(),\n  sendSms: jest.fn(), // Never used, but required\n  sendPush: jest.fn(), // Never used, but required\n  sendSlack: jest.fn(), // Never used, but required\n  logNotification: jest.fn(), // Never used, but required\n  getDeliveryStatus: jest.fn(), // Never used, but required\n  retryFailed: jest.fn(), // Never used, but required\n  scheduleNotification: jest.fn(), // Never used, but required\n};\n```\n\n**Correct (segregated interfaces by capability):**\n\n```typescript\n// Segregated interfaces - each focused on one capability\ninterface EmailSender {\n  sendEmail(to: string, subject: string, body: string): Promise<void>;\n}\n\ninterface SmsSender {\n  sendSms(phone: string, message: string): Promise<void>;\n}\n\ninterface PushSender {\n  sendPush(userId: string, notification: PushPayload): Promise<void>;\n}\n\ninterface NotificationLogger {\n  logNotification(type: string, payload: any): Promise<void>;\n}\n\ninterface NotificationScheduler {\n  scheduleNotification(dto: ScheduleDto): Promise<string>;\n}\n\n// Implementation can implement multiple interfaces\n@Injectable()\nexport class NotificationService implements EmailSender, SmsSender, PushSender {\n  async sendEmail(to: string, subject: string, body: string): Promise<void> {\n    // Email implementation\n  }\n\n  async sendSms(phone: string, message: string): Promise<void> {\n    // SMS implementation\n  }\n\n  async sendPush(userId: string, notification: PushPayload): Promise<void> {\n    // Push implementation\n  }\n}\n\n// Or separate implementations\n@Injectable()\nexport class SendGridEmailService implements EmailSender {\n  async sendEmail(to: string, subject: string, body: string): Promise<void> {\n    // SendGrid-specific implementation\n  }\n}\n\n// Consumer depends only on what it needs\n@Injectable()\nexport class OrdersService {\n  constructor(\n    @Inject(EMAIL_SENDER) private emailSender: EmailSender, // Minimal dependency\n  ) {}\n\n  async confirmOrder(order: Order): Promise<void> {\n    await this.emailSender.sendEmail(\n      order.customer.email,\n      'Order Confirmed',\n      `Your order ${order.id} has been confirmed.`,\n    );\n  }\n}\n\n// Testing is simple - only mock what's used\nconst mockEmailSender: EmailSender = {\n  sendEmail: jest.fn(),\n};\n\n// Module registration with tokens\nexport const EMAIL_SENDER = Symbol('EMAIL_SENDER');\nexport const SMS_SENDER = Symbol('SMS_SENDER');\n\n@Module({\n  providers: [\n    { provide: EMAIL_SENDER, useClass: SendGridEmailService },\n    { provide: SMS_SENDER, useClass: TwilioSmsService },\n  ],\n  exports: [EMAIL_SENDER, SMS_SENDER],\n})\nexport class NotificationModule {}\n```\n\n**Combining interfaces when needed:**\n\n```typescript\n// Sometimes a consumer legitimately needs multiple capabilities\ninterface EmailAndSmsSender extends EmailSender, SmsSender {}\n\n// Or use intersection types\ntype MultiChannelSender = EmailSender & SmsSender & PushSender;\n\n// Consumer that genuinely needs multiple channels\n@Injectable()\nexport class AlertService {\n  constructor(\n    @Inject(MULTI_CHANNEL_SENDER)\n    private sender: EmailSender & SmsSender,\n  ) {}\n\n  async sendCriticalAlert(user: User, message: string): Promise<void> {\n    await Promise.all([\n      this.sender.sendEmail(user.email, 'Critical Alert', message),\n      this.sender.sendSms(user.phone, message),\n    ]);\n  }\n}\n```\n\nReference: [Interface Segregation Principle](https://en.wikipedia.org/wiki/Interface_segregation_principle)\n\n---\n\n### 2.3 Honor Liskov Substitution Principle\n\n**Impact: HIGH** — Ensures implementations are truly interchangeable without breaking callers\n\nSubtypes must be substitutable for their base types without altering program correctness. In NestJS with dependency injection, this means any implementation of an interface or abstract class must honor the contract completely. A mock payment service used in tests must behave like a real payment service (return similar shapes, handle errors the same way). Violating LSP causes subtle bugs when swapping implementations.\n\n**Incorrect (implementation violates the contract):**\n\n```typescript\n// Base interface with clear contract\ninterface PaymentGateway {\n  /**\n   * Charges the specified amount.\n   * @returns PaymentResult on success\n   * @throws PaymentFailedException on payment failure\n   */\n  charge(amount: number, currency: string): Promise<PaymentResult>;\n}\n\n// Production implementation - follows the contract\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    const response = await this.stripe.charges.create({ amount, currency });\n    return { success: true, transactionId: response.id, amount };\n  }\n}\n\n// Mock that violates LSP - different behavior!\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    // VIOLATION 1: Throws for valid input (contract says return PaymentResult)\n    if (amount > 1000) {\n      throw new Error('Mock does not support large amounts');\n    }\n\n    // VIOLATION 2: Returns null instead of PaymentResult\n    if (currency !== 'USD') {\n      return null as any; // Real service would convert or reject properly\n    }\n\n    // VIOLATION 3: Missing required field\n    return { success: true } as PaymentResult; // Missing transactionId!\n  }\n}\n\n// Consumer trusts the contract\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async checkout(order: Order): Promise<void> {\n    const result = await this.payment.charge(order.total, order.currency);\n    // These fail with MockPaymentService:\n    await this.saveTransaction(result.transactionId); // undefined!\n    await this.sendReceipt(result); // might be null!\n  }\n}\n```\n\n**Correct (implementations honor the contract):**\n\n```typescript\n// Well-defined interface with documented behavior\ninterface PaymentGateway {\n  /**\n   * Charges the specified amount.\n   * @param amount - Amount in smallest currency unit (cents)\n   * @param currency - ISO 4217 currency code\n   * @returns PaymentResult with transactionId, success status, and amount\n   * @throws PaymentFailedException if charge is declined\n   * @throws InvalidCurrencyException if currency is not supported\n   */\n  charge(amount: number, currency: string): Promise<PaymentResult>;\n\n  /**\n   * Refunds a previous charge.\n   * @throws TransactionNotFoundException if transactionId is invalid\n   */\n  refund(transactionId: string, amount?: number): Promise<RefundResult>;\n}\n\n// Production implementation\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    try {\n      const response = await this.stripe.charges.create({ amount, currency });\n      return {\n        success: true,\n        transactionId: response.id,\n        amount: response.amount,\n      };\n    } catch (error) {\n      if (error.type === 'card_error') {\n        throw new PaymentFailedException(error.message);\n      }\n      throw error;\n    }\n  }\n\n  async refund(transactionId: string, amount?: number): Promise<RefundResult> {\n    // Implementation...\n  }\n}\n\n// Mock that honors LSP - same contract, same behavior shape\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  private transactions = new Map<string, PaymentResult>();\n\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    // Honor the contract: validate currency like real service would\n    if (!['USD', 'EUR', 'GBP'].includes(currency)) {\n      throw new InvalidCurrencyException(`Unsupported currency: ${currency}`);\n    }\n\n    // Simulate decline for specific test scenarios\n    if (amount === 99999) {\n      throw new PaymentFailedException('Card declined (test scenario)');\n    }\n\n    // Return same shape as production\n    const result: PaymentResult = {\n      success: true,\n      transactionId: `mock_${Date.now()}_${Math.random().toString(36)}`,\n      amount,\n    };\n\n    this.transactions.set(result.transactionId, result);\n    return result;\n  }\n\n  async refund(transactionId: string, amount?: number): Promise<RefundResult> {\n    // Honor the contract: throw if transaction not found\n    if (!this.transactions.has(transactionId)) {\n      throw new TransactionNotFoundException(transactionId);\n    }\n\n    return {\n      success: true,\n      refundId: `refund_${transactionId}`,\n      amount: amount ?? this.transactions.get(transactionId)!.amount,\n    };\n  }\n}\n\n// Consumer can swap implementations safely\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async checkout(order: Order): Promise<Order> {\n    try {\n      const result = await this.payment.charge(order.total, order.currency);\n      // Works with both StripeService and MockPaymentService\n      order.transactionId = result.transactionId;\n      order.status = 'paid';\n      return order;\n    } catch (error) {\n      if (error instanceof PaymentFailedException) {\n        order.status = 'payment_failed';\n        return order;\n      }\n      throw error;\n    }\n  }\n}\n```\n\n**Testing LSP compliance:**\n\n```typescript\n// Shared test suite that any implementation must pass\nfunction testPaymentGatewayContract(createGateway: () => PaymentGateway) {\n  describe('PaymentGateway contract', () => {\n    let gateway: PaymentGateway;\n\n    beforeEach(() => {\n      gateway = createGateway();\n    });\n\n    it('returns PaymentResult with all required fields', async () => {\n      const result = await gateway.charge(1000, 'USD');\n      expect(result).toHaveProperty('success');\n      expect(result).toHaveProperty('transactionId');\n      expect(result).toHaveProperty('amount');\n      expect(typeof result.transactionId).toBe('string');\n    });\n\n    it('throws InvalidCurrencyException for unsupported currency', async () => {\n      await expect(gateway.charge(1000, 'INVALID')).rejects.toThrow(InvalidCurrencyException);\n    });\n\n    it('throws TransactionNotFoundException for invalid refund', async () => {\n      await expect(gateway.refund('nonexistent')).rejects.toThrow(TransactionNotFoundException);\n    });\n  });\n}\n\n// Run against all implementations\ndescribe('StripeService', () => {\n  testPaymentGatewayContract(() => new StripeService(mockStripeClient));\n});\n\ndescribe('MockPaymentService', () => {\n  testPaymentGatewayContract(() => new MockPaymentService());\n});\n```\n\nReference: [Liskov Substitution Principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle)\n\n---\n\n### 2.4 Prefer Constructor Injection\n\n**Impact: CRITICAL** — Required for proper DI and testing\n\nAlways use constructor injection over property injection. Constructor injection makes dependencies explicit, enables TypeScript type checking, ensures dependencies are available when the class is instantiated, and improves testability. This is required for proper DI, testing, and TypeScript support.\n\n**Incorrect (property injection with hidden dependencies):**\n\n```typescript\n// Property injection - avoid unless necessary\n@Injectable()\nexport class UsersService {\n  @Inject()\n  private userRepo: UserRepository; // Hidden dependency\n\n  @Inject('CONFIG')\n  private config: ConfigType; // Also hidden\n\n  async findAll() {\n    return this.userRepo.find();\n  }\n}\n\n// Problems:\n// 1. Dependencies not visible in constructor\n// 2. Service can be instantiated without dependencies in tests\n// 3. TypeScript can't enforce dependency types at instantiation\n```\n\n**Correct (constructor injection with explicit dependencies):**\n\n```typescript\n// Constructor injection - explicit and testable\n@Injectable()\nexport class UsersService {\n  constructor(\n    private readonly userRepo: UserRepository,\n    @Inject('CONFIG') private readonly config: ConfigType,\n  ) {}\n\n  async findAll(): Promise<User[]> {\n    return this.userRepo.find();\n  }\n}\n\n// Testing is straightforward\ndescribe('UsersService', () => {\n  let service: UsersService;\n  let mockRepo: jest.Mocked<UserRepository>;\n\n  beforeEach(() => {\n    mockRepo = {\n      find: jest.fn(),\n      save: jest.fn(),\n    } as any;\n\n    service = new UsersService(mockRepo, { dbUrl: 'test' });\n  });\n\n  it('should find all users', async () => {\n    mockRepo.find.mockResolvedValue([{ id: '1', name: 'Test' }]);\n    const result = await service.findAll();\n    expect(result).toHaveLength(1);\n  });\n});\n\n// Only use property injection for optional dependencies\n@Injectable()\nexport class LoggingService {\n  @Optional()\n  @Inject('ANALYTICS')\n  private analytics?: AnalyticsService;\n\n  log(message: string) {\n    console.log(message);\n    this.analytics?.track('log', message); // Optional enhancement\n  }\n}\n```\n\nReference: [NestJS Providers](https://docs.nestjs.com/providers)\n\n---\n\n### 2.5 Understand Provider Scopes\n\n**Impact: CRITICAL** — Prevents data leaks and performance issues\n\nNestJS has three provider scopes: DEFAULT (singleton), REQUEST (per-request instance), and TRANSIENT (new instance for each injection). Most providers should be singletons. Request-scoped providers have performance implications as they bubble up through the dependency tree. Understanding scopes prevents memory leaks and incorrect data sharing.\n\n**Incorrect (wrong scope usage):**\n\n```typescript\n// Request-scoped when not needed (performance hit)\n@Injectable({ scope: Scope.REQUEST })\nexport class UsersService {\n  // This creates a new instance for EVERY request\n  // All dependencies also become request-scoped\n  async findAll() {\n    return this.userRepo.find();\n  }\n}\n\n// Singleton with mutable request state\n@Injectable() // Default: singleton\nexport class RequestContextService {\n  private userId: string; // DANGER: Shared across all requests!\n\n  setUser(userId: string) {\n    this.userId = userId; // Overwrites for all concurrent requests\n  }\n\n  getUser() {\n    return this.userId; // Returns wrong user!\n  }\n}\n```\n\n**Correct (appropriate scope for each use case):**\n\n```typescript\n// Singleton for stateless services (default, most common)\n@Injectable()\nexport class UsersService {\n  constructor(private readonly userRepo: UserRepository) {}\n\n  async findById(id: string): Promise<User> {\n    return this.userRepo.findOne({ where: { id } });\n  }\n}\n\n// Request-scoped ONLY when you need request context\n@Injectable({ scope: Scope.REQUEST })\nexport class RequestContextService {\n  private userId: string;\n\n  setUser(userId: string) {\n    this.userId = userId;\n  }\n\n  getUser(): string {\n    return this.userId;\n  }\n}\n\n// Better: Use NestJS built-in request context\nimport { REQUEST } from '@nestjs/core';\nimport { Request } from 'express';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class AuditService {\n  constructor(@Inject(REQUEST) private request: Request) {}\n\n  log(action: string) {\n    console.log(`User ${this.request.user?.id} performed ${action}`);\n  }\n}\n\n// Best: Use ClsModule for async context (no scope bubble-up)\nimport { ClsService } from 'nestjs-cls';\n\n@Injectable() // Stays singleton!\nexport class AuditService {\n  constructor(private cls: ClsService) {}\n\n  log(action: string) {\n    const userId = this.cls.get('userId');\n    console.log(`User ${userId} performed ${action}`);\n  }\n}\n```\n\nReference: [NestJS Injection Scopes](https://docs.nestjs.com/fundamentals/injection-scopes)\n\n---\n\n### 2.6 Use Injection Tokens for Interfaces\n\n**Impact: HIGH** — Enables interface-based DI at runtime\n\nTypeScript interfaces are erased at compile time and can't be used as injection tokens. Use string tokens, symbols, or abstract classes when you want to inject implementations of interfaces. This enables swapping implementations for testing or different environments.\n\n**Incorrect (interface can't be used as token):**\n\n```typescript\n// Interface can't be used as injection token\ninterface PaymentGateway {\n  charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  charge(amount: number) {\n    /* ... */\n  }\n}\n\n@Injectable()\nexport class OrdersService {\n  // This WON'T work - PaymentGateway doesn't exist at runtime\n  constructor(private payment: PaymentGateway) {}\n}\n```\n\n**Correct (symbol tokens or abstract classes):**\n\n```typescript\n// Option 1: String/Symbol tokens (most flexible)\nexport const PAYMENT_GATEWAY = Symbol('PAYMENT_GATEWAY');\n\nexport interface PaymentGateway {\n  charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    // Stripe implementation\n  }\n}\n\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    return { success: true, id: 'mock-id' };\n  }\n}\n\n// Module registration\n@Module({\n  providers: [\n    {\n      provide: PAYMENT_GATEWAY,\n      useClass: process.env.NODE_ENV === 'test' ? MockPaymentService : StripeService,\n    },\n  ],\n  exports: [PAYMENT_GATEWAY],\n})\nexport class PaymentModule {}\n\n// Injection\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async createOrder(dto: CreateOrderDto) {\n    await this.payment.charge(dto.amount);\n  }\n}\n\n// Option 2: Abstract class (carries runtime type info)\nexport abstract class PaymentGateway {\n  abstract charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService extends PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    // Implementation\n  }\n}\n\n// No @Inject needed with abstract class\n@Injectable()\nexport class OrdersService {\n  constructor(private payment: PaymentGateway) {}\n}\n```\n\nReference: [NestJS Custom Providers](https://docs.nestjs.com/fundamentals/custom-providers)\n\n---\n\n## 3. Error Handling\n\n**Section Impact: HIGH**\n\n### 3.1 Handle Async Errors Properly\n\n**Impact: HIGH** — Prevents process crashes from unhandled rejections\n\nNestJS automatically catches errors from async route handlers, but errors from background tasks, event handlers, and manually created promises can crash your application. Always handle async errors explicitly and use global handlers as a safety net.\n\n**Incorrect (fire-and-forget without error handling):**\n\n```typescript\n// Fire-and-forget without error handling\n@Injectable()\nexport class UsersService {\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Fire and forget - if this fails, error is unhandled!\n    this.emailService.sendWelcome(user.email);\n\n    return user;\n  }\n}\n\n// Unhandled promise in event handler\n@Injectable()\nexport class OrdersService {\n  @OnEvent('order.created')\n  handleOrderCreated(event: OrderCreatedEvent) {\n    // This returns a promise but it's not awaited!\n    this.processOrder(event);\n    // Errors will crash the process\n  }\n\n  private async processOrder(event: OrderCreatedEvent): Promise<void> {\n    await this.inventoryService.reserve(event.items);\n    await this.notificationService.send(event.userId);\n  }\n}\n\n// Missing try-catch in scheduled tasks\n@Cron('0 0 * * *')\nasync dailyCleanup(): Promise<void> {\n  await this.cleanupService.run();\n  // If this throws, no error handling\n}\n```\n\n**Correct (explicit async error handling):**\n\n```typescript\n// Handle fire-and-forget with explicit catch\n@Injectable()\nexport class UsersService {\n  private readonly logger = new Logger(UsersService.name);\n\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Explicitly catch and log errors\n    this.emailService.sendWelcome(user.email).catch(error => {\n      this.logger.error('Failed to send welcome email', error.stack);\n      // Optionally queue for retry\n    });\n\n    return user;\n  }\n}\n\n// Properly handle async event handlers\n@Injectable()\nexport class OrdersService {\n  private readonly logger = new Logger(OrdersService.name);\n\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    try {\n      await this.processOrder(event);\n    } catch (error) {\n      this.logger.error('Failed to process order', { event, error });\n      // Don't rethrow - would crash the process\n      await this.deadLetterQueue.add('order.created', event);\n    }\n  }\n}\n\n// Safe scheduled tasks\n@Injectable()\nexport class CleanupService {\n  private readonly logger = new Logger(CleanupService.name);\n\n  @Cron('0 0 * * *')\n  async dailyCleanup(): Promise<void> {\n    try {\n      await this.cleanupService.run();\n      this.logger.log('Daily cleanup completed');\n    } catch (error) {\n      this.logger.error('Daily cleanup failed', error.stack);\n      // Alert or retry logic\n    }\n  }\n}\n\n// Global unhandled rejection handler in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  const logger = new Logger('Bootstrap');\n\n  process.on('unhandledRejection', (reason, promise) => {\n    logger.error('Unhandled Rejection at:', promise, 'reason:', reason);\n  });\n\n  process.on('uncaughtException', error => {\n    logger.error('Uncaught Exception:', error);\n    process.exit(1);\n  });\n\n  await app.listen(3000);\n}\n```\n\nReference: [Node.js Unhandled Rejections](https://nodejs.org/api/process.html#event-unhandledrejection)\n\n---\n\n### 3.2 Throw HTTP Exceptions from Services\n\n**Impact: HIGH** — Keeps controllers thin and simplifies error handling\n\nIt's acceptable (and often preferable) to throw `HttpException` subclasses from services in HTTP applications. This keeps controllers thin and allows services to communicate appropriate error states. For truly layer-agnostic services, use domain exceptions that map to HTTP status codes.\n\n**Incorrect (return error objects instead of throwing):**\n\n```typescript\n// Return error objects instead of throwing\n@Injectable()\nexport class UsersService {\n  async findById(id: string): Promise<{ user?: User; error?: string }> {\n    const user = await this.repo.findOne({ where: { id } });\n    if (!user) {\n      return { error: 'User not found' }; // Controller must check this\n    }\n    return { user };\n  }\n}\n\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string) {\n    const result = await this.usersService.findById(id);\n    if (result.error) {\n      throw new NotFoundException(result.error);\n    }\n    return result.user;\n  }\n}\n```\n\n**Correct (throw exceptions directly from service):**\n\n```typescript\n// Throw exceptions directly from service\n@Injectable()\nexport class UsersService {\n  constructor(private readonly repo: UserRepository) {}\n\n  async findById(id: string): Promise<User> {\n    const user = await this.repo.findOne({ where: { id } });\n    if (!user) {\n      throw new NotFoundException(`User #${id} not found`);\n    }\n    return user;\n  }\n\n  async create(dto: CreateUserDto): Promise<User> {\n    const existing = await this.repo.findOne({\n      where: { email: dto.email },\n    });\n    if (existing) {\n      throw new ConflictException('Email already registered');\n    }\n    return this.repo.save(dto);\n  }\n\n  async update(id: string, dto: UpdateUserDto): Promise<User> {\n    const user = await this.findById(id); // Throws if not found\n    Object.assign(user, dto);\n    return this.repo.save(user);\n  }\n}\n\n// Controller stays thin\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findById(id);\n  }\n\n  @Post()\n  create(@Body() dto: CreateUserDto): Promise<User> {\n    return this.usersService.create(dto);\n  }\n}\n\n// For layer-agnostic services, use domain exceptions\nexport class EntityNotFoundException extends Error {\n  constructor(\n    public readonly entity: string,\n    public readonly id: string,\n  ) {\n    super(`${entity} with ID \"${id}\" not found`);\n  }\n}\n\n// Map to HTTP in exception filter\n@Catch(EntityNotFoundException)\nexport class EntityNotFoundFilter implements ExceptionFilter {\n  catch(exception: EntityNotFoundException, host: ArgumentsHost) {\n    const ctx = host.switchToHttp();\n    const response = ctx.getResponse<Response>();\n\n    response.status(404).json({\n      statusCode: 404,\n      message: exception.message,\n      entity: exception.entity,\n      id: exception.id,\n    });\n  }\n}\n```\n\nReference: [NestJS Exception Filters](https://docs.nestjs.com/exception-filters)\n\n---\n\n### 3.3 Use Exception Filters for Error Handling\n\n**Impact: HIGH** — Consistent\n\n<!-- [TRUNCATED AT 50KB] -->","isInternal":false,"tokens":11088,"sizeBytes":50350},{"name":"README.md","path":".agents/skills/nestjs-best-practices/README.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/README.md","title":"Nestjs-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# NestJS Best Practices\n\n📖 [For Humans <3](https://kadajett.github.io/agent-nestjs-skills/)\n\nA structured repository for creating and maintaining NestJS Best Practices optimized for agents and LLMs.\n\n## Installation\n\nInstall this skill using [skills](https://github.com/vercel-labs/skills):\n\n```bash\n# GitHub shorthand\nnpx skills add Kadajett/agent-nestjs-skills\n\n# Install globally (available across all projects)\nnpx skills add Kadajett/agent-nestjs-skills --global\n\n# Install for specific agents\nnpx skills add Kadajett/agent-nestjs-skills -a claude-code -a cursor\n```\n\n### Supported Agents\n\n- Claude Code\n- OpenCode\n- Codex\n- Cursor\n- Antigravity\n- Roo Code\n\n## Structure\n\n- `rules/` - Individual rule files (one per rule)\n  - `_sections.md` - Section metadata (titles, impacts, descriptions)\n  - `_template.md` - Template for creating new rules\n  - `area-description.md` - Individual rule files\n- `scripts/` - Build scripts and utilities\n- `metadata.json` - Document metadata (version, organization, abstract)\n- **`AGENTS.md`** - Compiled output (generated)\n\n## Getting Started\n\n1. Install dependencies:\n\n   ```bash\n   cd scripts && npm install\n   ```\n\n2. Build AGENTS.md from rules:\n   ```bash\n   npm run build\n   # or\n   ./scripts/build.sh\n   ```\n\n## Creating a New Rule\n\n1. Copy `rules/_template.md` to `rules/area-description.md`\n2. Choose the appropriate area prefix:\n   - `arch-` for Architecture (Section 1)\n   - `di-` for Dependency Injection (Section 2)\n   - `error-` for Error Handling (Section 3)\n   - `security-` for Security (Section 4)\n   - `perf-` for Performance (Section 5)\n   - `test-` for Testing (Section 6)\n   - `db-` for Database & ORM (Section 7)\n   - `api-` for API Design (Section 8)\n   - `micro-` for Microservices (Section 9)\n   - `devops-` for DevOps & Deployment (Section 10)\n3. Fill in the frontmatter and content\n4. Ensure you have clear examples with explanations\n5. Run the build script to regenerate AGENTS.md\n\n## Rule File Structure\n\nEach rule file should follow this structure:\n\n````markdown\n---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: Optional description\ntags: tag1, tag2, tag3\n---\n\n## Rule Title Here\n\nBrief explanation of the rule and why it matters.\n\n**Incorrect (description of what's wrong):**\n\n```typescript\n// Bad code example\n```\n````\n\n**Correct (description of what's right):**\n\n```typescript\n// Good code example\n```\n\nOptional explanatory text after examples.\n\nReference: [NestJS Documentation](https://docs.nestjs.com)\n\n## File Naming Convention\n\n- Files starting with `_` are special (excluded from build)\n- Rule files: `area-description.md` (e.g., `arch-avoid-circular-deps.md`)\n- Section is automatically inferred from filename prefix\n- Rules are sorted alphabetically by title within each section\n- IDs (e.g., 1.1, 1.2) are auto-generated during build\n\n## Impact Levels\n\n| Level       | Description                                                                           |\n| ----------- | ------------------------------------------------------------------------------------- |\n| CRITICAL    | Violations cause runtime errors, security vulnerabilities, or architectural breakdown |\n| HIGH        | Significant impact on reliability, security, or maintainability                       |\n| MEDIUM-HIGH | Notable impact on quality and developer experience                                    |\n| MEDIUM      | Moderate impact on code quality and best practices                                    |\n| LOW-MEDIUM  | Minor improvements for consistency and maintainability                                |\n\n## Scripts\n\n- `npm run build` (in scripts/) - Compile rules into AGENTS.md\n\n## Contributing\n\nWhen adding or modifying rules:\n\n1. Use the correct filename prefix for your section\n2. Follow the `_template.md` structure\n3. Include clear bad/good examples with explanations\n4. Add appropriate tags\n5. Run the build script to regenerate AGENTS.md\n6. Rules are automatically sorted by title - no need to manage numbers!\n\n## Documentation Website\n\nThe documentation website source code lives on the [`docs` branch](https://github.com/Kadajett/agent-nestjs-skills/tree/docs/website). This separation keeps the skill installation lightweight while maintaining the full documentation site.\n\nTo contribute to the website:\n\n```bash\ngit checkout docs\ncd website\nnpm install\nnpm run dev\n```\n\n## Acknowledgments\n\n- Inspired by the [Vercel React Best Practices](https://github.com/vercel-labs/agent-skills) skill structure\n- Compatible with [skills](https://github.com/vercel-labs/skills) for easy installation across coding agents\n\n## Compatible Agents\n\nThese NestJS skills work with:\n\n- [Claude Code](https://claude.ai/code) - Anthropic's official CLI\n- [AdaL](https://sylph.ai/adal) - Self-evolving AI coding agent with MCP support","isInternal":false,"tokens":1129,"sizeBytes":4796},{"name":"api-use-dto-serialization.md","path":".agents/skills/nestjs-best-practices/rules/api-use-dto-serialization.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/api-use-dto-serialization.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use DTOs and Serialization for API Responses\nimpact: MEDIUM\nimpactDescription: Response DTOs prevent accidental data exposure and ensure consistency\ntags: api, dto, serialization, class-transformer\n---\n\n## Use DTOs and Serialization for API Responses\n\nNever return entity objects directly from controllers. Use response DTOs with class-transformer's `@Exclude()` and `@Expose()` decorators to control exactly what data is sent to clients. This prevents accidental exposure of sensitive fields and provides a stable API contract.\n\n**Incorrect (returning entities directly or manual spreading):**\n\n```typescript\n// Return entities directly\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findById(id);\n    // Returns: { id, email, passwordHash, ssn, internalNotes, ... }\n    // Exposes sensitive data!\n  }\n}\n\n// Manual object spreading (error-prone)\n@Get(':id')\nasync findOne(@Param('id') id: string) {\n  const user = await this.usersService.findById(id);\n  return {\n    id: user.id,\n    email: user.email,\n    name: user.name,\n    // Easy to forget to exclude sensitive fields\n    // Hard to maintain across endpoints\n  };\n}\n```\n\n**Correct (use class-transformer with @Exclude and response DTOs):**\n\n```typescript\n// Enable class-transformer globally\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));\n  await app.listen(3000);\n}\n\n// Entity with serialization control\n@Entity()\nexport class User {\n  @PrimaryGeneratedColumn('uuid')\n  id: string;\n\n  @Column()\n  email: string;\n\n  @Column()\n  name: string;\n\n  @Column()\n  @Exclude() // Never include in responses\n  passwordHash: string;\n\n  @Column({ nullable: true })\n  @Exclude()\n  ssn: string;\n\n  @Column({ default: false })\n  @Exclude({ toPlainOnly: true }) // Exclude from response, allow in requests\n  isAdmin: boolean;\n\n  @CreateDateColumn()\n  createdAt: Date;\n\n  @Column()\n  @Exclude()\n  internalNotes: string;\n}\n\n// Now returning entity is safe\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findById(id);\n    // Returns: { id, email, name, createdAt }\n    // Sensitive fields excluded automatically\n  }\n}\n\n// For different response shapes, use explicit DTOs\nexport class UserResponseDto {\n  @Expose()\n  id: string;\n\n  @Expose()\n  email: string;\n\n  @Expose()\n  name: string;\n\n  @Expose()\n  @Transform(({ obj }) => obj.posts?.length || 0)\n  postCount: number;\n\n  constructor(partial: Partial<User>) {\n    Object.assign(this, partial);\n  }\n}\n\nexport class UserDetailResponseDto extends UserResponseDto {\n  @Expose()\n  createdAt: Date;\n\n  @Expose()\n  @Type(() => PostResponseDto)\n  posts: PostResponseDto[];\n}\n\n// Controller with explicit DTOs\n@Controller('users')\nexport class UsersController {\n  @Get()\n  @SerializeOptions({ type: UserResponseDto })\n  async findAll(): Promise<UserResponseDto[]> {\n    const users = await this.usersService.findAll();\n    return users.map(u => plainToInstance(UserResponseDto, u));\n  }\n\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<UserDetailResponseDto> {\n    const user = await this.usersService.findByIdWithPosts(id);\n    return plainToInstance(UserDetailResponseDto, user, {\n      excludeExtraneousValues: true,\n    });\n  }\n}\n\n// Groups for conditional serialization\nexport class UserDto {\n  @Expose()\n  id: string;\n\n  @Expose()\n  name: string;\n\n  @Expose({ groups: ['admin'] })\n  email: string;\n\n  @Expose({ groups: ['admin'] })\n  createdAt: Date;\n\n  @Expose({ groups: ['admin', 'owner'] })\n  settings: UserSettings;\n}\n\n@Controller('users')\nexport class UsersController {\n  @Get()\n  @SerializeOptions({ groups: ['public'] })\n  async findAllPublic(): Promise<UserDto[]> {\n    // Returns: { id, name }\n  }\n\n  @Get('admin')\n  @UseGuards(AdminGuard)\n  @SerializeOptions({ groups: ['admin'] })\n  async findAllAdmin(): Promise<UserDto[]> {\n    // Returns: { id, name, email, createdAt }\n  }\n\n  @Get('me')\n  @SerializeOptions({ groups: ['owner'] })\n  async getProfile(@CurrentUser() user: User): Promise<UserDto> {\n    // Returns: { id, name, settings }\n  }\n}\n```\n\nReference: [NestJS Serialization](https://docs.nestjs.com/techniques/serialization)","frontmatter":{"title":"Use DTOs and Serialization for API Responses","impact":"MEDIUM","impactDescription":"Response DTOs prevent accidental data exposure and ensure consistency","tags":"api, dto, serialization, class-transformer"},"isInternal":false,"tokens":1053,"sizeBytes":4384},{"name":"api-use-interceptors.md","path":".agents/skills/nestjs-best-practices/rules/api-use-interceptors.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/api-use-interceptors.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Interceptors for Cross-Cutting Concerns\nimpact: MEDIUM-HIGH\nimpactDescription: Interceptors provide clean separation for cross-cutting logic\ntags: api, interceptors, logging, caching\n---\n\n## Use Interceptors for Cross-Cutting Concerns\n\nInterceptors can transform responses, add logging, handle caching, and measure performance without polluting your business logic. They wrap the route handler execution, giving you access to both the request and response streams.\n\n**Incorrect (logging and transformation in every method):**\n\n```typescript\n// Logging in every controller method\n@Controller('users')\nexport class UsersController {\n  @Get()\n  async findAll(): Promise<User[]> {\n    const start = Date.now();\n    this.logger.log('findAll called');\n\n    const users = await this.usersService.findAll();\n\n    this.logger.log(`findAll completed in ${Date.now() - start}ms`);\n    return users;\n  }\n\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    const start = Date.now();\n    this.logger.log(`findOne called with id: ${id}`);\n\n    const user = await this.usersService.findOne(id);\n\n    this.logger.log(`findOne completed in ${Date.now() - start}ms`);\n    return user;\n  }\n  // Repeated in every method!\n}\n\n// Manual response wrapping\n@Get()\nasync findAll(): Promise<{ data: User[]; meta: Meta }> {\n  const users = await this.usersService.findAll();\n  return {\n    data: users,\n    meta: { timestamp: new Date(), count: users.length },\n  };\n}\n```\n\n**Correct (use interceptors for cross-cutting concerns):**\n\n```typescript\n// Logging interceptor\n@Injectable()\nexport class LoggingInterceptor implements NestInterceptor {\n  private readonly logger = new Logger('HTTP');\n\n  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n    const request = context.switchToHttp().getRequest();\n    const { method, url, body } = request;\n    const now = Date.now();\n\n    return next.handle().pipe(\n      tap({\n        next: (data) => {\n          const response = context.switchToHttp().getResponse();\n          this.logger.log(\n            `${method} ${url} ${response.statusCode} - ${Date.now() - now}ms`,\n          );\n        },\n        error: (error) => {\n          this.logger.error(\n            `${method} ${url} ${error.status || 500} - ${Date.now() - now}ms`,\n            error.stack,\n          );\n        },\n      }),\n    );\n  }\n}\n\n// Response transformation interceptor\n@Injectable()\nexport class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {\n  intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {\n    return next.handle().pipe(\n      map((data) => ({\n        data,\n        meta: {\n          timestamp: new Date().toISOString(),\n          path: context.switchToHttp().getRequest().url,\n        },\n      })),\n    );\n  }\n}\n\n// Timeout interceptor\n@Injectable()\nexport class TimeoutInterceptor implements NestInterceptor {\n  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n    return next.handle().pipe(\n      timeout(5000),\n      catchError((err) => {\n        if (err instanceof TimeoutError) {\n          throw new RequestTimeoutException('Request timed out');\n        }\n        throw err;\n      }),\n    );\n  }\n}\n\n// Apply globally or per-controller\n@Module({\n  providers: [\n    { provide: APP_INTERCEPTOR, useClass: LoggingInterceptor },\n    { provide: APP_INTERCEPTOR, useClass: TransformInterceptor },\n  ],\n})\nexport class AppModule {}\n\n// Or per-controller\n@Controller('users')\n@UseInterceptors(LoggingInterceptor)\nexport class UsersController {\n  @Get()\n  async findAll(): Promise<User[]> {\n    // Clean business logic only\n    return this.usersService.findAll();\n  }\n}\n\n// Custom cache interceptor with TTL\n@Injectable()\nexport class HttpCacheInterceptor implements NestInterceptor {\n  constructor(\n    private cacheManager: Cache,\n    private reflector: Reflector,\n  ) {}\n\n  async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {\n    const request = context.switchToHttp().getRequest();\n\n    // Only cache GET requests\n    if (request.method !== 'GET') {\n      return next.handle();\n    }\n\n    const cacheKey = this.generateKey(request);\n    const ttl = this.reflector.get<number>('cacheTTL', context.getHandler()) || 300;\n\n    const cached = await this.cacheManager.get(cacheKey);\n    if (cached) {\n      return of(cached);\n    }\n\n    return next.handle().pipe(\n      tap((response) => {\n        this.cacheManager.set(cacheKey, response, ttl);\n      }),\n    );\n  }\n\n  private generateKey(request: Request): string {\n    return `cache:${request.url}:${JSON.stringify(request.query)}`;\n  }\n}\n\n// Usage with custom TTL\n@Get()\n@SetMetadata('cacheTTL', 600)\n@UseInterceptors(HttpCacheInterceptor)\nasync findAll(): Promise<User[]> {\n  return this.usersService.findAll();\n}\n\n// Error mapping interceptor\n@Injectable()\nexport class ErrorMappingInterceptor implements NestInterceptor {\n  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n    return next.handle().pipe(\n      catchError((error) => {\n        if (error instanceof EntityNotFoundError) {\n          throw new NotFoundException(error.message);\n        }\n        if (error instanceof QueryFailedError) {\n          if (error.message.includes('duplicate')) {\n            throw new ConflictException('Resource already exists');\n          }\n        }\n        throw error;\n      }),\n    );\n  }\n}\n```\n\nReference: [NestJS Interceptors](https://docs.nestjs.com/interceptors)","frontmatter":{"title":"Use Interceptors for Cross-Cutting Concerns","impact":"MEDIUM-HIGH","impactDescription":"Interceptors provide clean separation for cross-cutting logic","tags":"api, interceptors, logging, caching"},"isInternal":false,"tokens":1177,"sizeBytes":5517},{"name":"api-use-pipes.md","path":".agents/skills/nestjs-best-practices/rules/api-use-pipes.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/api-use-pipes.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Pipes for Input Transformation\nimpact: MEDIUM\nimpactDescription: Pipes ensure clean, validated data reaches your handlers\ntags: api, pipes, validation, transformation\n---\n\n## Use Pipes for Input Transformation\n\nUse built-in pipes like `ParseIntPipe`, `ParseUUIDPipe`, and `DefaultValuePipe` for common transformations. Create custom pipes for business-specific transformations. Pipes separate validation/transformation logic from controllers.\n\n**Incorrect (manual type parsing in handlers):**\n\n```typescript\n// Manual type parsing in handlers\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    // Manual validation in every handler\n    const uuid = id.trim();\n    if (!isUUID(uuid)) {\n      throw new BadRequestException('Invalid UUID');\n    }\n    return this.usersService.findOne(uuid);\n  }\n\n  @Get()\n  async findAll(\n    @Query('page') page: string,\n    @Query('limit') limit: string,\n  ): Promise<User[]> {\n    // Manual parsing and defaults\n    const pageNum = parseInt(page) || 1;\n    const limitNum = parseInt(limit) || 10;\n    return this.usersService.findAll(pageNum, limitNum);\n  }\n}\n\n// Type coercion without validation\n@Get()\nasync search(@Query('price') price: string): Promise<Product[]> {\n  const priceNum = +price; // NaN if invalid, no error\n  return this.productsService.findByPrice(priceNum);\n}\n```\n\n**Correct (use built-in and custom pipes):**\n\n```typescript\n// Use built-in pipes for common transformations\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id', ParseUUIDPipe) id: string): Promise<User> {\n    // id is guaranteed to be a valid UUID\n    return this.usersService.findOne(id);\n  }\n\n  @Get()\n  async findAll(\n    @Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number,\n    @Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number,\n  ): Promise<User[]> {\n    // Automatic defaults and type conversion\n    return this.usersService.findAll(page, limit);\n  }\n\n  @Get('by-status/:status')\n  async findByStatus(\n    @Param('status', new ParseEnumPipe(UserStatus)) status: UserStatus,\n  ): Promise<User[]> {\n    return this.usersService.findByStatus(status);\n  }\n}\n\n// Custom pipe for business logic\n@Injectable()\nexport class ParseDatePipe implements PipeTransform<string, Date> {\n  transform(value: string): Date {\n    const date = new Date(value);\n    if (isNaN(date.getTime())) {\n      throw new BadRequestException('Invalid date format');\n    }\n    return date;\n  }\n}\n\n@Get('reports')\nasync getReports(\n  @Query('from', ParseDatePipe) from: Date,\n  @Query('to', ParseDatePipe) to: Date,\n): Promise<Report[]> {\n  return this.reportsService.findBetween(from, to);\n}\n\n// Custom transformation pipes\n@Injectable()\nexport class NormalizeEmailPipe implements PipeTransform<string, string> {\n  transform(value: string): string {\n    if (!value) return value;\n    return value.trim().toLowerCase();\n  }\n}\n\n// Parse comma-separated values\n@Injectable()\nexport class ParseArrayPipe implements PipeTransform<string, string[]> {\n  transform(value: string): string[] {\n    if (!value) return [];\n    return value.split(',').map((v) => v.trim()).filter(Boolean);\n  }\n}\n\n@Get('products')\nasync findProducts(\n  @Query('ids', ParseArrayPipe) ids: string[],\n  @Query('email', NormalizeEmailPipe) email: string,\n): Promise<Product[]> {\n  // ids is already an array, email is normalized\n  return this.productsService.findByIds(ids);\n}\n\n// Sanitize HTML input\n@Injectable()\nexport class SanitizeHtmlPipe implements PipeTransform<string, string> {\n  transform(value: string): string {\n    if (!value) return value;\n    return sanitizeHtml(value, { allowedTags: [] });\n  }\n}\n\n// Global validation pipe with transformation\napp.useGlobalPipes(\n  new ValidationPipe({\n    whitelist: true, // Strip non-DTO properties\n    transform: true, // Auto-transform to DTO types\n    transformOptions: {\n      enableImplicitConversion: true, // Convert query strings to numbers\n    },\n    forbidNonWhitelisted: true, // Throw on extra properties\n  }),\n);\n\n// DTO with transformation decorators\nexport class FindProductsDto {\n  @IsOptional()\n  @Type(() => Number)\n  @IsInt()\n  @Min(1)\n  page?: number = 1;\n\n  @IsOptional()\n  @Type(() => Number)\n  @IsInt()\n  @Min(1)\n  @Max(100)\n  limit?: number = 10;\n\n  @IsOptional()\n  @Transform(({ value }) => value?.toLowerCase())\n  @IsString()\n  search?: string;\n\n  @IsOptional()\n  @Transform(({ value }) => value?.split(','))\n  @IsArray()\n  @IsString({ each: true })\n  categories?: string[];\n}\n\n@Get()\nasync findAll(@Query() dto: FindProductsDto): Promise<Product[]> {\n  // dto is already transformed and validated\n  return this.productsService.findAll(dto);\n}\n\n// Pipe error customization\n@Injectable()\nexport class CustomParseIntPipe extends ParseIntPipe {\n  constructor() {\n    super({\n      exceptionFactory: (error) =>\n        new BadRequestException(`${error} must be a valid integer`),\n    });\n  }\n}\n\n// Or use options on built-in pipes\n@Get(':id')\nasync findOne(\n  @Param(\n    'id',\n    new ParseIntPipe({\n      errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE,\n      exceptionFactory: () => new NotAcceptableException('ID must be numeric'),\n    }),\n  )\n  id: number,\n): Promise<Item> {\n  return this.itemsService.findOne(id);\n}\n```\n\nReference: [NestJS Pipes](https://docs.nestjs.com/pipes)","frontmatter":{"title":"Use Pipes for Input Transformation","impact":"MEDIUM","impactDescription":"Pipes ensure clean, validated data reaches your handlers","tags":"api, pipes, validation, transformation"},"isInternal":false,"tokens":1267,"sizeBytes":5385},{"name":"api-versioning.md","path":".agents/skills/nestjs-best-practices/rules/api-versioning.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/api-versioning.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use API Versioning for Breaking Changes\nimpact: MEDIUM\nimpactDescription: Versioning allows you to evolve APIs without breaking existing clients\ntags: api, versioning, breaking-changes, compatibility\n---\n\n## Use API Versioning for Breaking Changes\n\nUse NestJS built-in versioning when making breaking changes to your API. Choose a versioning strategy (URI, header, or media type) and apply it consistently. This allows old clients to continue working while new clients use updated endpoints.\n\n**Incorrect (breaking changes without versioning):**\n\n```typescript\n// Breaking changes without versioning\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    // Original response: { id, name, email }\n    // Later changed to: { id, firstName, lastName, emailAddress }\n    // Old clients break!\n    return this.usersService.findOne(id);\n  }\n}\n\n// Manual versioning in routes\n@Controller('v1/users')\nexport class UsersV1Controller {}\n\n@Controller('v2/users')\nexport class UsersV2Controller {}\n// Inconsistent, error-prone, hard to maintain\n```\n\n**Correct (use NestJS built-in versioning):**\n\n```typescript\n// Enable versioning in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n\n  // URI versioning: /v1/users, /v2/users\n  app.enableVersioning({\n    type: VersioningType.URI,\n    defaultVersion: '1',\n  });\n\n  // Or header versioning: X-API-Version: 1\n  app.enableVersioning({\n    type: VersioningType.HEADER,\n    header: 'X-API-Version',\n    defaultVersion: '1',\n  });\n\n  // Or media type: Accept: application/json;v=1\n  app.enableVersioning({\n    type: VersioningType.MEDIA_TYPE,\n    key: 'v=',\n    defaultVersion: '1',\n  });\n\n  await app.listen(3000);\n}\n\n// Version-specific controllers\n@Controller('users')\n@Version('1')\nexport class UsersV1Controller {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<UserV1Response> {\n    const user = await this.usersService.findOne(id);\n    // V1 response format\n    return {\n      id: user.id,\n      name: user.name,\n      email: user.email,\n    };\n  }\n}\n\n@Controller('users')\n@Version('2')\nexport class UsersV2Controller {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<UserV2Response> {\n    const user = await this.usersService.findOne(id);\n    // V2 response format with breaking changes\n    return {\n      id: user.id,\n      firstName: user.firstName,\n      lastName: user.lastName,\n      emailAddress: user.email,\n      createdAt: user.createdAt,\n    };\n  }\n}\n\n// Per-route versioning - different versions for different routes\n@Controller('users')\nexport class UsersController {\n  @Get()\n  @Version('1')\n  findAllV1(): Promise<UserV1Response[]> {\n    return this.usersService.findAllV1();\n  }\n\n  @Get()\n  @Version('2')\n  findAllV2(): Promise<UserV2Response[]> {\n    return this.usersService.findAllV2();\n  }\n\n  @Get(':id')\n  @Version(['1', '2']) // Same handler for multiple versions\n  findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findOne(id);\n  }\n\n  @Post()\n  @Version(VERSION_NEUTRAL) // Available in all versions\n  create(@Body() dto: CreateUserDto): Promise<User> {\n    return this.usersService.create(dto);\n  }\n}\n\n// Shared service with version-specific logic\n@Injectable()\nexport class UsersService {\n  async findOne(id: string, version: string): Promise<any> {\n    const user = await this.repo.findOne({ where: { id } });\n\n    if (version === '1') {\n      return this.toV1Response(user);\n    }\n    return this.toV2Response(user);\n  }\n\n  private toV1Response(user: User): UserV1Response {\n    return {\n      id: user.id,\n      name: `${user.firstName} ${user.lastName}`,\n      email: user.email,\n    };\n  }\n\n  private toV2Response(user: User): UserV2Response {\n    return {\n      id: user.id,\n      firstName: user.firstName,\n      lastName: user.lastName,\n      emailAddress: user.email,\n      createdAt: user.createdAt,\n    };\n  }\n}\n\n// Controller extracts version\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string, @Headers('X-API-Version') version: string = '1'): Promise<any> {\n    return this.usersService.findOne(id, version);\n  }\n}\n\n// Deprecation strategy - mark old versions as deprecated\n@Controller('users')\n@Version('1')\n@UseInterceptors(DeprecationInterceptor)\nexport class UsersV1Controller {\n  // All V1 routes will include deprecation warning\n}\n\n@Injectable()\nexport class DeprecationInterceptor implements NestInterceptor {\n  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n    const response = context.switchToHttp().getResponse();\n    response.setHeader('Deprecation', 'true');\n    response.setHeader('Sunset', 'Sat, 1 Jan 2025 00:00:00 GMT');\n    response.setHeader('Link', '</v2/users>; rel=\"successor-version\"');\n\n    return next.handle();\n  }\n}\n```\n\nReference: [NestJS Versioning](https://docs.nestjs.com/techniques/versioning)","frontmatter":{"title":"Use API Versioning for Breaking Changes","impact":"MEDIUM","impactDescription":"Versioning allows you to evolve APIs without breaking existing clients","tags":"api, versioning, breaking-changes, compatibility"},"isInternal":false,"tokens":1199,"sizeBytes":4972},{"name":"arch-avoid-circular-deps.md","path":".agents/skills/nestjs-best-practices/rules/arch-avoid-circular-deps.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/arch-avoid-circular-deps.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Avoid Circular Dependencies\nimpact: CRITICAL\nimpactDescription: '#1 cause of runtime crashes'\ntags: architecture, modules, dependencies\n---\n\n## Avoid Circular Dependencies\n\nCircular dependencies occur when Module A imports Module B, and Module B imports Module A (directly or transitively). NestJS can sometimes resolve these through forward references, but they indicate architectural problems and should be avoided. This is the #1 cause of runtime crashes in NestJS applications.\n\n**Incorrect (circular module imports):**\n\n```typescript\n// users.module.ts\n@Module({\n  imports: [OrdersModule], // Orders needs Users, Users needs Orders = circular\n  providers: [UsersService],\n  exports: [UsersService],\n})\nexport class UsersModule {}\n\n// orders.module.ts\n@Module({\n  imports: [UsersModule], // Circular dependency!\n  providers: [OrdersService],\n  exports: [OrdersService],\n})\nexport class OrdersModule {}\n```\n\n**Correct (extract shared logic or use events):**\n\n```typescript\n// Option 1: Extract shared logic to a third module\n// shared.module.ts\n@Module({\n  providers: [SharedService],\n  exports: [SharedService],\n})\nexport class SharedModule {}\n\n// users.module.ts\n@Module({\n  imports: [SharedModule],\n  providers: [UsersService],\n})\nexport class UsersModule {}\n\n// orders.module.ts\n@Module({\n  imports: [SharedModule],\n  providers: [OrdersService],\n})\nexport class OrdersModule {}\n\n// Option 2: Use events for decoupled communication\n// users.service.ts\n@Injectable()\nexport class UsersService {\n  constructor(private eventEmitter: EventEmitter2) {}\n\n  async createUser(data: CreateUserDto) {\n    const user = await this.userRepo.save(data);\n    this.eventEmitter.emit('user.created', user);\n    return user;\n  }\n}\n\n// orders.service.ts\n@Injectable()\nexport class OrdersService {\n  @OnEvent('user.created')\n  handleUserCreated(user: User) {\n    // React to user creation without direct dependency\n  }\n}\n```\n\nReference: [NestJS Circular Dependency](https://docs.nestjs.com/fundamentals/circular-dependency)","frontmatter":{"title":"Avoid Circular Dependencies","impact":"CRITICAL","impactDescription":"#1 cause of runtime crashes","tags":"architecture, modules, dependencies"},"isInternal":false,"tokens":438,"sizeBytes":2020},{"name":"arch-feature-modules.md","path":".agents/skills/nestjs-best-practices/rules/arch-feature-modules.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/arch-feature-modules.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Organize by Feature Modules\nimpact: CRITICAL\nimpactDescription: '3-5x faster onboarding and development'\ntags: architecture, modules, organization\n---\n\n## Organize by Feature Modules\n\nOrganize your application into feature modules that encapsulate related functionality. Each feature module should be self-contained with its own controllers, services, entities, and DTOs. Avoid organizing by technical layer (all controllers together, all services together). This enables 3-5x faster onboarding and feature development.\n\n**Incorrect (technical layer organization):**\n\n```typescript\n// Technical layer organization (anti-pattern)\nsrc/\n├── controllers/\n│   ├── users.controller.ts\n│   ├── orders.controller.ts\n│   └── products.controller.ts\n├── services/\n│   ├── users.service.ts\n│   ├── orders.service.ts\n│   └── products.service.ts\n├── entities/\n│   ├── user.entity.ts\n│   ├── order.entity.ts\n│   └── product.entity.ts\n└── app.module.ts  // Imports everything directly\n```\n\n**Correct (feature module organization):**\n\n```typescript\n// Feature module organization\nsrc/\n├── users/\n│   ├── dto/\n│   │   ├── create-user.dto.ts\n│   │   └── update-user.dto.ts\n│   ├── entities/\n│   │   └── user.entity.ts\n│   ├── users.controller.ts\n│   ├── users.service.ts\n│   ├── users.repository.ts\n│   └── users.module.ts\n├── orders/\n│   ├── dto/\n│   ├── entities/\n│   ├── orders.controller.ts\n│   ├── orders.service.ts\n│   └── orders.module.ts\n├── shared/\n│   ├── guards/\n│   ├── interceptors/\n│   ├── filters/\n│   └── shared.module.ts\n└── app.module.ts\n\n// users.module.ts\n@Module({\n  imports: [TypeOrmModule.forFeature([User])],\n  controllers: [UsersController],\n  providers: [UsersService, UsersRepository],\n  exports: [UsersService], // Only export what others need\n})\nexport class UsersModule {}\n\n// app.module.ts\n@Module({\n  imports: [\n    ConfigModule.forRoot(),\n    TypeOrmModule.forRoot(),\n    UsersModule,\n    OrdersModule,\n    SharedModule,\n  ],\n})\nexport class AppModule {}\n```\n\nReference: [NestJS Modules](https://docs.nestjs.com/modules)","frontmatter":{"title":"Organize by Feature Modules","impact":"CRITICAL","impactDescription":"3-5x faster onboarding and development","tags":"architecture, modules, organization"},"isInternal":false,"tokens":511,"sizeBytes":2315},{"name":"arch-module-sharing.md","path":".agents/skills/nestjs-best-practices/rules/arch-module-sharing.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/arch-module-sharing.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Proper Module Sharing Patterns\nimpact: CRITICAL\nimpactDescription: Prevents duplicate instances, memory leaks, and state inconsistency\ntags: architecture, modules, sharing, exports\n---\n\n## Use Proper Module Sharing Patterns\n\nNestJS modules are singletons by default. When a service is properly exported from a module and that module is imported elsewhere, the same instance is shared. However, providing a service in multiple modules creates separate instances, leading to memory waste, state inconsistency, and confusing behavior. Always encapsulate services in dedicated modules, export them explicitly, and import the module where needed.\n\n**Incorrect (service provided in multiple modules):**\n\n```typescript\n// StorageService provided directly in multiple modules - WRONG\n// storage.service.ts\n@Injectable()\nexport class StorageService {\n  private cache = new Map(); // Each instance has separate state!\n\n  store(key: string, value: any) {\n    this.cache.set(key, value);\n  }\n}\n\n// app.module.ts\n@Module({\n  providers: [StorageService], // Instance #1\n  controllers: [AppController],\n})\nexport class AppModule {}\n\n// videos.module.ts\n@Module({\n  providers: [StorageService], // Instance #2 - different from AppModule!\n  controllers: [VideosController],\n})\nexport class VideosModule {}\n\n// Problems:\n// 1. Two separate StorageService instances exist\n// 2. cache.set() in VideosModule doesn't affect AppModule's cache\n// 3. Memory wasted on duplicate instances\n// 4. Debugging nightmares when state doesn't sync\n```\n\n**Correct (dedicated module with exports):**\n\n```typescript\n// storage/storage.module.ts\n@Module({\n  providers: [StorageService],\n  exports: [StorageService], // Make available to importers\n})\nexport class StorageModule {}\n\n// videos/videos.module.ts\n@Module({\n  imports: [StorageModule], // Import the module, not the service\n  controllers: [VideosController],\n  providers: [VideosService],\n})\nexport class VideosModule {}\n\n// channels/channels.module.ts\n@Module({\n  imports: [StorageModule], // Same instance shared\n  controllers: [ChannelsController],\n  providers: [ChannelsService],\n})\nexport class ChannelsModule {}\n\n// app.module.ts\n@Module({\n  imports: [\n    StorageModule, // Only if AppModule itself needs StorageService\n    VideosModule,\n    ChannelsModule,\n  ],\n})\nexport class AppModule {}\n\n// Now all modules share the SAME StorageService instance\n```\n\n**When to use @Global() (sparingly):**\n\n```typescript\n// ONLY for truly cross-cutting concerns\n@Global()\n@Module({\n  providers: [ConfigService, LoggerService],\n  exports: [ConfigService, LoggerService],\n})\nexport class CoreModule {}\n\n// Import once in AppModule\n@Module({\n  imports: [CoreModule], // Registered globally, available everywhere\n})\nexport class AppModule {}\n\n// Other modules don't need to import CoreModule\n@Module({\n  controllers: [UsersController],\n  providers: [UsersService], // Can inject ConfigService without importing\n})\nexport class UsersModule {}\n\n// WARNING: Don't make everything global!\n// - Hides dependencies (can't see what a module needs from imports)\n// - Makes testing harder\n// - Reserve for: config, logging, database connections\n```\n\n**Module re-exporting pattern:**\n\n```typescript\n// common.module.ts - shared utilities\n@Module({\n  providers: [DateService, ValidationService],\n  exports: [DateService, ValidationService],\n})\nexport class CommonModule {}\n\n// core.module.ts - re-exports common for convenience\n@Module({\n  imports: [CommonModule, DatabaseModule],\n  exports: [CommonModule, DatabaseModule], // Re-export for consumers\n})\nexport class CoreModule {}\n\n// feature.module.ts - imports CoreModule, gets both\n@Module({\n  imports: [CoreModule], // Gets CommonModule + DatabaseModule\n  controllers: [FeatureController],\n})\nexport class FeatureModule {}\n```\n\nReference: [NestJS Modules](https://docs.nestjs.com/modules#shared-modules)","frontmatter":{"title":"Use Proper Module Sharing Patterns","impact":"CRITICAL","impactDescription":"Prevents duplicate instances, memory leaks, and state inconsistency","tags":"architecture, modules, sharing, exports"},"isInternal":false,"tokens":825,"sizeBytes":3875},{"name":"arch-single-responsibility.md","path":".agents/skills/nestjs-best-practices/rules/arch-single-responsibility.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/arch-single-responsibility.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Single Responsibility for Services\nimpact: CRITICAL\nimpactDescription: '40%+ improvement in testability'\ntags: architecture, services, single-responsibility\n---\n\n## Single Responsibility for Services\n\nEach service should have a single, well-defined responsibility. Avoid \"god services\" that handle multiple unrelated concerns. If a service name includes \"And\" or handles more than one domain concept, it likely violates single responsibility. This reduces complexity and improves testability by 40%+.\n\n**Incorrect (god service anti-pattern):**\n\n```typescript\n// God service anti-pattern\n@Injectable()\nexport class UserAndOrderService {\n  constructor(\n    private userRepo: UserRepository,\n    private orderRepo: OrderRepository,\n    private mailer: MailService,\n    private payment: PaymentService,\n  ) {}\n\n  async createUser(dto: CreateUserDto) {\n    const user = await this.userRepo.save(dto);\n    await this.mailer.sendWelcome(user);\n    return user;\n  }\n\n  async createOrder(userId: string, dto: CreateOrderDto) {\n    const order = await this.orderRepo.save({ userId, ...dto });\n    await this.payment.charge(order);\n    await this.mailer.sendOrderConfirmation(order);\n    return order;\n  }\n\n  async calculateOrderStats(userId: string) {\n    // Stats logic mixed in\n  }\n\n  async validatePayment(orderId: string) {\n    // Payment logic mixed in\n  }\n}\n```\n\n**Correct (focused services with single responsibility):**\n\n```typescript\n// Focused services with single responsibility\n@Injectable()\nexport class UsersService {\n  constructor(private userRepo: UserRepository) {}\n\n  async create(dto: CreateUserDto): Promise<User> {\n    return this.userRepo.save(dto);\n  }\n\n  async findById(id: string): Promise<User> {\n    return this.userRepo.findOneOrFail({ where: { id } });\n  }\n}\n\n@Injectable()\nexport class OrdersService {\n  constructor(private orderRepo: OrderRepository) {}\n\n  async create(userId: string, dto: CreateOrderDto): Promise<Order> {\n    return this.orderRepo.save({ userId, ...dto });\n  }\n\n  async findByUser(userId: string): Promise<Order[]> {\n    return this.orderRepo.find({ where: { userId } });\n  }\n}\n\n@Injectable()\nexport class OrderStatsService {\n  constructor(private orderRepo: OrderRepository) {}\n\n  async calculateForUser(userId: string): Promise<OrderStats> {\n    // Focused stats calculation\n  }\n}\n\n// Orchestration in controller or dedicated orchestrator\n@Controller('orders')\nexport class OrdersController {\n  constructor(\n    private orders: OrdersService,\n    private payment: PaymentService,\n    private notifications: NotificationService,\n  ) {}\n\n  @Post()\n  async create(@CurrentUser() user: User, @Body() dto: CreateOrderDto) {\n    const order = await this.orders.create(user.id, dto);\n    await this.payment.charge(order);\n    await this.notifications.sendOrderConfirmation(order);\n    return order;\n  }\n}\n```\n\nReference: [NestJS Providers](https://docs.nestjs.com/providers)","frontmatter":{"title":"Single Responsibility for Services","impact":"CRITICAL","impactDescription":"40%+ improvement in testability","tags":"architecture, services, single-responsibility"},"isInternal":false,"tokens":625,"sizeBytes":2921},{"name":"arch-use-events.md","path":".agents/skills/nestjs-best-practices/rules/arch-use-events.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/arch-use-events.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Event-Driven Architecture for Decoupling\nimpact: MEDIUM-HIGH\nimpactDescription: Enables async processing and modularity\ntags: architecture, events, decoupling\n---\n\n## Use Event-Driven Architecture for Decoupling\n\nUse `@nestjs/event-emitter` for intra-service events and message brokers for inter-service communication. Events allow modules to react to changes without direct dependencies, improving modularity and enabling async processing.\n\n**Incorrect (direct service coupling):**\n\n```typescript\n// Direct service coupling\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private inventoryService: InventoryService,\n    private emailService: EmailService,\n    private analyticsService: AnalyticsService,\n    private notificationService: NotificationService,\n    private loyaltyService: LoyaltyService,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Tight coupling - OrdersService knows about all consumers\n    await this.inventoryService.reserve(order.items);\n    await this.emailService.sendConfirmation(order);\n    await this.analyticsService.track('order_created', order);\n    await this.notificationService.push(order.userId, 'Order placed');\n    await this.loyaltyService.addPoints(order.userId, order.total);\n\n    // Adding new behavior requires modifying this service\n    return order;\n  }\n}\n```\n\n**Correct (event-driven decoupling):**\n\n```typescript\n// Use EventEmitter for decoupling\nimport { EventEmitter2 } from '@nestjs/event-emitter';\n\n// Define event\nexport class OrderCreatedEvent {\n  constructor(\n    public readonly orderId: string,\n    public readonly userId: string,\n    public readonly items: OrderItem[],\n    public readonly total: number,\n  ) {}\n}\n\n// Service emits events\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private eventEmitter: EventEmitter2,\n    private repo: Repository<Order>,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Emit event - no knowledge of consumers\n    this.eventEmitter.emit('order.created', new OrderCreatedEvent(order.id, order.userId, order.items, order.total));\n\n    return order;\n  }\n}\n\n// Listeners in separate modules\n@Injectable()\nexport class InventoryListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.inventoryService.reserve(event.items);\n  }\n}\n\n@Injectable()\nexport class EmailListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.emailService.sendConfirmation(event.orderId);\n  }\n}\n\n@Injectable()\nexport class AnalyticsListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.analyticsService.track('order_created', {\n      orderId: event.orderId,\n      total: event.total,\n    });\n  }\n}\n```\n\nReference: [NestJS Events](https://docs.nestjs.com/techniques/events)","frontmatter":{"title":"Use Event-Driven Architecture for Decoupling","impact":"MEDIUM-HIGH","impactDescription":"Enables async processing and modularity","tags":"architecture, events, decoupling"},"isInternal":false,"tokens":626,"sizeBytes":3031},{"name":"arch-use-repository-pattern.md","path":".agents/skills/nestjs-best-practices/rules/arch-use-repository-pattern.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/arch-use-repository-pattern.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Repository Pattern for Data Access\nimpact: HIGH\nimpactDescription: Decouples business logic from database\ntags: architecture, repository, data-access\n---\n\n## Use Repository Pattern for Data Access\n\nCreate custom repositories to encapsulate complex queries and database logic. This keeps services focused on business logic, makes testing easier with mock repositories, and allows changing database implementations without affecting business code.\n\n**Incorrect (complex queries in services):**\n\n```typescript\n// Complex queries in services\n@Injectable()\nexport class UsersService {\n  constructor(@InjectRepository(User) private repo: Repository<User>) {}\n\n  async findActiveWithOrders(minOrders: number): Promise<User[]> {\n    // Complex query logic mixed with business logic\n    return this.repo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.orders', 'order')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('user.deletedAt IS NULL')\n      .groupBy('user.id')\n      .having('COUNT(order.id) >= :min', { min: minOrders })\n      .orderBy('user.createdAt', 'DESC')\n      .getMany();\n  }\n\n  // Service becomes bloated with query logic\n}\n```\n\n**Correct (custom repository with encapsulated queries):**\n\n```typescript\n// Custom repository with encapsulated queries\n@Injectable()\nexport class UsersRepository {\n  constructor(@InjectRepository(User) private repo: Repository<User>) {}\n\n  async findById(id: string): Promise<User | null> {\n    return this.repo.findOne({ where: { id } });\n  }\n\n  async findByEmail(email: string): Promise<User | null> {\n    return this.repo.findOne({ where: { email } });\n  }\n\n  async findActiveWithMinOrders(minOrders: number): Promise<User[]> {\n    return this.repo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.orders', 'order')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('user.deletedAt IS NULL')\n      .groupBy('user.id')\n      .having('COUNT(order.id) >= :min', { min: minOrders })\n      .orderBy('user.createdAt', 'DESC')\n      .getMany();\n  }\n\n  async save(user: User): Promise<User> {\n    return this.repo.save(user);\n  }\n}\n\n// Clean service with business logic only\n@Injectable()\nexport class UsersService {\n  constructor(private usersRepo: UsersRepository) {}\n\n  async getActiveUsersWithOrders(): Promise<User[]> {\n    return this.usersRepo.findActiveWithMinOrders(1);\n  }\n\n  async create(dto: CreateUserDto): Promise<User> {\n    const existing = await this.usersRepo.findByEmail(dto.email);\n    if (existing) {\n      throw new ConflictException('Email already registered');\n    }\n\n    const user = new User();\n    user.email = dto.email;\n    user.name = dto.name;\n    return this.usersRepo.save(user);\n  }\n}\n```\n\nReference: [Repository Pattern](https://martinfowler.com/eaaCatalog/repository.html)","frontmatter":{"title":"Use Repository Pattern for Data Access","impact":"HIGH","impactDescription":"Decouples business logic from database","tags":"architecture, repository, data-access"},"isInternal":false,"tokens":618,"sizeBytes":2842},{"name":"db-avoid-n-plus-one.md","path":".agents/skills/nestjs-best-practices/rules/db-avoid-n-plus-one.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/db-avoid-n-plus-one.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Avoid N+1 Query Problems\nimpact: HIGH\nimpactDescription: N+1 queries are one of the most common performance killers\ntags: database, n-plus-one, queries, performance\n---\n\n## Avoid N+1 Query Problems\n\nN+1 queries occur when you fetch a list of entities, then make an additional query for each entity to load related data. Use eager loading with `relations`, query builder joins, or DataLoader to batch queries efficiently.\n\n**Incorrect (lazy loading in loops causes N+1):**\n\n```typescript\n// Lazy loading in loops causes N+1\n@Injectable()\nexport class OrdersService {\n  async getOrdersWithItems(userId: string): Promise<Order[]> {\n    const orders = await this.orderRepo.find({ where: { userId } });\n    // 1 query for orders\n\n    for (const order of orders) {\n      // N additional queries - one per order!\n      order.items = await this.itemRepo.find({ where: { orderId: order.id } });\n    }\n\n    return orders;\n  }\n}\n\n// Accessing lazy relations without loading\n@Controller('users')\nexport class UsersController {\n  @Get()\n  async findAll(): Promise<User[]> {\n    const users = await this.userRepo.find();\n    // If User.posts is lazy-loaded, serializing triggers N queries\n    return users; // Each user.posts access = 1 query\n  }\n}\n```\n\n**Correct (use relations for eager loading):**\n\n```typescript\n// Use relations option for eager loading\n@Injectable()\nexport class OrdersService {\n  async getOrdersWithItems(userId: string): Promise<Order[]> {\n    // Single query with JOIN\n    return this.orderRepo.find({\n      where: { userId },\n      relations: ['items', 'items.product'],\n    });\n  }\n}\n\n// Use QueryBuilder for complex joins\n@Injectable()\nexport class UsersService {\n  async getUsersWithPostCounts(): Promise<UserWithPostCount[]> {\n    return this.userRepo\n      .createQueryBuilder('user')\n      .leftJoin('user.posts', 'post')\n      .select('user.id', 'id')\n      .addSelect('user.name', 'name')\n      .addSelect('COUNT(post.id)', 'postCount')\n      .groupBy('user.id')\n      .getRawMany();\n  }\n\n  async getActiveUsersWithPosts(): Promise<User[]> {\n    return this.userRepo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.posts', 'post')\n      .leftJoinAndSelect('post.comments', 'comment')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('post.status = :status', { status: 'published' })\n      .getMany();\n  }\n}\n\n// Use find options for specific fields\nasync getOrderSummaries(userId: string): Promise<OrderSummary[]> {\n  return this.orderRepo.find({\n    where: { userId },\n    relations: ['items'],\n    select: {\n      id: true,\n      total: true,\n      status: true,\n      items: {\n        id: true,\n        quantity: true,\n        price: true,\n      },\n    },\n  });\n}\n\n// Use DataLoader for GraphQL to batch and cache queries\nimport DataLoader from 'dataloader';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class PostsLoader {\n  constructor(private postsService: PostsService) {}\n\n  readonly batchPosts = new DataLoader<string, Post[]>(async (userIds) => {\n    // Single query for all users' posts\n    const posts = await this.postsService.findByUserIds([...userIds]);\n\n    // Group by userId\n    const postsMap = new Map<string, Post[]>();\n    for (const post of posts) {\n      const userPosts = postsMap.get(post.userId) || [];\n      userPosts.push(post);\n      postsMap.set(post.userId, userPosts);\n    }\n\n    // Return in same order as input\n    return userIds.map((id) => postsMap.get(id) || []);\n  });\n}\n\n// In resolver\n@ResolveField()\nasync posts(@Parent() user: User): Promise<Post[]> {\n  // DataLoader batches multiple calls into single query\n  return this.postsLoader.batchPosts.load(user.id);\n}\n\n// Enable query logging in development to detect N+1\nTypeOrmModule.forRoot({\n  logging: ['query', 'error'],\n  logger: 'advanced-console',\n});\n```\n\nReference: [TypeORM Relations](https://typeorm.io/relations)","frontmatter":{"title":"Avoid N+1 Query Problems","impact":"HIGH","impactDescription":"N+1 queries are one of the most common performance killers","tags":"database, n-plus-one, queries, performance"},"isInternal":false,"tokens":897,"sizeBytes":3887},{"name":"db-use-migrations.md","path":".agents/skills/nestjs-best-practices/rules/db-use-migrations.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/db-use-migrations.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Database Migrations\nimpact: HIGH\nimpactDescription: Enables safe, repeatable database schema changes\ntags: database, migrations, typeorm, schema\n---\n\n## Use Database Migrations\n\nNever use `synchronize: true` in production. Use migrations for all schema changes. Migrations provide version control for your database, enable safe rollbacks, and ensure consistency across all environments.\n\n**Incorrect (using synchronize or manual SQL):**\n\n```typescript\n// Use synchronize in production\nTypeOrmModule.forRoot({\n  type: 'postgres',\n  synchronize: true, // DANGEROUS in production!\n  // Can drop columns, tables, or data\n});\n\n// Manual SQL in production\n@Injectable()\nexport class DatabaseService {\n  async addColumn(): Promise<void> {\n    await this.dataSource.query('ALTER TABLE users ADD COLUMN age INT');\n    // No version control, no rollback, inconsistent across envs\n  }\n}\n\n// Modify entities without migration\n@Entity()\nexport class User {\n  @Column()\n  email: string;\n\n  @Column() // Added without migration\n  newField: string; // Will crash in production if synchronize is false\n}\n```\n\n**Correct (use migrations for all schema changes):**\n\n```typescript\n// Configure TypeORM for migrations\n// data-source.ts\nexport const dataSource = new DataSource({\n  type: 'postgres',\n  host: process.env.DB_HOST,\n  port: parseInt(process.env.DB_PORT),\n  username: process.env.DB_USERNAME,\n  password: process.env.DB_PASSWORD,\n  database: process.env.DB_NAME,\n  entities: ['dist/**/*.entity.js'],\n  migrations: ['dist/migrations/*.js'],\n  synchronize: false, // Always false in production\n  migrationsRun: true, // Run migrations on startup\n});\n\n// app.module.ts\nTypeOrmModule.forRootAsync({\n  inject: [ConfigService],\n  useFactory: (config: ConfigService) => ({\n    type: 'postgres',\n    host: config.get('DB_HOST'),\n    synchronize: config.get('NODE_ENV') === 'development', // Only in dev\n    migrations: ['dist/migrations/*.js'],\n    migrationsRun: true,\n  }),\n});\n\n// migrations/1705312800000-AddUserAge.ts\nimport { MigrationInterface, QueryRunner } from 'typeorm';\n\nexport class AddUserAge1705312800000 implements MigrationInterface {\n  name = 'AddUserAge1705312800000';\n\n  public async up(queryRunner: QueryRunner): Promise<void> {\n    // Add column with default to handle existing rows\n    await queryRunner.query(`\n      ALTER TABLE \"users\" ADD \"age\" integer DEFAULT 0\n    `);\n\n    // Add index for frequently queried columns\n    await queryRunner.query(`\n      CREATE INDEX \"IDX_users_age\" ON \"users\" (\"age\")\n    `);\n  }\n\n  public async down(queryRunner: QueryRunner): Promise<void> {\n    // Always implement down for rollback\n    await queryRunner.query(`DROP INDEX \"IDX_users_age\"`);\n    await queryRunner.query(`ALTER TABLE \"users\" DROP COLUMN \"age\"`);\n  }\n}\n\n// Safe column rename (two-step)\nexport class RenameNameToFullName1705312900000 implements MigrationInterface {\n  public async up(queryRunner: QueryRunner): Promise<void> {\n    // Step 1: Add new column\n    await queryRunner.query(`\n      ALTER TABLE \"users\" ADD \"full_name\" varchar(255)\n    `);\n\n    // Step 2: Copy data\n    await queryRunner.query(`\n      UPDATE \"users\" SET \"full_name\" = \"name\"\n    `);\n\n    // Step 3: Add NOT NULL constraint\n    await queryRunner.query(`\n      ALTER TABLE \"users\" ALTER COLUMN \"full_name\" SET NOT NULL\n    `);\n\n    // Step 4: Drop old column (after verifying app works)\n    await queryRunner.query(`\n      ALTER TABLE \"users\" DROP COLUMN \"name\"\n    `);\n  }\n\n  public async down(queryRunner: QueryRunner): Promise<void> {\n    await queryRunner.query(`ALTER TABLE \"users\" ADD \"name\" varchar(255)`);\n    await queryRunner.query(`UPDATE \"users\" SET \"name\" = \"full_name\"`);\n    await queryRunner.query(`ALTER TABLE \"users\" DROP COLUMN \"full_name\"`);\n  }\n}\n```\n\nReference: [TypeORM Migrations](https://typeorm.io/migrations)","frontmatter":{"title":"Use Database Migrations","impact":"HIGH","impactDescription":"Enables safe, repeatable database schema changes","tags":"database, migrations, typeorm, schema"},"isInternal":false,"tokens":904,"sizeBytes":3836},{"name":"db-use-transactions.md","path":".agents/skills/nestjs-best-practices/rules/db-use-transactions.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/db-use-transactions.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Transactions for Multi-Step Operations\nimpact: HIGH\nimpactDescription: Ensures data consistency in multi-step operations\ntags: database, transactions, typeorm, consistency\n---\n\n## Use Transactions for Multi-Step Operations\n\nWhen multiple database operations must succeed or fail together, wrap them in a transaction. This prevents partial updates that leave your data in an inconsistent state. Use TypeORM's transaction APIs or the DataSource query runner for complex scenarios.\n\n**Incorrect (multiple saves without transaction):**\n\n```typescript\n// Multiple saves without transaction\n@Injectable()\nexport class OrdersService {\n  async createOrder(userId: string, items: OrderItem[]): Promise<Order> {\n    // If any step fails, data is inconsistent\n    const order = await this.orderRepo.save({ userId, status: 'pending' });\n\n    for (const item of items) {\n      await this.orderItemRepo.save({ orderId: order.id, ...item });\n      await this.inventoryRepo.decrement({ productId: item.productId }, 'stock', item.quantity);\n    }\n\n    await this.paymentService.charge(order.id);\n    // If payment fails, order and inventory are already modified!\n\n    return order;\n  }\n}\n```\n\n**Correct (use DataSource.transaction for automatic rollback):**\n\n```typescript\n// Use DataSource.transaction() for automatic rollback\n@Injectable()\nexport class OrdersService {\n  constructor(private dataSource: DataSource) {}\n\n  async createOrder(userId: string, items: OrderItem[]): Promise<Order> {\n    return this.dataSource.transaction(async manager => {\n      // All operations use the same transactional manager\n      const order = await manager.save(Order, { userId, status: 'pending' });\n\n      for (const item of items) {\n        await manager.save(OrderItem, { orderId: order.id, ...item });\n        await manager.decrement(Inventory, { productId: item.productId }, 'stock', item.quantity);\n      }\n\n      // If this throws, everything rolls back\n      await this.paymentService.chargeWithManager(manager, order.id);\n\n      return order;\n    });\n  }\n}\n\n// QueryRunner for manual transaction control\n@Injectable()\nexport class TransferService {\n  constructor(private dataSource: DataSource) {}\n\n  async transfer(fromId: string, toId: string, amount: number): Promise<void> {\n    const queryRunner = this.dataSource.createQueryRunner();\n    await queryRunner.connect();\n    await queryRunner.startTransaction();\n\n    try {\n      // Debit source account\n      await queryRunner.manager.decrement(Account, { id: fromId }, 'balance', amount);\n\n      // Verify sufficient funds\n      const source = await queryRunner.manager.findOne(Account, {\n        where: { id: fromId },\n      });\n      if (source.balance < 0) {\n        throw new BadRequestException('Insufficient funds');\n      }\n\n      // Credit destination account\n      await queryRunner.manager.increment(Account, { id: toId }, 'balance', amount);\n\n      // Log the transaction\n      await queryRunner.manager.save(TransactionLog, {\n        fromId,\n        toId,\n        amount,\n        timestamp: new Date(),\n      });\n\n      await queryRunner.commitTransaction();\n    } catch (error) {\n      await queryRunner.rollbackTransaction();\n      throw error;\n    } finally {\n      await queryRunner.release();\n    }\n  }\n}\n\n// Repository method with transaction support\n@Injectable()\nexport class UsersRepository {\n  constructor(\n    @InjectRepository(User) private repo: Repository<User>,\n    private dataSource: DataSource,\n  ) {}\n\n  async createWithProfile(userData: CreateUserDto, profileData: CreateProfileDto): Promise<User> {\n    return this.dataSource.transaction(async manager => {\n      const user = await manager.save(User, userData);\n      await manager.save(Profile, { ...profileData, userId: user.id });\n      return user;\n    });\n  }\n}\n```\n\nReference: [TypeORM Transactions](https://typeorm.io/transactions)","frontmatter":{"title":"Use Transactions for Multi-Step Operations","impact":"HIGH","impactDescription":"Ensures data consistency in multi-step operations","tags":"database, transactions, typeorm, consistency"},"isInternal":false,"tokens":782,"sizeBytes":3872},{"name":"devops-graceful-shutdown.md","path":".agents/skills/nestjs-best-practices/rules/devops-graceful-shutdown.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/devops-graceful-shutdown.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Implement Graceful Shutdown\nimpact: MEDIUM-HIGH\nimpactDescription: Proper shutdown handling ensures zero-downtime deployments\ntags: devops, graceful-shutdown, lifecycle, kubernetes\n---\n\n## Implement Graceful Shutdown\n\nHandle SIGTERM and SIGINT signals to gracefully shutdown your NestJS application. Stop accepting new requests, wait for in-flight requests to complete, close database connections, and clean up resources. This prevents data loss and connection errors during deployments.\n\n**Incorrect (ignoring shutdown signals):**\n\n```typescript\n// Ignore shutdown signals\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  await app.listen(3000);\n  // App crashes immediately on SIGTERM\n  // In-flight requests fail\n  // Database connections are abruptly closed\n}\n\n// Long-running tasks without cancellation\n@Injectable()\nexport class ProcessingService {\n  async processLargeFile(file: File): Promise<void> {\n    // No way to interrupt this during shutdown\n    for (let i = 0; i < file.chunks.length; i++) {\n      await this.processChunk(file.chunks[i]);\n      // May run for minutes, blocking shutdown\n    }\n  }\n}\n```\n\n**Correct (enable shutdown hooks and handle cleanup):**\n\n```typescript\n// Enable shutdown hooks in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n\n  // Enable shutdown hooks\n  app.enableShutdownHooks();\n\n  // Optional: Add timeout for forced shutdown\n  const server = await app.listen(3000);\n  server.setTimeout(30000); // 30 second timeout\n\n  // Handle graceful shutdown\n  const signals = ['SIGTERM', 'SIGINT'];\n  signals.forEach(signal => {\n    process.on(signal, async () => {\n      console.log(`Received ${signal}, starting graceful shutdown...`);\n\n      // Stop accepting new connections\n      server.close(async () => {\n        console.log('HTTP server closed');\n        await app.close();\n        process.exit(0);\n      });\n\n      // Force exit after timeout\n      setTimeout(() => {\n        console.error('Forced shutdown after timeout');\n        process.exit(1);\n      }, 30000);\n    });\n  });\n}\n\n// Lifecycle hooks for cleanup\n@Injectable()\nexport class DatabaseService implements OnApplicationShutdown {\n  private readonly connections: Connection[] = [];\n\n  async onApplicationShutdown(signal?: string): Promise<void> {\n    console.log(`Database service shutting down on ${signal}`);\n\n    // Close all connections gracefully\n    await Promise.all(this.connections.map(conn => conn.close()));\n\n    console.log('All database connections closed');\n  }\n}\n\n// Queue processor with graceful shutdown\n@Injectable()\nexport class QueueService implements OnApplicationShutdown, OnModuleDestroy {\n  private isShuttingDown = false;\n\n  onModuleDestroy(): void {\n    this.isShuttingDown = true;\n  }\n\n  async onApplicationShutdown(): Promise<void> {\n    // Wait for current jobs to complete\n    await this.queue.close();\n  }\n\n  async processJob(job: Job): Promise<void> {\n    if (this.isShuttingDown) {\n      throw new Error('Service is shutting down');\n    }\n    await this.doWork(job);\n  }\n}\n\n// WebSocket gateway cleanup\n@WebSocketGateway()\nexport class EventsGateway implements OnApplicationShutdown {\n  @WebSocketServer()\n  server: Server;\n\n  async onApplicationShutdown(): Promise<void> {\n    // Notify all connected clients\n    this.server.emit('shutdown', { message: 'Server is shutting down' });\n\n    // Close all connections\n    this.server.disconnectSockets();\n  }\n}\n\n// Health check integration\n@Injectable()\nexport class ShutdownService {\n  private isShuttingDown = false;\n\n  startShutdown(): void {\n    this.isShuttingDown = true;\n  }\n\n  isShutdown(): boolean {\n    return this.isShuttingDown;\n  }\n}\n\n@Controller('health')\nexport class HealthController {\n  constructor(private shutdownService: ShutdownService) {}\n\n  @Get('ready')\n  @HealthCheck()\n  readiness(): Promise<HealthCheckResult> {\n    // Return 503 during shutdown - k8s stops sending traffic\n    if (this.shutdownService.isShutdown()) {\n      throw new ServiceUnavailableException('Shutting down');\n    }\n\n    return this.health.check([() => this.db.pingCheck('database')]);\n  }\n}\n\n// Integrate with shutdown\n@Injectable()\nexport class AppShutdownService implements OnApplicationShutdown {\n  constructor(private shutdownService: ShutdownService) {}\n\n  async onApplicationShutdown(): Promise<void> {\n    // Mark as unhealthy first\n    this.shutdownService.startShutdown();\n\n    // Wait for k8s to update endpoints\n    await this.sleep(5000);\n\n    // Then proceed with cleanup\n  }\n}\n\n// Request tracking for in-flight requests\n@Injectable()\nexport class RequestTracker implements NestMiddleware, OnApplicationShutdown {\n  private activeRequests = 0;\n  private isShuttingDown = false;\n  private shutdownPromise: Promise<void> | null = null;\n  private resolveShutdown: (() => void) | null = null;\n\n  use(req: Request, res: Response, next: NextFunction): void {\n    if (this.isShuttingDown) {\n      res.status(503).send('Service Unavailable');\n      return;\n    }\n\n    this.activeRequests++;\n\n    res.on('finish', () => {\n      this.activeRequests--;\n      if (this.isShuttingDown && this.activeRequests === 0 && this.resolveShutdown) {\n        this.resolveShutdown();\n      }\n    });\n\n    next();\n  }\n\n  async onApplicationShutdown(): Promise<void> {\n    this.isShuttingDown = true;\n\n    if (this.activeRequests > 0) {\n      console.log(`Waiting for ${this.activeRequests} requests to complete`);\n      this.shutdownPromise = new Promise(resolve => {\n        this.resolveShutdown = resolve;\n      });\n\n      // Wait with timeout\n      await Promise.race([this.shutdownPromise, new Promise(resolve => setTimeout(resolve, 30000))]);\n    }\n\n    console.log('All requests completed');\n  }\n}\n```\n\nReference: [NestJS Lifecycle Events](https://docs.nestjs.com/fundamentals/lifecycle-events)","frontmatter":{"title":"Implement Graceful Shutdown","impact":"MEDIUM-HIGH","impactDescription":"Proper shutdown handling ensures zero-downtime deployments","tags":"devops, graceful-shutdown, lifecycle, kubernetes"},"isInternal":false,"tokens":1256,"sizeBytes":5856},{"name":"devops-use-config-module.md","path":".agents/skills/nestjs-best-practices/rules/devops-use-config-module.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/devops-use-config-module.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use ConfigModule for Environment Configuration\nimpact: LOW-MEDIUM\nimpactDescription: Proper configuration prevents deployment failures\ntags: devops, configuration, environment, validation\n---\n\n## Use ConfigModule for Environment Configuration\n\nUse `@nestjs/config` for environment-based configuration. Validate configuration at startup to fail fast on misconfigurations. Use namespaced configuration for organization and type safety.\n\n**Incorrect (accessing process.env directly):**\n\n```typescript\n// Access process.env directly\n@Injectable()\nexport class DatabaseService {\n  constructor() {\n    // No validation, can fail at runtime\n    this.connection = new Pool({\n      host: process.env.DB_HOST,\n      port: parseInt(process.env.DB_PORT), // NaN if missing\n      password: process.env.DB_PASSWORD, // undefined if missing\n    });\n  }\n}\n\n// Scattered env access\n@Injectable()\nexport class EmailService {\n  sendEmail() {\n    // Different services access env differently\n    const apiKey = process.env.SENDGRID_API_KEY || 'default';\n    // Typos go unnoticed: process.env.SENDGRID_API_KY\n  }\n}\n```\n\n**Correct (use @nestjs/config with validation):**\n\n```typescript\n// Setup validated configuration\nimport { ConfigModule, ConfigService, registerAs } from '@nestjs/config';\nimport * as Joi from 'joi';\n\n// config/database.config.ts\nexport const databaseConfig = registerAs('database', () => ({\n  host: process.env.DB_HOST,\n  port: parseInt(process.env.DB_PORT, 10),\n  username: process.env.DB_USERNAME,\n  password: process.env.DB_PASSWORD,\n  database: process.env.DB_NAME,\n}));\n\n// config/app.config.ts\nexport const appConfig = registerAs('app', () => ({\n  port: parseInt(process.env.PORT, 10) || 3000,\n  environment: process.env.NODE_ENV || 'development',\n  apiPrefix: process.env.API_PREFIX || 'api',\n}));\n\n// config/validation.schema.ts\nexport const validationSchema = Joi.object({\n  NODE_ENV: Joi.string().valid('development', 'production', 'test').default('development'),\n  PORT: Joi.number().default(3000),\n  DB_HOST: Joi.string().required(),\n  DB_PORT: Joi.number().default(5432),\n  DB_USERNAME: Joi.string().required(),\n  DB_PASSWORD: Joi.string().required(),\n  DB_NAME: Joi.string().required(),\n  JWT_SECRET: Joi.string().min(32).required(),\n  REDIS_URL: Joi.string().uri().required(),\n});\n\n// app.module.ts\n@Module({\n  imports: [\n    ConfigModule.forRoot({\n      isGlobal: true, // Available everywhere without importing\n      load: [databaseConfig, appConfig],\n      validationSchema,\n      validationOptions: {\n        abortEarly: true, // Stop on first error\n        allowUnknown: true, // Allow other env vars\n      },\n    }),\n    TypeOrmModule.forRootAsync({\n      inject: [ConfigService],\n      useFactory: (config: ConfigService) => ({\n        type: 'postgres',\n        host: config.get('database.host'),\n        port: config.get('database.port'),\n        username: config.get('database.username'),\n        password: config.get('database.password'),\n        database: config.get('database.database'),\n        autoLoadEntities: true,\n      }),\n    }),\n  ],\n})\nexport class AppModule {}\n\n// Type-safe configuration access\nexport interface AppConfig {\n  port: number;\n  environment: 'development' | 'production' | 'test';\n  apiPrefix: string;\n}\n\nexport interface DatabaseConfig {\n  host: string;\n  port: number;\n  username: string;\n  password: string;\n  database: string;\n}\n\n// Type-safe access\n@Injectable()\nexport class AppService {\n  constructor(private config: ConfigService) {}\n\n  getPort(): number {\n    // Type-safe with generic\n    return this.config.get<number>('app.port');\n  }\n\n  getDatabaseConfig(): DatabaseConfig {\n    return this.config.get<DatabaseConfig>('database');\n  }\n}\n\n// Inject namespaced config directly\n@Injectable()\nexport class DatabaseService {\n  constructor(\n    @Inject(databaseConfig.KEY)\n    private dbConfig: ConfigType<typeof databaseConfig>,\n  ) {\n    // Full type inference!\n    const host = this.dbConfig.host; // string\n    const port = this.dbConfig.port; // number\n  }\n}\n\n// Environment files support\nConfigModule.forRoot({\n  envFilePath: [`.env.${process.env.NODE_ENV}.local`, `.env.${process.env.NODE_ENV}`, '.env.local', '.env'],\n});\n\n// .env.development\n// DB_HOST=localhost\n// DB_PORT=5432\n\n// .env.production\n// DB_HOST=prod-db.example.com\n// DB_PORT=5432\n```\n\nReference: [NestJS Configuration](https://docs.nestjs.com/techniques/configuration)","frontmatter":{"title":"Use ConfigModule for Environment Configuration","impact":"LOW-MEDIUM","impactDescription":"Proper configuration prevents deployment failures","tags":"devops, configuration, environment, validation"},"isInternal":false,"tokens":991,"sizeBytes":4414},{"name":"devops-use-logging.md","path":".agents/skills/nestjs-best-practices/rules/devops-use-logging.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/devops-use-logging.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Structured Logging\nimpact: MEDIUM-HIGH\nimpactDescription: Structured logging enables effective debugging and monitoring\ntags: devops, logging, structured-logs, pino\n---\n\n## Use Structured Logging\n\nUse NestJS Logger with structured JSON output in production. Include contextual information (request ID, user ID, operation) to trace requests across services. Avoid console.log and implement proper log levels.\n\n**Incorrect (using console.log in production):**\n\n```typescript\n// Use console.log in production\n@Injectable()\nexport class UsersService {\n  async createUser(dto: CreateUserDto): Promise<User> {\n    console.log('Creating user:', dto);\n    // Not structured, no levels, lost in production logs\n\n    try {\n      const user = await this.repo.save(dto);\n      console.log('User created:', user.id);\n      return user;\n    } catch (error) {\n      console.log('Error:', error); // Using log for errors\n      throw error;\n    }\n  }\n}\n\n// Log sensitive data\nconsole.log('Login attempt:', { email, password }); // SECURITY RISK!\n\n// Inconsistent log format\nlogger.log('User ' + userId + ' created at ' + new Date());\n// Hard to parse, no structure\n```\n\n**Correct (use structured logging with context):**\n\n```typescript\n// Configure logger in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule, {\n    logger:\n      process.env.NODE_ENV === 'production' ? ['error', 'warn', 'log'] : ['error', 'warn', 'log', 'debug', 'verbose'],\n  });\n}\n\n// Use NestJS Logger with context\n@Injectable()\nexport class UsersService {\n  private readonly logger = new Logger(UsersService.name);\n\n  async createUser(dto: CreateUserDto): Promise<User> {\n    this.logger.log('Creating user', { email: dto.email });\n\n    try {\n      const user = await this.repo.save(dto);\n      this.logger.log('User created', { userId: user.id });\n      return user;\n    } catch (error) {\n      this.logger.error('Failed to create user', error.stack, {\n        email: dto.email,\n      });\n      throw error;\n    }\n  }\n}\n\n// Custom logger for JSON output\n@Injectable()\nexport class JsonLogger implements LoggerService {\n  log(message: string, context?: object): void {\n    console.log(\n      JSON.stringify({\n        level: 'info',\n        timestamp: new Date().toISOString(),\n        message,\n        ...context,\n      }),\n    );\n  }\n\n  error(message: string, trace?: string, context?: object): void {\n    console.error(\n      JSON.stringify({\n        level: 'error',\n        timestamp: new Date().toISOString(),\n        message,\n        trace,\n        ...context,\n      }),\n    );\n  }\n\n  warn(message: string, context?: object): void {\n    console.warn(\n      JSON.stringify({\n        level: 'warn',\n        timestamp: new Date().toISOString(),\n        message,\n        ...context,\n      }),\n    );\n  }\n\n  debug(message: string, context?: object): void {\n    console.debug(\n      JSON.stringify({\n        level: 'debug',\n        timestamp: new Date().toISOString(),\n        message,\n        ...context,\n      }),\n    );\n  }\n}\n\n// Request context logging with ClsModule\nimport { ClsModule, ClsService } from 'nestjs-cls';\n\n@Module({\n  imports: [\n    ClsModule.forRoot({\n      global: true,\n      middleware: {\n        mount: true,\n        generateId: true,\n      },\n    }),\n  ],\n})\nexport class AppModule {}\n\n// Middleware to set request context\n@Injectable()\nexport class RequestContextMiddleware implements NestMiddleware {\n  constructor(private cls: ClsService) {}\n\n  use(req: Request, res: Response, next: NextFunction): void {\n    const requestId = req.headers['x-request-id'] || randomUUID();\n    this.cls.set('requestId', requestId);\n    this.cls.set('userId', req.user?.id);\n\n    res.setHeader('x-request-id', requestId);\n    next();\n  }\n}\n\n// Logger that includes request context\n@Injectable()\nexport class ContextLogger {\n  constructor(private cls: ClsService) {}\n\n  log(message: string, data?: object): void {\n    console.log(\n      JSON.stringify({\n        level: 'info',\n        timestamp: new Date().toISOString(),\n        requestId: this.cls.get('requestId'),\n        userId: this.cls.get('userId'),\n        message,\n        ...data,\n      }),\n    );\n  }\n\n  error(message: string, error: Error, data?: object): void {\n    console.error(\n      JSON.stringify({\n        level: 'error',\n        timestamp: new Date().toISOString(),\n        requestId: this.cls.get('requestId'),\n        userId: this.cls.get('userId'),\n        message,\n        error: error.message,\n        stack: error.stack,\n        ...data,\n      }),\n    );\n  }\n}\n\n// Pino integration for high-performance logging\nimport { LoggerModule } from 'nestjs-pino';\n\n@Module({\n  imports: [\n    LoggerModule.forRoot({\n      pinoHttp: {\n        level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',\n        transport: process.env.NODE_ENV !== 'production' ? { target: 'pino-pretty' } : undefined,\n        redact: ['req.headers.authorization', 'req.body.password'],\n        serializers: {\n          req: req => ({\n            method: req.method,\n            url: req.url,\n            query: req.query,\n          }),\n          res: res => ({\n            statusCode: res.statusCode,\n          }),\n        },\n      },\n    }),\n  ],\n})\nexport class AppModule {}\n\n// Usage with Pino\n@Injectable()\nexport class UsersService {\n  constructor(private logger: PinoLogger) {\n    this.logger.setContext(UsersService.name);\n  }\n\n  async findOne(id: string): Promise<User> {\n    this.logger.info({ userId: id }, 'Finding user');\n    // Pino uses first arg for data, second for message\n  }\n}\n```\n\nReference: [NestJS Logger](https://docs.nestjs.com/techniques/logger)","frontmatter":{"title":"Use Structured Logging","impact":"MEDIUM-HIGH","impactDescription":"Structured logging enables effective debugging and monitoring","tags":"devops, logging, structured-logs, pino"},"isInternal":false,"tokens":1258,"sizeBytes":5638},{"name":"di-avoid-service-locator.md","path":".agents/skills/nestjs-best-practices/rules/di-avoid-service-locator.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/di-avoid-service-locator.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Avoid Service Locator Anti-Pattern\nimpact: HIGH\nimpactDescription: Hides dependencies and breaks testability\ntags: dependency-injection, anti-patterns, testing\n---\n\n## Avoid Service Locator Anti-Pattern\n\nAvoid using `ModuleRef.get()` or global containers to resolve dependencies at runtime. This hides dependencies, makes code harder to test, and breaks the benefits of dependency injection. Use constructor injection instead.\n\n**Incorrect (service locator anti-pattern):**\n\n```typescript\n// Use ModuleRef to get dependencies dynamically\n@Injectable()\nexport class OrdersService {\n  constructor(private moduleRef: ModuleRef) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    // Dependencies are hidden - not visible in constructor\n    const usersService = this.moduleRef.get(UsersService);\n    const inventoryService = this.moduleRef.get(InventoryService);\n    const paymentService = this.moduleRef.get(PaymentService);\n\n    const user = await usersService.findOne(dto.userId);\n    // ... rest of logic\n  }\n}\n\n// Global singleton container\nclass ServiceContainer {\n  private static instance: ServiceContainer;\n  private services = new Map<string, any>();\n\n  static getInstance(): ServiceContainer {\n    if (!this.instance) {\n      this.instance = new ServiceContainer();\n    }\n    return this.instance;\n  }\n\n  get<T>(key: string): T {\n    return this.services.get(key);\n  }\n}\n```\n\n**Correct (constructor injection with explicit dependencies):**\n\n```typescript\n// Use constructor injection - dependencies are explicit\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private usersService: UsersService,\n    private inventoryService: InventoryService,\n    private paymentService: PaymentService,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const user = await this.usersService.findOne(dto.userId);\n    const inventory = await this.inventoryService.check(dto.items);\n    // Dependencies are clear and testable\n  }\n}\n\n// Easy to test with mocks\ndescribe('OrdersService', () => {\n  let service: OrdersService;\n\n  beforeEach(async () => {\n    const module = await Test.createTestingModule({\n      providers: [\n        OrdersService,\n        { provide: UsersService, useValue: mockUsersService },\n        { provide: InventoryService, useValue: mockInventoryService },\n        { provide: PaymentService, useValue: mockPaymentService },\n      ],\n    }).compile();\n\n    service = module.get(OrdersService);\n  });\n});\n\n// VALID: Factory pattern for dynamic instantiation\n@Injectable()\nexport class HandlerFactory {\n  constructor(private moduleRef: ModuleRef) {}\n\n  getHandler(type: string): Handler {\n    switch (type) {\n      case 'email':\n        return this.moduleRef.get(EmailHandler);\n      case 'sms':\n        return this.moduleRef.get(SmsHandler);\n      default:\n        return this.moduleRef.get(DefaultHandler);\n    }\n  }\n}\n```\n\nReference: [NestJS Module Reference](https://docs.nestjs.com/fundamentals/module-ref)","frontmatter":{"title":"Avoid Service Locator Anti-Pattern","impact":"HIGH","impactDescription":"Hides dependencies and breaks testability","tags":"dependency-injection, anti-patterns, testing"},"isInternal":false,"tokens":614,"sizeBytes":2980},{"name":"di-interface-segregation.md","path":".agents/skills/nestjs-best-practices/rules/di-interface-segregation.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/di-interface-segregation.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Apply Interface Segregation Principle\nimpact: HIGH\nimpactDescription: Reduces coupling and improves testability by 30-50%\ntags: dependency-injection, interfaces, solid, isp\n---\n\n## Apply Interface Segregation Principle\n\nClients should not be forced to depend on interfaces they don't use. In NestJS, this means keeping interfaces small and focused on specific capabilities rather than creating \"fat\" interfaces that bundle unrelated methods. When a service only needs to send emails, it shouldn't depend on an interface that also includes SMS, push notifications, and logging. Split large interfaces into role-based ones.\n\n**Incorrect (fat interface forcing unused dependencies):**\n\n```typescript\n// Fat interface - forces all consumers to depend on everything\ninterface NotificationService {\n  sendEmail(to: string, subject: string, body: string): Promise<void>;\n  sendSms(phone: string, message: string): Promise<void>;\n  sendPush(userId: string, notification: PushPayload): Promise<void>;\n  sendSlack(channel: string, message: string): Promise<void>;\n  logNotification(type: string, payload: any): Promise<void>;\n  getDeliveryStatus(id: string): Promise<DeliveryStatus>;\n  retryFailed(id: string): Promise<void>;\n  scheduleNotification(dto: ScheduleDto): Promise<string>;\n}\n\n// Consumer only needs email, but must mock everything for tests\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private notifications: NotificationService, // Depends on 8 methods, uses 1\n  ) {}\n\n  async confirmOrder(order: Order): Promise<void> {\n    await this.notifications.sendEmail(\n      order.customer.email,\n      'Order Confirmed',\n      `Your order ${order.id} has been confirmed.`,\n    );\n  }\n}\n\n// Testing is painful - must mock unused methods\nconst mockNotificationService = {\n  sendEmail: jest.fn(),\n  sendSms: jest.fn(), // Never used, but required\n  sendPush: jest.fn(), // Never used, but required\n  sendSlack: jest.fn(), // Never used, but required\n  logNotification: jest.fn(), // Never used, but required\n  getDeliveryStatus: jest.fn(), // Never used, but required\n  retryFailed: jest.fn(), // Never used, but required\n  scheduleNotification: jest.fn(), // Never used, but required\n};\n```\n\n**Correct (segregated interfaces by capability):**\n\n```typescript\n// Segregated interfaces - each focused on one capability\ninterface EmailSender {\n  sendEmail(to: string, subject: string, body: string): Promise<void>;\n}\n\ninterface SmsSender {\n  sendSms(phone: string, message: string): Promise<void>;\n}\n\ninterface PushSender {\n  sendPush(userId: string, notification: PushPayload): Promise<void>;\n}\n\ninterface NotificationLogger {\n  logNotification(type: string, payload: any): Promise<void>;\n}\n\ninterface NotificationScheduler {\n  scheduleNotification(dto: ScheduleDto): Promise<string>;\n}\n\n// Implementation can implement multiple interfaces\n@Injectable()\nexport class NotificationService implements EmailSender, SmsSender, PushSender {\n  async sendEmail(to: string, subject: string, body: string): Promise<void> {\n    // Email implementation\n  }\n\n  async sendSms(phone: string, message: string): Promise<void> {\n    // SMS implementation\n  }\n\n  async sendPush(userId: string, notification: PushPayload): Promise<void> {\n    // Push implementation\n  }\n}\n\n// Or separate implementations\n@Injectable()\nexport class SendGridEmailService implements EmailSender {\n  async sendEmail(to: string, subject: string, body: string): Promise<void> {\n    // SendGrid-specific implementation\n  }\n}\n\n// Consumer depends only on what it needs\n@Injectable()\nexport class OrdersService {\n  constructor(\n    @Inject(EMAIL_SENDER) private emailSender: EmailSender, // Minimal dependency\n  ) {}\n\n  async confirmOrder(order: Order): Promise<void> {\n    await this.emailSender.sendEmail(\n      order.customer.email,\n      'Order Confirmed',\n      `Your order ${order.id} has been confirmed.`,\n    );\n  }\n}\n\n// Testing is simple - only mock what's used\nconst mockEmailSender: EmailSender = {\n  sendEmail: jest.fn(),\n};\n\n// Module registration with tokens\nexport const EMAIL_SENDER = Symbol('EMAIL_SENDER');\nexport const SMS_SENDER = Symbol('SMS_SENDER');\n\n@Module({\n  providers: [\n    { provide: EMAIL_SENDER, useClass: SendGridEmailService },\n    { provide: SMS_SENDER, useClass: TwilioSmsService },\n  ],\n  exports: [EMAIL_SENDER, SMS_SENDER],\n})\nexport class NotificationModule {}\n```\n\n**Combining interfaces when needed:**\n\n```typescript\n// Sometimes a consumer legitimately needs multiple capabilities\ninterface EmailAndSmsSender extends EmailSender, SmsSender {}\n\n// Or use intersection types\ntype MultiChannelSender = EmailSender & SmsSender & PushSender;\n\n// Consumer that genuinely needs multiple channels\n@Injectable()\nexport class AlertService {\n  constructor(\n    @Inject(MULTI_CHANNEL_SENDER)\n    private sender: EmailSender & SmsSender,\n  ) {}\n\n  async sendCriticalAlert(user: User, message: string): Promise<void> {\n    await Promise.all([\n      this.sender.sendEmail(user.email, 'Critical Alert', message),\n      this.sender.sendSms(user.phone, message),\n    ]);\n  }\n}\n```\n\nReference: [Interface Segregation Principle](https://en.wikipedia.org/wiki/Interface_segregation_principle)","frontmatter":{"title":"Apply Interface Segregation Principle","impact":"HIGH","impactDescription":"Reduces coupling and improves testability by 30-50%","tags":"dependency-injection, interfaces, solid, isp"},"isInternal":false,"tokens":1127,"sizeBytes":5188},{"name":"di-liskov-substitution.md","path":".agents/skills/nestjs-best-practices/rules/di-liskov-substitution.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/di-liskov-substitution.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Honor Liskov Substitution Principle\nimpact: HIGH\nimpactDescription: Ensures implementations are truly interchangeable without breaking callers\ntags: dependency-injection, inheritance, solid, lsp\n---\n\n## Honor Liskov Substitution Principle\n\nSubtypes must be substitutable for their base types without altering program correctness. In NestJS with dependency injection, this means any implementation of an interface or abstract class must honor the contract completely. A mock payment service used in tests must behave like a real payment service (return similar shapes, handle errors the same way). Violating LSP causes subtle bugs when swapping implementations.\n\n**Incorrect (implementation violates the contract):**\n\n```typescript\n// Base interface with clear contract\ninterface PaymentGateway {\n  /**\n   * Charges the specified amount.\n   * @returns PaymentResult on success\n   * @throws PaymentFailedException on payment failure\n   */\n  charge(amount: number, currency: string): Promise<PaymentResult>;\n}\n\n// Production implementation - follows the contract\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    const response = await this.stripe.charges.create({ amount, currency });\n    return { success: true, transactionId: response.id, amount };\n  }\n}\n\n// Mock that violates LSP - different behavior!\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    // VIOLATION 1: Throws for valid input (contract says return PaymentResult)\n    if (amount > 1000) {\n      throw new Error('Mock does not support large amounts');\n    }\n\n    // VIOLATION 2: Returns null instead of PaymentResult\n    if (currency !== 'USD') {\n      return null as any; // Real service would convert or reject properly\n    }\n\n    // VIOLATION 3: Missing required field\n    return { success: true } as PaymentResult; // Missing transactionId!\n  }\n}\n\n// Consumer trusts the contract\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async checkout(order: Order): Promise<void> {\n    const result = await this.payment.charge(order.total, order.currency);\n    // These fail with MockPaymentService:\n    await this.saveTransaction(result.transactionId); // undefined!\n    await this.sendReceipt(result); // might be null!\n  }\n}\n```\n\n**Correct (implementations honor the contract):**\n\n```typescript\n// Well-defined interface with documented behavior\ninterface PaymentGateway {\n  /**\n   * Charges the specified amount.\n   * @param amount - Amount in smallest currency unit (cents)\n   * @param currency - ISO 4217 currency code\n   * @returns PaymentResult with transactionId, success status, and amount\n   * @throws PaymentFailedException if charge is declined\n   * @throws InvalidCurrencyException if currency is not supported\n   */\n  charge(amount: number, currency: string): Promise<PaymentResult>;\n\n  /**\n   * Refunds a previous charge.\n   * @throws TransactionNotFoundException if transactionId is invalid\n   */\n  refund(transactionId: string, amount?: number): Promise<RefundResult>;\n}\n\n// Production implementation\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    try {\n      const response = await this.stripe.charges.create({ amount, currency });\n      return {\n        success: true,\n        transactionId: response.id,\n        amount: response.amount,\n      };\n    } catch (error) {\n      if (error.type === 'card_error') {\n        throw new PaymentFailedException(error.message);\n      }\n      throw error;\n    }\n  }\n\n  async refund(transactionId: string, amount?: number): Promise<RefundResult> {\n    // Implementation...\n  }\n}\n\n// Mock that honors LSP - same contract, same behavior shape\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  private transactions = new Map<string, PaymentResult>();\n\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    // Honor the contract: validate currency like real service would\n    if (!['USD', 'EUR', 'GBP'].includes(currency)) {\n      throw new InvalidCurrencyException(`Unsupported currency: ${currency}`);\n    }\n\n    // Simulate decline for specific test scenarios\n    if (amount === 99999) {\n      throw new PaymentFailedException('Card declined (test scenario)');\n    }\n\n    // Return same shape as production\n    const result: PaymentResult = {\n      success: true,\n      transactionId: `mock_${Date.now()}_${Math.random().toString(36)}`,\n      amount,\n    };\n\n    this.transactions.set(result.transactionId, result);\n    return result;\n  }\n\n  async refund(transactionId: string, amount?: number): Promise<RefundResult> {\n    // Honor the contract: throw if transaction not found\n    if (!this.transactions.has(transactionId)) {\n      throw new TransactionNotFoundException(transactionId);\n    }\n\n    return {\n      success: true,\n      refundId: `refund_${transactionId}`,\n      amount: amount ?? this.transactions.get(transactionId)!.amount,\n    };\n  }\n}\n\n// Consumer can swap implementations safely\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async checkout(order: Order): Promise<Order> {\n    try {\n      const result = await this.payment.charge(order.total, order.currency);\n      // Works with both StripeService and MockPaymentService\n      order.transactionId = result.transactionId;\n      order.status = 'paid';\n      return order;\n    } catch (error) {\n      if (error instanceof PaymentFailedException) {\n        order.status = 'payment_failed';\n        return order;\n      }\n      throw error;\n    }\n  }\n}\n```\n\n**Testing LSP compliance:**\n\n```typescript\n// Shared test suite that any implementation must pass\nfunction testPaymentGatewayContract(createGateway: () => PaymentGateway) {\n  describe('PaymentGateway contract', () => {\n    let gateway: PaymentGateway;\n\n    beforeEach(() => {\n      gateway = createGateway();\n    });\n\n    it('returns PaymentResult with all required fields', async () => {\n      const result = await gateway.charge(1000, 'USD');\n      expect(result).toHaveProperty('success');\n      expect(result).toHaveProperty('transactionId');\n      expect(result).toHaveProperty('amount');\n      expect(typeof result.transactionId).toBe('string');\n    });\n\n    it('throws InvalidCurrencyException for unsupported currency', async () => {\n      await expect(gateway.charge(1000, 'INVALID')).rejects.toThrow(InvalidCurrencyException);\n    });\n\n    it('throws TransactionNotFoundException for invalid refund', async () => {\n      await expect(gateway.refund('nonexistent')).rejects.toThrow(TransactionNotFoundException);\n    });\n  });\n}\n\n// Run against all implementations\ndescribe('StripeService', () => {\n  testPaymentGatewayContract(() => new StripeService(mockStripeClient));\n});\n\ndescribe('MockPaymentService', () => {\n  testPaymentGatewayContract(() => new MockPaymentService());\n});\n```\n\nReference: [Liskov Substitution Principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle)","frontmatter":{"title":"Honor Liskov Substitution Principle","impact":"HIGH","impactDescription":"Ensures implementations are truly interchangeable without breaking callers","tags":"dependency-injection, inheritance, solid, lsp"},"isInternal":false,"tokens":1511,"sizeBytes":7251},{"name":"di-prefer-constructor-injection.md","path":".agents/skills/nestjs-best-practices/rules/di-prefer-constructor-injection.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/di-prefer-constructor-injection.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Prefer Constructor Injection\nimpact: CRITICAL\nimpactDescription: Required for proper DI and testing\ntags: dependency-injection, constructor, testing\n---\n\n## Prefer Constructor Injection\n\nAlways use constructor injection over property injection. Constructor injection makes dependencies explicit, enables TypeScript type checking, ensures dependencies are available when the class is instantiated, and improves testability. This is required for proper DI, testing, and TypeScript support.\n\n**Incorrect (property injection with hidden dependencies):**\n\n```typescript\n// Property injection - avoid unless necessary\n@Injectable()\nexport class UsersService {\n  @Inject()\n  private userRepo: UserRepository; // Hidden dependency\n\n  @Inject('CONFIG')\n  private config: ConfigType; // Also hidden\n\n  async findAll() {\n    return this.userRepo.find();\n  }\n}\n\n// Problems:\n// 1. Dependencies not visible in constructor\n// 2. Service can be instantiated without dependencies in tests\n// 3. TypeScript can't enforce dependency types at instantiation\n```\n\n**Correct (constructor injection with explicit dependencies):**\n\n```typescript\n// Constructor injection - explicit and testable\n@Injectable()\nexport class UsersService {\n  constructor(\n    private readonly userRepo: UserRepository,\n    @Inject('CONFIG') private readonly config: ConfigType,\n  ) {}\n\n  async findAll(): Promise<User[]> {\n    return this.userRepo.find();\n  }\n}\n\n// Testing is straightforward\ndescribe('UsersService', () => {\n  let service: UsersService;\n  let mockRepo: jest.Mocked<UserRepository>;\n\n  beforeEach(() => {\n    mockRepo = {\n      find: jest.fn(),\n      save: jest.fn(),\n    } as any;\n\n    service = new UsersService(mockRepo, { dbUrl: 'test' });\n  });\n\n  it('should find all users', async () => {\n    mockRepo.find.mockResolvedValue([{ id: '1', name: 'Test' }]);\n    const result = await service.findAll();\n    expect(result).toHaveLength(1);\n  });\n});\n\n// Only use property injection for optional dependencies\n@Injectable()\nexport class LoggingService {\n  @Optional()\n  @Inject('ANALYTICS')\n  private analytics?: AnalyticsService;\n\n  log(message: string) {\n    console.log(message);\n    this.analytics?.track('log', message); // Optional enhancement\n  }\n}\n```\n\nReference: [NestJS Providers](https://docs.nestjs.com/providers)","frontmatter":{"title":"Prefer Constructor Injection","impact":"CRITICAL","impactDescription":"Required for proper DI and testing","tags":"dependency-injection, constructor, testing"},"isInternal":false,"tokens":486,"sizeBytes":2308},{"name":"di-scope-awareness.md","path":".agents/skills/nestjs-best-practices/rules/di-scope-awareness.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/di-scope-awareness.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Understand Provider Scopes\nimpact: CRITICAL\nimpactDescription: Prevents data leaks and performance issues\ntags: dependency-injection, scopes, request-context\n---\n\n## Understand Provider Scopes\n\nNestJS has three provider scopes: DEFAULT (singleton), REQUEST (per-request instance), and TRANSIENT (new instance for each injection). Most providers should be singletons. Request-scoped providers have performance implications as they bubble up through the dependency tree. Understanding scopes prevents memory leaks and incorrect data sharing.\n\n**Incorrect (wrong scope usage):**\n\n```typescript\n// Request-scoped when not needed (performance hit)\n@Injectable({ scope: Scope.REQUEST })\nexport class UsersService {\n  // This creates a new instance for EVERY request\n  // All dependencies also become request-scoped\n  async findAll() {\n    return this.userRepo.find();\n  }\n}\n\n// Singleton with mutable request state\n@Injectable() // Default: singleton\nexport class RequestContextService {\n  private userId: string; // DANGER: Shared across all requests!\n\n  setUser(userId: string) {\n    this.userId = userId; // Overwrites for all concurrent requests\n  }\n\n  getUser() {\n    return this.userId; // Returns wrong user!\n  }\n}\n```\n\n**Correct (appropriate scope for each use case):**\n\n```typescript\n// Singleton for stateless services (default, most common)\n@Injectable()\nexport class UsersService {\n  constructor(private readonly userRepo: UserRepository) {}\n\n  async findById(id: string): Promise<User> {\n    return this.userRepo.findOne({ where: { id } });\n  }\n}\n\n// Request-scoped ONLY when you need request context\n@Injectable({ scope: Scope.REQUEST })\nexport class RequestContextService {\n  private userId: string;\n\n  setUser(userId: string) {\n    this.userId = userId;\n  }\n\n  getUser(): string {\n    return this.userId;\n  }\n}\n\n// Better: Use NestJS built-in request context\nimport { REQUEST } from '@nestjs/core';\nimport { Request } from 'express';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class AuditService {\n  constructor(@Inject(REQUEST) private request: Request) {}\n\n  log(action: string) {\n    console.log(`User ${this.request.user?.id} performed ${action}`);\n  }\n}\n\n// Best: Use ClsModule for async context (no scope bubble-up)\nimport { ClsService } from 'nestjs-cls';\n\n@Injectable() // Stays singleton!\nexport class AuditService {\n  constructor(private cls: ClsService) {}\n\n  log(action: string) {\n    const userId = this.cls.get('userId');\n    console.log(`User ${userId} performed ${action}`);\n  }\n}\n```\n\nReference: [NestJS Injection Scopes](https://docs.nestjs.com/fundamentals/injection-scopes)","frontmatter":{"title":"Understand Provider Scopes","impact":"CRITICAL","impactDescription":"Prevents data leaks and performance issues","tags":"dependency-injection, scopes, request-context"},"isInternal":false,"tokens":564,"sizeBytes":2617},{"name":"di-use-interfaces-tokens.md","path":".agents/skills/nestjs-best-practices/rules/di-use-interfaces-tokens.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/di-use-interfaces-tokens.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Injection Tokens for Interfaces\nimpact: HIGH\nimpactDescription: Enables interface-based DI at runtime\ntags: dependency-injection, tokens, interfaces\n---\n\n## Use Injection Tokens for Interfaces\n\nTypeScript interfaces are erased at compile time and can't be used as injection tokens. Use string tokens, symbols, or abstract classes when you want to inject implementations of interfaces. This enables swapping implementations for testing or different environments.\n\n**Incorrect (interface can't be used as token):**\n\n```typescript\n// Interface can't be used as injection token\ninterface PaymentGateway {\n  charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  charge(amount: number) {\n    /* ... */\n  }\n}\n\n@Injectable()\nexport class OrdersService {\n  // This WON'T work - PaymentGateway doesn't exist at runtime\n  constructor(private payment: PaymentGateway) {}\n}\n```\n\n**Correct (symbol tokens or abstract classes):**\n\n```typescript\n// Option 1: String/Symbol tokens (most flexible)\nexport const PAYMENT_GATEWAY = Symbol('PAYMENT_GATEWAY');\n\nexport interface PaymentGateway {\n  charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    // Stripe implementation\n  }\n}\n\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    return { success: true, id: 'mock-id' };\n  }\n}\n\n// Module registration\n@Module({\n  providers: [\n    {\n      provide: PAYMENT_GATEWAY,\n      useClass: process.env.NODE_ENV === 'test' ? MockPaymentService : StripeService,\n    },\n  ],\n  exports: [PAYMENT_GATEWAY],\n})\nexport class PaymentModule {}\n\n// Injection\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async createOrder(dto: CreateOrderDto) {\n    await this.payment.charge(dto.amount);\n  }\n}\n\n// Option 2: Abstract class (carries runtime type info)\nexport abstract class PaymentGateway {\n  abstract charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService extends PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    // Implementation\n  }\n}\n\n// No @Inject needed with abstract class\n@Injectable()\nexport class OrdersService {\n  constructor(private payment: PaymentGateway) {}\n}\n```\n\nReference: [NestJS Custom Providers](https://docs.nestjs.com/fundamentals/custom-providers)","frontmatter":{"title":"Use Injection Tokens for Interfaces","impact":"HIGH","impactDescription":"Enables interface-based DI at runtime","tags":"dependency-injection, tokens, interfaces"},"isInternal":false,"tokens":536,"sizeBytes":2568},{"name":"error-handle-async-errors.md","path":".agents/skills/nestjs-best-practices/rules/error-handle-async-errors.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/error-handle-async-errors.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Handle Async Errors Properly\nimpact: HIGH\nimpactDescription: Prevents process crashes from unhandled rejections\ntags: error-handling, async, promises\n---\n\n## Handle Async Errors Properly\n\nNestJS automatically catches errors from async route handlers, but errors from background tasks, event handlers, and manually created promises can crash your application. Always handle async errors explicitly and use global handlers as a safety net.\n\n**Incorrect (fire-and-forget without error handling):**\n\n```typescript\n// Fire-and-forget without error handling\n@Injectable()\nexport class UsersService {\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Fire and forget - if this fails, error is unhandled!\n    this.emailService.sendWelcome(user.email);\n\n    return user;\n  }\n}\n\n// Unhandled promise in event handler\n@Injectable()\nexport class OrdersService {\n  @OnEvent('order.created')\n  handleOrderCreated(event: OrderCreatedEvent) {\n    // This returns a promise but it's not awaited!\n    this.processOrder(event);\n    // Errors will crash the process\n  }\n\n  private async processOrder(event: OrderCreatedEvent): Promise<void> {\n    await this.inventoryService.reserve(event.items);\n    await this.notificationService.send(event.userId);\n  }\n}\n\n// Missing try-catch in scheduled tasks\n@Cron('0 0 * * *')\nasync dailyCleanup(): Promise<void> {\n  await this.cleanupService.run();\n  // If this throws, no error handling\n}\n```\n\n**Correct (explicit async error handling):**\n\n```typescript\n// Handle fire-and-forget with explicit catch\n@Injectable()\nexport class UsersService {\n  private readonly logger = new Logger(UsersService.name);\n\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Explicitly catch and log errors\n    this.emailService.sendWelcome(user.email).catch(error => {\n      this.logger.error('Failed to send welcome email', error.stack);\n      // Optionally queue for retry\n    });\n\n    return user;\n  }\n}\n\n// Properly handle async event handlers\n@Injectable()\nexport class OrdersService {\n  private readonly logger = new Logger(OrdersService.name);\n\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    try {\n      await this.processOrder(event);\n    } catch (error) {\n      this.logger.error('Failed to process order', { event, error });\n      // Don't rethrow - would crash the process\n      await this.deadLetterQueue.add('order.created', event);\n    }\n  }\n}\n\n// Safe scheduled tasks\n@Injectable()\nexport class CleanupService {\n  private readonly logger = new Logger(CleanupService.name);\n\n  @Cron('0 0 * * *')\n  async dailyCleanup(): Promise<void> {\n    try {\n      await this.cleanupService.run();\n      this.logger.log('Daily cleanup completed');\n    } catch (error) {\n      this.logger.error('Daily cleanup failed', error.stack);\n      // Alert or retry logic\n    }\n  }\n}\n\n// Global unhandled rejection handler in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  const logger = new Logger('Bootstrap');\n\n  process.on('unhandledRejection', (reason, promise) => {\n    logger.error('Unhandled Rejection at:', promise, 'reason:', reason);\n  });\n\n  process.on('uncaughtException', error => {\n    logger.error('Uncaught Exception:', error);\n    process.exit(1);\n  });\n\n  await app.listen(3000);\n}\n```\n\nReference: [Node.js Unhandled Rejections](https://nodejs.org/api/process.html#event-unhandledrejection)","frontmatter":{"title":"Handle Async Errors Properly","impact":"HIGH","impactDescription":"Prevents process crashes from unhandled rejections","tags":"error-handling, async, promises"},"isInternal":false,"tokens":773,"sizeBytes":3526},{"name":"error-throw-http-exceptions.md","path":".agents/skills/nestjs-best-practices/rules/error-throw-http-exceptions.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/error-throw-http-exceptions.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Throw HTTP Exceptions from Services\nimpact: HIGH\nimpactDescription: Keeps controllers thin and simplifies error handling\ntags: error-handling, exceptions, services\n---\n\n## Throw HTTP Exceptions from Services\n\nIt's acceptable (and often preferable) to throw `HttpException` subclasses from services in HTTP applications. This keeps controllers thin and allows services to communicate appropriate error states. For truly layer-agnostic services, use domain exceptions that map to HTTP status codes.\n\n**Incorrect (return error objects instead of throwing):**\n\n```typescript\n// Return error objects instead of throwing\n@Injectable()\nexport class UsersService {\n  async findById(id: string): Promise<{ user?: User; error?: string }> {\n    const user = await this.repo.findOne({ where: { id } });\n    if (!user) {\n      return { error: 'User not found' }; // Controller must check this\n    }\n    return { user };\n  }\n}\n\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string) {\n    const result = await this.usersService.findById(id);\n    if (result.error) {\n      throw new NotFoundException(result.error);\n    }\n    return result.user;\n  }\n}\n```\n\n**Correct (throw exceptions directly from service):**\n\n```typescript\n// Throw exceptions directly from service\n@Injectable()\nexport class UsersService {\n  constructor(private readonly repo: UserRepository) {}\n\n  async findById(id: string): Promise<User> {\n    const user = await this.repo.findOne({ where: { id } });\n    if (!user) {\n      throw new NotFoundException(`User #${id} not found`);\n    }\n    return user;\n  }\n\n  async create(dto: CreateUserDto): Promise<User> {\n    const existing = await this.repo.findOne({\n      where: { email: dto.email },\n    });\n    if (existing) {\n      throw new ConflictException('Email already registered');\n    }\n    return this.repo.save(dto);\n  }\n\n  async update(id: string, dto: UpdateUserDto): Promise<User> {\n    const user = await this.findById(id); // Throws if not found\n    Object.assign(user, dto);\n    return this.repo.save(user);\n  }\n}\n\n// Controller stays thin\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findById(id);\n  }\n\n  @Post()\n  create(@Body() dto: CreateUserDto): Promise<User> {\n    return this.usersService.create(dto);\n  }\n}\n\n// For layer-agnostic services, use domain exceptions\nexport class EntityNotFoundException extends Error {\n  constructor(\n    public readonly entity: string,\n    public readonly id: string,\n  ) {\n    super(`${entity} with ID \"${id}\" not found`);\n  }\n}\n\n// Map to HTTP in exception filter\n@Catch(EntityNotFoundException)\nexport class EntityNotFoundFilter implements ExceptionFilter {\n  catch(exception: EntityNotFoundException, host: ArgumentsHost) {\n    const ctx = host.switchToHttp();\n    const response = ctx.getResponse<Response>();\n\n    response.status(404).json({\n      statusCode: 404,\n      message: exception.message,\n      entity: exception.entity,\n      id: exception.id,\n    });\n  }\n}\n```\n\nReference: [NestJS Exception Filters](https://docs.nestjs.com/exception-filters)","frontmatter":{"title":"Throw HTTP Exceptions from Services","impact":"HIGH","impactDescription":"Keeps controllers thin and simplifies error handling","tags":"error-handling, exceptions, services"},"isInternal":false,"tokens":688,"sizeBytes":3180},{"name":"error-use-exception-filters.md","path":".agents/skills/nestjs-best-practices/rules/error-use-exception-filters.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/error-use-exception-filters.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Exception Filters for Error Handling\nimpact: HIGH\nimpactDescription: Consistent, centralized error handling\ntags: error-handling, exception-filters, consistency\n---\n\n## Use Exception Filters for Error Handling\n\nNever catch exceptions and manually format error responses in controllers. Use NestJS exception filters to handle errors consistently across your application. Create custom exception filters for specific error types and a global filter for unhandled exceptions.\n\n**Incorrect (manual error handling in controllers):**\n\n```typescript\n// Manual error handling in controllers\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string, @Res() res: Response) {\n    try {\n      const user = await this.usersService.findById(id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found',\n        });\n      }\n      return res.json(user);\n    } catch (error) {\n      console.error(error);\n      return res.status(500).json({\n        statusCode: 500,\n        message: 'Internal server error',\n      });\n    }\n  }\n}\n```\n\n**Correct (exception filters with consistent handling):**\n\n```typescript\n// Use built-in and custom exceptions\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    const user = await this.usersService.findById(id);\n    if (!user) {\n      throw new NotFoundException(`User #${id} not found`);\n    }\n    return user;\n  }\n}\n\n// Custom domain exception\nexport class UserNotFoundException extends NotFoundException {\n  constructor(userId: string) {\n    super({\n      statusCode: 404,\n      error: 'Not Found',\n      message: `User with ID \"${userId}\" not found`,\n      code: 'USER_NOT_FOUND',\n    });\n  }\n}\n\n// Custom exception filter for domain errors\n@Catch(DomainException)\nexport class DomainExceptionFilter implements ExceptionFilter {\n  catch(exception: DomainException, host: ArgumentsHost) {\n    const ctx = host.switchToHttp();\n    const response = ctx.getResponse<Response>();\n    const request = ctx.getRequest<Request>();\n\n    const status = exception.getStatus?.() || 400;\n\n    response.status(status).json({\n      statusCode: status,\n      code: exception.code,\n      message: exception.message,\n      timestamp: new Date().toISOString(),\n      path: request.url,\n    });\n  }\n}\n\n// Global exception filter for unhandled errors\n@Catch()\nexport class AllExceptionsFilter implements ExceptionFilter {\n  constructor(private readonly logger: Logger) {}\n\n  catch(exception: unknown, host: ArgumentsHost) {\n    const ctx = host.switchToHttp();\n    const response = ctx.getResponse<Response>();\n    const request = ctx.getRequest<Request>();\n\n    const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;\n\n    const message = exception instanceof HttpException ? exception.message : 'Internal server error';\n\n    this.logger.error(`${request.method} ${request.url}`, exception instanceof Error ? exception.stack : exception);\n\n    response.status(status).json({\n      statusCode: status,\n      message,\n      timestamp: new Date().toISOString(),\n      path: request.url,\n    });\n  }\n}\n\n// Register globally in main.ts\napp.useGlobalFilters(new AllExceptionsFilter(app.get(Logger)), new DomainExceptionFilter());\n\n// Or via module\n@Module({\n  providers: [\n    {\n      provide: APP_FILTER,\n      useClass: AllExceptionsFilter,\n    },\n  ],\n})\nexport class AppModule {}\n```\n\nReference: [NestJS Exception Filters](https://docs.nestjs.com/exception-filters)","frontmatter":{"title":"Use Exception Filters for Error Handling","impact":"HIGH","impactDescription":"Consistent, centralized error handling","tags":"error-handling, exception-filters, consistency"},"isInternal":false,"tokens":745,"sizeBytes":3624},{"name":"micro-use-health-checks.md","path":".agents/skills/nestjs-best-practices/rules/micro-use-health-checks.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/micro-use-health-checks.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Implement Health Checks for Microservices\nimpact: MEDIUM-HIGH\nimpactDescription: Health checks enable orchestrators to manage service lifecycle\ntags: microservices, health-checks, terminus, kubernetes\n---\n\n## Implement Health Checks for Microservices\n\nImplement liveness and readiness probes using `@nestjs/terminus`. Liveness checks determine if the service should be restarted. Readiness checks determine if the service can accept traffic. Proper health checks enable Kubernetes and load balancers to route traffic correctly.\n\n**Incorrect (simple ping that doesn't check dependencies):**\n\n```typescript\n// Simple ping that doesn't check dependencies\n@Controller('health')\nexport class HealthController {\n  @Get()\n  check(): string {\n    return 'OK'; // Service might be unhealthy but returns OK\n  }\n}\n\n// Health check that blocks on slow dependencies\n@Controller('health')\nexport class HealthController {\n  @Get()\n  async check(): Promise<string> {\n    // If database is slow, health check times out\n    await this.userRepo.findOne({ where: { id: '1' } });\n    await this.redis.ping();\n    await this.externalApi.healthCheck();\n    return 'OK';\n  }\n}\n```\n\n**Correct (use @nestjs/terminus for comprehensive health checks):**\n\n```typescript\n// Use @nestjs/terminus for comprehensive health checks\nimport {\n  HealthCheckService,\n  HttpHealthIndicator,\n  TypeOrmHealthIndicator,\n  HealthCheck,\n  DiskHealthIndicator,\n  MemoryHealthIndicator,\n} from '@nestjs/terminus';\n\n@Controller('health')\nexport class HealthController {\n  constructor(\n    private health: HealthCheckService,\n    private http: HttpHealthIndicator,\n    private db: TypeOrmHealthIndicator,\n    private disk: DiskHealthIndicator,\n    private memory: MemoryHealthIndicator,\n  ) {}\n\n  // Liveness probe - is the service alive?\n  @Get('live')\n  @HealthCheck()\n  liveness() {\n    return this.health.check([\n      // Basic checks only\n      () => this.memory.checkHeap('memory_heap', 200 * 1024 * 1024), // 200MB\n    ]);\n  }\n\n  // Readiness probe - can the service handle traffic?\n  @Get('ready')\n  @HealthCheck()\n  readiness() {\n    return this.health.check([\n      () => this.db.pingCheck('database'),\n      () =>\n        this.http.pingCheck('redis', 'http://redis:6379', { timeout: 1000 }),\n      () =>\n        this.disk.checkStorage('disk', { path: '/', thresholdPercent: 0.9 }),\n    ]);\n  }\n\n  // Deep health check for debugging\n  @Get('deep')\n  @HealthCheck()\n  deepCheck() {\n    return this.health.check([\n      () => this.db.pingCheck('database'),\n      () => this.memory.checkHeap('memory_heap', 200 * 1024 * 1024),\n      () => this.memory.checkRSS('memory_rss', 300 * 1024 * 1024),\n      () =>\n        this.disk.checkStorage('disk', { path: '/', thresholdPercent: 0.9 }),\n      () =>\n        this.http.pingCheck('external-api', 'https://api.example.com/health'),\n    ]);\n  }\n}\n\n// Custom indicator for business-specific health\n@Injectable()\nexport class QueueHealthIndicator extends HealthIndicator {\n  constructor(private queueService: QueueService) {\n    super();\n  }\n\n  async isHealthy(key: string): Promise<HealthIndicatorResult> {\n    const queueStats = await this.queueService.getStats();\n\n    const isHealthy = queueStats.failedCount < 100;\n    const result = this.getStatus(key, isHealthy, {\n      waiting: queueStats.waitingCount,\n      active: queueStats.activeCount,\n      failed: queueStats.failedCount,\n    });\n\n    if (!isHealthy) {\n      throw new HealthCheckError('Queue unhealthy', result);\n    }\n\n    return result;\n  }\n}\n\n// Redis health indicator\n@Injectable()\nexport class RedisHealthIndicator extends HealthIndicator {\n  constructor(@InjectRedis() private redis: Redis) {\n    super();\n  }\n\n  async isHealthy(key: string): Promise<HealthIndicatorResult> {\n    try {\n      const pong = await this.redis.ping();\n      return this.getStatus(key, pong === 'PONG');\n    } catch (error) {\n      throw new HealthCheckError('Redis check failed', this.getStatus(key, false));\n    }\n  }\n}\n\n// Use custom indicators\n@Get('ready')\n@HealthCheck()\nreadiness() {\n  return this.health.check([\n    () => this.db.pingCheck('database'),\n    () => this.redis.isHealthy('redis'),\n    () => this.queue.isHealthy('job-queue'),\n  ]);\n}\n\n// Graceful shutdown handling\n@Injectable()\nexport class GracefulShutdownService implements OnApplicationShutdown {\n  private isShuttingDown = false;\n\n  isShutdown(): boolean {\n    return this.isShuttingDown;\n  }\n\n  async onApplicationShutdown(signal: string): Promise<void> {\n    this.isShuttingDown = true;\n    console.log(`Shutting down on ${signal}`);\n\n    // Wait for in-flight requests\n    await new Promise((resolve) => setTimeout(resolve, 5000));\n  }\n}\n\n// Health check respects shutdown state\n@Get('ready')\n@HealthCheck()\nreadiness() {\n  if (this.shutdownService.isShutdown()) {\n    throw new ServiceUnavailableException('Shutting down');\n  }\n\n  return this.health.check([\n    () => this.db.pingCheck('database'),\n  ]);\n}\n```\n\n### Kubernetes Configuration\n\n```yaml\n# Kubernetes deployment with probes\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: api-service\nspec:\n  template:\n    spec:\n      containers:\n        - name: api\n          image: api-service:latest\n          ports:\n            - containerPort: 3000\n          livenessProbe:\n            httpGet:\n              path: /health/live\n              port: 3000\n            initialDelaySeconds: 30\n            periodSeconds: 10\n            timeoutSeconds: 5\n            failureThreshold: 3\n          readinessProbe:\n            httpGet:\n              path: /health/ready\n              port: 3000\n            initialDelaySeconds: 5\n            periodSeconds: 5\n            timeoutSeconds: 3\n            failureThreshold: 3\n          startupProbe:\n            httpGet:\n              path: /health/live\n              port: 3000\n            initialDelaySeconds: 0\n            periodSeconds: 5\n            failureThreshold: 30\n```\n\nReference: [NestJS Terminus](https://docs.nestjs.com/recipes/terminus)","frontmatter":{"title":"Implement Health Checks for Microservices","impact":"MEDIUM-HIGH","impactDescription":"Health checks enable orchestrators to manage service lifecycle","tags":"microservices, health-checks, terminus, kubernetes"},"isInternal":false,"tokens":1381,"sizeBytes":5984},{"name":"micro-use-patterns.md","path":".agents/skills/nestjs-best-practices/rules/micro-use-patterns.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/micro-use-patterns.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Message and Event Patterns Correctly\nimpact: MEDIUM\nimpactDescription: Proper patterns ensure reliable microservice communication\ntags: microservices, message-pattern, event-pattern, communication\n---\n\n## Use Message and Event Patterns Correctly\n\nNestJS microservices support two communication patterns: request-response (MessagePattern) and event-based (EventPattern). Use MessagePattern when you need a response, and EventPattern for fire-and-forget notifications. Understanding the difference prevents communication bugs.\n\n**Incorrect (using wrong pattern for use case):**\n\n```typescript\n// Use @MessagePattern for fire-and-forget\n@Controller()\nexport class NotificationsController {\n  @MessagePattern('user.created')\n  async handleUserCreated(data: UserCreatedEvent) {\n    // This WAITS for response, blocking the sender\n    await this.emailService.sendWelcome(data.email);\n    // If email fails, sender gets an error (coupling!)\n  }\n}\n\n// Use @EventPattern expecting a response\n@Controller()\nexport class OrdersController {\n  @EventPattern('inventory.check')\n  async checkInventory(data: CheckInventoryDto) {\n    const available = await this.inventory.check(data);\n    return available; // This return value is IGNORED with @EventPattern!\n  }\n}\n\n// Tight coupling in client\n@Injectable()\nexport class UsersService {\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Blocks until notification service responds\n    await this.client.send('user.created', user).toPromise();\n    // If notification service is down, user creation fails!\n\n    return user;\n  }\n}\n```\n\n**Correct (use MessagePattern for request-response, EventPattern for fire-and-forget):**\n\n```typescript\n// MessagePattern: Request-Response (when you NEED a response)\n@Controller()\nexport class InventoryController {\n  @MessagePattern({ cmd: 'check_inventory' })\n  async checkInventory(data: CheckInventoryDto): Promise<InventoryResult> {\n    const result = await this.inventoryService.check(data.productId, data.quantity);\n    return result; // Response sent back to caller\n  }\n}\n\n// Client expects response\n@Injectable()\nexport class OrdersService {\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    // Check inventory - we NEED this response to proceed\n    const inventory = await firstValueFrom(\n      this.inventoryClient.send<InventoryResult>(\n        { cmd: 'check_inventory' },\n        { productId: dto.productId, quantity: dto.quantity },\n      ),\n    );\n\n    if (!inventory.available) {\n      throw new BadRequestException('Insufficient inventory');\n    }\n\n    return this.repo.save(dto);\n  }\n}\n\n// EventPattern: Fire-and-Forget (for notifications, side effects)\n@Controller()\nexport class NotificationsController {\n  @EventPattern('user.created')\n  async handleUserCreated(data: UserCreatedEvent): Promise<void> {\n    // No return value needed - just process the event\n    await this.emailService.sendWelcome(data.email);\n    await this.analyticsService.track('user_signup', data);\n    // If this fails, it doesn't affect the sender\n  }\n}\n\n// Client emits event without waiting\n@Injectable()\nexport class UsersService {\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Fire and forget - doesn't block, doesn't wait\n    this.eventClient.emit('user.created', {\n      userId: user.id,\n      email: user.email,\n      timestamp: new Date(),\n    });\n\n    return user; // User creation succeeds regardless of event handling\n  }\n}\n\n// Hybrid pattern for critical events\n@Injectable()\nexport class OrdersService {\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Critical: inventory reservation (use MessagePattern)\n    const reserved = await firstValueFrom(\n      this.inventoryClient.send({ cmd: 'reserve_inventory' }, {\n        orderId: order.id,\n        items: dto.items,\n      }),\n    );\n\n    if (!reserved.success) {\n      await this.repo.delete(order.id);\n      throw new BadRequestException('Could not reserve inventory');\n    }\n\n    // Non-critical: notifications (use EventPattern)\n    this.eventClient.emit('order.created', {\n      orderId: order.id,\n      userId: dto.userId,\n      total: dto.total,\n    });\n\n    return order;\n  }\n}\n\n// Error handling patterns\n// MessagePattern errors propagate to caller\n@MessagePattern({ cmd: 'get_user' })\nasync getUser(userId: string): Promise<User> {\n  const user = await this.repo.findOne({ where: { id: userId } });\n  if (!user) {\n    throw new RpcException('User not found'); // Received by caller\n  }\n  return user;\n}\n\n// EventPattern errors should be handled locally\n@EventPattern('order.created')\nasync handleOrderCreated(data: OrderCreatedEvent): Promise<void> {\n  try {\n    await this.processOrder(data);\n  } catch (error) {\n    // Log and potentially retry - don't throw\n    this.logger.error('Failed to process order event', error);\n    await this.deadLetterQueue.add(data);\n  }\n}\n```\n\nReference: [NestJS Microservices](https://docs.nestjs.com/microservices/basics)","frontmatter":{"title":"Use Message and Event Patterns Correctly","impact":"MEDIUM","impactDescription":"Proper patterns ensure reliable microservice communication","tags":"microservices, message-pattern, event-pattern, communication"},"isInternal":false,"tokens":1083,"sizeBytes":5101},{"name":"micro-use-queues.md","path":".agents/skills/nestjs-best-practices/rules/micro-use-queues.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/micro-use-queues.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Message Queues for Background Jobs\nimpact: MEDIUM-HIGH\nimpactDescription: Queues enable reliable background processing\ntags: microservices, queues, bullmq, background-jobs\n---\n\n## Use Message Queues for Background Jobs\n\nUse `@nestjs/bullmq` for background job processing. Queues decouple long-running tasks from HTTP requests, enable retry logic, and distribute workload across workers. Use them for emails, file processing, notifications, and any task that shouldn't block user requests.\n\n**Incorrect (long-running tasks in HTTP handlers):**\n\n```typescript\n// Long-running tasks in HTTP handlers\n@Controller('reports')\nexport class ReportsController {\n  @Post()\n  async generate(@Body() dto: GenerateReportDto): Promise<Report> {\n    // This blocks the request for potentially minutes\n    const data = await this.fetchLargeDataset(dto);\n    const report = await this.processData(data); // Slow!\n    await this.sendEmail(dto.email, report); // Can fail!\n    return report; // Client times out\n  }\n}\n\n// Fire-and-forget without retry\n@Injectable()\nexport class EmailService {\n  async sendWelcome(email: string): Promise<void> {\n    // If this fails, email is never sent\n    await this.mailer.send({ to: email, template: 'welcome' });\n    // No retry, no tracking, no visibility\n  }\n}\n\n// Use setInterval for scheduled tasks\nsetInterval(async () => {\n  await cleanupOldRecords();\n}, 60000); // No error handling, memory leaks\n```\n\n**Correct (use BullMQ for background processing):**\n\n```typescript\n// Configure BullMQ\nimport { BullModule } from '@nestjs/bullmq';\n\n@Module({\n  imports: [\n    BullModule.forRoot({\n      connection: {\n        host: 'localhost',\n        port: 6379,\n      },\n      defaultJobOptions: {\n        removeOnComplete: 1000,\n        removeOnFail: 5000,\n        attempts: 3,\n        backoff: {\n          type: 'exponential',\n          delay: 1000,\n        },\n      },\n    }),\n    BullModule.registerQueue({ name: 'email' }, { name: 'reports' }, { name: 'notifications' }),\n  ],\n})\nexport class QueueModule {}\n\n// Producer: Add jobs to queue\n@Injectable()\nexport class ReportsService {\n  constructor(@InjectQueue('reports') private reportsQueue: Queue) {}\n\n  async requestReport(dto: GenerateReportDto): Promise<{ jobId: string }> {\n    // Return immediately, process in background\n    const job = await this.reportsQueue.add('generate', dto, {\n      priority: dto.urgent ? 1 : 10,\n      delay: dto.scheduledFor ? Date.parse(dto.scheduledFor) - Date.now() : 0,\n    });\n\n    return { jobId: job.id };\n  }\n\n  async getJobStatus(jobId: string): Promise<JobStatus> {\n    const job = await this.reportsQueue.getJob(jobId);\n    return {\n      status: await job.getState(),\n      progress: job.progress,\n      result: job.returnvalue,\n    };\n  }\n}\n\n// Consumer: Process jobs\n@Processor('reports')\nexport class ReportsProcessor {\n  private readonly logger = new Logger(ReportsProcessor.name);\n\n  @Process('generate')\n  async generateReport(job: Job<GenerateReportDto>): Promise<Report> {\n    this.logger.log(`Processing report job ${job.id}`);\n\n    // Update progress\n    await job.updateProgress(10);\n\n    const data = await this.fetchData(job.data);\n    await job.updateProgress(50);\n\n    const report = await this.processData(data);\n    await job.updateProgress(90);\n\n    await this.saveReport(report);\n    await job.updateProgress(100);\n\n    return report;\n  }\n\n  @OnQueueActive()\n  onActive(job: Job) {\n    this.logger.log(`Processing job ${job.id}`);\n  }\n\n  @OnQueueCompleted()\n  onCompleted(job: Job, result: any) {\n    this.logger.log(`Job ${job.id} completed`);\n  }\n\n  @OnQueueFailed()\n  onFailed(job: Job, error: Error) {\n    this.logger.error(`Job ${job.id} failed: ${error.message}`);\n  }\n}\n\n// Email queue with retry\n@Processor('email')\nexport class EmailProcessor {\n  @Process('send')\n  async sendEmail(job: Job<SendEmailDto>): Promise<void> {\n    const { to, template, data } = job.data;\n\n    try {\n      await this.mailer.send({\n        to,\n        template,\n        context: data,\n      });\n    } catch (error) {\n      // BullMQ will retry based on job options\n      throw error;\n    }\n  }\n}\n\n// Usage\n@Injectable()\nexport class NotificationService {\n  constructor(@InjectQueue('email') private emailQueue: Queue) {}\n\n  async sendWelcome(user: User): Promise<void> {\n    await this.emailQueue.add(\n      'send',\n      {\n        to: user.email,\n        template: 'welcome',\n        data: { name: user.name },\n      },\n      {\n        attempts: 5,\n        backoff: { type: 'exponential', delay: 5000 },\n      },\n    );\n  }\n}\n\n// Scheduled jobs\n@Injectable()\nexport class ScheduledJobsService implements OnModuleInit {\n  constructor(@InjectQueue('maintenance') private queue: Queue) {}\n\n  async onModuleInit(): Promise<void> {\n    // Clean up old reports daily at midnight\n    await this.queue.add(\n      'cleanup',\n      {},\n      {\n        repeat: { cron: '0 0 * * *' },\n        jobId: 'daily-cleanup', // Prevent duplicates\n      },\n    );\n\n    // Send digest every hour\n    await this.queue.add(\n      'digest',\n      {},\n      {\n        repeat: { every: 60 * 60 * 1000 },\n        jobId: 'hourly-digest',\n      },\n    );\n  }\n}\n\n@Processor('maintenance')\nexport class MaintenanceProcessor {\n  @Process('cleanup')\n  async cleanup(): Promise<void> {\n    await this.cleanupOldReports();\n    await this.cleanupExpiredSessions();\n  }\n\n  @Process('digest')\n  async sendDigest(): Promise<void> {\n    const users = await this.getUsersForDigest();\n    for (const user of users) {\n      await this.emailQueue.add('send', { to: user.email, template: 'digest' });\n    }\n  }\n}\n\n// Queue monitoring with Bull Board\nimport { BullBoardModule } from '@bull-board/nestjs';\nimport { BullMQAdapter } from '@bull-board/api/bullMQAdapter';\n\n@Module({\n  imports: [\n    BullBoardModule.forRoot({\n      route: '/admin/queues',\n      adapter: ExpressAdapter,\n    }),\n    BullBoardModule.forFeature({\n      name: 'email',\n      adapter: BullMQAdapter,\n    }),\n    BullBoardModule.forFeature({\n      name: 'reports',\n      adapter: BullMQAdapter,\n    }),\n  ],\n})\nexport class AdminModule {}\n```\n\nReference: [NestJS Queues](https://docs.nestjs.com/techniques/queues)","frontmatter":{"title":"Use Message Queues for Background Jobs","impact":"MEDIUM-HIGH","impactDescription":"Queues enable reliable background processing","tags":"microservices, queues, bullmq, background-jobs"},"isInternal":false,"tokens":1475,"sizeBytes":6179},{"name":"perf-async-hooks.md","path":".agents/skills/nestjs-best-practices/rules/perf-async-hooks.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/perf-async-hooks.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Async Lifecycle Hooks Correctly\nimpact: HIGH\nimpactDescription: Improper async handling blocks application startup\ntags: performance, lifecycle, async, hooks\n---\n\n## Use Async Lifecycle Hooks Correctly\n\nNestJS lifecycle hooks (`onModuleInit`, `onApplicationBootstrap`, etc.) support async operations. However, misusing them can block application startup or cause race conditions. Understand the lifecycle order and use hooks appropriately.\n\n**Incorrect (fire-and-forget async without await):**\n\n```typescript\n// Fire-and-forget async without await\n@Injectable()\nexport class DatabaseService implements OnModuleInit {\n  onModuleInit() {\n    // This runs but doesn't block - app starts before DB is ready!\n    this.connect();\n  }\n\n  private async connect() {\n    await this.pool.connect();\n    console.log('Database connected');\n  }\n}\n\n// Heavy blocking operations in constructor\n@Injectable()\nexport class ConfigService {\n  private config: Config;\n\n  constructor() {\n    // BLOCKS entire module instantiation synchronously\n    this.config = fs.readFileSync('config.json');\n  }\n}\n```\n\n**Correct (return promises from async hooks):**\n\n```typescript\n// Return promise from async hooks\n@Injectable()\nexport class DatabaseService implements OnModuleInit {\n  private pool: Pool;\n\n  async onModuleInit(): Promise<void> {\n    // NestJS waits for this to complete before continuing\n    await this.pool.connect();\n    console.log('Database connected');\n  }\n\n  async onModuleDestroy(): Promise<void> {\n    // Clean up resources on shutdown\n    await this.pool.end();\n    console.log('Database disconnected');\n  }\n}\n\n// Use onApplicationBootstrap for cross-module dependencies\n@Injectable()\nexport class CacheWarmerService implements OnApplicationBootstrap {\n  constructor(\n    private cache: CacheService,\n    private products: ProductsService,\n  ) {}\n\n  async onApplicationBootstrap(): Promise<void> {\n    // All modules are initialized, safe to warm cache\n    const products = await this.products.findPopular();\n    await this.cache.warmup(products);\n  }\n}\n\n// Heavy init in async hooks, not constructor\n@Injectable()\nexport class ConfigService implements OnModuleInit {\n  private config: Config;\n\n  constructor() {\n    // Keep constructor synchronous and fast\n  }\n\n  async onModuleInit(): Promise<void> {\n    // Async loading in lifecycle hook\n    this.config = await this.loadConfig();\n  }\n\n  private async loadConfig(): Promise<Config> {\n    const file = await fs.promises.readFile('config.json');\n    return JSON.parse(file.toString());\n  }\n\n  get<T>(key: string): T {\n    return this.config[key];\n  }\n}\n\n// Enable shutdown hooks in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  app.enableShutdownHooks(); // Enable SIGTERM/SIGINT handling\n  await app.listen(3000);\n}\n```\n\nReference: [NestJS Lifecycle Events](https://docs.nestjs.com/fundamentals/lifecycle-events)","frontmatter":{"title":"Use Async Lifecycle Hooks Correctly","impact":"HIGH","impactDescription":"Improper async handling blocks application startup","tags":"performance, lifecycle, async, hooks"},"isInternal":false,"tokens":612,"sizeBytes":2917},{"name":"perf-lazy-loading.md","path":".agents/skills/nestjs-best-practices/rules/perf-lazy-loading.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/perf-lazy-loading.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Lazy Loading for Large Modules\nimpact: MEDIUM\nimpactDescription: Improves startup time for large applications\ntags: performance, lazy-loading, modules, optimization\n---\n\n## Use Lazy Loading for Large Modules\n\nNestJS supports lazy-loading modules, which defers initialization until first use. This is valuable for large applications where some features are rarely used, serverless deployments where cold start time matters, or when certain modules have heavy initialization costs.\n\n**Incorrect (loading everything eagerly):**\n\n```typescript\n// Load everything eagerly in a large app\n@Module({\n  imports: [\n    UsersModule,\n    OrdersModule,\n    PaymentsModule,\n    ReportsModule, // Heavy, rarely used\n    AnalyticsModule, // Heavy, rarely used\n    AdminModule, // Only admins use this\n    LegacyModule, // Migration module, rarely used\n    BulkImportModule, // Used once a month\n  ],\n})\nexport class AppModule {}\n\n// All modules initialize at startup, even if never used\n// Slow cold starts in serverless\n// Memory wasted on unused modules\n```\n\n**Correct (lazy load rarely-used modules):**\n\n```typescript\n// Use LazyModuleLoader for optional modules\nimport { LazyModuleLoader } from '@nestjs/core';\n\n@Injectable()\nexport class ReportsService {\n  constructor(private lazyModuleLoader: LazyModuleLoader) {}\n\n  async generateReport(type: string): Promise<Report> {\n    // Load module only when needed\n    const { ReportsModule } = await import('./reports/reports.module');\n    const moduleRef = await this.lazyModuleLoader.load(() => ReportsModule);\n\n    const reportsService = moduleRef.get(ReportsGeneratorService);\n    return reportsService.generate(type);\n  }\n}\n\n// Lazy load admin features with caching\n@Injectable()\nexport class AdminService {\n  private adminModule: ModuleRef | null = null;\n\n  constructor(private lazyModuleLoader: LazyModuleLoader) {}\n\n  private async getAdminModule(): Promise<ModuleRef> {\n    if (!this.adminModule) {\n      const { AdminModule } = await import('./admin/admin.module');\n      this.adminModule = await this.lazyModuleLoader.load(() => AdminModule);\n    }\n    return this.adminModule;\n  }\n\n  async runAdminTask(task: string): Promise<void> {\n    const moduleRef = await this.getAdminModule();\n    const taskRunner = moduleRef.get(AdminTaskRunner);\n    await taskRunner.run(task);\n  }\n}\n\n// Reusable lazy loader service\n@Injectable()\nexport class ModuleLoaderService {\n  private loadedModules = new Map<string, ModuleRef>();\n\n  constructor(private lazyModuleLoader: LazyModuleLoader) {}\n\n  async load<T>(key: string, importFn: () => Promise<{ default: Type<T> } | Type<T>>): Promise<ModuleRef> {\n    if (!this.loadedModules.has(key)) {\n      const module = await importFn();\n      const moduleType = 'default' in module ? module.default : module;\n      const moduleRef = await this.lazyModuleLoader.load(() => moduleType);\n      this.loadedModules.set(key, moduleRef);\n    }\n    return this.loadedModules.get(key)!;\n  }\n}\n\n// Preload modules in background after startup\n@Injectable()\nexport class ModulePreloader implements OnApplicationBootstrap {\n  constructor(private lazyModuleLoader: LazyModuleLoader) {}\n\n  async onApplicationBootstrap(): Promise<void> {\n    setTimeout(async () => {\n      await this.preloadModule(() => import('./reports/reports.module'));\n    }, 5000); // 5 seconds after startup\n  }\n\n  private async preloadModule(importFn: () => Promise<any>): Promise<void> {\n    try {\n      const module = await importFn();\n      const moduleType = module.default || Object.values(module)[0];\n      await this.lazyModuleLoader.load(() => moduleType);\n    } catch (error) {\n      console.warn('Failed to preload module', error);\n    }\n  }\n}\n```\n\nReference: [NestJS Lazy Loading Modules](https://docs.nestjs.com/fundamentals/lazy-loading-modules)","frontmatter":{"title":"Use Lazy Loading for Large Modules","impact":"MEDIUM","impactDescription":"Improves startup time for large applications","tags":"performance, lazy-loading, modules, optimization"},"isInternal":false,"tokens":822,"sizeBytes":3808},{"name":"perf-optimize-database.md","path":".agents/skills/nestjs-best-practices/rules/perf-optimize-database.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/perf-optimize-database.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Optimize Database Queries\nimpact: HIGH\nimpactDescription: Database queries are typically the largest source of latency\ntags: performance, database, queries, optimization\n---\n\n## Optimize Database Queries\n\nSelect only needed columns, use proper indexes, avoid over-fetching relations, and consider query performance when designing your data access. Most API slowness traces back to inefficient database queries.\n\n**Incorrect (over-fetching data and missing indexes):**\n\n```typescript\n// Select everything when you need few fields\n@Injectable()\nexport class UsersService {\n  async findAllEmails(): Promise<string[]> {\n    const users = await this.repo.find();\n    // Fetches ALL columns for ALL users\n    return users.map(u => u.email);\n  }\n\n  async getUserSummary(id: string): Promise<UserSummary> {\n    const user = await this.repo.findOne({\n      where: { id },\n      relations: ['posts', 'posts.comments', 'posts.comments.author', 'followers'],\n    });\n    // Over-fetches massive relation tree\n    return { name: user.name, postCount: user.posts.length };\n  }\n}\n\n// No indexes on frequently queried columns\n@Entity()\nexport class Order {\n  @Column()\n  userId: string; // No index - full table scan on every lookup\n\n  @Column()\n  status: string; // No index - slow status filtering\n}\n```\n\n**Correct (select only needed data with proper indexes):**\n\n```typescript\n// Select only needed columns\n@Injectable()\nexport class UsersService {\n  async findAllEmails(): Promise<string[]> {\n    const users = await this.repo.find({\n      select: ['email'], // Only fetch email column\n    });\n    return users.map(u => u.email);\n  }\n\n  // Use QueryBuilder for complex selections\n  async getUserSummary(id: string): Promise<UserSummary> {\n    return this.repo\n      .createQueryBuilder('user')\n      .select('user.name', 'name')\n      .addSelect('COUNT(post.id)', 'postCount')\n      .leftJoin('user.posts', 'post')\n      .where('user.id = :id', { id })\n      .groupBy('user.id')\n      .getRawOne();\n  }\n\n  // Fetch relations only when needed\n  async getFullProfile(id: string): Promise<User> {\n    return this.repo.findOne({\n      where: { id },\n      relations: ['posts'], // Only immediate relation\n      select: {\n        id: true,\n        name: true,\n        email: true,\n        posts: {\n          id: true,\n          title: true,\n        },\n      },\n    });\n  }\n}\n\n// Add indexes on frequently queried columns\n@Entity()\n@Index(['userId'])\n@Index(['status'])\n@Index(['createdAt'])\n@Index(['userId', 'status']) // Composite index for common query pattern\nexport class Order {\n  @PrimaryGeneratedColumn('uuid')\n  id: string;\n\n  @Column()\n  userId: string;\n\n  @Column()\n  status: string;\n\n  @CreateDateColumn()\n  createdAt: Date;\n}\n\n// Always paginate large datasets\n@Injectable()\nexport class OrdersService {\n  async findAll(page = 1, limit = 20): Promise<PaginatedResult<Order>> {\n    const [items, total] = await this.repo.findAndCount({\n      skip: (page - 1) * limit,\n      take: limit,\n      order: { createdAt: 'DESC' },\n    });\n\n    return {\n      items,\n      meta: {\n        page,\n        limit,\n        total,\n        totalPages: Math.ceil(total / limit),\n      },\n    };\n  }\n}\n```\n\nReference: [TypeORM Query Builder](https://typeorm.io/select-query-builder)","frontmatter":{"title":"Optimize Database Queries","impact":"HIGH","impactDescription":"Database queries are typically the largest source of latency","tags":"performance, database, queries, optimization"},"isInternal":false,"tokens":749,"sizeBytes":3271},{"name":"perf-use-caching.md","path":".agents/skills/nestjs-best-practices/rules/perf-use-caching.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/perf-use-caching.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Caching Strategically\nimpact: HIGH\nimpactDescription: Dramatically reduces database load and response times\ntags: performance, caching, redis, optimization\n---\n\n## Use Caching Strategically\n\nImplement caching for expensive operations, frequently accessed data, and external API calls. Use NestJS CacheModule with appropriate TTLs and cache invalidation strategies. Don't cache everything - focus on high-impact areas.\n\n**Incorrect (no caching or caching everything):**\n\n```typescript\n// No caching for expensive, repeated queries\n@Injectable()\nexport class ProductsService {\n  async getPopular(): Promise<Product[]> {\n    // Runs complex aggregation query EVERY request\n    return this.productsRepo\n      .createQueryBuilder('p')\n      .leftJoin('p.orders', 'o')\n      .select('p.*, COUNT(o.id) as orderCount')\n      .groupBy('p.id')\n      .orderBy('orderCount', 'DESC')\n      .limit(20)\n      .getMany();\n  }\n}\n\n// Cache everything without thought\n@Injectable()\nexport class UsersService {\n  @CacheKey('users')\n  @CacheTTL(3600)\n  @UseInterceptors(CacheInterceptor)\n  async findAll(): Promise<User[]> {\n    // Caching user list for 1 hour is wrong if data changes frequently\n    return this.usersRepo.find();\n  }\n}\n```\n\n**Correct (strategic caching with proper invalidation):**\n\n```typescript\n// Setup caching module\n@Module({\n  imports: [\n    CacheModule.registerAsync({\n      imports: [ConfigModule],\n      inject: [ConfigService],\n      useFactory: (config: ConfigService) => ({\n        stores: [new KeyvRedis(config.get('REDIS_URL'))],\n        ttl: 60 * 1000, // Default 60s\n      }),\n    }),\n  ],\n})\nexport class AppModule {}\n\n// Manual caching for granular control\n@Injectable()\nexport class ProductsService {\n  constructor(\n    @Inject(CACHE_MANAGER) private cache: Cache,\n    private productsRepo: ProductRepository,\n  ) {}\n\n  async getPopular(): Promise<Product[]> {\n    const cacheKey = 'products:popular';\n\n    // Try cache first\n    const cached = await this.cache.get<Product[]>(cacheKey);\n    if (cached) return cached;\n\n    // Cache miss - fetch and cache\n    const products = await this.fetchPopularProducts();\n    await this.cache.set(cacheKey, products, 5 * 60 * 1000); // 5 min TTL\n    return products;\n  }\n\n  // Invalidate cache on changes\n  async updateProduct(id: string, dto: UpdateProductDto): Promise<Product> {\n    const product = await this.productsRepo.save({ id, ...dto });\n    await this.cache.del('products:popular'); // Invalidate\n    return product;\n  }\n}\n\n// Decorator-based caching with auto-interceptor\n@Controller('categories')\n@UseInterceptors(CacheInterceptor)\nexport class CategoriesController {\n  @Get()\n  @CacheTTL(30 * 60 * 1000) // 30 minutes - categories rarely change\n  findAll(): Promise<Category[]> {\n    return this.categoriesService.findAll();\n  }\n\n  @Get(':id')\n  @CacheTTL(60 * 1000) // 1 minute\n  @CacheKey('category')\n  findOne(@Param('id') id: string): Promise<Category> {\n    return this.categoriesService.findOne(id);\n  }\n}\n\n// Event-based cache invalidation\n@Injectable()\nexport class CacheInvalidationService {\n  constructor(@Inject(CACHE_MANAGER) private cache: Cache) {}\n\n  @OnEvent('product.created')\n  @OnEvent('product.updated')\n  @OnEvent('product.deleted')\n  async invalidateProductCaches(event: ProductEvent) {\n    await Promise.all([this.cache.del('products:popular'), this.cache.del(`product:${event.productId}`)]);\n  }\n}\n```\n\nReference: [NestJS Caching](https://docs.nestjs.com/techniques/caching)","frontmatter":{"title":"Use Caching Strategically","impact":"HIGH","impactDescription":"Dramatically reduces database load and response times","tags":"performance, caching, redis, optimization"},"isInternal":false,"tokens":815,"sizeBytes":3483},{"name":"security-auth-jwt.md","path":".agents/skills/nestjs-best-practices/rules/security-auth-jwt.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/security-auth-jwt.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Implement Secure JWT Authentication\nimpact: CRITICAL\nimpactDescription: Essential for secure APIs\ntags: security, jwt, authentication, tokens\n---\n\n## Implement Secure JWT Authentication\n\nUse `@nestjs/jwt` with `@nestjs/passport` for authentication. Store secrets securely, use appropriate token lifetimes, implement refresh tokens, and validate tokens properly. Never expose sensitive data in JWT payloads.\n\n**Incorrect (insecure JWT implementation):**\n\n```typescript\n// Hardcode secrets\n@Module({\n  imports: [\n    JwtModule.register({\n      secret: 'my-secret-key', // Exposed in code\n      signOptions: { expiresIn: '7d' }, // Too long\n    }),\n  ],\n})\nexport class AuthModule {}\n\n// Store sensitive data in JWT\nasync login(user: User): Promise<{ accessToken: string }> {\n  const payload = {\n    sub: user.id,\n    email: user.email,\n    password: user.password, // NEVER include password!\n    ssn: user.ssn, // NEVER include sensitive data!\n    isAdmin: user.isAdmin, // Can be tampered if not verified\n  };\n  return { accessToken: this.jwtService.sign(payload) };\n}\n\n// Skip token validation\n@Injectable()\nexport class JwtStrategy extends PassportStrategy(Strategy) {\n  constructor() {\n    super({\n      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),\n      secretOrKey: 'my-secret',\n    });\n  }\n\n  async validate(payload: any): Promise<any> {\n    return payload; // No validation of user existence\n  }\n}\n```\n\n**Correct (secure JWT with refresh tokens):**\n\n```typescript\n// Secure JWT configuration\n@Module({\n  imports: [\n    JwtModule.registerAsync({\n      imports: [ConfigModule],\n      inject: [ConfigService],\n      useFactory: (config: ConfigService) => ({\n        secret: config.get<string>('JWT_SECRET'),\n        signOptions: {\n          expiresIn: '15m', // Short-lived access tokens\n          issuer: config.get<string>('JWT_ISSUER'),\n          audience: config.get<string>('JWT_AUDIENCE'),\n        },\n      }),\n    }),\n    PassportModule.register({ defaultStrategy: 'jwt' }),\n  ],\n})\nexport class AuthModule {}\n\n// Minimal JWT payload\n@Injectable()\nexport class AuthService {\n  async login(user: User): Promise<TokenResponse> {\n    // Only include necessary, non-sensitive data\n    const payload: JwtPayload = {\n      sub: user.id,\n      email: user.email,\n      roles: user.roles,\n      iat: Math.floor(Date.now() / 1000),\n    };\n\n    const accessToken = this.jwtService.sign(payload);\n    const refreshToken = await this.createRefreshToken(user.id);\n\n    return { accessToken, refreshToken, expiresIn: 900 };\n  }\n\n  private async createRefreshToken(userId: string): Promise<string> {\n    const token = randomBytes(32).toString('hex');\n    const hashedToken = await bcrypt.hash(token, 10);\n\n    await this.refreshTokenRepo.save({\n      userId,\n      token: hashedToken,\n      expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days\n    });\n\n    return token;\n  }\n}\n\n// Proper JWT strategy with validation\n@Injectable()\nexport class JwtStrategy extends PassportStrategy(Strategy) {\n  constructor(\n    private config: ConfigService,\n    private usersService: UsersService,\n  ) {\n    super({\n      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),\n      secretOrKey: config.get<string>('JWT_SECRET'),\n      ignoreExpiration: false,\n      issuer: config.get<string>('JWT_ISSUER'),\n      audience: config.get<string>('JWT_AUDIENCE'),\n    });\n  }\n\n  async validate(payload: JwtPayload): Promise<User> {\n    // Verify user still exists and is active\n    const user = await this.usersService.findById(payload.sub);\n\n    if (!user || !user.isActive) {\n      throw new UnauthorizedException('User not found or inactive');\n    }\n\n    // Verify token wasn't issued before password change\n    if (user.passwordChangedAt) {\n      const tokenIssuedAt = new Date(payload.iat * 1000);\n      if (tokenIssuedAt < user.passwordChangedAt) {\n        throw new UnauthorizedException('Token invalidated by password change');\n      }\n    }\n\n    return user;\n  }\n}\n```\n\nReference: [NestJS Authentication](https://docs.nestjs.com/security/authentication)","frontmatter":{"title":"Implement Secure JWT Authentication","impact":"CRITICAL","impactDescription":"Essential for secure APIs","tags":"security, jwt, authentication, tokens"},"isInternal":false,"tokens":917,"sizeBytes":4082},{"name":"security-rate-limiting.md","path":".agents/skills/nestjs-best-practices/rules/security-rate-limiting.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/security-rate-limiting.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Implement Rate Limiting\nimpact: HIGH\nimpactDescription: Protects against abuse and ensures fair resource usage\ntags: security, rate-limiting, throttler, protection\n---\n\n## Implement Rate Limiting\n\nUse `@nestjs/throttler` to limit request rates per client. Apply different limits for different endpoints - stricter for auth endpoints, more relaxed for read operations. Consider using Redis for distributed rate limiting in clustered deployments.\n\n**Incorrect (no rate limiting on sensitive endpoints):**\n\n```typescript\n// No rate limiting on sensitive endpoints\n@Controller('auth')\nexport class AuthController {\n  @Post('login')\n  async login(@Body() dto: LoginDto): Promise<TokenResponse> {\n    // Attackers can brute-force credentials\n    return this.authService.login(dto);\n  }\n\n  @Post('forgot-password')\n  async forgotPassword(@Body() dto: ForgotPasswordDto): Promise<void> {\n    // Can be abused to spam users with emails\n    return this.authService.sendResetEmail(dto.email);\n  }\n}\n\n// Same limits for all endpoints\n@UseGuards(ThrottlerGuard)\n@Controller('api')\nexport class ApiController {\n  @Get('public-data')\n  async getPublic() {} // Should allow more requests\n\n  @Post('process-payment')\n  async payment() {} // Should be more restrictive\n}\n```\n\n**Correct (configured throttler with endpoint-specific limits):**\n\n```typescript\n// Configure throttler globally with multiple limits\nimport { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';\n\n@Module({\n  imports: [\n    ThrottlerModule.forRoot([\n      {\n        name: 'short',\n        ttl: 1000, // 1 second\n        limit: 3, // 3 requests per second\n      },\n      {\n        name: 'medium',\n        ttl: 10000, // 10 seconds\n        limit: 20, // 20 requests per 10 seconds\n      },\n      {\n        name: 'long',\n        ttl: 60000, // 1 minute\n        limit: 100, // 100 requests per minute\n      },\n    ]),\n  ],\n  providers: [\n    {\n      provide: APP_GUARD,\n      useClass: ThrottlerGuard,\n    },\n  ],\n})\nexport class AppModule {}\n\n// Override limits per endpoint\n@Controller('auth')\nexport class AuthController {\n  @Post('login')\n  @Throttle({ short: { limit: 5, ttl: 60000 } }) // 5 attempts per minute\n  async login(@Body() dto: LoginDto): Promise<TokenResponse> {\n    return this.authService.login(dto);\n  }\n\n  @Post('forgot-password')\n  @Throttle({ short: { limit: 3, ttl: 3600000 } }) // 3 per hour\n  async forgotPassword(@Body() dto: ForgotPasswordDto): Promise<void> {\n    return this.authService.sendResetEmail(dto.email);\n  }\n}\n\n// Skip throttling for certain routes\n@Controller('health')\nexport class HealthController {\n  @Get()\n  @SkipThrottle()\n  check(): string {\n    return 'OK';\n  }\n}\n\n// Custom throttle per user type\n@Injectable()\nexport class CustomThrottlerGuard extends ThrottlerGuard {\n  protected async getTracker(req: Request): Promise<string> {\n    // Use user ID if authenticated, IP otherwise\n    return req.user?.id || req.ip;\n  }\n\n  protected async getLimit(context: ExecutionContext): Promise<number> {\n    const request = context.switchToHttp().getRequest();\n\n    // Higher limits for authenticated users\n    if (request.user) {\n      return request.user.isPremium ? 1000 : 200;\n    }\n\n    return 50; // Anonymous users\n  }\n}\n```\n\nReference: [NestJS Throttler](https://docs.nestjs.com/security/rate-limiting)","frontmatter":{"title":"Implement Rate Limiting","impact":"HIGH","impactDescription":"Protects against abuse and ensures fair resource usage","tags":"security, rate-limiting, throttler, protection"},"isInternal":false,"tokens":797,"sizeBytes":3323},{"name":"security-sanitize-output.md","path":".agents/skills/nestjs-best-practices/rules/security-sanitize-output.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/security-sanitize-output.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Sanitize Output to Prevent XSS\nimpact: HIGH\nimpactDescription: XSS vulnerabilities can compromise user sessions and data\ntags: security, xss, sanitization, html\n---\n\n## Sanitize Output to Prevent XSS\n\nWhile NestJS APIs typically return JSON (which browsers don't execute), XSS risks exist when rendering HTML, storing user content, or when frontend frameworks improperly handle API responses. Sanitize user-generated content before storage and use proper Content-Type headers.\n\n**Incorrect (storing raw HTML without sanitization):**\n\n```typescript\n// Store raw HTML from users\n@Injectable()\nexport class CommentsService {\n  async create(dto: CreateCommentDto): Promise<Comment> {\n    // User can inject: <script>steal(document.cookie)</script>\n    return this.repo.save({\n      content: dto.content, // Raw, unsanitized\n      authorId: dto.authorId,\n    });\n  }\n}\n\n// Return HTML without sanitization\n@Controller('pages')\nexport class PagesController {\n  @Get(':slug')\n  @Header('Content-Type', 'text/html')\n  async getPage(@Param('slug') slug: string): Promise<string> {\n    const page = await this.pagesService.findBySlug(slug);\n    // If page.content contains user input, XSS is possible\n    return `<html><body>${page.content}</body></html>`;\n  }\n}\n\n// Reflect user input in errors\n@Get(':id')\nasync findOne(@Param('id') id: string): Promise<User> {\n  const user = await this.repo.findOne({ where: { id } });\n  if (!user) {\n    // XSS if id contains malicious content and error is rendered\n    throw new NotFoundException(`User ${id} not found`);\n  }\n  return user;\n}\n```\n\n**Correct (sanitize content and use proper headers):**\n\n```typescript\n// Sanitize HTML content before storage\nimport * as sanitizeHtml from 'sanitize-html';\n\n@Injectable()\nexport class CommentsService {\n  private readonly sanitizeOptions: sanitizeHtml.IOptions = {\n    allowedTags: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],\n    allowedAttributes: {\n      a: ['href', 'title'],\n    },\n    allowedSchemes: ['http', 'https', 'mailto'],\n  };\n\n  async create(dto: CreateCommentDto): Promise<Comment> {\n    return this.repo.save({\n      content: sanitizeHtml(dto.content, this.sanitizeOptions),\n      authorId: dto.authorId,\n    });\n  }\n}\n\n// Use validation pipe to strip HTML\nimport { Transform } from 'class-transformer';\n\nexport class CreatePostDto {\n  @IsString()\n  @MaxLength(1000)\n  @Transform(({ value }) => sanitizeHtml(value, { allowedTags: [] }))\n  title: string;\n\n  @IsString()\n  @Transform(({ value }) =>\n    sanitizeHtml(value, {\n      allowedTags: ['p', 'br', 'b', 'i', 'a'],\n      allowedAttributes: { a: ['href'] },\n    }),\n  )\n  content: string;\n}\n\n// Set proper Content-Type headers\n@Controller('api')\nexport class ApiController {\n  @Get('data')\n  @Header('Content-Type', 'application/json')\n  async getData(): Promise<DataResponse> {\n    // JSON response - browser won't execute scripts\n    return this.service.getData();\n  }\n}\n\n// Sanitize error messages\n@Get(':id')\nasync findOne(@Param('id', ParseUUIDPipe) id: string): Promise<User> {\n  const user = await this.repo.findOne({ where: { id } });\n  if (!user) {\n    // UUID validation ensures safe format\n    throw new NotFoundException('User not found');\n  }\n  return user;\n}\n\n// Use Helmet for CSP headers\nimport helmet from 'helmet';\n\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n\n  app.use(\n    helmet({\n      contentSecurityPolicy: {\n        directives: {\n          defaultSrc: [\"'self'\"],\n          scriptSrc: [\"'self'\"],\n          styleSrc: [\"'self'\", \"'unsafe-inline'\"],\n          imgSrc: [\"'self'\", 'data:', 'https:'],\n        },\n      },\n    }),\n  );\n\n  await app.listen(3000);\n}\n```\n\nReference: [OWASP XSS Prevention](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)","frontmatter":{"title":"Sanitize Output to Prevent XSS","impact":"HIGH","impactDescription":"XSS vulnerabilities can compromise user sessions and data","tags":"security, xss, sanitization, html"},"isInternal":false,"tokens":912,"sizeBytes":3814},{"name":"security-use-guards.md","path":".agents/skills/nestjs-best-practices/rules/security-use-guards.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/security-use-guards.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Guards for Authentication and Authorization\nimpact: HIGH\nimpactDescription: Enforces access control before handlers execute\ntags: security, guards, authentication, authorization\n---\n\n## Use Guards for Authentication and Authorization\n\nGuards determine whether a request should be handled based on authentication state, roles, permissions, or other conditions. They run after middleware but before pipes and interceptors, making them ideal for access control. Use guards instead of manual checks in controllers.\n\n**Incorrect (manual auth checks in every handler):**\n\n```typescript\n// Manual auth checks in every handler\n@Controller('admin')\nexport class AdminController {\n  @Get('users')\n  async getUsers(@Request() req) {\n    if (!req.user) {\n      throw new UnauthorizedException();\n    }\n    if (!req.user.roles.includes('admin')) {\n      throw new ForbiddenException();\n    }\n    return this.adminService.getUsers();\n  }\n\n  @Delete('users/:id')\n  async deleteUser(@Request() req, @Param('id') id: string) {\n    if (!req.user) {\n      throw new UnauthorizedException();\n    }\n    if (!req.user.roles.includes('admin')) {\n      throw new ForbiddenException();\n    }\n    return this.adminService.deleteUser(id);\n  }\n}\n```\n\n**Correct (guards with declarative decorators):**\n\n```typescript\n// JWT Auth Guard\n@Injectable()\nexport class JwtAuthGuard implements CanActivate {\n  constructor(\n    private jwtService: JwtService,\n    private reflector: Reflector,\n  ) {}\n\n  async canActivate(context: ExecutionContext): Promise<boolean> {\n    // Check for @Public() decorator\n    const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [context.getHandler(), context.getClass()]);\n    if (isPublic) return true;\n\n    const request = context.switchToHttp().getRequest();\n    const token = this.extractToken(request);\n\n    if (!token) {\n      throw new UnauthorizedException('No token provided');\n    }\n\n    try {\n      request.user = await this.jwtService.verifyAsync(token);\n      return true;\n    } catch {\n      throw new UnauthorizedException('Invalid token');\n    }\n  }\n\n  private extractToken(request: Request): string | undefined {\n    const [type, token] = request.headers.authorization?.split(' ') ?? [];\n    return type === 'Bearer' ? token : undefined;\n  }\n}\n\n// Roles Guard\n@Injectable()\nexport class RolesGuard implements CanActivate {\n  constructor(private reflector: Reflector) {}\n\n  canActivate(context: ExecutionContext): boolean {\n    const requiredRoles = this.reflector.getAllAndOverride<Role[]>('roles', [context.getHandler(), context.getClass()]);\n\n    if (!requiredRoles) return true;\n\n    const { user } = context.switchToHttp().getRequest();\n    return requiredRoles.some(role => user.roles?.includes(role));\n  }\n}\n\n// Decorators\nexport const Public = () => SetMetadata('isPublic', true);\nexport const Roles = (...roles: Role[]) => SetMetadata('roles', roles);\n\n// Register guards globally\n@Module({\n  providers: [\n    { provide: APP_GUARD, useClass: JwtAuthGuard },\n    { provide: APP_GUARD, useClass: RolesGuard },\n  ],\n})\nexport class AppModule {}\n\n// Clean controller\n@Controller('admin')\n@Roles(Role.Admin) // Applied to all routes\nexport class AdminController {\n  @Get('users')\n  getUsers(): Promise<User[]> {\n    return this.adminService.getUsers();\n  }\n\n  @Delete('users/:id')\n  deleteUser(@Param('id') id: string): Promise<void> {\n    return this.adminService.deleteUser(id);\n  }\n\n  @Public() // Override: no auth required\n  @Get('health')\n  health() {\n    return { status: 'ok' };\n  }\n}\n```\n\nReference: [NestJS Guards](https://docs.nestjs.com/guards)","frontmatter":{"title":"Use Guards for Authentication and Authorization","impact":"HIGH","impactDescription":"Enforces access control before handlers execute","tags":"security, guards, authentication, authorization"},"isInternal":false,"tokens":786,"sizeBytes":3606},{"name":"security-validate-all-input.md","path":".agents/skills/nestjs-best-practices/rules/security-validate-all-input.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/security-validate-all-input.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Validate All Input with DTOs and Pipes\nimpact: HIGH\nimpactDescription: First line of defense against attacks\ntags: security, validation, dto, pipes\n---\n\n## Validate All Input with DTOs and Pipes\n\nAlways validate incoming data using class-validator decorators on DTOs and the global ValidationPipe. Never trust user input. Validate all request bodies, query parameters, and route parameters before processing.\n\n**Incorrect (trust raw input without validation):**\n\n```typescript\n// Trust raw input without validation\n@Controller('users')\nexport class UsersController {\n  @Post()\n  create(@Body() body: any) {\n    // body could contain anything - SQL injection, XSS, etc.\n    return this.usersService.create(body);\n  }\n\n  @Get()\n  findAll(@Query() query: any) {\n    // query.limit could be \"'; DROP TABLE users; --\"\n    return this.usersService.findAll(query.limit);\n  }\n}\n\n// DTOs without validation decorators\nexport class CreateUserDto {\n  name: string; // No validation\n  email: string; // Could be \"not-an-email\"\n  age: number; // Could be \"abc\" or -999\n}\n```\n\n**Correct (validated DTOs with global ValidationPipe):**\n\n```typescript\n// Enable ValidationPipe globally in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n\n  app.useGlobalPipes(\n    new ValidationPipe({\n      whitelist: true, // Strip unknown properties\n      forbidNonWhitelisted: true, // Throw on unknown properties\n      transform: true, // Auto-transform to DTO types\n      transformOptions: {\n        enableImplicitConversion: true,\n      },\n    }),\n  );\n\n  await app.listen(3000);\n}\n\n// Create well-validated DTOs\nimport {\n  IsString,\n  IsEmail,\n  IsInt,\n  Min,\n  Max,\n  IsOptional,\n  MinLength,\n  MaxLength,\n  Matches,\n  IsNotEmpty,\n} from 'class-validator';\nimport { Transform, Type } from 'class-transformer';\n\nexport class CreateUserDto {\n  @IsString()\n  @IsNotEmpty()\n  @MinLength(2)\n  @MaxLength(100)\n  @Transform(({ value }) => value?.trim())\n  name: string;\n\n  @IsEmail()\n  @Transform(({ value }) => value?.toLowerCase().trim())\n  email: string;\n\n  @IsInt()\n  @Min(0)\n  @Max(150)\n  age: number;\n\n  @IsString()\n  @MinLength(8)\n  @MaxLength(100)\n  @Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)/, {\n    message: 'Password must contain uppercase, lowercase, and number',\n  })\n  password: string;\n}\n\n// Query DTO with defaults and transformation\nexport class FindUsersQueryDto {\n  @IsOptional()\n  @IsString()\n  @MaxLength(100)\n  search?: string;\n\n  @IsOptional()\n  @Type(() => Number)\n  @IsInt()\n  @Min(1)\n  @Max(100)\n  limit: number = 20;\n\n  @IsOptional()\n  @Type(() => Number)\n  @IsInt()\n  @Min(0)\n  offset: number = 0;\n}\n\n// Param validation\nexport class UserIdParamDto {\n  @IsUUID('4')\n  id: string;\n}\n\n@Controller('users')\nexport class UsersController {\n  @Post()\n  create(@Body() dto: CreateUserDto): Promise<User> {\n    // dto is guaranteed to be valid\n    return this.usersService.create(dto);\n  }\n\n  @Get()\n  findAll(@Query() query: FindUsersQueryDto): Promise<User[]> {\n    // query.limit is a number, query.search is sanitized\n    return this.usersService.findAll(query);\n  }\n\n  @Get(':id')\n  findOne(@Param() params: UserIdParamDto): Promise<User> {\n    // params.id is a valid UUID\n    return this.usersService.findById(params.id);\n  }\n}\n```\n\nReference: [NestJS Validation](https://docs.nestjs.com/techniques/validation)","frontmatter":{"title":"Validate All Input with DTOs and Pipes","impact":"HIGH","impactDescription":"First line of defense against attacks","tags":"security, validation, dto, pipes"},"isInternal":false,"tokens":852,"sizeBytes":3355},{"name":"test-e2e-supertest.md","path":".agents/skills/nestjs-best-practices/rules/test-e2e-supertest.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/test-e2e-supertest.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Supertest for E2E Testing\nimpact: HIGH\nimpactDescription: Validates the full request/response cycle\ntags: testing, e2e, supertest, integration\n---\n\n## Use Supertest for E2E Testing\n\nEnd-to-end tests use Supertest to make real HTTP requests against your NestJS application. They test the full stack including middleware, guards, pipes, and interceptors. E2E tests catch integration issues that unit tests miss.\n\n**Incorrect (no proper E2E setup or teardown):**\n\n```typescript\n// Only unit test controllers\ndescribe('UsersController', () => {\n  it('should return users', async () => {\n    const service = { findAll: jest.fn().mockResolvedValue([]) };\n    const controller = new UsersController(service as any);\n\n    const result = await controller.findAll();\n\n    expect(result).toEqual([]);\n    // Doesn't test: routes, guards, pipes, serialization\n  });\n});\n\n// E2E tests without proper setup/teardown\ndescribe('Users API', () => {\n  it('should create user', async () => {\n    const app = await NestFactory.create(AppModule);\n    // No proper initialization\n    // No cleanup after test\n    // Hits real database\n  });\n});\n```\n\n**Correct (proper E2E setup with Supertest):**\n\n```typescript\n// Proper E2E test setup\nimport { Test, TestingModule } from '@nestjs/testing';\nimport { INestApplication, ValidationPipe } from '@nestjs/common';\nimport * as request from 'supertest';\nimport { AppModule } from '../src/app.module';\n\ndescribe('UsersController (e2e)', () => {\n  let app: INestApplication;\n\n  beforeAll(async () => {\n    const moduleFixture: TestingModule = await Test.createTestingModule({\n      imports: [AppModule],\n    }).compile();\n\n    app = moduleFixture.createNestApplication();\n\n    // Apply same config as production\n    app.useGlobalPipes(\n      new ValidationPipe({\n        whitelist: true,\n        transform: true,\n        forbidNonWhitelisted: true,\n      }),\n    );\n\n    await app.init();\n  });\n\n  afterAll(async () => {\n    await app.close();\n  });\n\n  describe('/users (POST)', () => {\n    it('should create a user', () => {\n      return request(app.getHttpServer())\n        .post('/users')\n        .send({ name: 'John', email: 'john@test.com' })\n        .expect(201)\n        .expect(res => {\n          expect(res.body).toHaveProperty('id');\n          expect(res.body.name).toBe('John');\n          expect(res.body.email).toBe('john@test.com');\n        });\n    });\n\n    it('should return 400 for invalid email', () => {\n      return request(app.getHttpServer())\n        .post('/users')\n        .send({ name: 'John', email: 'invalid-email' })\n        .expect(400)\n        .expect(res => {\n          expect(res.body.message).toContain('email');\n        });\n    });\n  });\n\n  describe('/users/:id (GET)', () => {\n    it('should return 404 for non-existent user', () => {\n      return request(app.getHttpServer()).get('/users/non-existent-id').expect(404);\n    });\n  });\n});\n\n// Testing with authentication\ndescribe('Protected Routes (e2e)', () => {\n  let app: INestApplication;\n  let authToken: string;\n\n  beforeAll(async () => {\n    const moduleFixture = await Test.createTestingModule({\n      imports: [AppModule],\n    }).compile();\n\n    app = moduleFixture.createNestApplication();\n    app.useGlobalPipes(new ValidationPipe({ whitelist: true }));\n    await app.init();\n\n    // Get auth token\n    const loginResponse = await request(app.getHttpServer())\n      .post('/auth/login')\n      .send({ email: 'test@test.com', password: 'password' });\n\n    authToken = loginResponse.body.accessToken;\n  });\n\n  it('should return 401 without token', () => {\n    return request(app.getHttpServer()).get('/users/me').expect(401);\n  });\n\n  it('should return user profile with valid token', () => {\n    return request(app.getHttpServer())\n      .get('/users/me')\n      .set('Authorization', `Bearer ${authToken}`)\n      .expect(200)\n      .expect(res => {\n        expect(res.body.email).toBe('test@test.com');\n      });\n  });\n});\n\n// Database isolation for E2E tests\ndescribe('Orders API (e2e)', () => {\n  let app: INestApplication;\n  let dataSource: DataSource;\n\n  beforeAll(async () => {\n    const moduleFixture = await Test.createTestingModule({\n      imports: [\n        ConfigModule.forRoot({\n          envFilePath: '.env.test', // Test database config\n        }),\n        AppModule,\n      ],\n    }).compile();\n\n    app = moduleFixture.createNestApplication();\n    dataSource = moduleFixture.get(DataSource);\n    await app.init();\n  });\n\n  beforeEach(async () => {\n    // Clean database between tests\n    await dataSource.synchronize(true);\n  });\n\n  afterAll(async () => {\n    await dataSource.destroy();\n    await app.close();\n  });\n});\n```\n\nReference: [NestJS E2E Testing](https://docs.nestjs.com/fundamentals/testing#end-to-end-testing)","frontmatter":{"title":"Use Supertest for E2E Testing","impact":"HIGH","impactDescription":"Validates the full request/response cycle","tags":"testing, e2e, supertest, integration"},"isInternal":false,"tokens":1102,"sizeBytes":4761},{"name":"test-mock-external-services.md","path":".agents/skills/nestjs-best-practices/rules/test-mock-external-services.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/test-mock-external-services.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Mock External Services in Tests\nimpact: HIGH\nimpactDescription: Ensures fast, reliable, deterministic tests\ntags: testing, mocking, external-services, jest\n---\n\n## Mock External Services in Tests\n\nNever call real external services (APIs, databases, message queues) in unit tests. Mock them to ensure tests are fast, deterministic, and don't incur costs. Use realistic mock data and test edge cases like timeouts and errors.\n\n**Incorrect (calling real APIs and databases):**\n\n```typescript\n// Call real APIs in tests\ndescribe('PaymentService', () => {\n  it('should process payment', async () => {\n    const service = new PaymentService(new StripeClient(realApiKey));\n    // Hits real Stripe API!\n    const result = await service.charge('tok_visa', 1000);\n    // Slow, costs money, flaky\n  });\n});\n\n// Use real database\ndescribe('UsersService', () => {\n  beforeEach(async () => {\n    await connection.query('DELETE FROM users'); // Modifies real DB\n  });\n\n  it('should create user', async () => {\n    const user = await service.create({ email: 'test@test.com' });\n    // Side effects on shared database\n  });\n});\n\n// Incomplete mocks\nconst mockHttpService = {\n  get: jest.fn().mockResolvedValue({ data: {} }),\n  // Missing error scenarios, missing other methods\n};\n```\n\n**Correct (mock all external dependencies):**\n\n```typescript\n// Mock HTTP service properly\ndescribe('WeatherService', () => {\n  let service: WeatherService;\n  let httpService: jest.Mocked<HttpService>;\n\n  beforeEach(async () => {\n    const module = await Test.createTestingModule({\n      providers: [\n        WeatherService,\n        {\n          provide: HttpService,\n          useValue: {\n            get: jest.fn(),\n            post: jest.fn(),\n          },\n        },\n      ],\n    }).compile();\n\n    service = module.get(WeatherService);\n    httpService = module.get(HttpService);\n  });\n\n  it('should return weather data', async () => {\n    const mockResponse = {\n      data: { temperature: 72, humidity: 45 },\n      status: 200,\n      statusText: 'OK',\n      headers: {},\n      config: {},\n    };\n\n    httpService.get.mockReturnValue(of(mockResponse));\n\n    const result = await service.getWeather('NYC');\n\n    expect(result).toEqual({ temperature: 72, humidity: 45 });\n  });\n\n  it('should handle API timeout', async () => {\n    httpService.get.mockReturnValue(throwError(() => new Error('ETIMEDOUT')));\n\n    await expect(service.getWeather('NYC')).rejects.toThrow('Weather service unavailable');\n  });\n\n  it('should handle rate limiting', async () => {\n    httpService.get.mockReturnValue(\n      throwError(() => ({\n        response: { status: 429, data: { message: 'Rate limited' } },\n      })),\n    );\n\n    await expect(service.getWeather('NYC')).rejects.toThrow(TooManyRequestsException);\n  });\n});\n\n// Mock repository instead of database\ndescribe('UsersService', () => {\n  let service: UsersService;\n  let repo: jest.Mocked<Repository<User>>;\n\n  beforeEach(async () => {\n    const mockRepo = {\n      find: jest.fn(),\n      findOne: jest.fn(),\n      save: jest.fn(),\n      delete: jest.fn(),\n      createQueryBuilder: jest.fn(),\n    };\n\n    const module = await Test.createTestingModule({\n      providers: [UsersService, { provide: getRepositoryToken(User), useValue: mockRepo }],\n    }).compile();\n\n    service = module.get(UsersService);\n    repo = module.get(getRepositoryToken(User));\n  });\n\n  it('should find user by id', async () => {\n    const mockUser = { id: '1', name: 'John', email: 'john@test.com' };\n    repo.findOne.mockResolvedValue(mockUser);\n\n    const result = await service.findById('1');\n\n    expect(result).toEqual(mockUser);\n    expect(repo.findOne).toHaveBeenCalledWith({ where: { id: '1' } });\n  });\n});\n\n// Create mock factory for complex SDKs\nfunction createMockStripe(): jest.Mocked<Stripe> {\n  return {\n    paymentIntents: {\n      create: jest.fn(),\n      retrieve: jest.fn(),\n      confirm: jest.fn(),\n      cancel: jest.fn(),\n    },\n    customers: {\n      create: jest.fn(),\n      retrieve: jest.fn(),\n    },\n  } as any;\n}\n\n// Mock time for time-dependent tests\ndescribe('TokenService', () => {\n  beforeEach(() => {\n    jest.useFakeTimers();\n    jest.setSystemTime(new Date('2024-01-15'));\n  });\n\n  afterEach(() => {\n    jest.useRealTimers();\n  });\n\n  it('should expire token after 1 hour', async () => {\n    const token = await service.createToken();\n\n    // Fast-forward time\n    jest.advanceTimersByTime(61 * 60 * 1000);\n\n    expect(await service.isValid(token)).toBe(false);\n  });\n});\n```\n\nReference: [Jest Mocking](https://jestjs.io/docs/mock-functions)","frontmatter":{"title":"Mock External Services in Tests","impact":"HIGH","impactDescription":"Ensures fast, reliable, deterministic tests","tags":"testing, mocking, external-services, jest"},"isInternal":false,"tokens":1069,"sizeBytes":4574},{"name":"test-use-testing-module.md","path":".agents/skills/nestjs-best-practices/rules/test-use-testing-module.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/rules/test-use-testing-module.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Testing Module for Unit Tests\nimpact: HIGH\nimpactDescription: Enables proper isolated testing with mocked dependencies\ntags: testing, unit-tests, mocking, jest\n---\n\n## Use Testing Module for Unit Tests\n\nUse `@nestjs/testing` module to create isolated test environments with mocked dependencies. This ensures your tests run fast, don't depend on external services, and properly test your business logic in isolation.\n\n**Incorrect (manual instantiation bypassing DI):**\n\n```typescript\n// Instantiate services manually without DI\ndescribe('UsersService', () => {\n  it('should create user', async () => {\n    // Manual instantiation bypasses DI\n    const repo = new UserRepository(); // Real repo!\n    const service = new UsersService(repo);\n\n    const user = await service.create({ name: 'Test' });\n    // This hits the real database!\n  });\n});\n\n// Test implementation details\ndescribe('UsersController', () => {\n  it('should call service', async () => {\n    const service = { create: jest.fn() };\n    const controller = new UsersController(service as any);\n\n    await controller.create({ name: 'Test' });\n\n    expect(service.create).toHaveBeenCalled(); // Tests implementation, not behavior\n  });\n});\n```\n\n**Correct (use Test.createTestingModule with mocked dependencies):**\n\n```typescript\n// Use Test.createTestingModule for proper DI\nimport { Test, TestingModule } from '@nestjs/testing';\n\ndescribe('UsersService', () => {\n  let service: UsersService;\n  let repo: jest.Mocked<UserRepository>;\n\n  beforeEach(async () => {\n    const module: TestingModule = await Test.createTestingModule({\n      providers: [\n        UsersService,\n        {\n          provide: UserRepository,\n          useValue: {\n            save: jest.fn(),\n            findOne: jest.fn(),\n            find: jest.fn(),\n          },\n        },\n      ],\n    }).compile();\n\n    service = module.get<UsersService>(UsersService);\n    repo = module.get(UserRepository);\n  });\n\n  afterEach(() => {\n    jest.clearAllMocks();\n  });\n\n  describe('create', () => {\n    it('should save and return user', async () => {\n      const dto = { name: 'John', email: 'john@test.com' };\n      const expectedUser = { id: '1', ...dto };\n\n      repo.save.mockResolvedValue(expectedUser);\n\n      const result = await service.create(dto);\n\n      expect(result).toEqual(expectedUser);\n      expect(repo.save).toHaveBeenCalledWith(dto);\n    });\n\n    it('should throw on duplicate email', async () => {\n      repo.findOne.mockResolvedValue({ id: '1', email: 'test@test.com' });\n\n      await expect(service.create({ name: 'Test', email: 'test@test.com' })).rejects.toThrow(ConflictException);\n    });\n  });\n\n  describe('findById', () => {\n    it('should return user when found', async () => {\n      const user = { id: '1', name: 'John' };\n      repo.findOne.mockResolvedValue(user);\n\n      const result = await service.findById('1');\n\n      expect(result).toEqual(user);\n    });\n\n    it('should throw NotFoundException when not found', async () => {\n      repo.findOne.mockResolvedValue(null);\n\n      await expect(service.findById('999')).rejects.toThrow(NotFoundException);\n    });\n  });\n});\n\n// Testing guards and interceptors\ndescribe('RolesGuard', () => {\n  let guard: RolesGuard;\n  let reflector: Reflector;\n\n  beforeEach(async () => {\n    const module = await Test.createTestingModule({\n      providers: [RolesGuard, Reflector],\n    }).compile();\n\n    guard = module.get<RolesGuard>(RolesGuard);\n    reflector = module.get<Reflector>(Reflector);\n  });\n\n  it('should allow when no roles required', () => {\n    const context = createMockExecutionContext({ user: { roles: [] } });\n    jest.spyOn(reflector, 'getAllAndOverride').mockReturnValue(undefined);\n\n    expect(guard.canActivate(context)).toBe(true);\n  });\n\n  it('should allow admin for admin-only route', () => {\n    const context = createMockExecutionContext({ user: { roles: ['admin'] } });\n    jest.spyOn(reflector, 'getAllAndOverride').mockReturnValue(['admin']);\n\n    expect(guard.canActivate(context)).toBe(true);\n  });\n});\n\nfunction createMockExecutionContext(request: Partial<Request>): ExecutionContext {\n  return {\n    switchToHttp: () => ({\n      getRequest: () => request,\n    }),\n    getHandler: () => jest.fn(),\n    getClass: () => jest.fn(),\n  } as ExecutionContext;\n}\n```\n\nReference: [NestJS Testing](https://docs.nestjs.com/fundamentals/testing)","frontmatter":{"title":"Use Testing Module for Unit Tests","impact":"HIGH","impactDescription":"Enables proper isolated testing with mocked dependencies","tags":"testing, unit-tests, mocking, jest"},"isInternal":false,"tokens":956,"sizeBytes":4374},{"name":"package.json","path":".agents/skills/nestjs-best-practices/scripts/package.json","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/scripts/package.json","title":"nestjs-best-practices-scripts","category":"anthropic-skill","format":"json","content":"{\n  \"name\": \"nestjs-best-practices-scripts\",\n  \"version\": \"1.0.0\",\n  \"type\": \"module\",\n  \"description\": \"Build scripts for NestJS Best Practices skillset\",\n  \"scripts\": {\n    \"build\": \"npx ts-node build-agents.ts\",\n    \"build:watch\": \"npx nodemon --watch ../rules --ext md --exec 'npx ts-node build-agents.ts'\"\n  },\n  \"devDependencies\": {\n    \"typescript\": \"^5.0.0\",\n    \"ts-node\": \"^10.9.0\",\n    \"@types/node\": \"^20.0.0\"\n  }\n}\n","frontmatter":{"name":"nestjs-best-practices-scripts","version":"1.0.0","type":"module","description":"Build scripts for NestJS Best Practices skillset","scripts":{"build":"npx ts-node build-agents.ts","build:watch":"npx nodemon --watch ../rules --ext md --exec 'npx ts-node build-agents.ts'"},"devDependencies":{"typescript":"^5.0.0","ts-node":"^10.9.0","@types/node":"^20.0.0"}},"isInternal":false,"tokens":140,"sizeBytes":428},{"name":"SKILL.md","path":".agents/skills/nestjs-best-practices/SKILL.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/nestjs-best-practices/SKILL.md","title":"nestjs-best-practices","category":"anthropic-skill","format":"markdown","content":"---\nname: nestjs-best-practices\ndescription: NestJS best practices and architecture patterns for building production-ready applications. This skill should be used when writing, reviewing, or refactoring NestJS code to ensure proper patterns for modules, dependency injection, security, and performance.\nlicense: MIT\nmetadata:\n  author: Kadajett\n  version: '1.1.0'\n---\n\n# NestJS Best Practices\n\nComprehensive best practices guide for NestJS applications. Contains 40 rules across 10 categories, prioritized by impact to guide automated refactoring and code generation.\n\n## When to Apply\n\nReference these guidelines when:\n\n- Writing new NestJS modules, controllers, or services\n- Implementing authentication and authorization\n- Reviewing code for architecture and security issues\n- Refactoring existing NestJS codebases\n- Optimizing performance or database queries\n- Building microservices architectures\n\n## Rule Categories by Priority\n\n| Priority | Category             | Impact      | Prefix      |\n| -------- | -------------------- | ----------- | ----------- |\n| 1        | Architecture         | CRITICAL    | `arch-`     |\n| 2        | Dependency Injection | CRITICAL    | `di-`       |\n| 3        | Error Handling       | HIGH        | `error-`    |\n| 4        | Security             | HIGH        | `security-` |\n| 5        | Performance          | HIGH        | `perf-`     |\n| 6        | Testing              | MEDIUM-HIGH | `test-`     |\n| 7        | Database & ORM       | MEDIUM-HIGH | `db-`       |\n| 8        | API Design           | MEDIUM      | `api-`      |\n| 9        | Microservices        | MEDIUM      | `micro-`    |\n| 10       | DevOps & Deployment  | LOW-MEDIUM  | `devops-`   |\n\n## Quick Reference\n\n### 1. Architecture (CRITICAL)\n\n- `arch-avoid-circular-deps` - Avoid circular module dependencies\n- `arch-feature-modules` - Organize by feature, not technical layer\n- `arch-module-sharing` - Proper module exports/imports, avoid duplicate providers\n- `arch-single-responsibility` - Focused services over \"god services\"\n- `arch-use-repository-pattern` - Abstract database logic for testability\n- `arch-use-events` - Event-driven architecture for decoupling\n\n### 2. Dependency Injection (CRITICAL)\n\n- `di-avoid-service-locator` - Avoid service locator anti-pattern\n- `di-interface-segregation` - Interface Segregation Principle (ISP)\n- `di-liskov-substitution` - Liskov Substitution Principle (LSP)\n- `di-prefer-constructor-injection` - Constructor over property injection\n- `di-scope-awareness` - Understand singleton/request/transient scopes\n- `di-use-interfaces-tokens` - Use injection tokens for interfaces\n\n### 3. Error Handling (HIGH)\n\n- `error-use-exception-filters` - Centralized exception handling\n- `error-throw-http-exceptions` - Use NestJS HTTP exceptions\n- `error-handle-async-errors` - Handle async errors properly\n\n### 4. Security (HIGH)\n\n- `security-auth-jwt` - Secure JWT authentication\n- `security-validate-all-input` - Validate with class-validator\n- `security-use-guards` - Authentication and authorization guards\n- `security-sanitize-output` - Prevent XSS attacks\n- `security-rate-limiting` - Implement rate limiting\n\n### 5. Performance (HIGH)\n\n- `perf-async-hooks` - Proper async lifecycle hooks\n- `perf-use-caching` - Implement caching strategies\n- `perf-optimize-database` - Optimize database queries\n- `perf-lazy-loading` - Lazy load modules for faster startup\n\n### 6. Testing (MEDIUM-HIGH)\n\n- `test-use-testing-module` - Use NestJS testing utilities\n- `test-e2e-supertest` - E2E testing with Supertest\n- `test-mock-external-services` - Mock external dependencies\n\n### 7. Database & ORM (MEDIUM-HIGH)\n\n- `db-use-transactions` - Transaction management\n- `db-avoid-n-plus-one` - Avoid N+1 query problems\n- `db-use-migrations` - Use migrations for schema changes\n\n### 8. API Design (MEDIUM)\n\n- `api-use-dto-serialization` - DTO and response serialization\n- `api-use-interceptors` - Cross-cutting concerns\n- `api-versioning` - API versioning strategies\n- `api-use-pipes` - Input transformation with pipes\n\n### 9. Microservices (MEDIUM)\n\n- `micro-use-patterns` - Message and event patterns\n- `micro-use-health-checks` - Health checks for orchestration\n- `micro-use-queues` - Background job processing\n\n### 10. DevOps & Deployment (LOW-MEDIUM)\n\n- `devops-use-config-module` - Environment configuration\n- `devops-use-logging` - Structured logging\n- `devops-graceful-shutdown` - Zero-downtime deployments\n\n## How to Use\n\nRead individual rule files for detailed explanations and code examples:\n\n```\nrules/arch-avoid-circular-deps.md\nrules/security-validate-all-input.md\nrules/_sections.md\n```\n\nEach rule file contains:\n\n- Brief explanation of why it matters\n- Incorrect code example with explanation\n- Correct code example with explanation\n- Additional context and references\n\n## Full Compiled Document\n\nFor the complete guide with all rules expanded: `AGENTS.md`","frontmatter":{"name":"nestjs-best-practices","description":"NestJS best practices and architecture patterns for building production-ready applications. This skill should be used when writing, reviewing, or refactoring NestJS code to ensure proper patterns for modules, dependency injection, security, and performance.","license":"MIT","metadata":{"author":"Kadajett","version":"1.1.0"}},"isInternal":false,"tokens":1174,"sizeBytes":4901},{"name":"SKILL.md","path":".agents/skills/typeorm/SKILL.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/typeorm/SKILL.md","title":"typeorm","category":"anthropic-skill","format":"markdown","content":"---\nname: typeorm\ndescription: Guidelines for developing with TypeORM, a full-featured ORM for TypeScript and JavaScript supporting multiple databases\n---\n\n# TypeORM Development Guidelines\n\nYou are an expert in TypeORM, TypeScript, and database design with a focus on the Data Mapper pattern and enterprise application architecture.\n\n## Core Principles\n\n- TypeORM supports both Active Record and Data Mapper patterns\n- Uses TypeScript decorators for entity and column definitions\n- Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, and more\n- Works in Node.js, Browser, Ionic, Cordova, React Native, NativeScript, Expo, and Electron\n- First-class support for database migrations\n\n## TypeScript Configuration\n\nRequired settings in tsconfig.json:\n\n```json\n{\n  \"compilerOptions\": {\n    \"experimentalDecorators\": true,\n    \"emitDecoratorMetadata\": true,\n    \"strict\": true,\n    \"target\": \"ES2020\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\"\n  }\n}\n```\n\n## Entity Definition\n\n### Basic Entity\n\n```typescript\nimport { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';\n\n@Entity('users')\nexport class User {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column({ type: 'varchar', length: 255, unique: true })\n  email: string;\n\n  @Column({ type: 'varchar', length: 255, nullable: true })\n  name: string | null;\n\n  @Column({ type: 'boolean', default: true })\n  isActive: boolean;\n\n  @CreateDateColumn()\n  createdAt: Date;\n\n  @UpdateDateColumn()\n  updatedAt: Date;\n}\n```\n\n### Primary Key Options\n\n```typescript\n// Auto-increment\n@PrimaryGeneratedColumn()\nid: number;\n\n// UUID\n@PrimaryGeneratedColumn(\"uuid\")\nid: string;\n\n// Custom primary key\n@PrimaryColumn()\nid: string;\n\n// Composite primary key\n@Entity()\nexport class OrderItem {\n  @PrimaryColumn()\n  orderId: number;\n\n  @PrimaryColumn()\n  productId: number;\n}\n```\n\n### Column Decorators\n\n```typescript\n@Entity()\nexport class Product {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  // String columns\n  @Column({ type: 'varchar', length: 255 })\n  name: string;\n\n  @Column({ type: 'text', nullable: true })\n  description: string | null;\n\n  // Numeric columns\n  @Column({ type: 'decimal', precision: 10, scale: 2 })\n  price: number;\n\n  @Column({ type: 'int', default: 0 })\n  stock: number;\n\n  // Boolean\n  @Column({ type: 'boolean', default: true })\n  isAvailable: boolean;\n\n  // JSON\n  @Column({ type: 'jsonb', nullable: true })\n  metadata: Record<string, any> | null;\n\n  // Enum\n  @Column({\n    type: 'enum',\n    enum: ['active', 'inactive', 'pending'],\n    default: 'pending',\n  })\n  status: 'active' | 'inactive' | 'pending';\n\n  // Timestamps\n  @CreateDateColumn()\n  createdAt: Date;\n\n  @UpdateDateColumn()\n  updatedAt: Date;\n\n  @DeleteDateColumn()\n  deletedAt: Date | null; // For soft deletes\n\n  // Version column for optimistic locking\n  @VersionColumn()\n  version: number;\n}\n```\n\n## Relationships\n\n### One-to-One\n\n```typescript\n@Entity()\nexport class User {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @OneToOne(() => Profile, profile => profile.user, { cascade: true })\n  @JoinColumn()\n  profile: Profile;\n}\n\n@Entity()\nexport class Profile {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column()\n  bio: string;\n\n  @OneToOne(() => User, user => user.profile)\n  user: User;\n}\n```\n\n### One-to-Many / Many-to-One\n\n```typescript\n@Entity()\nexport class User {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column()\n  name: string;\n\n  @OneToMany(() => Post, post => post.author)\n  posts: Post[];\n}\n\n@Entity()\nexport class Post {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column()\n  title: string;\n\n  @ManyToOne(() => User, user => user.posts, { onDelete: 'CASCADE' })\n  @JoinColumn({ name: 'author_id' })\n  author: User;\n\n  @Column()\n  authorId: number; // Explicit foreign key column\n}\n```\n\n### Many-to-Many\n\n```typescript\n@Entity()\nexport class Post {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column()\n  title: string;\n\n  @ManyToMany(() => Tag, tag => tag.posts)\n  @JoinTable({\n    name: 'post_tags',\n    joinColumn: { name: 'post_id' },\n    inverseJoinColumn: { name: 'tag_id' },\n  })\n  tags: Tag[];\n}\n\n@Entity()\nexport class Tag {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column({ unique: true })\n  name: string;\n\n  @ManyToMany(() => Post, post => post.tags)\n  posts: Post[];\n}\n```\n\n## Repository Pattern\n\n### Basic Repository Usage\n\n```typescript\nimport { AppDataSource } from './data-source';\nimport { User } from './entities/User';\n\nconst userRepository = AppDataSource.getRepository(User);\n\n// Find all\nconst users = await userRepository.find();\n\n// Find with conditions\nconst activeUsers = await userRepository.find({\n  where: { isActive: true },\n});\n\n// Find one\nconst user = await userRepository.findOne({\n  where: { id: 1 },\n});\n\n// Find or fail\nconst user = await userRepository.findOneOrFail({\n  where: { id: 1 },\n});\n\n// Save\nconst newUser = userRepository.create({\n  email: 'user@example.com',\n  name: 'John Doe',\n});\nawait userRepository.save(newUser);\n\n// Update\nawait userRepository.update({ id: 1 }, { name: 'Jane Doe' });\n\n// Delete\nawait userRepository.delete({ id: 1 });\n\n// Soft delete (requires @DeleteDateColumn)\nawait userRepository.softDelete({ id: 1 });\n```\n\n### Custom Repository\n\n```typescript\nimport { Repository, DataSource } from 'typeorm';\nimport { User } from './entities/User';\n\nexport class UserRepository extends Repository<User> {\n  constructor(private dataSource: DataSource) {\n    super(User, dataSource.createEntityManager());\n  }\n\n  async findByEmail(email: string): Promise<User | null> {\n    return this.findOne({ where: { email } });\n  }\n\n  async findActiveUsers(): Promise<User[]> {\n    return this.find({\n      where: { isActive: true },\n      order: { createdAt: 'DESC' },\n    });\n  }\n\n  async findWithPosts(userId: number): Promise<User | null> {\n    return this.findOne({\n      where: { id: userId },\n      relations: ['posts'],\n    });\n  }\n}\n```\n\n### Query Builder\n\n```typescript\nconst users = await userRepository\n  .createQueryBuilder('user')\n  .leftJoinAndSelect('user.posts', 'post')\n  .where('user.isActive = :isActive', { isActive: true })\n  .andWhere('post.publishedAt IS NOT NULL')\n  .orderBy('user.createdAt', 'DESC')\n  .skip(0)\n  .take(10)\n  .getMany();\n\n// With raw results\nconst result = await userRepository\n  .createQueryBuilder('user')\n  .select('COUNT(*)', 'count')\n  .where('user.isActive = :isActive', { isActive: true })\n  .getRawOne();\n\n// Insert with query builder\nawait userRepository\n  .createQueryBuilder()\n  .insert()\n  .into(User)\n  .values([\n    { email: 'user1@example.com', name: 'User 1' },\n    { email: 'user2@example.com', name: 'User 2' },\n  ])\n  .execute();\n```\n\n## Data Source Configuration\n\n```typescript\n// data-source.ts\nimport { DataSource } from 'typeorm';\nimport { User } from './entities/User';\nimport { Post } from './entities/Post';\n\nexport const AppDataSource = new DataSource({\n  type: 'postgres',\n  host: process.env.DB_HOST || 'localhost',\n  port: parseInt(process.env.DB_PORT || '5432'),\n  username: process.env.DB_USERNAME,\n  password: process.env.DB_PASSWORD,\n  database: process.env.DB_NAME,\n\n  // Entity configuration\n  entities: [User, Post],\n  // Or use glob pattern: entities: [\"src/entities/**/*.ts\"]\n\n  // Migrations\n  migrations: ['src/migrations/**/*.ts'],\n\n  // Synchronize - NEVER use in production\n  synchronize: false,\n\n  // Logging\n  logging: process.env.NODE_ENV === 'development',\n\n  // Connection pool\n  poolSize: 10,\n\n  // SSL (for production)\n  ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,\n});\n\n// Initialize connection\nAppDataSource.initialize()\n  .then(() => console.log('Data Source initialized'))\n  .catch(error => console.error('Error initializing Data Source:', error));\n```\n\n## Migrations\n\n### Creating Migrations\n\n```bash\n# Generate migration from entity changes\nnpx typeorm migration:generate src/migrations/CreateUsers -d src/data-source.ts\n\n# Create empty migration\nnpx typeorm migration:create src/migrations/SeedUsers\n\n# Run migrations\nnpx typeorm migration:run -d src/data-source.ts\n\n# Revert last migration\nnpx typeorm migration:revert -d src/data-source.ts\n```\n\n### Migration File Structure\n\n```typescript\nimport { MigrationInterface, QueryRunner, Table, TableIndex } from 'typeorm';\n\nexport class CreateUsers1234567890 implements MigrationInterface {\n  public async up(queryRunner: QueryRunner): Promise<void> {\n    await queryRunner.createTable(\n      new Table({\n        name: 'users',\n        columns: [\n          {\n            name: 'id',\n            type: 'int',\n            isPrimary: true,\n            isGenerated: true,\n            generationStrategy: 'increment',\n          },\n          {\n            name: 'email',\n            type: 'varchar',\n            length: '255',\n            isUnique: true,\n          },\n          {\n            name: 'name',\n            type: 'varchar',\n            length: '255',\n            isNullable: true,\n          },\n          {\n            name: 'is_active',\n            type: 'boolean',\n            default: true,\n          },\n          {\n            name: 'created_at',\n            type: 'timestamp',\n            default: 'CURRENT_TIMESTAMP',\n          },\n          {\n            name: 'updated_at',\n            type: 'timestamp',\n            default: 'CURRENT_TIMESTAMP',\n            onUpdate: 'CURRENT_TIMESTAMP',\n          },\n        ],\n      }),\n      true,\n    );\n\n    await queryRunner.createIndex(\n      'users',\n      new TableIndex({\n        name: 'IDX_USERS_EMAIL',\n        columnNames: ['email'],\n      }),\n    );\n  }\n\n  public async down(queryRunner: QueryRunner): Promise<void> {\n    await queryRunner.dropIndex('users', 'IDX_USERS_EMAIL');\n    await queryRunner.dropTable('users');\n  }\n}\n```\n\n## Transactions\n\n```typescript\n// Using QueryRunner\nconst queryRunner = AppDataSource.createQueryRunner();\nawait queryRunner.connect();\nawait queryRunner.startTransaction();\n\ntry {\n  const user = queryRunner.manager.create(User, {\n    email: 'user@example.com',\n    name: 'User',\n  });\n  await queryRunner.manager.save(user);\n\n  const post = queryRunner.manager.create(Post, {\n    title: 'First Post',\n    author: user,\n  });\n  await queryRunner.manager.save(post);\n\n  await queryRunner.commitTransaction();\n} catch (error) {\n  await queryRunner.rollbackTransaction();\n  throw error;\n} finally {\n  await queryRunner.release();\n}\n\n// Using transaction method\nawait AppDataSource.transaction(async manager => {\n  const user = manager.create(User, {\n    email: 'user@example.com',\n    name: 'User',\n  });\n  await manager.save(user);\n\n  const post = manager.create(Post, {\n    title: 'First Post',\n    author: user,\n  });\n  await manager.save(post);\n});\n```\n\n## NestJS Integration\n\n```typescript\n// app.module.ts\nimport { Module } from '@nestjs/common';\nimport { TypeOrmModule } from '@nestjs/typeorm';\nimport { User } from './entities/user.entity';\nimport { UsersModule } from './users/users.module';\n\n@Module({\n  imports: [\n    TypeOrmModule.forRoot({\n      type: 'postgres',\n      host: 'localhost',\n      port: 5432,\n      username: 'user',\n      password: 'password',\n      database: 'db',\n      entities: [User],\n      synchronize: false,\n    }),\n    UsersModule,\n  ],\n})\nexport class AppModule {}\n\n// users/users.module.ts\n@Module({\n  imports: [TypeOrmModule.forFeature([User])],\n  providers: [UsersService],\n  controllers: [UsersController],\n})\nexport class UsersModule {}\n\n// users/users.service.ts\n@Injectable()\nexport class UsersService {\n  constructor(\n    @InjectRepository(User)\n    private usersRepository: Repository<User>,\n  ) {}\n\n  findAll(): Promise<User[]> {\n    return this.usersRepository.find();\n  }\n\n  findOne(id: number): Promise<User | null> {\n    return this.usersRepository.findOneBy({ id });\n  }\n}\n```\n\n## Best Practices\n\n### Use Migrations in Production\n\nNever use `synchronize: true` in production. Always use migrations:\n\n```typescript\n// Development: Use migrations, not sync\nsynchronize: false,\n```\n\n### Eager vs Lazy Loading\n\n```typescript\n// Eager loading - loads relations automatically\n@OneToMany(() => Post, (post) => post.author, { eager: true })\nposts: Post[];\n\n// Lazy loading - loads relations on access\n@OneToMany(() => Post, (post) => post.author)\nposts: Promise<Post[]>;\n\n// Explicit loading (recommended)\nconst user = await userRepository.findOne({\n  where: { id: 1 },\n  relations: [\"posts\"],\n});\n```\n\n### Avoid N+1 Queries\n\n```typescript\n// Bad: N+1 queries\nconst users = await userRepository.find();\nfor (const user of users) {\n  console.log(user.posts); // Separate query for each user\n}\n\n// Good: Eager load relations\nconst users = await userRepository.find({\n  relations: ['posts'],\n});\n```\n\n### Use Indexes\n\n```typescript\n@Entity()\n@Index(['email'])\n@Index(['firstName', 'lastName'])\nexport class User {\n  @Column()\n  @Index()\n  email: string;\n\n  @Column()\n  firstName: string;\n\n  @Column()\n  lastName: string;\n}\n```\n\n### Cascade Operations\n\n```typescript\n@OneToMany(() => Post, (post) => post.author, {\n  cascade: true, // Saves/removes related posts\n  onDelete: \"CASCADE\", // Database-level cascade\n})\nposts: Post[];\n```\n\n### Naming Strategies\n\nFor consistent naming between TypeScript and database:\n\n```typescript\nimport { DefaultNamingStrategy, NamingStrategyInterface } from \"typeorm\";\nimport { snakeCase } from \"typeorm/util/StringUtils\";\n\nexport class SnakeNamingStrategy extends DefaultNamingStrategy implements NamingStrategyInterface {\n  tableName(targetName: string, userSpecifiedName: string | undefined): string {\n    return userSpecifiedName ? userSpecifiedName : snakeCase(targetName);\n  }\n\n  columnName(propertyName: string, customName: string, embeddedPrefixes: string[]): string {\n    return snakeCase(embeddedPrefixes.join(\"_\")) + (customName ? customName : snakeCase(propertyName));\n  }\n}\n\n// Use in data source config\nnamingStrategy: new SnakeNamingStrategy(),\n```","frontmatter":{"name":"typeorm","description":"Guidelines for developing with TypeORM, a full-featured ORM for TypeScript and JavaScript supporting multiple databases"},"isInternal":false,"tokens":3280,"sizeBytes":13895},{"name":"SKILL.md","path":".agents/skills/typescript-advanced-types/SKILL.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/.agents/skills/typescript-advanced-types/SKILL.md","title":"typescript-advanced-types","category":"anthropic-skill","format":"markdown","content":"---\nname: typescript-advanced-types\ndescription: Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects.\n---\n\n# TypeScript Advanced Types\n\nComprehensive guidance for mastering TypeScript's advanced type system including generics, conditional types, mapped types, template literal types, and utility types for building robust, type-safe applications.\n\n## When to Use This Skill\n\n- Building type-safe libraries or frameworks\n- Creating reusable generic components\n- Implementing complex type inference logic\n- Designing type-safe API clients\n- Building form validation systems\n- Creating strongly-typed configuration objects\n- Implementing type-safe state management\n- Migrating JavaScript codebases to TypeScript\n\n## Core Concepts\n\n### 1. Generics\n\n**Purpose:** Create reusable, type-flexible components while maintaining type safety.\n\n**Basic Generic Function:**\n\n```typescript\nfunction identity<T>(value: T): T {\n  return value;\n}\n\nconst num = identity<number>(42); // Type: number\nconst str = identity<string>('hello'); // Type: string\nconst auto = identity(true); // Type inferred: boolean\n```\n\n**Generic Constraints:**\n\n```typescript\ninterface HasLength {\n  length: number;\n}\n\nfunction logLength<T extends HasLength>(item: T): T {\n  console.log(item.length);\n  return item;\n}\n\nlogLength('hello'); // OK: string has length\nlogLength([1, 2, 3]); // OK: array has length\nlogLength({ length: 10 }); // OK: object has length\n// logLength(42);             // Error: number has no length\n```\n\n**Multiple Type Parameters:**\n\n```typescript\nfunction merge<T, U>(obj1: T, obj2: U): T & U {\n  return { ...obj1, ...obj2 };\n}\n\nconst merged = merge({ name: 'John' }, { age: 30 });\n// Type: { name: string } & { age: number }\n```\n\n### 2. Conditional Types\n\n**Purpose:** Create types that depend on conditions, enabling sophisticated type logic.\n\n**Basic Conditional Type:**\n\n```typescript\ntype IsString<T> = T extends string ? true : false;\n\ntype A = IsString<string>; // true\ntype B = IsString<number>; // false\n```\n\n**Extracting Return Types:**\n\n```typescript\ntype ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;\n\nfunction getUser() {\n  return { id: 1, name: 'John' };\n}\n\ntype User = ReturnType<typeof getUser>;\n// Type: { id: number; name: string; }\n```\n\n**Distributive Conditional Types:**\n\n```typescript\ntype ToArray<T> = T extends any ? T[] : never;\n\ntype StrOrNumArray = ToArray<string | number>;\n// Type: string[] | number[]\n```\n\n**Nested Conditions:**\n\n```typescript\ntype TypeName<T> = T extends string\n  ? 'string'\n  : T extends number\n    ? 'number'\n    : T extends boolean\n      ? 'boolean'\n      : T extends undefined\n        ? 'undefined'\n        : T extends Function\n          ? 'function'\n          : 'object';\n\ntype T1 = TypeName<string>; // \"string\"\ntype T2 = TypeName<() => void>; // \"function\"\n```\n\n### 3. Mapped Types\n\n**Purpose:** Transform existing types by iterating over their properties.\n\n**Basic Mapped Type:**\n\n```typescript\ntype Readonly<T> = {\n  readonly [P in keyof T]: T[P];\n};\n\ninterface User {\n  id: number;\n  name: string;\n}\n\ntype ReadonlyUser = Readonly<User>;\n// Type: { readonly id: number; readonly name: string; }\n```\n\n**Optional Properties:**\n\n```typescript\ntype Partial<T> = {\n  [P in keyof T]?: T[P];\n};\n\ntype PartialUser = Partial<User>;\n// Type: { id?: number; name?: string; }\n```\n\n**Key Remapping:**\n\n```typescript\ntype Getters<T> = {\n  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];\n};\n\ninterface Person {\n  name: string;\n  age: number;\n}\n\ntype PersonGetters = Getters<Person>;\n// Type: { getName: () => string; getAge: () => number; }\n```\n\n**Filtering Properties:**\n\n```typescript\ntype PickByType<T, U> = {\n  [K in keyof T as T[K] extends U ? K : never]: T[K];\n};\n\ninterface Mixed {\n  id: number;\n  name: string;\n  age: number;\n  active: boolean;\n}\n\ntype OnlyNumbers = PickByType<Mixed, number>;\n// Type: { id: number; age: number; }\n```\n\n### 4. Template Literal Types\n\n**Purpose:** Create string-based types with pattern matching and transformation.\n\n**Basic Template Literal:**\n\n```typescript\ntype EventName = 'click' | 'focus' | 'blur';\ntype EventHandler = `on${Capitalize<EventName>}`;\n// Type: \"onClick\" | \"onFocus\" | \"onBlur\"\n```\n\n**String Manipulation:**\n\n```typescript\ntype UppercaseGreeting = Uppercase<'hello'>; // \"HELLO\"\ntype LowercaseGreeting = Lowercase<'HELLO'>; // \"hello\"\ntype CapitalizedName = Capitalize<'john'>; // \"John\"\ntype UncapitalizedName = Uncapitalize<'John'>; // \"john\"\n```\n\n**Path Building:**\n\n```typescript\ntype Path<T> = T extends object\n  ? {\n      [K in keyof T]: K extends string ? `${K}` | `${K}.${Path<T[K]>}` : never;\n    }[keyof T]\n  : never;\n\ninterface Config {\n  server: {\n    host: string;\n    port: number;\n  };\n  database: {\n    url: string;\n  };\n}\n\ntype ConfigPath = Path<Config>;\n// Type: \"server\" | \"database\" | \"server.host\" | \"server.port\" | \"database.url\"\n```\n\n### 5. Utility Types\n\n**Built-in Utility Types:**\n\n```typescript\n// Partial<T> - Make all properties optional\ntype PartialUser = Partial<User>;\n\n// Required<T> - Make all properties required\ntype RequiredUser = Required<PartialUser>;\n\n// Readonly<T> - Make all properties readonly\ntype ReadonlyUser = Readonly<User>;\n\n// Pick<T, K> - Select specific properties\ntype UserName = Pick<User, 'name' | 'email'>;\n\n// Omit<T, K> - Remove specific properties\ntype UserWithoutPassword = Omit<User, 'password'>;\n\n// Exclude<T, U> - Exclude types from union\ntype T1 = Exclude<'a' | 'b' | 'c', 'a'>; // \"b\" | \"c\"\n\n// Extract<T, U> - Extract types from union\ntype T2 = Extract<'a' | 'b' | 'c', 'a' | 'b'>; // \"a\" | \"b\"\n\n// NonNullable<T> - Exclude null and undefined\ntype T3 = NonNullable<string | null | undefined>; // string\n\n// Record<K, T> - Create object type with keys K and values T\ntype PageInfo = Record<'home' | 'about', { title: string }>;\n```\n\n## Advanced Patterns\n\n### Pattern 1: Type-Safe Event Emitter\n\n```typescript\ntype EventMap = {\n  'user:created': { id: string; name: string };\n  'user:updated': { id: string };\n  'user:deleted': { id: string };\n};\n\nclass TypedEventEmitter<T extends Record<string, any>> {\n  private listeners: {\n    [K in keyof T]?: Array<(data: T[K]) => void>;\n  } = {};\n\n  on<K extends keyof T>(event: K, callback: (data: T[K]) => void): void {\n    if (!this.listeners[event]) {\n      this.listeners[event] = [];\n    }\n    this.listeners[event]!.push(callback);\n  }\n\n  emit<K extends keyof T>(event: K, data: T[K]): void {\n    const callbacks = this.listeners[event];\n    if (callbacks) {\n      callbacks.forEach(callback => callback(data));\n    }\n  }\n}\n\nconst emitter = new TypedEventEmitter<EventMap>();\n\nemitter.on('user:created', data => {\n  console.log(data.id, data.name); // Type-safe!\n});\n\nemitter.emit('user:created', { id: '1', name: 'John' });\n// emitter.emit(\"user:created\", { id: \"1\" });  // Error: missing 'name'\n```\n\n### Pattern 2: Type-Safe API Client\n\n```typescript\ntype HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';\n\ntype EndpointConfig = {\n  '/users': {\n    GET: { response: User[] };\n    POST: { body: { name: string; email: string }; response: User };\n  };\n  '/users/:id': {\n    GET: { params: { id: string }; response: User };\n    PUT: { params: { id: string }; body: Partial<User>; response: User };\n    DELETE: { params: { id: string }; response: void };\n  };\n};\n\ntype ExtractParams<T> = T extends { params: infer P } ? P : never;\ntype ExtractBody<T> = T extends { body: infer B } ? B : never;\ntype ExtractResponse<T> = T extends { response: infer R } ? R : never;\n\nclass APIClient<Config extends Record<string, Record<HTTPMethod, any>>> {\n  async request<Path extends keyof Config, Method extends keyof Config[Path]>(\n    path: Path,\n    method: Method,\n    ...[options]: ExtractParams<Config[Path][Method]> extends never\n      ? ExtractBody<Config[Path][Method]> extends never\n        ? []\n        : [{ body: ExtractBody<Config[Path][Method]> }]\n      : [\n          {\n            params: ExtractParams<Config[Path][Method]>;\n            body?: ExtractBody<Config[Path][Method]>;\n          },\n        ]\n  ): Promise<ExtractResponse<Config[Path][Method]>> {\n    // Implementation here\n    return {} as any;\n  }\n}\n\nconst api = new APIClient<EndpointConfig>();\n\n// Type-safe API calls\nconst users = await api.request('/users', 'GET');\n// Type: User[]\n\nconst newUser = await api.request('/users', 'POST', {\n  body: { name: 'John', email: 'john@example.com' },\n});\n// Type: User\n\nconst user = await api.request('/users/:id', 'GET', {\n  params: { id: '123' },\n});\n// Type: User\n```\n\n### Pattern 3: Builder Pattern with Type Safety\n\n```typescript\ntype BuilderState<T> = {\n  [K in keyof T]: T[K] | undefined;\n};\n\ntype RequiredKeys<T> = {\n  [K in keyof T]-?: {} extends Pick<T, K> ? never : K;\n}[keyof T];\n\ntype OptionalKeys<T> = {\n  [K in keyof T]-?: {} extends Pick<T, K> ? K : never;\n}[keyof T];\n\ntype IsComplete<T, S> = RequiredKeys<T> extends keyof S ? (S[RequiredKeys<T>] extends undefined ? false : true) : false;\n\nclass Builder<T, S extends BuilderState<T> = {}> {\n  private state: S = {} as S;\n\n  set<K extends keyof T>(key: K, value: T[K]): Builder<T, S & Record<K, T[K]>> {\n    this.state[key] = value;\n    return this as any;\n  }\n\n  build(this: IsComplete<T, S> extends true ? this : never): T {\n    return this.state as T;\n  }\n}\n\ninterface User {\n  id: string;\n  name: string;\n  email: string;\n  age?: number;\n}\n\nconst builder = new Builder<User>();\n\nconst user = builder.set('id', '1').set('name', 'John').set('email', 'john@example.com').build(); // OK: all required fields set\n\n// const incomplete = builder\n//   .set(\"id\", \"1\")\n//   .build();  // Error: missing required fields\n```\n\n### Pattern 4: Deep Readonly/Partial\n\n```typescript\ntype DeepReadonly<T> = {\n  readonly [P in keyof T]: T[P] extends object ? (T[P] extends Function ? T[P] : DeepReadonly<T[P]>) : T[P];\n};\n\ntype DeepPartial<T> = {\n  [P in keyof T]?: T[P] extends object\n    ? T[P] extends Array<infer U>\n      ? Array<DeepPartial<U>>\n      : DeepPartial<T[P]>\n    : T[P];\n};\n\ninterface Config {\n  server: {\n    host: string;\n    port: number;\n    ssl: {\n      enabled: boolean;\n      cert: string;\n    };\n  };\n  database: {\n    url: string;\n    pool: {\n      min: number;\n      max: number;\n    };\n  };\n}\n\ntype ReadonlyConfig = DeepReadonly<Config>;\n// All nested properties are readonly\n\ntype PartialConfig = DeepPartial<Config>;\n// All nested properties are optional\n```\n\n### Pattern 5: Type-Safe Form Validation\n\n```typescript\ntype ValidationRule<T> = {\n  validate: (value: T) => boolean;\n  message: string;\n};\n\ntype FieldValidation<T> = {\n  [K in keyof T]?: ValidationRule<T[K]>[];\n};\n\ntype ValidationErrors<T> = {\n  [K in keyof T]?: string[];\n};\n\nclass FormValidator<T extends Record<string, any>> {\n  constructor(private rules: FieldValidation<T>) {}\n\n  validate(data: T): ValidationErrors<T> | null {\n    const errors: ValidationErrors<T> = {};\n    let hasErrors = false;\n\n    for (const key in this.rules) {\n      const fieldRules = this.rules[key];\n      const value = data[key];\n\n      if (fieldRules) {\n        const fieldErrors: string[] = [];\n\n        for (const rule of fieldRules) {\n          if (!rule.validate(value)) {\n            fieldErrors.push(rule.message);\n          }\n        }\n\n        if (fieldErrors.length > 0) {\n          errors[key] = fieldErrors;\n          hasErrors = true;\n        }\n      }\n    }\n\n    return hasErrors ? errors : null;\n  }\n}\n\ninterface LoginForm {\n  email: string;\n  password: string;\n}\n\nconst validator = new FormValidator<LoginForm>({\n  email: [\n    {\n      validate: v => v.includes('@'),\n      message: 'Email must contain @',\n    },\n    {\n      validate: v => v.length > 0,\n      message: 'Email is required',\n    },\n  ],\n  password: [\n    {\n      validate: v => v.length >= 8,\n      message: 'Password must be at least 8 characters',\n    },\n  ],\n});\n\nconst errors = validator.validate({\n  email: 'invalid',\n  password: 'short',\n});\n// Type: { email?: string[]; password?: string[]; } | null\n```\n\n### Pattern 6: Discriminated Unions\n\n```typescript\ntype Success<T> = {\n  status: 'success';\n  data: T;\n};\n\ntype Error = {\n  status: 'error';\n  error: string;\n};\n\ntype Loading = {\n  status: 'loading';\n};\n\ntype AsyncState<T> = Success<T> | Error | Loading;\n\nfunction handleState<T>(state: AsyncState<T>): void {\n  switch (state.status) {\n    case 'success':\n      console.log(state.data); // Type: T\n      break;\n    case 'error':\n      console.log(state.error); // Type: string\n      break;\n    case 'loading':\n      console.log('Loading...');\n      break;\n  }\n}\n\n// Type-safe state machine\ntype State =\n  | { type: 'idle' }\n  | { type: 'fetching'; requestId: string }\n  | { type: 'success'; data: any }\n  | { type: 'error'; error: Error };\n\ntype Event =\n  | { type: 'FETCH'; requestId: string }\n  | { type: 'SUCCESS'; data: any }\n  | { type: 'ERROR'; error: Error }\n  | { type: 'RESET' };\n\nfunction reducer(state: State, event: Event): State {\n  switch (state.type) {\n    case 'idle':\n      return event.type === 'FETCH' ? { type: 'fetching', requestId: event.requestId } : state;\n    case 'fetching':\n      if (event.type === 'SUCCESS') {\n        return { type: 'success', data: event.data };\n      }\n      if (event.type === 'ERROR') {\n        return { type: 'error', error: event.error };\n      }\n      return state;\n    case 'success':\n    case 'error':\n      return event.type === 'RESET' ? { type: 'idle' } : state;\n  }\n}\n```\n\n## Type Inference Techniques\n\n### 1. Infer Keyword\n\n```typescript\n// Extract array element type\ntype ElementType<T> = T extends (infer U)[] ? U : never;\n\ntype NumArray = number[];\ntype Num = ElementType<NumArray>; // number\n\n// Extract promise type\ntype PromiseType<T> = T extends Promise<infer U> ? U : never;\n\ntype AsyncNum = PromiseType<Promise<number>>; // number\n\n// Extract function parameters\ntype Parameters<T> = T extends (...args: infer P) => any ? P : never;\n\nfunction foo(a: string, b: number) {}\ntype FooParams = Parameters<typeof foo>; // [string, number]\n```\n\n### 2. Type Guards\n\n```typescript\nfunction isString(value: unknown): value is string {\n  return typeof value === 'string';\n}\n\nfunction isArrayOf<T>(value: unknown, guard: (item: unknown) => item is T): value is T[] {\n  return Array.isArray(value) && value.every(guard);\n}\n\nconst data: unknown = ['a', 'b', 'c'];\n\nif (isArrayOf(data, isString)) {\n  data.forEach(s => s.toUpperCase()); // Type: string[]\n}\n```\n\n### 3. Assertion Functions\n\n```typescript\nfunction assertIsString(value: unknown): asserts value is string {\n  if (typeof value !== 'string') {\n    throw new Error('Not a string');\n  }\n}\n\nfunction processValue(value: unknown) {\n  assertIsString(value);\n  // value is now typed as string\n  console.log(value.toUpperCase());\n}\n```\n\n## Best Practices\n\n1. **Use `unknown` over `any`**: Enforce type checking\n2. **Prefer `interface` for object shapes**: Better error messages\n3. **Use `type` for unions and complex types**: More flexible\n4. **Leverage type inference**: Let TypeScript infer when possible\n5. **Create helper types**: Build reusable type utilities\n6. **Use const assertions**: Preserve literal types\n7. **Avoid type assertions**: Use type guards instead\n8. **Document complex types**: Add JSDoc comments\n9. **Use strict mode**: Enable all strict compiler options\n10. **Test your types**: Use type tests to verify type behavior\n\n## Type Testing\n\n```typescript\n// Type assertion tests\ntype AssertEqual<T, U> = [T] extends [U] ? ([U] extends [T] ? true : false) : false;\n\ntype Test1 = AssertEqual<string, string>; // true\ntype Test2 = AssertEqual<string, number>; // false\ntype Test3 = AssertEqual<string | number, string>; // false\n\n// Expect error helper\ntype ExpectError<T extends never> = T;\n\n// Example usage\ntype ShouldError = ExpectError<AssertEqual<string, number>>;\n```\n\n## Common Pitfalls\n\n1. **Over-using `any`**: Defeats the purpose of TypeScript\n2. **Ignoring strict null checks**: Can lead to runtime errors\n3. **Too complex types**: Can slow down compilation\n4. **Not using discriminated unions**: Misses type narrowing opportunities\n5. **Forgetting readonly modifiers**: Allows unintended mutations\n6. **Circular type references**: Can cause compiler errors\n7. **Not handling edge cases**: Like empty arrays or null values\n\n## Performance Considerations\n\n- Avoid deeply nested conditional types\n- Use simple types when possible\n- Cache complex type computations\n- Limit recursion depth in recursive types\n- Use build tools to skip type checking in production\n\n## Resources\n\n- **TypeScript Handbook**: https://www.typescriptlang.org/docs/handbook/\n- **Type Challenges**: https://github.com/type-challenges/type-challenges\n- **TypeScript Deep Dive**: https://basarat.gitbook.io/typescript/\n- **Effective TypeScript**: Book by Dan Vanderkam","frontmatter":{"name":"typescript-advanced-types","description":"Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects."},"isInternal":false,"tokens":4427,"sizeBytes":16989},{"name":"README.md","path":"tools/sloths/README.md","rawUrl":"https://raw.githubusercontent.com/rolling-scopes/rsschool-app/HEAD/tools/sloths/README.md","title":"sloths Documentation","category":"plugin-manifest","format":"markdown","content":"# rs-sloths\n\nRS SLOTHS is a depository of sloths. View all existing stickers, download the necessary ones or chill a bit - why not? Feel a sloth!\n\n## Stack\n\nVue3, Pinia, Vue Router, TypeScript, Vite\n\n## Project Setup\n\n```sh\nnpm install\n```\n\n### Compile and Hot-Reload for Development\n\n```sh\nnpm run dev\n```\n\n### Type-Check, Compile and Minify for Production\n\n```sh\nnpm run build\n```\n\n### Lint with [ESLint](https://eslint.org/)\n\n```sh\nnpm run lint\n```","isInternal":false,"tokens":131,"sizeBytes":451}],"systemPromptSnippet":"<yakaai_skills repo=\"rolling-scopes/rsschool-app\">\n<!-- File: AGENTS.md (Tokens: ~418 | Category: root-instruction) -->\n# LLM Agents Instructions\n\n## General\n\n- Read `README.md` first for project overview and architecture\n- Read `CONTRIBUTING.md` for local development setup, commands, commit messages, and PR conventions\n- Read `GUIDELINES.md` for code style and testing patterns\n- Read `DOMAIN.md` for application domain and data models\n- Read all `*/README.md` files for workspace specific architecture and development guidelines\n\n## Instructions\n\n- Use the `tmp` folder in the repository to store temporary files during execution and planning if needed\n- Never run deploy commands\n- Run `npm run lint`, `npm run test`, `npm run compile`, and `npm run format` at the end of each completed task if you modified any files. If any of these steps fail, fix the issues and run the commands again.\n- Prefer telegraph style for communications and reasoning. Sacrifice grammar and punctuation for clarity and conciseness. Save tokens.\n\n## Documentation Style\n\nUse the following writing style for documentation, comments and README files.\n\nAudience: Software Engineers. Expect half to be native speakers and half non-native speakers.\n\n- Keep the reading level at or below 9th grade (Flesch-Kincaid)\n- Write in clear, concise, active voice; don't assume passive voice reads well\n- Keep noun phrases short and avoid stacked modifiers\n- Limit embedded clauses to one level\n- Use conditionals sparingly and stick to simple if/then forms; avoid mixed or inverted versions\n- Use transition words (however, therefore, because) to show logic instead of expecting readers to infer it\n- Spread new ideas across sentences; avoid packing many concepts into one\n- Do not end list items with a period\n- Do not use emojis\n- Do not write obvious comments; explain why, not how or what\n\n## Commands\n\n- `npm run lint`: Run linting\n- `npm run test`: Run tests\n- `npm run compile`: Compile the project\n- `npm run format`: Format the code\n\n<!-- File: .github/copilot-instructions.md (Tokens: ~13 | Category: copilot-instructions) -->\nUse instructions from `AGENTS.md` to guide your work.\n\n<!-- File: .agents/skills/nestjs-best-practices/AGENTS.md (Tokens: ~11088 | Category: anthropic-skill) -->\n# NestJS Best Practices\n\n**Version 1.1.0**\nNestJS Best Practices\nJanuary 2026\n\n> **Note:**\n> This document is mainly for agents and LLMs to follow when maintaining,\n> generating, or refactoring NestJS codebases. Humans may also find it\n> useful, but guidance here is optimized for automation and consistency\n> by AI-assisted workflows.\n\n---\n\n## Abstract\n\nComprehensive best practices and architecture guide for NestJS applications, designed for AI agents and LLMs. Contains 40 rules across 10 categories, prioritized by impact from critical (architecture, dependency injection) to incremental (DevOps patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.\n\n---\n\n## Table of Contents\n\n1. [Architecture](#1-architecture) — **CRITICAL**\n   - 1.1 [Avoid Circular Dependencies](#11-avoid-circular-dependencies)\n   - 1.2 [Organize by Feature Modules](#12-organize-by-feature-modules)\n   - 1.3 [Use Proper Module Sharing Patterns](#13-use-proper-module-sharing-patterns)\n   - 1.4 [Single Responsibility for Services](#14-single-responsibility-for-services)\n   - 1.5 [Use Event-Driven Architecture for Decoupling](#15-use-event-driven-architecture-for-decoupling)\n   - 1.6 [Use Repository Pattern for Data Access](#16-use-repository-pattern-for-data-access)\n2. [Dependency Injection](#2-dependency-injection) — **CRITICAL**\n   - 2.1 [Avoid Service Locator Anti-Pattern](#21-avoid-service-locator-anti-pattern)\n   - 2.2 [Apply Interface Segregation Principle](#22-apply-interface-segregation-principle)\n   - 2.3 [Honor Liskov Substitution Principle](#23-honor-liskov-substitution-principle)\n   - 2.4 [Prefer Constructor Injection](#24-prefer-constructor-injection)\n   - 2.5 [Understand Provider Scopes](#25-understand-provider-scopes)\n   - 2.6 [Use Injection Tokens for Interfaces](#26-use-injection-tokens-for-interfaces)\n3. [Error Handling](#3-error-handling) — **HIGH**\n   - 3.1 [Handle Async Errors Properly](#31-handle-async-errors-properly)\n   - 3.2 [Throw HTTP Exceptions from Services](#32-throw-http-exceptions-from-services)\n   - 3.3 [Use Exception Filters for Error Handling](#33-use-exception-filters-for-error-handling)\n4. [Security](#4-security) — **HIGH**\n   - 4.1 [Implement Secure JWT Authentication](#41-implement-secure-jwt-authentication)\n   - 4.2 [Implement Rate Limiting](#42-implement-rate-limiting)\n   - 4.3 [Sanitize Output to Prevent XSS](#43-sanitize-output-to-prevent-xss)\n   - 4.4 [Use Guards for Authentication and Authorization](#44-use-guards-for-authentication-and-authorization)\n   - 4.5 [Validate All Input with DTOs and Pipes](#45-validate-all-input-with-dtos-and-pipes)\n5. [Performance](#5-performance) — **HIGH**\n   - 5.1 [Use Async Lifecycle Hooks Correctly](#51-use-async-lifecycle-hooks-correctly)\n   - 5.2 [Use Lazy Loading for Large Modules](#52-use-lazy-loading-for-large-modules)\n   - 5.3 [Optimize Database Queries](#53-optimize-database-queries)\n   - 5.4 [Use Caching Strategically](#54-use-caching-strategically)\n6. [Testing](#6-testing) — **MEDIUM-HIGH**\n   - 6.1 [Use Supertest for E2E Testing](#61-use-supertest-for-e2e-testing)\n   - 6.2 [Mock External Services in Tests](#62-mock-external-services-in-tests)\n   - 6.3 [Use Testing Module for Unit Tests](#63-use-testing-module-for-unit-tests)\n7. [Database & ORM](#7-database-orm) — **MEDIUM-HIGH**\n   - 7.1 [Avoid N+1 Query Problems](#71-avoid-n-1-query-problems)\n   - 7.2 [Use Database Migrations](#72-use-database-migrations)\n   - 7.3 [Use Transactions for Multi-Step Operations](#73-use-transactions-for-multi-step-operations)\n8. [API Design](#8-api-design) — **MEDIUM**\n   - 8.1 [Use DTOs and Serialization for API Responses](#81-use-dtos-and-serialization-for-api-responses)\n   - 8.2 [Use Interceptors for Cross-Cutting Concerns](#82-use-interceptors-for-cross-cutting-concerns)\n   - 8.3 [Use Pipes for Input Transformation](#83-use-pipes-for-input-transformation)\n   - 8.4 [Use API Versioning for Breaking Changes](#84-use-api-versioning-for-breaking-changes)\n9. [Microservices](#9-microservices) — **MEDIUM**\n   - 9.1 [Implement Health Checks for Microservices](#91-implement-health-checks-for-microservices)\n   - 9.2 [Use Message and Event Patterns Correctly](#92-use-message-and-event-patterns-correctly)\n   - 9.3 [Use Message Queues for Background Jobs](#93-use-message-queues-for-background-jobs)\n10. [DevOps & Deployment](#10-devops-deployment) — **LOW-MEDIUM**\n\n- 10.1 [Implement Graceful Shutdown](#101-implement-graceful-shutdown)\n- 10.2 [Use ConfigModule for Environment Configuration](#102-use-configmodule-for-environment-configuration)\n- 10.3 [Use Structured Logging](#103-use-structured-logging)\n\n---\n\n## 1. Architecture\n\n**Section Impact: CRITICAL**\n\n### 1.1 Avoid Circular Dependencies\n\n**Impact: CRITICAL** — \"#1 cause of runtime crashes\"\n\nCircular dependencies occur when Module A imports Module B, and Module B imports Module A (directly or transitively). NestJS can sometimes resolve these through forward references, but they indicate architectural problems and should be avoided. This is the #1 cause of runtime crashes in NestJS applications.\n\n**Incorrect (circular module imports):**\n\n```typescript\n// users.module.ts\n@Module({\n  imports: [OrdersModule], // Orders needs Users, Users needs Orders = circular\n  providers: [UsersService],\n  exports: [UsersService],\n})\nexport class UsersModule {}\n\n// orders.module.ts\n@Module({\n  imports: [UsersModule], // Circular dependency!\n  providers: [OrdersService],\n  exports: [OrdersService],\n})\nexport class OrdersModule {}\n```\n\n**Correct (extract shared logic or use events):**\n\n```typescript\n// Option 1: Extract shared logic to a third module\n// shared.module.ts\n@Module({\n  providers: [SharedService],\n  exports: [SharedService],\n})\nexport class SharedModule {}\n\n// users.module.ts\n@Module({\n  imports: [SharedModule],\n  providers: [UsersService],\n})\nexport class UsersModule {}\n\n// orders.module.ts\n@Module({\n  imports: [SharedModule],\n  providers: [OrdersService],\n})\nexport class OrdersModule {}\n\n// Option 2: Use events for decoupled communication\n// users.service.ts\n@Injectable()\nexport class UsersService {\n  constructor(private eventEmitter: EventEmitter2) {}\n\n  async createUser(data: CreateUserDto) {\n    const user = await this.userRepo.save(data);\n    this.eventEmitter.emit('user.created', user);\n    return user;\n  }\n}\n\n// orders.service.ts\n@Injectable()\nexport class OrdersService {\n  @OnEvent('user.created')\n  handleUserCreated(user: User) {\n    // React to user creation without direct dependency\n  }\n}\n```\n\nReference: [NestJS Circular Dependency](https://docs.nestjs.com/fundamentals/circular-dependency)\n\n---\n\n### 1.2 Organize by Feature Modules\n\n**Impact: CRITICAL** — \"3-5x faster onboarding and development\"\n\nOrganize your application into feature modules that encapsulate related functionality. Each feature module should be self-contained with its own controllers, services, entities, and DTOs. Avoid organizing by technical layer (all controllers together, all services together). This enables 3-5x faster onboarding and feature development.\n\n**Incorrect (technical layer organization):**\n\n```typescript\n// Technical layer organization (anti-pattern)\nsrc/\n├── controllers/\n│   ├── users.controller.ts\n│   ├── orders.controller.ts\n│   └── products.controller.ts\n├── services/\n│   ├── users.service.ts\n│   ├── orders.service.ts\n│   └── products.service.ts\n├── entities/\n│   ├── user.entity.ts\n│   ├── order.entity.ts\n│   └── product.entity.ts\n└── app.module.ts  // Imports everything directly\n```\n\n**Correct (feature module organization):**\n\n```typescript\n// Feature module organization\nsrc/\n├── users/\n│   ├── dto/\n│   │   ├── create-user.dto.ts\n│   │   └── update-user.dto.ts\n│   ├── entities/\n│   │   └── user.entity.ts\n│   ├── users.controller.ts\n│   ├── users.service.ts\n│   ├── users.repository.ts\n│   └── users.module.ts\n├── orders/\n│   ├── dto/\n│   ├── entities/\n│   ├── orders.controller.ts\n│   ├── orders.service.ts\n│   └── orders.module.ts\n├── shared/\n│   ├── guards/\n│   ├── interceptors/\n│   ├── filters/\n│   └── shared.module.ts\n└── app.module.ts\n\n// users.module.ts\n@Module({\n  imports: [TypeOrmModule.forFeature([User])],\n  controllers: [UsersController],\n  providers: [UsersService, UsersRepository],\n  exports: [UsersService], // Only export what others need\n})\nexport class UsersModule {}\n\n// app.module.ts\n@Module({\n  imports: [\n    ConfigModule.forRoot(),\n    TypeOrmModule.forRoot(),\n    UsersModule,\n    OrdersModule,\n    SharedModule,\n  ],\n})\nexport class AppModule {}\n```\n\nReference: [NestJS Modules](https://docs.nestjs.com/modules)\n\n---\n\n### 1.3 Use Proper Module Sharing Patterns\n\n**Impact: CRITICAL** — Prevents duplicate instances, memory leaks, and state inconsistency\n\nNestJS modules are singletons by default. When a service is properly exported from a module and that module is imported elsewhere, the same instance is shared. However, providing a service in multiple modules creates separate instances, leading to memory waste, state inconsistency, and confusing behavior. Always encapsulate services in dedicated modules, export them explicitly, and import the module where needed.\n\n**Incorrect (service provided in multiple modules):**\n\n```typescript\n// StorageService provided directly in multiple modules - WRONG\n// storage.service.ts\n@Injectable()\nexport class StorageService {\n  private cache = new Map(); // Each instance has separate state!\n\n  store(key: string, value: any) {\n    this.cache.set(key, value);\n  }\n}\n\n// app.module.ts\n@Module({\n  providers: [StorageService], // Instance #1\n  controllers: [AppController],\n})\nexport class AppModule {}\n\n// videos.module.ts\n@Module({\n  providers: [StorageService], // Instance #2 - different from AppModule!\n  controllers: [VideosController],\n})\nexport class VideosModule {}\n\n// Problems:\n// 1. Two separate StorageService instances exist\n// 2. cache.set() in VideosModule doesn't affect AppModule's cache\n// 3. Memory wasted on duplicate instances\n// 4. Debugging nightmares when state doesn't sync\n```\n\n**Correct (dedicated module with exports):**\n\n```typescript\n// storage/storage.module.ts\n@Module({\n  providers: [StorageService],\n  exports: [StorageService], // Make available to importers\n})\nexport class StorageModule {}\n\n// videos/videos.module.ts\n@Module({\n  imports: [StorageModule], // Import the module, not the service\n  controllers: [VideosController],\n  providers: [VideosService],\n})\nexport class VideosModule {}\n\n// channels/channels.module.ts\n@Module({\n  imports: [StorageModule], // Same instance shared\n  controllers: [ChannelsController],\n  providers: [ChannelsService],\n})\nexport class ChannelsModule {}\n\n// app.module.ts\n@Module({\n  imports: [\n    StorageModule, // Only if AppModule itself needs StorageService\n    VideosModule,\n    ChannelsModule,\n  ],\n})\nexport class AppModule {}\n\n// Now all modules share the SAME StorageService instance\n```\n\n**When to use @Global() (sparingly):**\n\n```typescript\n// ONLY for truly cross-cutting concerns\n@Global()\n@Module({\n  providers: [ConfigService, LoggerService],\n  exports: [ConfigService, LoggerService],\n})\nexport class CoreModule {}\n\n// Import once in AppModule\n@Module({\n  imports: [CoreModule], // Registered globally, available everywhere\n})\nexport class AppModule {}\n\n// Other modules don't need to import CoreModule\n@Module({\n  controllers: [UsersController],\n  providers: [UsersService], // Can inject ConfigService without importing\n})\nexport class UsersModule {}\n\n// WARNING: Don't make everything global!\n// - Hides dependencies (can't see what a module needs from imports)\n// - Makes testing harder\n// - Reserve for: config, logging, database connections\n```\n\n**Module re-exporting pattern:**\n\n```typescript\n// common.module.ts - shared utilities\n@Module({\n  providers: [DateService, ValidationService],\n  exports: [DateService, ValidationService],\n})\nexport class CommonModule {}\n\n// core.module.ts - re-exports common for convenience\n@Module({\n  imports: [CommonModule, DatabaseModule],\n  exports: [CommonModule, DatabaseModule], // Re-export for consumers\n})\nexport class CoreModule {}\n\n// feature.module.ts - imports CoreModule, gets both\n@Module({\n  imports: [CoreModule], // Gets CommonModule + DatabaseModule\n  controllers: [FeatureController],\n})\nexport class FeatureModule {}\n```\n\nReference: [NestJS Modules](https://docs.nestjs.com/modules#shared-modules)\n\n---\n\n### 1.4 Single Responsibility for Services\n\n**Impact: CRITICAL** — \"40%+ improvement in testability\"\n\nEach service should have a single, well-defined responsibility. Avoid \"god services\" that handle multiple unrelated concerns. If a service name includes \"And\" or handles more than one domain concept, it likely violates single responsibility. This reduces complexity and improves testability by 40%+.\n\n**Incorrect (god service anti-pattern):**\n\n```typescript\n// God service anti-pattern\n@Injectable()\nexport class UserAndOrderService {\n  constructor(\n    private userRepo: UserRepository,\n    private orderRepo: OrderRepository,\n    private mailer: MailService,\n    private payment: PaymentService,\n  ) {}\n\n  async createUser(dto: CreateUserDto) {\n    const user = await this.userRepo.save(dto);\n    await this.mailer.sendWelcome(user);\n    return user;\n  }\n\n  async createOrder(userId: string, dto: CreateOrderDto) {\n    const order = await this.orderRepo.save({ userId, ...dto });\n    await this.payment.charge(order);\n    await this.mailer.sendOrderConfirmation(order);\n    return order;\n  }\n\n  async calculateOrderStats(userId: string) {\n    // Stats logic mixed in\n  }\n\n  async validatePayment(orderId: string) {\n    // Payment logic mixed in\n  }\n}\n```\n\n**Correct (focused services with single responsibility):**\n\n```typescript\n// Focused services with single responsibility\n@Injectable()\nexport class UsersService {\n  constructor(private userRepo: UserRepository) {}\n\n  async create(dto: CreateUserDto): Promise<User> {\n    return this.userRepo.save(dto);\n  }\n\n  async findById(id: string): Promise<User> {\n    return this.userRepo.findOneOrFail({ where: { id } });\n  }\n}\n\n@Injectable()\nexport class OrdersService {\n  constructor(private orderRepo: OrderRepository) {}\n\n  async create(userId: string, dto: CreateOrderDto): Promise<Order> {\n    return this.orderRepo.save({ userId, ...dto });\n  }\n\n  async findByUser(userId: string): Promise<Order[]> {\n    return this.orderRepo.find({ where: { userId } });\n  }\n}\n\n@Injectable()\nexport class OrderStatsService {\n  constructor(private orderRepo: OrderRepository) {}\n\n  async calculateForUser(userId: string): Promise<OrderStats> {\n    // Focused stats calculation\n  }\n}\n\n// Orchestration in controller or dedicated orchestrator\n@Controller('orders')\nexport class OrdersController {\n  constructor(\n    private orders: OrdersService,\n    private payment: PaymentService,\n    private notifications: NotificationService,\n  ) {}\n\n  @Post()\n  async create(@CurrentUser() user: User, @Body() dto: CreateOrderDto) {\n    const order = await this.orders.create(user.id, dto);\n    await this.payment.charge(order);\n    await this.notifications.sendOrderConfirmation(order);\n    return order;\n  }\n}\n```\n\nReference: [NestJS Providers](https://docs.nestjs.com/providers)\n\n---\n\n### 1.5 Use Event-Driven Architecture for Decoupling\n\n**Impact: MEDIUM-HIGH** — Enables async processing and modularity\n\nUse `@nestjs/event-emitter` for intra-service events and message brokers for inter-service communication. Events allow modules to react to changes without direct dependencies, improving modularity and enabling async processing.\n\n**Incorrect (direct service coupling):**\n\n```typescript\n// Direct service coupling\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private inventoryService: InventoryService,\n    private emailService: EmailService,\n    private analyticsService: AnalyticsService,\n    private notificationService: NotificationService,\n    private loyaltyService: LoyaltyService,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Tight coupling - OrdersService knows about all consumers\n    await this.inventoryService.reserve(order.items);\n    await this.emailService.sendConfirmation(order);\n    await this.analyticsService.track('order_created', order);\n    await this.notificationService.push(order.userId, 'Order placed');\n    await this.loyaltyService.addPoints(order.userId, order.total);\n\n    // Adding new behavior requires modifying this service\n    return order;\n  }\n}\n```\n\n**Correct (event-driven decoupling):**\n\n```typescript\n// Use EventEmitter for decoupling\nimport { EventEmitter2 } from '@nestjs/event-emitter';\n\n// Define event\nexport class OrderCreatedEvent {\n  constructor(\n    public readonly orderId: string,\n    public readonly userId: string,\n    public readonly items: OrderItem[],\n    public readonly total: number,\n  ) {}\n}\n\n// Service emits events\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private eventEmitter: EventEmitter2,\n    private repo: Repository<Order>,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Emit event - no knowledge of consumers\n    this.eventEmitter.emit('order.created', new OrderCreatedEvent(order.id, order.userId, order.items, order.total));\n\n    return order;\n  }\n}\n\n// Listeners in separate modules\n@Injectable()\nexport class InventoryListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.inventoryService.reserve(event.items);\n  }\n}\n\n@Injectable()\nexport class EmailListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.emailService.sendConfirmation(event.orderId);\n  }\n}\n\n@Injectable()\nexport class AnalyticsListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.analyticsService.track('order_created', {\n      orderId: event.orderId,\n      total: event.total,\n    });\n  }\n}\n```\n\nReference: [NestJS Events](https://docs.nestjs.com/techniques/events)\n\n---\n\n### 1.6 Use Repository Pattern for Data Access\n\n**Impact: HIGH** — Decouples business logic from database\n\nCreate custom repositories to encapsulate complex queries and database logic. This keeps services focused on business logic, makes testing easier with mock repositories, and allows changing database implementations without affecting business code.\n\n**Incorrect (complex queries in services):**\n\n```typescript\n// Complex queries in services\n@Injectable()\nexport class UsersService {\n  constructor(@InjectRepository(User) private repo: Repository<User>) {}\n\n  async findActiveWithOrders(minOrders: number): Promise<User[]> {\n    // Complex query logic mixed with business logic\n    return this.repo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.orders', 'order')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('user.deletedAt IS NULL')\n      .groupBy('user.id')\n      .having('COUNT(order.id) >= :min', { min: minOrders })\n      .orderBy('user.createdAt', 'DESC')\n      .getMany();\n  }\n\n  // Service becomes bloated with query logic\n}\n```\n\n**Correct (custom repository with encapsulated queries):**\n\n```typescript\n// Custom repository with encapsulated queries\n@Injectable()\nexport class UsersRepository {\n  constructor(@InjectRepository(User) private repo: Repository<User>) {}\n\n  async findById(id: string): Promise<User | null> {\n    return this.repo.findOne({ where: { id } });\n  }\n\n  async findByEmail(email: string): Promise<User | null> {\n    return this.repo.findOne({ where: { email } });\n  }\n\n  async findActiveWithMinOrders(minOrders: number): Promise<User[]> {\n    return this.repo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.orders', 'order')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('user.deletedAt IS NULL')\n      .groupBy('user.id')\n      .having('COUNT(order.id) >= :min', { min: minOrders })\n      .orderBy('user.createdAt', 'DESC')\n      .getMany();\n  }\n\n  async save(user: User): Promise<User> {\n    return this.repo.save(user);\n  }\n}\n\n// Clean service with business logic only\n@Injectable()\nexport class UsersService {\n  constructor(private usersRepo: UsersRepository) {}\n\n  async getActiveUsersWithOrders(): Promise<User[]> {\n    return this.usersRepo.findActiveWithMinOrders(1);\n  }\n\n  async create(dto: CreateUserDto): Promise<User> {\n    const existing = await this.usersRepo.findByEmail(dto.email);\n    if (existing) {\n      throw new ConflictException('Email already registered');\n    }\n\n    const user = new User();\n    user.email = dto.email;\n    user.name = dto.name;\n    return this.usersRepo.save(user);\n  }\n}\n```\n\nReference: [Repository Pattern](https://martinfowler.com/eaaCatalog/repository.html)\n\n---\n\n## 2. Dependency Injection\n\n**Section Impact: CRITICAL**\n\n### 2.1 Avoid Service Locator Anti-Pattern\n\n**Impact: HIGH** — Hides dependencies and breaks testability\n\nAvoid using `ModuleRef.get()` or global containers to resolve dependencies at runtime. This hides dependencies, makes code harder to test, and breaks the benefits of dependency injection. Use constructor injection instead.\n\n**Incorrect (service locator anti-pattern):**\n\n```typescript\n// Use ModuleRef to get dependencies dynamically\n@Injectable()\nexport class OrdersService {\n  constructor(private moduleRef: ModuleRef) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    // Dependencies are hidden - not visible in constructor\n    const usersService = this.moduleRef.get(UsersService);\n    const inventoryService = this.moduleRef.get(InventoryService);\n    const paymentService = this.moduleRef.get(PaymentService);\n\n    const user = await usersService.findOne(dto.userId);\n    // ... rest of logic\n  }\n}\n\n// Global singleton container\nclass ServiceContainer {\n  private static instance: ServiceContainer;\n  private services = new Map<string, any>();\n\n  static getInstance(): ServiceContainer {\n    if (!this.instance) {\n      this.instance = new ServiceContainer();\n    }\n    return this.instance;\n  }\n\n  get<T>(key: string): T {\n    return this.services.get(key);\n  }\n}\n```\n\n**Correct (constructor injection with explicit dependencies):**\n\n```typescript\n// Use constructor injection - dependencies are explicit\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private usersService: UsersService,\n    private inventoryService: InventoryService,\n    private paymentService: PaymentService,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const user = await this.usersService.findOne(dto.userId);\n    const inventory = await this.inventoryService.check(dto.items);\n    // Dependencies are clear and testable\n  }\n}\n\n// Easy to test with mocks\ndescribe('OrdersService', () => {\n  let service: OrdersService;\n\n  beforeEach(async () => {\n    const module = await Test.createTestingModule({\n      providers: [\n        OrdersService,\n        { provide: UsersService, useValue: mockUsersService },\n        { provide: InventoryService, useValue: mockInventoryService },\n        { provide: PaymentService, useValue: mockPaymentService },\n      ],\n    }).compile();\n\n    service = module.get(OrdersService);\n  });\n});\n\n// VALID: Factory pattern for dynamic instantiation\n@Injectable()\nexport class HandlerFactory {\n  constructor(private moduleRef: ModuleRef) {}\n\n  getHandler(type: string): Handler {\n    switch (type) {\n      case 'email':\n        return this.moduleRef.get(EmailHandler);\n      case 'sms':\n        return this.moduleRef.get(SmsHandler);\n      default:\n        return this.moduleRef.get(DefaultHandler);\n    }\n  }\n}\n```\n\nReference: [NestJS Module Reference](https://docs.nestjs.com/fundamentals/module-ref)\n\n---\n\n### 2.2 Apply Interface Segregation Principle\n\n**Impact: HIGH** — Reduces coupling and improves testability by 30-50%\n\nClients should not be forced to depend on interfaces they don't use. In NestJS, this means keeping interfaces small and focused on specific capabilities rather than creating \"fat\" interfaces that bundle unrelated methods. When a service only needs to send emails, it shouldn't depend on an interface that also includes SMS, push notifications, and logging. Split large interfaces into role-based ones.\n\n**Incorrect (fat interface forcing unused dependencies):**\n\n```typescript\n// Fat interface - forces all consumers to depend on everything\ninterface NotificationService {\n  sendEmail(to: string, subject: string, body: string): Promise<void>;\n  sendSms(phone: string, message: string): Promise<void>;\n  sendPush(userId: string, notification: PushPayload): Promise<void>;\n  sendSlack(channel: string, message: string): Promise<void>;\n  logNotification(type: string, payload: any): Promise<void>;\n  getDeliveryStatus(id: string): Promise<DeliveryStatus>;\n  retryFailed(id: string): Promise<void>;\n  scheduleNotification(dto: ScheduleDto): Promise<string>;\n}\n\n// Consumer only needs email, but must mock everything for tests\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private notifications: NotificationService, // Depends on 8 methods, uses 1\n  ) {}\n\n  async confirmOrder(order: Order): Promise<void> {\n    await this.notifications.sendEmail(\n      order.customer.email,\n      'Order Confirmed',\n      `Your order ${order.id} has been confirmed.`,\n    );\n  }\n}\n\n// Testing is painful - must mock unused methods\nconst mockNotificationService = {\n  sendEmail: jest.fn(),\n  sendSms: jest.fn(), // Never used, but required\n  sendPush: jest.fn(), // Never used, but required\n  sendSlack: jest.fn(), // Never used, but required\n  logNotification: jest.fn(), // Never used, but required\n  getDeliveryStatus: jest.fn(), // Never used, but required\n  retryFailed: jest.fn(), // Never used, but required\n  scheduleNotification: jest.fn(), // Never used, but required\n};\n```\n\n**Correct (segregated interfaces by capability):**\n\n```typescript\n// Segregated interfaces - each focused on one capability\ninterface EmailSender {\n  sendEmail(to: string, subject: string, body: string): Promise<void>;\n}\n\ninterface SmsSender {\n  sendSms(phone: string, message: string): Promise<void>;\n}\n\ninterface PushSender {\n  sendPush(userId: string, notification: PushPayload): Promise<void>;\n}\n\ninterface NotificationLogger {\n  logNotification(type: string, payload: any): Promise<void>;\n}\n\ninterface NotificationScheduler {\n  scheduleNotification(dto: ScheduleDto): Promise<string>;\n}\n\n// Implementation can implement multiple interfaces\n@Injectable()\nexport class NotificationService implements EmailSender, SmsSender, PushSender {\n  async sendEmail(to: string, subject: string, body: string): Promise<void> {\n    // Email implementation\n  }\n\n  async sendSms(phone: string, message: string): Promise<void> {\n    // SMS implementation\n  }\n\n  async sendPush(userId: string, notification: PushPayload): Promise<void> {\n    // Push implementation\n  }\n}\n\n// Or separate implementations\n@Injectable()\nexport class SendGridEmailService implements EmailSender {\n  async sendEmail(to: string, subject: string, body: string): Promise<void> {\n    // SendGrid-specific implementation\n  }\n}\n\n// Consumer depends only on what it needs\n@Injectable()\nexport class OrdersService {\n  constructor(\n    @Inject(EMAIL_SENDER) private emailSender: EmailSender, // Minimal dependency\n  ) {}\n\n  async confirmOrder(order: Order): Promise<void> {\n    await this.emailSender.sendEmail(\n      order.customer.email,\n      'Order Confirmed',\n      `Your order ${order.id} has been confirmed.`,\n    );\n  }\n}\n\n// Testing is simple - only mock what's used\nconst mockEmailSender: EmailSender = {\n  sendEmail: jest.fn(),\n};\n\n// Module registration with tokens\nexport const EMAIL_SENDER = Symbol('EMAIL_SENDER');\nexport const SMS_SENDER = Symbol('SMS_SENDER');\n\n@Module({\n  providers: [\n    { provide: EMAIL_SENDER, useClass: SendGridEmailService },\n    { provide: SMS_SENDER, useClass: TwilioSmsService },\n  ],\n  exports: [EMAIL_SENDER, SMS_SENDER],\n})\nexport class NotificationModule {}\n```\n\n**Combining interfaces when needed:**\n\n```typescript\n// Sometimes a consumer legitimately needs multiple capabilities\ninterface EmailAndSmsSender extends EmailSender, SmsSender {}\n\n// Or use intersection types\ntype MultiChannelSender = EmailSender & SmsSender & PushSender;\n\n// Consumer that genuinely needs multiple channels\n@Injectable()\nexport class AlertService {\n  constructor(\n    @Inject(MULTI_CHANNEL_SENDER)\n    private sender: EmailSender & SmsSender,\n  ) {}\n\n  async sendCriticalAlert(user: User, message: string): Promise<void> {\n    await Promise.all([\n      this.sender.sendEmail(user.email, 'Critical Alert', message),\n      this.sender.sendSms(user.phone, message),\n    ]);\n  }\n}\n```\n\nReference: [Interface Segregation Principle](https://en.wikipedia.org/wiki/Interface_segregation_principle)\n\n---\n\n### 2.3 Honor Liskov Substitution Principle\n\n**Impact: HIGH** — Ensures implementations are truly interchangeable without breaking callers\n\nSubtypes must be substitutable for their base types without altering program correctness. In NestJS with dependency injection, this means any implementation of an interface or abstract class must honor the contract completely. A mock payment service used in tests must behave like a real payment service (return similar shapes, handle errors the same way). Violating LSP causes subtle bugs when swapping implementations.\n\n**Incorrect (implementation violates the contract):**\n\n```typescript\n// Base interface with clear contract\ninterface PaymentGateway {\n  /**\n   * Charges the specified amount.\n   * @returns PaymentResult on success\n   * @throws PaymentFailedException on payment failure\n   */\n  charge(amount: number, currency: string): Promise<PaymentResult>;\n}\n\n// Production implementation - follows the contract\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    const response = await this.stripe.charges.create({ amount, currency });\n    return { success: true, transactionId: response.id, amount };\n  }\n}\n\n// Mock that violates LSP - different behavior!\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    // VIOLATION 1: Throws for valid input (contract says return PaymentResult)\n    if (amount > 1000) {\n      throw new Error('Mock does not support large amounts');\n    }\n\n    // VIOLATION 2: Returns null instead of PaymentResult\n    if (currency !== 'USD') {\n      return null as any; // Real service would convert or reject properly\n    }\n\n    // VIOLATION 3: Missing required field\n    return { success: true } as PaymentResult; // Missing transactionId!\n  }\n}\n\n// Consumer trusts the contract\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async checkout(order: Order): Promise<void> {\n    const result = await this.payment.charge(order.total, order.currency);\n    // These fail with MockPaymentService:\n    await this.saveTransaction(result.transactionId); // undefined!\n    await this.sendReceipt(result); // might be null!\n  }\n}\n```\n\n**Correct (implementations honor the contract):**\n\n```typescript\n// Well-defined interface with documented behavior\ninterface PaymentGateway {\n  /**\n   * Charges the specified amount.\n   * @param amount - Amount in smallest currency unit (cents)\n   * @param currency - ISO 4217 currency code\n   * @returns PaymentResult with transactionId, success status, and amount\n   * @throws PaymentFailedException if charge is declined\n   * @throws InvalidCurrencyException if currency is not supported\n   */\n  charge(amount: number, currency: string): Promise<PaymentResult>;\n\n  /**\n   * Refunds a previous charge.\n   * @throws TransactionNotFoundException if transactionId is invalid\n   */\n  refund(transactionId: string, amount?: number): Promise<RefundResult>;\n}\n\n// Production implementation\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    try {\n      const response = await this.stripe.charges.create({ amount, currency });\n      return {\n        success: true,\n        transactionId: response.id,\n        amount: response.amount,\n      };\n    } catch (error) {\n      if (error.type === 'card_error') {\n        throw new PaymentFailedException(error.message);\n      }\n      throw error;\n    }\n  }\n\n  async refund(transactionId: string, amount?: number): Promise<RefundResult> {\n    // Implementation...\n  }\n}\n\n// Mock that honors LSP - same contract, same behavior shape\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  private transactions = new Map<string, PaymentResult>();\n\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    // Honor the contract: validate currency like real service would\n    if (!['USD', 'EUR', 'GBP'].includes(currency)) {\n      throw new InvalidCurrencyException(`Unsupported currency: ${currency}`);\n    }\n\n    // Simulate decline for specific test scenarios\n    if (amount === 99999) {\n      throw new PaymentFailedException('Card declined (test scenario)');\n    }\n\n    // Return same shape as production\n    const result: PaymentResult = {\n      success: true,\n      transactionId: `mock_${Date.now()}_${Math.random().toString(36)}`,\n      amount,\n    };\n\n    this.transactions.set(result.transactionId, result);\n    return result;\n  }\n\n  async refund(transactionId: string, amount?: number): Promise<RefundResult> {\n    // Honor the contract: throw if transaction not found\n    if (!this.transactions.has(transactionId)) {\n      throw new TransactionNotFoundException(transactionId);\n    }\n\n    return {\n      success: true,\n      refundId: `refund_${transactionId}`,\n      amount: amount ?? this.transactions.get(transactionId)!.amount,\n    };\n  }\n}\n\n// Consumer can swap implementations safely\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async checkout(order: Order): Promise<Order> {\n    try {\n      const result = await this.payment.charge(order.total, order.currency);\n      // Works with both StripeService and MockPaymentService\n      order.transactionId = result.transactionId;\n      order.status = 'paid';\n      return order;\n    } catch (error) {\n      if (error instanceof PaymentFailedException) {\n        order.status = 'payment_failed';\n        return order;\n      }\n      throw error;\n    }\n  }\n}\n```\n\n**Testing LSP compliance:**\n\n```typescript\n// Shared test suite that any implementation must pass\nfunction testPaymentGatewayContract(createGateway: () => PaymentGateway) {\n  describe('PaymentGateway contract', () => {\n    let gateway: PaymentGateway;\n\n    beforeEach(() => {\n      gateway = createGateway();\n    });\n\n    it('returns PaymentResult with all required fields', async () => {\n      const result = await gateway.charge(1000, 'USD');\n      expect(result).toHaveProperty('success');\n      expect(result).toHaveProperty('transactionId');\n      expect(result).toHaveProperty('amount');\n      expect(typeof result.transactionId).toBe('string');\n    });\n\n    it('throws InvalidCurrencyException for unsupported currency', async () => {\n      await expect(gateway.charge(1000, 'INVALID')).rejects.toThrow(InvalidCurrencyException);\n    });\n\n    it('throws TransactionNotFoundException for invalid refund', async () => {\n      await expect(gateway.refund('nonexistent')).rejects.toThrow(TransactionNotFoundException);\n    });\n  });\n}\n\n// Run against all implementations\ndescribe('StripeService', () => {\n  testPaymentGatewayContract(() => new StripeService(mockStripeClient));\n});\n\ndescribe('MockPaymentService', () => {\n  testPaymentGatewayContract(() => new MockPaymentService());\n});\n```\n\nReference: [Liskov Substitution Principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle)\n\n---\n\n### 2.4 Prefer Constructor Injection\n\n**Impact: CRITICAL** — Required for proper DI and testing\n\nAlways use constructor injection over property injection. Constructor injection makes dependencies explicit, enables TypeScript type checking, ensures dependencies are available when the class is instantiated, and improves testability. This is required for proper DI, testing, and TypeScript support.\n\n**Incorrect (property injection with hidden dependencies):**\n\n```typescript\n// Property injection - avoid unless necessary\n@Injectable()\nexport class UsersService {\n  @Inject()\n  private userRepo: UserRepository; // Hidden dependency\n\n  @Inject('CONFIG')\n  private config: ConfigType; // Also hidden\n\n  async findAll() {\n    return this.userRepo.find();\n  }\n}\n\n// Problems:\n// 1. Dependencies not visible in constructor\n// 2. Service can be instantiated without dependencies in tests\n// 3. TypeScript can't enforce dependency types at instantiation\n```\n\n**Correct (constructor injection with explicit dependencies):**\n\n```typescript\n// Constructor injection - explicit and testable\n@Injectable()\nexport class UsersService {\n  constructor(\n    private readonly userRepo: UserRepository,\n    @Inject('CONFIG') private readonly config: ConfigType,\n  ) {}\n\n  async findAll(): Promise<User[]> {\n    return this.userRepo.find();\n  }\n}\n\n// Testing is straightforward\ndescribe('UsersService', () => {\n  let service: UsersService;\n  let mockRepo: jest.Mocked<UserRepository>;\n\n  beforeEach(() => {\n    mockRepo = {\n      find: jest.fn(),\n      save: jest.fn(),\n    } as any;\n\n    service = new UsersService(mockRepo, { dbUrl: 'test' });\n  });\n\n  it('should find all users', async () => {\n    mockRepo.find.mockResolvedValue([{ id: '1', name: 'Test' }]);\n    const result = await service.findAll();\n    expect(result).toHaveLength(1);\n  });\n});\n\n// Only use property injection for optional dependencies\n@Injectable()\nexport class LoggingService {\n  @Optional()\n  @Inject('ANALYTICS')\n  private analytics?: AnalyticsService;\n\n  log(message: string) {\n    console.log(message);\n    this.analytics?.track('log', message); // Optional enhancement\n  }\n}\n```\n\nReference: [NestJS Providers](https://docs.nestjs.com/providers)\n\n---\n\n### 2.5 Understand Provider Scopes\n\n**Impact: CRITICAL** — Prevents data leaks and performance issues\n\nNestJS has three provider scopes: DEFAULT (singleton), REQUEST (per-request instance), and TRANSIENT (new instance for each injection). Most providers should be singletons. Request-scoped providers have performance implications as they bubble up through the dependency tree. Understanding scopes prevents memory leaks and incorrect data sharing.\n\n**Incorrect (wrong scope usage):**\n\n```typescript\n// Request-scoped when not needed (performance hit)\n@Injectable({ scope: Scope.REQUEST })\nexport class UsersService {\n  // This creates a new instance for EVERY request\n  // All dependencies also become request-scoped\n  async findAll() {\n    return this.userRepo.find();\n  }\n}\n\n// Singleton with mutable request state\n@Injectable() // Default: singleton\nexport class RequestContextService {\n  private userId: string; // DANGER: Shared across all requests!\n\n  setUser(userId: string) {\n    this.userId = userId; // Overwrites for all concurrent requests\n  }\n\n  getUser() {\n    return this.userId; // Returns wrong user!\n  }\n}\n```\n\n**Correct (appropriate scope for each use case):**\n\n```typescript\n// Singleton for stateless services (default, most common)\n@Injectable()\nexport class UsersService {\n  constructor(private readonly userRepo: UserRepository) {}\n\n  async findById(id: string): Promise<User> {\n    return this.userRepo.findOne({ where: { id } });\n  }\n}\n\n// Request-scoped ONLY when you need request context\n@Injectable({ scope: Scope.REQUEST })\nexport class RequestContextService {\n  private userId: string;\n\n  setUser(userId: string) {\n    this.userId = userId;\n  }\n\n  getUser(): string {\n    return this.userId;\n  }\n}\n\n// Better: Use NestJS built-in request context\nimport { REQUEST } from '@nestjs/core';\nimport { Request } from 'express';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class AuditService {\n  constructor(@Inject(REQUEST) private request: Request) {}\n\n  log(action: string) {\n    console.log(`User ${this.request.user?.id} performed ${action}`);\n  }\n}\n\n// Best: Use ClsModule for async context (no scope bubble-up)\nimport { ClsService } from 'nestjs-cls';\n\n@Injectable() // Stays singleton!\nexport class AuditService {\n  constructor(private cls: ClsService) {}\n\n  log(action: string) {\n    const userId = this.cls.get('userId');\n    console.log(`User ${userId} performed ${action}`);\n  }\n}\n```\n\nReference: [NestJS Injection Scopes](https://docs.nestjs.com/fundamentals/injection-scopes)\n\n---\n\n### 2.6 Use Injection Tokens for Interfaces\n\n**Impact: HIGH** — Enables interface-based DI at runtime\n\nTypeScript interfaces are erased at compile time and can't be used as injection tokens. Use string tokens, symbols, or abstract classes when you want to inject implementations of interfaces. This enables swapping implementations for testing or different environments.\n\n**Incorrect (interface can't be used as token):**\n\n```typescript\n// Interface can't be used as injection token\ninterface PaymentGateway {\n  charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  charge(amount: number) {\n    /* ... */\n  }\n}\n\n@Injectable()\nexport class OrdersService {\n  // This WON'T work - PaymentGateway doesn't exist at runtime\n  constructor(private payment: PaymentGateway) {}\n}\n```\n\n**Correct (symbol tokens or abstract classes):**\n\n```typescript\n// Option 1: String/Symbol tokens (most flexible)\nexport const PAYMENT_GATEWAY = Symbol('PAYMENT_GATEWAY');\n\nexport interface PaymentGateway {\n  charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    // Stripe implementation\n  }\n}\n\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    return { success: true, id: 'mock-id' };\n  }\n}\n\n// Module registration\n@Module({\n  providers: [\n    {\n      provide: PAYMENT_GATEWAY,\n      useClass: process.env.NODE_ENV === 'test' ? MockPaymentService : StripeService,\n    },\n  ],\n  exports: [PAYMENT_GATEWAY],\n})\nexport class PaymentModule {}\n\n// Injection\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async createOrder(dto: CreateOrderDto) {\n    await this.payment.charge(dto.amount);\n  }\n}\n\n// Option 2: Abstract class (carries runtime type info)\nexport abstract class PaymentGateway {\n  abstract charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService extends PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    // Implementation\n  }\n}\n\n// No @Inject needed with abstract class\n@Injectable()\nexport class OrdersService {\n  constructor(private payment: PaymentGateway) {}\n}\n```\n\nReference: [NestJS Custom Providers](https://docs.nestjs.com/fundamentals/custom-providers)\n\n---\n\n## 3. Error Handling\n\n**Section Impact: HIGH**\n\n### 3.1 Handle Async Errors Properly\n\n**Impact: HIGH** — Prevents process crashes from unhandled rejections\n\nNestJS automatically catches errors from async route handlers, but errors from background tasks, event handlers, and manually created promises can crash your application. Always handle async errors explicitly and use global handlers as a safety net.\n\n**Incorrect (fire-and-forget without error handling):**\n\n```typescript\n// Fire-and-forget without error handling\n@Injectable()\nexport class UsersService {\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Fire and forget - if this fails, error is unhandled!\n    this.emailService.sendWelcome(user.email);\n\n    return user;\n  }\n}\n\n// Unhandled promise in event handler\n@Injectable()\nexport class OrdersService {\n  @OnEvent('order.created')\n  handleOrderCreated(event: OrderCreatedEvent) {\n    // This returns a promise but it's not awaited!\n    this.processOrder(event);\n    // Errors will crash the process\n  }\n\n  private async processOrder(event: OrderCreatedEvent): Promise<void> {\n    await this.inventoryService.reserve(event.items);\n    await this.notificationService.send(event.userId);\n  }\n}\n\n// Missing try-catch in scheduled tasks\n@Cron('0 0 * * *')\nasync dailyCleanup(): Promise<void> {\n  await this.cleanupService.run();\n  // If this throws, no error handling\n}\n```\n\n**Correct (explicit async error handling):**\n\n```typescript\n// Handle fire-and-forget with explicit catch\n@Injectable()\nexport class UsersService {\n  private readonly logger = new Logger(UsersService.name);\n\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Explicitly catch and log errors\n    this.emailService.sendWelcome(user.email).catch(error => {\n      this.logger.error('Failed to send welcome email', error.stack);\n      // Optionally queue for retry\n    });\n\n    return user;\n  }\n}\n\n// Properly handle async event handlers\n@Injectable()\nexport class OrdersService {\n  private readonly logger = new Logger(OrdersService.name);\n\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    try {\n      await this.processOrder(event);\n    } catch (error) {\n      this.logger.error('Failed to process order', { event, error });\n      // Don't rethrow - would crash the process\n      await this.deadLetterQueue.add('order.created', event);\n    }\n  }\n}\n\n// Safe scheduled tasks\n@Injectable()\nexport class CleanupService {\n  private readonly logger = new Logger(CleanupService.name);\n\n  @Cron('0 0 * * *')\n  async dailyCleanup(): Promise<void> {\n    try {\n      await this.cleanupService.run();\n      this.logger.log('Daily cleanup completed');\n    } catch (error) {\n      this.logger.error('Daily cleanup failed', error.stack);\n      // Alert or retry logic\n    }\n  }\n}\n\n// Global unhandled rejection handler in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  const logger = new Logger('Bootstrap');\n\n  process.on('unhandledRejection', (reason, promise) => {\n    logger.error('Unhandled Rejection at:', promise, 'reason:', reason);\n  });\n\n  process.on('uncaughtException', error => {\n    logger.error('Uncaught Exception:', error);\n    process.exit(1);\n  });\n\n  await app.listen(3000);\n}\n```\n\nReference: [Node.js Unhandled Rejections](https://nodejs.org/api/process.html#event-unhandledrejection)\n\n---\n\n### 3.2 Throw HTTP Exceptions from Services\n\n**Impact: HIGH** — Keeps controllers thin and simplifies error handling\n\nIt's acceptable (and often preferable) to throw `HttpException` subclasses from services in HTTP applications. This keeps controllers thin and allows services to communicate appropriate error states. For truly layer-agnostic services, use domain exceptions that map to HTTP status codes.\n\n**Incorrect (return error objects instead of throwing):**\n\n```typescript\n// Return error objects instead of throwing\n@Injectable()\nexport class UsersService {\n  async findById(id: string): Promise<{ user?: User; error?: string }> {\n    const user = await this.repo.findOne({ where: { id } });\n    if (!user) {\n      return { error: 'User not found' }; // Controller must check this\n    }\n    return { user };\n  }\n}\n\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string) {\n    const result = await this.usersService.findById(id);\n    if (result.error) {\n      throw new NotFoundException(result.error);\n    }\n    return result.user;\n  }\n}\n```\n\n**Correct (throw exceptions directly from service):**\n\n```typescript\n// Throw exceptions directly from service\n@Injectable()\nexport class UsersService {\n  constructor(private readonly repo: UserRepository) {}\n\n  async findById(id: string): Promise<User> {\n    const user = await this.repo.findOne({ where: { id } });\n    if (!user) {\n      throw new NotFoundException(`User #${id} not found`);\n    }\n    return user;\n  }\n\n  async create(dto: CreateUserDto): Promise<User> {\n    const existing = await this.repo.findOne({\n      where: { email: dto.email },\n    });\n    if (existing) {\n      throw new ConflictException('Email already registered');\n    }\n    return this.repo.save(dto);\n  }\n\n  async update(id: string, dto: UpdateUserDto): Promise<User> {\n    const user = await this.findById(id); // Throws if not found\n    Object.assign(user, dto);\n    return this.repo.save(user);\n  }\n}\n\n// Controller stays thin\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findById(id);\n  }\n\n  @Post()\n  create(@Body() dto: CreateUserDto): Promise<User> {\n    return this.usersService.create(dto);\n  }\n}\n\n// For layer-agnostic services, use domain exceptions\nexport class EntityNotFoundException extends Error {\n  constructor(\n    public readonly entity: string,\n    public readonly id: string,\n  ) {\n    super(`${entity} with ID \"${id}\" not found`);\n  }\n}\n\n// Map to HTTP in exception filter\n@Catch(EntityNotFoundException)\nexport class EntityNotFoundFilter implements ExceptionFilter {\n  catch(exception: EntityNotFoundException, host: ArgumentsHost) {\n    const ctx = host.switchToHttp();\n    const response = ctx.getResponse<Response>();\n\n    response.status(404).json({\n      statusCode: 404,\n      message: exception.message,\n      entity: exception.entity,\n      id: exception.id,\n    });\n  }\n}\n```\n\nReference: [NestJS Exception Filters](https://docs.nestjs.com/exception-filters)\n\n---\n\n### 3.3 Use Exception Filters for Error Handling\n\n**Impact: HIGH** — Consistent\n\n<!-- [TRUNCATED AT 50KB] -->\n\n<!-- File: .agents/skills/nestjs-best-practices/README.md (Tokens: ~1129 | Category: anthropic-skill) -->\n# NestJS Best Practices\n\n📖 [For Humans <3](https://kadajett.github.io/agent-nestjs-skills/)\n\nA structured repository for creating and maintaining NestJS Best Practices optimized for agents and LLMs.\n\n## Installation\n\nInstall this skill using [skills](https://github.com/vercel-labs/skills):\n\n```bash\n# GitHub shorthand\nnpx skills add Kadajett/agent-nestjs-skills\n\n# Install globally (available across all projects)\nnpx skills add Kadajett/agent-nestjs-skills --global\n\n# Install for specific agents\nnpx skills add Kadajett/agent-nestjs-skills -a claude-code -a cursor\n```\n\n### Supported Agents\n\n- Claude Code\n- OpenCode\n- Codex\n- Cursor\n- Antigravity\n- Roo Code\n\n## Structure\n\n- `rules/` - Individual rule files (one per rule)\n  - `_sections.md` - Section metadata (titles, impacts, descriptions)\n  - `_template.md` - Template for creating new rules\n  - `area-description.md` - Individual rule files\n- `scripts/` - Build scripts and utilities\n- `metadata.json` - Document metadata (version, organization, abstract)\n- **`AGENTS.md`** - Compiled output (generated)\n\n## Getting Started\n\n1. Install dependencies:\n\n   ```bash\n   cd scripts && npm install\n   ```\n\n2. Build AGENTS.md from rules:\n   ```bash\n   npm run build\n   # or\n   ./scripts/build.sh\n   ```\n\n## Creating a New Rule\n\n1. Copy `rules/_template.md` to `rules/area-description.md`\n2. Choose the appropriate area prefix:\n   - `arch-` for Architecture (Section 1)\n   - `di-` for Dependency Injection (Section 2)\n   - `error-` for Error Handling (Section 3)\n   - `security-` for Security (Section 4)\n   - `perf-` for Performance (Section 5)\n   - `test-` for Testing (Section 6)\n   - `db-` for Database & ORM (Section 7)\n   - `api-` for API Design (Section 8)\n   - `micro-` for Microservices (Section 9)\n   - `devops-` for DevOps & Deployment (Section 10)\n3. Fill in the frontmatter and content\n4. Ensure you have clear examples with explanations\n5. Run the build script to regenerate AGENTS.md\n\n## Rule File Structure\n\nEach rule file should follow this structure:\n\n````markdown\n---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: Optional description\ntags: tag1, tag2, tag3\n---\n\n## Rule Title Here\n\nBrief explanation of the rule and why it matters.\n\n**Incorrect (description of what's wrong):**\n\n```typescript\n// Bad code example\n```\n````\n\n**Correct (description of what's right):**\n\n```typescript\n// Good code example\n```\n\nOptional explanatory text after examples.\n\nReference: [NestJS Documentation](https://docs.nestjs.com)\n\n## File Naming Convention\n\n- Files starting with `_` are special (excluded from build)\n- Rule files: `area-description.md` (e.g., `arch-avoid-circular-deps.md`)\n- Section is automatically inferred from filename prefix\n- Rules are sorted alphabetically by title within each section\n- IDs (e.g., 1.1, 1.2) are auto-generated during build\n\n## Impact Levels\n\n| Level       | Description                                                                           |\n| ----------- | ------------------------------------------------------------------------------------- |\n| CRITICAL    | Violations cause runtime errors, security vulnerabilities, or architectural breakdown |\n| HIGH        | Significant impact on reliability, security, or maintainability                       |\n| MEDIUM-HIGH | Notable impact on quality and developer experience                                    |\n| MEDIUM      | Moderate impact on code quality and best practices                                    |\n| LOW-MEDIUM  | Minor improvements for consistency and maintainability                                |\n\n## Scripts\n\n- `npm run build` (in scripts/) - Compile rules into AGENTS.md\n\n## Contributing\n\nWhen adding or modifying rules:\n\n1. Use the correct filename prefix for your section\n2. Follow the `_template.md` structure\n3. Include clear bad/good examples with explanations\n4. Add appropriate tags\n5. Run the build script to regenerate AGENTS.md\n6. Rules are automatically sorted by title - no need to manage numbers!\n\n## Documentation Website\n\nThe documentation website source code lives on the [`docs` branch](https://github.com/Kadajett/agent-nestjs-skills/tree/docs/website). This separation keeps the skill installation lightweight while maintaining the full documentation site.\n\nTo contribute to the website:\n\n```bash\ngit checkout docs\ncd website\nnpm install\nnpm run dev\n```\n\n## Acknowledgments\n\n- Inspired by the [Vercel React Best Practices](https://github.com/vercel-labs/agent-skills) skill structure\n- Compatible with [skills](https://github.com/vercel-labs/skills) for easy installation across coding agents\n\n## Compatible Agents\n\nThese NestJS skills work with:\n\n- [Claude Code](https://claude.ai/code) - Anthropic's official CLI\n- [AdaL](https://sylph.ai/adal) - Self-evolving AI coding agent with MCP support\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/api-use-dto-serialization.md (Tokens: ~1053 | Category: anthropic-skill) -->\n---\ntitle: Use DTOs and Serialization for API Responses\nimpact: MEDIUM\nimpactDescription: Response DTOs prevent accidental data exposure and ensure consistency\ntags: api, dto, serialization, class-transformer\n---\n\n## Use DTOs and Serialization for API Responses\n\nNever return entity objects directly from controllers. Use response DTOs with class-transformer's `@Exclude()` and `@Expose()` decorators to control exactly what data is sent to clients. This prevents accidental exposure of sensitive fields and provides a stable API contract.\n\n**Incorrect (returning entities directly or manual spreading):**\n\n```typescript\n// Return entities directly\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findById(id);\n    // Returns: { id, email, passwordHash, ssn, internalNotes, ... }\n    // Exposes sensitive data!\n  }\n}\n\n// Manual object spreading (error-prone)\n@Get(':id')\nasync findOne(@Param('id') id: string) {\n  const user = await this.usersService.findById(id);\n  return {\n    id: user.id,\n    email: user.email,\n    name: user.name,\n    // Easy to forget to exclude sensitive fields\n    // Hard to maintain across endpoints\n  };\n}\n```\n\n**Correct (use class-transformer with @Exclude and response DTOs):**\n\n```typescript\n// Enable class-transformer globally\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));\n  await app.listen(3000);\n}\n\n// Entity with serialization control\n@Entity()\nexport class User {\n  @PrimaryGeneratedColumn('uuid')\n  id: string;\n\n  @Column()\n  email: string;\n\n  @Column()\n  name: string;\n\n  @Column()\n  @Exclude() // Never include in responses\n  passwordHash: string;\n\n  @Column({ nullable: true })\n  @Exclude()\n  ssn: string;\n\n  @Column({ default: false })\n  @Exclude({ toPlainOnly: true }) // Exclude from response, allow in requests\n  isAdmin: boolean;\n\n  @CreateDateColumn()\n  createdAt: Date;\n\n  @Column()\n  @Exclude()\n  internalNotes: string;\n}\n\n// Now returning entity is safe\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findById(id);\n    // Returns: { id, email, name, createdAt }\n    // Sensitive fields excluded automatically\n  }\n}\n\n// For different response shapes, use explicit DTOs\nexport class UserResponseDto {\n  @Expose()\n  id: string;\n\n  @Expose()\n  email: string;\n\n  @Expose()\n  name: string;\n\n  @Expose()\n  @Transform(({ obj }) => obj.posts?.length || 0)\n  postCount: number;\n\n  constructor(partial: Partial<User>) {\n    Object.assign(this, partial);\n  }\n}\n\nexport class UserDetailResponseDto extends UserResponseDto {\n  @Expose()\n  createdAt: Date;\n\n  @Expose()\n  @Type(() => PostResponseDto)\n  posts: PostResponseDto[];\n}\n\n// Controller with explicit DTOs\n@Controller('users')\nexport class UsersController {\n  @Get()\n  @SerializeOptions({ type: UserResponseDto })\n  async findAll(): Promise<UserResponseDto[]> {\n    const users = await this.usersService.findAll();\n    return users.map(u => plainToInstance(UserResponseDto, u));\n  }\n\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<UserDetailResponseDto> {\n    const user = await this.usersService.findByIdWithPosts(id);\n    return plainToInstance(UserDetailResponseDto, user, {\n      excludeExtraneousValues: true,\n    });\n  }\n}\n\n// Groups for conditional serialization\nexport class UserDto {\n  @Expose()\n  id: string;\n\n  @Expose()\n  name: string;\n\n  @Expose({ groups: ['admin'] })\n  email: string;\n\n  @Expose({ groups: ['admin'] })\n  createdAt: Date;\n\n  @Expose({ groups: ['admin', 'owner'] })\n  settings: UserSettings;\n}\n\n@Controller('users')\nexport class UsersController {\n  @Get()\n  @SerializeOptions({ groups: ['public'] })\n  async findAllPublic(): Promise<UserDto[]> {\n    // Returns: { id, name }\n  }\n\n  @Get('admin')\n  @UseGuards(AdminGuard)\n  @SerializeOptions({ groups: ['admin'] })\n  async findAllAdmin(): Promise<UserDto[]> {\n    // Returns: { id, name, email, createdAt }\n  }\n\n  @Get('me')\n  @SerializeOptions({ groups: ['owner'] })\n  async getProfile(@CurrentUser() user: User): Promise<UserDto> {\n    // Returns: { id, name, settings }\n  }\n}\n```\n\nReference: [NestJS Serialization](https://docs.nestjs.com/techniques/serialization)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/api-use-interceptors.md (Tokens: ~1177 | Category: anthropic-skill) -->\n---\ntitle: Use Interceptors for Cross-Cutting Concerns\nimpact: MEDIUM-HIGH\nimpactDescription: Interceptors provide clean separation for cross-cutting logic\ntags: api, interceptors, logging, caching\n---\n\n## Use Interceptors for Cross-Cutting Concerns\n\nInterceptors can transform responses, add logging, handle caching, and measure performance without polluting your business logic. They wrap the route handler execution, giving you access to both the request and response streams.\n\n**Incorrect (logging and transformation in every method):**\n\n```typescript\n// Logging in every controller method\n@Controller('users')\nexport class UsersController {\n  @Get()\n  async findAll(): Promise<User[]> {\n    const start = Date.now();\n    this.logger.log('findAll called');\n\n    const users = await this.usersService.findAll();\n\n    this.logger.log(`findAll completed in ${Date.now() - start}ms`);\n    return users;\n  }\n\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    const start = Date.now();\n    this.logger.log(`findOne called with id: ${id}`);\n\n    const user = await this.usersService.findOne(id);\n\n    this.logger.log(`findOne completed in ${Date.now() - start}ms`);\n    return user;\n  }\n  // Repeated in every method!\n}\n\n// Manual response wrapping\n@Get()\nasync findAll(): Promise<{ data: User[]; meta: Meta }> {\n  const users = await this.usersService.findAll();\n  return {\n    data: users,\n    meta: { timestamp: new Date(), count: users.length },\n  };\n}\n```\n\n**Correct (use interceptors for cross-cutting concerns):**\n\n```typescript\n// Logging interceptor\n@Injectable()\nexport class LoggingInterceptor implements NestInterceptor {\n  private readonly logger = new Logger('HTTP');\n\n  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n    const request = context.switchToHttp().getRequest();\n    const { method, url, body } = request;\n    const now = Date.now();\n\n    return next.handle().pipe(\n      tap({\n        next: (data) => {\n          const response = context.switchToHttp().getResponse();\n          this.logger.log(\n            `${method} ${url} ${response.statusCode} - ${Date.now() - now}ms`,\n          );\n        },\n        error: (error) => {\n          this.logger.error(\n            `${method} ${url} ${error.status || 500} - ${Date.now() - now}ms`,\n            error.stack,\n          );\n        },\n      }),\n    );\n  }\n}\n\n// Response transformation interceptor\n@Injectable()\nexport class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {\n  intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {\n    return next.handle().pipe(\n      map((data) => ({\n        data,\n        meta: {\n          timestamp: new Date().toISOString(),\n          path: context.switchToHttp().getRequest().url,\n        },\n      })),\n    );\n  }\n}\n\n// Timeout interceptor\n@Injectable()\nexport class TimeoutInterceptor implements NestInterceptor {\n  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n    return next.handle().pipe(\n      timeout(5000),\n      catchError((err) => {\n        if (err instanceof TimeoutError) {\n          throw new RequestTimeoutException('Request timed out');\n        }\n        throw err;\n      }),\n    );\n  }\n}\n\n// Apply globally or per-controller\n@Module({\n  providers: [\n    { provide: APP_INTERCEPTOR, useClass: LoggingInterceptor },\n    { provide: APP_INTERCEPTOR, useClass: TransformInterceptor },\n  ],\n})\nexport class AppModule {}\n\n// Or per-controller\n@Controller('users')\n@UseInterceptors(LoggingInterceptor)\nexport class UsersController {\n  @Get()\n  async findAll(): Promise<User[]> {\n    // Clean business logic only\n    return this.usersService.findAll();\n  }\n}\n\n// Custom cache interceptor with TTL\n@Injectable()\nexport class HttpCacheInterceptor implements NestInterceptor {\n  constructor(\n    private cacheManager: Cache,\n    private reflector: Reflector,\n  ) {}\n\n  async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {\n    const request = context.switchToHttp().getRequest();\n\n    // Only cache GET requests\n    if (request.method !== 'GET') {\n      return next.handle();\n    }\n\n    const cacheKey = this.generateKey(request);\n    const ttl = this.reflector.get<number>('cacheTTL', context.getHandler()) || 300;\n\n    const cached = await this.cacheManager.get(cacheKey);\n    if (cached) {\n      return of(cached);\n    }\n\n    return next.handle().pipe(\n      tap((response) => {\n        this.cacheManager.set(cacheKey, response, ttl);\n      }),\n    );\n  }\n\n  private generateKey(request: Request): string {\n    return `cache:${request.url}:${JSON.stringify(request.query)}`;\n  }\n}\n\n// Usage with custom TTL\n@Get()\n@SetMetadata('cacheTTL', 600)\n@UseInterceptors(HttpCacheInterceptor)\nasync findAll(): Promise<User[]> {\n  return this.usersService.findAll();\n}\n\n// Error mapping interceptor\n@Injectable()\nexport class ErrorMappingInterceptor implements NestInterceptor {\n  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n    return next.handle().pipe(\n      catchError((error) => {\n        if (error instanceof EntityNotFoundError) {\n          throw new NotFoundException(error.message);\n        }\n        if (error instanceof QueryFailedError) {\n          if (error.message.includes('duplicate')) {\n            throw new ConflictException('Resource already exists');\n          }\n        }\n        throw error;\n      }),\n    );\n  }\n}\n```\n\nReference: [NestJS Interceptors](https://docs.nestjs.com/interceptors)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/api-use-pipes.md (Tokens: ~1267 | Category: anthropic-skill) -->\n---\ntitle: Use Pipes for Input Transformation\nimpact: MEDIUM\nimpactDescription: Pipes ensure clean, validated data reaches your handlers\ntags: api, pipes, validation, transformation\n---\n\n## Use Pipes for Input Transformation\n\nUse built-in pipes like `ParseIntPipe`, `ParseUUIDPipe`, and `DefaultValuePipe` for common transformations. Create custom pipes for business-specific transformations. Pipes separate validation/transformation logic from controllers.\n\n**Incorrect (manual type parsing in handlers):**\n\n```typescript\n// Manual type parsing in handlers\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    // Manual validation in every handler\n    const uuid = id.trim();\n    if (!isUUID(uuid)) {\n      throw new BadRequestException('Invalid UUID');\n    }\n    return this.usersService.findOne(uuid);\n  }\n\n  @Get()\n  async findAll(\n    @Query('page') page: string,\n    @Query('limit') limit: string,\n  ): Promise<User[]> {\n    // Manual parsing and defaults\n    const pageNum = parseInt(page) || 1;\n    const limitNum = parseInt(limit) || 10;\n    return this.usersService.findAll(pageNum, limitNum);\n  }\n}\n\n// Type coercion without validation\n@Get()\nasync search(@Query('price') price: string): Promise<Product[]> {\n  const priceNum = +price; // NaN if invalid, no error\n  return this.productsService.findByPrice(priceNum);\n}\n```\n\n**Correct (use built-in and custom pipes):**\n\n```typescript\n// Use built-in pipes for common transformations\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id', ParseUUIDPipe) id: string): Promise<User> {\n    // id is guaranteed to be a valid UUID\n    return this.usersService.findOne(id);\n  }\n\n  @Get()\n  async findAll(\n    @Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number,\n    @Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number,\n  ): Promise<User[]> {\n    // Automatic defaults and type conversion\n    return this.usersService.findAll(page, limit);\n  }\n\n  @Get('by-status/:status')\n  async findByStatus(\n    @Param('status', new ParseEnumPipe(UserStatus)) status: UserStatus,\n  ): Promise<User[]> {\n    return this.usersService.findByStatus(status);\n  }\n}\n\n// Custom pipe for business logic\n@Injectable()\nexport class ParseDatePipe implements PipeTransform<string, Date> {\n  transform(value: string): Date {\n    const date = new Date(value);\n    if (isNaN(date.getTime())) {\n      throw new BadRequestException('Invalid date format');\n    }\n    return date;\n  }\n}\n\n@Get('reports')\nasync getReports(\n  @Query('from', ParseDatePipe) from: Date,\n  @Query('to', ParseDatePipe) to: Date,\n): Promise<Report[]> {\n  return this.reportsService.findBetween(from, to);\n}\n\n// Custom transformation pipes\n@Injectable()\nexport class NormalizeEmailPipe implements PipeTransform<string, string> {\n  transform(value: string): string {\n    if (!value) return value;\n    return value.trim().toLowerCase();\n  }\n}\n\n// Parse comma-separated values\n@Injectable()\nexport class ParseArrayPipe implements PipeTransform<string, string[]> {\n  transform(value: string): string[] {\n    if (!value) return [];\n    return value.split(',').map((v) => v.trim()).filter(Boolean);\n  }\n}\n\n@Get('products')\nasync findProducts(\n  @Query('ids', ParseArrayPipe) ids: string[],\n  @Query('email', NormalizeEmailPipe) email: string,\n): Promise<Product[]> {\n  // ids is already an array, email is normalized\n  return this.productsService.findByIds(ids);\n}\n\n// Sanitize HTML input\n@Injectable()\nexport class SanitizeHtmlPipe implements PipeTransform<string, string> {\n  transform(value: string): string {\n    if (!value) return value;\n    return sanitizeHtml(value, { allowedTags: [] });\n  }\n}\n\n// Global validation pipe with transformation\napp.useGlobalPipes(\n  new ValidationPipe({\n    whitelist: true, // Strip non-DTO properties\n    transform: true, // Auto-transform to DTO types\n    transformOptions: {\n      enableImplicitConversion: true, // Convert query strings to numbers\n    },\n    forbidNonWhitelisted: true, // Throw on extra properties\n  }),\n);\n\n// DTO with transformation decorators\nexport class FindProductsDto {\n  @IsOptional()\n  @Type(() => Number)\n  @IsInt()\n  @Min(1)\n  page?: number = 1;\n\n  @IsOptional()\n  @Type(() => Number)\n  @IsInt()\n  @Min(1)\n  @Max(100)\n  limit?: number = 10;\n\n  @IsOptional()\n  @Transform(({ value }) => value?.toLowerCase())\n  @IsString()\n  search?: string;\n\n  @IsOptional()\n  @Transform(({ value }) => value?.split(','))\n  @IsArray()\n  @IsString({ each: true })\n  categories?: string[];\n}\n\n@Get()\nasync findAll(@Query() dto: FindProductsDto): Promise<Product[]> {\n  // dto is already transformed and validated\n  return this.productsService.findAll(dto);\n}\n\n// Pipe error customization\n@Injectable()\nexport class CustomParseIntPipe extends ParseIntPipe {\n  constructor() {\n    super({\n      exceptionFactory: (error) =>\n        new BadRequestException(`${error} must be a valid integer`),\n    });\n  }\n}\n\n// Or use options on built-in pipes\n@Get(':id')\nasync findOne(\n  @Param(\n    'id',\n    new ParseIntPipe({\n      errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE,\n      exceptionFactory: () => new NotAcceptableException('ID must be numeric'),\n    }),\n  )\n  id: number,\n): Promise<Item> {\n  return this.itemsService.findOne(id);\n}\n```\n\nReference: [NestJS Pipes](https://docs.nestjs.com/pipes)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/api-versioning.md (Tokens: ~1199 | Category: anthropic-skill) -->\n---\ntitle: Use API Versioning for Breaking Changes\nimpact: MEDIUM\nimpactDescription: Versioning allows you to evolve APIs without breaking existing clients\ntags: api, versioning, breaking-changes, compatibility\n---\n\n## Use API Versioning for Breaking Changes\n\nUse NestJS built-in versioning when making breaking changes to your API. Choose a versioning strategy (URI, header, or media type) and apply it consistently. This allows old clients to continue working while new clients use updated endpoints.\n\n**Incorrect (breaking changes without versioning):**\n\n```typescript\n// Breaking changes without versioning\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    // Original response: { id, name, email }\n    // Later changed to: { id, firstName, lastName, emailAddress }\n    // Old clients break!\n    return this.usersService.findOne(id);\n  }\n}\n\n// Manual versioning in routes\n@Controller('v1/users')\nexport class UsersV1Controller {}\n\n@Controller('v2/users')\nexport class UsersV2Controller {}\n// Inconsistent, error-prone, hard to maintain\n```\n\n**Correct (use NestJS built-in versioning):**\n\n```typescript\n// Enable versioning in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n\n  // URI versioning: /v1/users, /v2/users\n  app.enableVersioning({\n    type: VersioningType.URI,\n    defaultVersion: '1',\n  });\n\n  // Or header versioning: X-API-Version: 1\n  app.enableVersioning({\n    type: VersioningType.HEADER,\n    header: 'X-API-Version',\n    defaultVersion: '1',\n  });\n\n  // Or media type: Accept: application/json;v=1\n  app.enableVersioning({\n    type: VersioningType.MEDIA_TYPE,\n    key: 'v=',\n    defaultVersion: '1',\n  });\n\n  await app.listen(3000);\n}\n\n// Version-specific controllers\n@Controller('users')\n@Version('1')\nexport class UsersV1Controller {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<UserV1Response> {\n    const user = await this.usersService.findOne(id);\n    // V1 response format\n    return {\n      id: user.id,\n      name: user.name,\n      email: user.email,\n    };\n  }\n}\n\n@Controller('users')\n@Version('2')\nexport class UsersV2Controller {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<UserV2Response> {\n    const user = await this.usersService.findOne(id);\n    // V2 response format with breaking changes\n    return {\n      id: user.id,\n      firstName: user.firstName,\n      lastName: user.lastName,\n      emailAddress: user.email,\n      createdAt: user.createdAt,\n    };\n  }\n}\n\n// Per-route versioning - different versions for different routes\n@Controller('users')\nexport class UsersController {\n  @Get()\n  @Version('1')\n  findAllV1(): Promise<UserV1Response[]> {\n    return this.usersService.findAllV1();\n  }\n\n  @Get()\n  @Version('2')\n  findAllV2(): Promise<UserV2Response[]> {\n    return this.usersService.findAllV2();\n  }\n\n  @Get(':id')\n  @Version(['1', '2']) // Same handler for multiple versions\n  findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findOne(id);\n  }\n\n  @Post()\n  @Version(VERSION_NEUTRAL) // Available in all versions\n  create(@Body() dto: CreateUserDto): Promise<User> {\n    return this.usersService.create(dto);\n  }\n}\n\n// Shared service with version-specific logic\n@Injectable()\nexport class UsersService {\n  async findOne(id: string, version: string): Promise<any> {\n    const user = await this.repo.findOne({ where: { id } });\n\n    if (version === '1') {\n      return this.toV1Response(user);\n    }\n    return this.toV2Response(user);\n  }\n\n  private toV1Response(user: User): UserV1Response {\n    return {\n      id: user.id,\n      name: `${user.firstName} ${user.lastName}`,\n      email: user.email,\n    };\n  }\n\n  private toV2Response(user: User): UserV2Response {\n    return {\n      id: user.id,\n      firstName: user.firstName,\n      lastName: user.lastName,\n      emailAddress: user.email,\n      createdAt: user.createdAt,\n    };\n  }\n}\n\n// Controller extracts version\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string, @Headers('X-API-Version') version: string = '1'): Promise<any> {\n    return this.usersService.findOne(id, version);\n  }\n}\n\n// Deprecation strategy - mark old versions as deprecated\n@Controller('users')\n@Version('1')\n@UseInterceptors(DeprecationInterceptor)\nexport class UsersV1Controller {\n  // All V1 routes will include deprecation warning\n}\n\n@Injectable()\nexport class DeprecationInterceptor implements NestInterceptor {\n  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n    const response = context.switchToHttp().getResponse();\n    response.setHeader('Deprecation', 'true');\n    response.setHeader('Sunset', 'Sat, 1 Jan 2025 00:00:00 GMT');\n    response.setHeader('Link', '</v2/users>; rel=\"successor-version\"');\n\n    return next.handle();\n  }\n}\n```\n\nReference: [NestJS Versioning](https://docs.nestjs.com/techniques/versioning)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/arch-avoid-circular-deps.md (Tokens: ~438 | Category: anthropic-skill) -->\n---\ntitle: Avoid Circular Dependencies\nimpact: CRITICAL\nimpactDescription: '#1 cause of runtime crashes'\ntags: architecture, modules, dependencies\n---\n\n## Avoid Circular Dependencies\n\nCircular dependencies occur when Module A imports Module B, and Module B imports Module A (directly or transitively). NestJS can sometimes resolve these through forward references, but they indicate architectural problems and should be avoided. This is the #1 cause of runtime crashes in NestJS applications.\n\n**Incorrect (circular module imports):**\n\n```typescript\n// users.module.ts\n@Module({\n  imports: [OrdersModule], // Orders needs Users, Users needs Orders = circular\n  providers: [UsersService],\n  exports: [UsersService],\n})\nexport class UsersModule {}\n\n// orders.module.ts\n@Module({\n  imports: [UsersModule], // Circular dependency!\n  providers: [OrdersService],\n  exports: [OrdersService],\n})\nexport class OrdersModule {}\n```\n\n**Correct (extract shared logic or use events):**\n\n```typescript\n// Option 1: Extract shared logic to a third module\n// shared.module.ts\n@Module({\n  providers: [SharedService],\n  exports: [SharedService],\n})\nexport class SharedModule {}\n\n// users.module.ts\n@Module({\n  imports: [SharedModule],\n  providers: [UsersService],\n})\nexport class UsersModule {}\n\n// orders.module.ts\n@Module({\n  imports: [SharedModule],\n  providers: [OrdersService],\n})\nexport class OrdersModule {}\n\n// Option 2: Use events for decoupled communication\n// users.service.ts\n@Injectable()\nexport class UsersService {\n  constructor(private eventEmitter: EventEmitter2) {}\n\n  async createUser(data: CreateUserDto) {\n    const user = await this.userRepo.save(data);\n    this.eventEmitter.emit('user.created', user);\n    return user;\n  }\n}\n\n// orders.service.ts\n@Injectable()\nexport class OrdersService {\n  @OnEvent('user.created')\n  handleUserCreated(user: User) {\n    // React to user creation without direct dependency\n  }\n}\n```\n\nReference: [NestJS Circular Dependency](https://docs.nestjs.com/fundamentals/circular-dependency)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/arch-feature-modules.md (Tokens: ~511 | Category: anthropic-skill) -->\n---\ntitle: Organize by Feature Modules\nimpact: CRITICAL\nimpactDescription: '3-5x faster onboarding and development'\ntags: architecture, modules, organization\n---\n\n## Organize by Feature Modules\n\nOrganize your application into feature modules that encapsulate related functionality. Each feature module should be self-contained with its own controllers, services, entities, and DTOs. Avoid organizing by technical layer (all controllers together, all services together). This enables 3-5x faster onboarding and feature development.\n\n**Incorrect (technical layer organization):**\n\n```typescript\n// Technical layer organization (anti-pattern)\nsrc/\n├── controllers/\n│   ├── users.controller.ts\n│   ├── orders.controller.ts\n│   └── products.controller.ts\n├── services/\n│   ├── users.service.ts\n│   ├── orders.service.ts\n│   └── products.service.ts\n├── entities/\n│   ├── user.entity.ts\n│   ├── order.entity.ts\n│   └── product.entity.ts\n└── app.module.ts  // Imports everything directly\n```\n\n**Correct (feature module organization):**\n\n```typescript\n// Feature module organization\nsrc/\n├── users/\n│   ├── dto/\n│   │   ├── create-user.dto.ts\n│   │   └── update-user.dto.ts\n│   ├── entities/\n│   │   └── user.entity.ts\n│   ├── users.controller.ts\n│   ├── users.service.ts\n│   ├── users.repository.ts\n│   └── users.module.ts\n├── orders/\n│   ├── dto/\n│   ├── entities/\n│   ├── orders.controller.ts\n│   ├── orders.service.ts\n│   └── orders.module.ts\n├── shared/\n│   ├── guards/\n│   ├── interceptors/\n│   ├── filters/\n│   └── shared.module.ts\n└── app.module.ts\n\n// users.module.ts\n@Module({\n  imports: [TypeOrmModule.forFeature([User])],\n  controllers: [UsersController],\n  providers: [UsersService, UsersRepository],\n  exports: [UsersService], // Only export what others need\n})\nexport class UsersModule {}\n\n// app.module.ts\n@Module({\n  imports: [\n    ConfigModule.forRoot(),\n    TypeOrmModule.forRoot(),\n    UsersModule,\n    OrdersModule,\n    SharedModule,\n  ],\n})\nexport class AppModule {}\n```\n\nReference: [NestJS Modules](https://docs.nestjs.com/modules)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/arch-module-sharing.md (Tokens: ~825 | Category: anthropic-skill) -->\n---\ntitle: Use Proper Module Sharing Patterns\nimpact: CRITICAL\nimpactDescription: Prevents duplicate instances, memory leaks, and state inconsistency\ntags: architecture, modules, sharing, exports\n---\n\n## Use Proper Module Sharing Patterns\n\nNestJS modules are singletons by default. When a service is properly exported from a module and that module is imported elsewhere, the same instance is shared. However, providing a service in multiple modules creates separate instances, leading to memory waste, state inconsistency, and confusing behavior. Always encapsulate services in dedicated modules, export them explicitly, and import the module where needed.\n\n**Incorrect (service provided in multiple modules):**\n\n```typescript\n// StorageService provided directly in multiple modules - WRONG\n// storage.service.ts\n@Injectable()\nexport class StorageService {\n  private cache = new Map(); // Each instance has separate state!\n\n  store(key: string, value: any) {\n    this.cache.set(key, value);\n  }\n}\n\n// app.module.ts\n@Module({\n  providers: [StorageService], // Instance #1\n  controllers: [AppController],\n})\nexport class AppModule {}\n\n// videos.module.ts\n@Module({\n  providers: [StorageService], // Instance #2 - different from AppModule!\n  controllers: [VideosController],\n})\nexport class VideosModule {}\n\n// Problems:\n// 1. Two separate StorageService instances exist\n// 2. cache.set() in VideosModule doesn't affect AppModule's cache\n// 3. Memory wasted on duplicate instances\n// 4. Debugging nightmares when state doesn't sync\n```\n\n**Correct (dedicated module with exports):**\n\n```typescript\n// storage/storage.module.ts\n@Module({\n  providers: [StorageService],\n  exports: [StorageService], // Make available to importers\n})\nexport class StorageModule {}\n\n// videos/videos.module.ts\n@Module({\n  imports: [StorageModule], // Import the module, not the service\n  controllers: [VideosController],\n  providers: [VideosService],\n})\nexport class VideosModule {}\n\n// channels/channels.module.ts\n@Module({\n  imports: [StorageModule], // Same instance shared\n  controllers: [ChannelsController],\n  providers: [ChannelsService],\n})\nexport class ChannelsModule {}\n\n// app.module.ts\n@Module({\n  imports: [\n    StorageModule, // Only if AppModule itself needs StorageService\n    VideosModule,\n    ChannelsModule,\n  ],\n})\nexport class AppModule {}\n\n// Now all modules share the SAME StorageService instance\n```\n\n**When to use @Global() (sparingly):**\n\n```typescript\n// ONLY for truly cross-cutting concerns\n@Global()\n@Module({\n  providers: [ConfigService, LoggerService],\n  exports: [ConfigService, LoggerService],\n})\nexport class CoreModule {}\n\n// Import once in AppModule\n@Module({\n  imports: [CoreModule], // Registered globally, available everywhere\n})\nexport class AppModule {}\n\n// Other modules don't need to import CoreModule\n@Module({\n  controllers: [UsersController],\n  providers: [UsersService], // Can inject ConfigService without importing\n})\nexport class UsersModule {}\n\n// WARNING: Don't make everything global!\n// - Hides dependencies (can't see what a module needs from imports)\n// - Makes testing harder\n// - Reserve for: config, logging, database connections\n```\n\n**Module re-exporting pattern:**\n\n```typescript\n// common.module.ts - shared utilities\n@Module({\n  providers: [DateService, ValidationService],\n  exports: [DateService, ValidationService],\n})\nexport class CommonModule {}\n\n// core.module.ts - re-exports common for convenience\n@Module({\n  imports: [CommonModule, DatabaseModule],\n  exports: [CommonModule, DatabaseModule], // Re-export for consumers\n})\nexport class CoreModule {}\n\n// feature.module.ts - imports CoreModule, gets both\n@Module({\n  imports: [CoreModule], // Gets CommonModule + DatabaseModule\n  controllers: [FeatureController],\n})\nexport class FeatureModule {}\n```\n\nReference: [NestJS Modules](https://docs.nestjs.com/modules#shared-modules)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/arch-single-responsibility.md (Tokens: ~625 | Category: anthropic-skill) -->\n---\ntitle: Single Responsibility for Services\nimpact: CRITICAL\nimpactDescription: '40%+ improvement in testability'\ntags: architecture, services, single-responsibility\n---\n\n## Single Responsibility for Services\n\nEach service should have a single, well-defined responsibility. Avoid \"god services\" that handle multiple unrelated concerns. If a service name includes \"And\" or handles more than one domain concept, it likely violates single responsibility. This reduces complexity and improves testability by 40%+.\n\n**Incorrect (god service anti-pattern):**\n\n```typescript\n// God service anti-pattern\n@Injectable()\nexport class UserAndOrderService {\n  constructor(\n    private userRepo: UserRepository,\n    private orderRepo: OrderRepository,\n    private mailer: MailService,\n    private payment: PaymentService,\n  ) {}\n\n  async createUser(dto: CreateUserDto) {\n    const user = await this.userRepo.save(dto);\n    await this.mailer.sendWelcome(user);\n    return user;\n  }\n\n  async createOrder(userId: string, dto: CreateOrderDto) {\n    const order = await this.orderRepo.save({ userId, ...dto });\n    await this.payment.charge(order);\n    await this.mailer.sendOrderConfirmation(order);\n    return order;\n  }\n\n  async calculateOrderStats(userId: string) {\n    // Stats logic mixed in\n  }\n\n  async validatePayment(orderId: string) {\n    // Payment logic mixed in\n  }\n}\n```\n\n**Correct (focused services with single responsibility):**\n\n```typescript\n// Focused services with single responsibility\n@Injectable()\nexport class UsersService {\n  constructor(private userRepo: UserRepository) {}\n\n  async create(dto: CreateUserDto): Promise<User> {\n    return this.userRepo.save(dto);\n  }\n\n  async findById(id: string): Promise<User> {\n    return this.userRepo.findOneOrFail({ where: { id } });\n  }\n}\n\n@Injectable()\nexport class OrdersService {\n  constructor(private orderRepo: OrderRepository) {}\n\n  async create(userId: string, dto: CreateOrderDto): Promise<Order> {\n    return this.orderRepo.save({ userId, ...dto });\n  }\n\n  async findByUser(userId: string): Promise<Order[]> {\n    return this.orderRepo.find({ where: { userId } });\n  }\n}\n\n@Injectable()\nexport class OrderStatsService {\n  constructor(private orderRepo: OrderRepository) {}\n\n  async calculateForUser(userId: string): Promise<OrderStats> {\n    // Focused stats calculation\n  }\n}\n\n// Orchestration in controller or dedicated orchestrator\n@Controller('orders')\nexport class OrdersController {\n  constructor(\n    private orders: OrdersService,\n    private payment: PaymentService,\n    private notifications: NotificationService,\n  ) {}\n\n  @Post()\n  async create(@CurrentUser() user: User, @Body() dto: CreateOrderDto) {\n    const order = await this.orders.create(user.id, dto);\n    await this.payment.charge(order);\n    await this.notifications.sendOrderConfirmation(order);\n    return order;\n  }\n}\n```\n\nReference: [NestJS Providers](https://docs.nestjs.com/providers)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/arch-use-events.md (Tokens: ~626 | Category: anthropic-skill) -->\n---\ntitle: Use Event-Driven Architecture for Decoupling\nimpact: MEDIUM-HIGH\nimpactDescription: Enables async processing and modularity\ntags: architecture, events, decoupling\n---\n\n## Use Event-Driven Architecture for Decoupling\n\nUse `@nestjs/event-emitter` for intra-service events and message brokers for inter-service communication. Events allow modules to react to changes without direct dependencies, improving modularity and enabling async processing.\n\n**Incorrect (direct service coupling):**\n\n```typescript\n// Direct service coupling\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private inventoryService: InventoryService,\n    private emailService: EmailService,\n    private analyticsService: AnalyticsService,\n    private notificationService: NotificationService,\n    private loyaltyService: LoyaltyService,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Tight coupling - OrdersService knows about all consumers\n    await this.inventoryService.reserve(order.items);\n    await this.emailService.sendConfirmation(order);\n    await this.analyticsService.track('order_created', order);\n    await this.notificationService.push(order.userId, 'Order placed');\n    await this.loyaltyService.addPoints(order.userId, order.total);\n\n    // Adding new behavior requires modifying this service\n    return order;\n  }\n}\n```\n\n**Correct (event-driven decoupling):**\n\n```typescript\n// Use EventEmitter for decoupling\nimport { EventEmitter2 } from '@nestjs/event-emitter';\n\n// Define event\nexport class OrderCreatedEvent {\n  constructor(\n    public readonly orderId: string,\n    public readonly userId: string,\n    public readonly items: OrderItem[],\n    public readonly total: number,\n  ) {}\n}\n\n// Service emits events\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private eventEmitter: EventEmitter2,\n    private repo: Repository<Order>,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const order = await this.repo.save(dto);\n\n    // Emit event - no knowledge of consumers\n    this.eventEmitter.emit('order.created', new OrderCreatedEvent(order.id, order.userId, order.items, order.total));\n\n    return order;\n  }\n}\n\n// Listeners in separate modules\n@Injectable()\nexport class InventoryListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.inventoryService.reserve(event.items);\n  }\n}\n\n@Injectable()\nexport class EmailListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.emailService.sendConfirmation(event.orderId);\n  }\n}\n\n@Injectable()\nexport class AnalyticsListener {\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    await this.analyticsService.track('order_created', {\n      orderId: event.orderId,\n      total: event.total,\n    });\n  }\n}\n```\n\nReference: [NestJS Events](https://docs.nestjs.com/techniques/events)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/arch-use-repository-pattern.md (Tokens: ~618 | Category: anthropic-skill) -->\n---\ntitle: Use Repository Pattern for Data Access\nimpact: HIGH\nimpactDescription: Decouples business logic from database\ntags: architecture, repository, data-access\n---\n\n## Use Repository Pattern for Data Access\n\nCreate custom repositories to encapsulate complex queries and database logic. This keeps services focused on business logic, makes testing easier with mock repositories, and allows changing database implementations without affecting business code.\n\n**Incorrect (complex queries in services):**\n\n```typescript\n// Complex queries in services\n@Injectable()\nexport class UsersService {\n  constructor(@InjectRepository(User) private repo: Repository<User>) {}\n\n  async findActiveWithOrders(minOrders: number): Promise<User[]> {\n    // Complex query logic mixed with business logic\n    return this.repo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.orders', 'order')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('user.deletedAt IS NULL')\n      .groupBy('user.id')\n      .having('COUNT(order.id) >= :min', { min: minOrders })\n      .orderBy('user.createdAt', 'DESC')\n      .getMany();\n  }\n\n  // Service becomes bloated with query logic\n}\n```\n\n**Correct (custom repository with encapsulated queries):**\n\n```typescript\n// Custom repository with encapsulated queries\n@Injectable()\nexport class UsersRepository {\n  constructor(@InjectRepository(User) private repo: Repository<User>) {}\n\n  async findById(id: string): Promise<User | null> {\n    return this.repo.findOne({ where: { id } });\n  }\n\n  async findByEmail(email: string): Promise<User | null> {\n    return this.repo.findOne({ where: { email } });\n  }\n\n  async findActiveWithMinOrders(minOrders: number): Promise<User[]> {\n    return this.repo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.orders', 'order')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('user.deletedAt IS NULL')\n      .groupBy('user.id')\n      .having('COUNT(order.id) >= :min', { min: minOrders })\n      .orderBy('user.createdAt', 'DESC')\n      .getMany();\n  }\n\n  async save(user: User): Promise<User> {\n    return this.repo.save(user);\n  }\n}\n\n// Clean service with business logic only\n@Injectable()\nexport class UsersService {\n  constructor(private usersRepo: UsersRepository) {}\n\n  async getActiveUsersWithOrders(): Promise<User[]> {\n    return this.usersRepo.findActiveWithMinOrders(1);\n  }\n\n  async create(dto: CreateUserDto): Promise<User> {\n    const existing = await this.usersRepo.findByEmail(dto.email);\n    if (existing) {\n      throw new ConflictException('Email already registered');\n    }\n\n    const user = new User();\n    user.email = dto.email;\n    user.name = dto.name;\n    return this.usersRepo.save(user);\n  }\n}\n```\n\nReference: [Repository Pattern](https://martinfowler.com/eaaCatalog/repository.html)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/db-avoid-n-plus-one.md (Tokens: ~897 | Category: anthropic-skill) -->\n---\ntitle: Avoid N+1 Query Problems\nimpact: HIGH\nimpactDescription: N+1 queries are one of the most common performance killers\ntags: database, n-plus-one, queries, performance\n---\n\n## Avoid N+1 Query Problems\n\nN+1 queries occur when you fetch a list of entities, then make an additional query for each entity to load related data. Use eager loading with `relations`, query builder joins, or DataLoader to batch queries efficiently.\n\n**Incorrect (lazy loading in loops causes N+1):**\n\n```typescript\n// Lazy loading in loops causes N+1\n@Injectable()\nexport class OrdersService {\n  async getOrdersWithItems(userId: string): Promise<Order[]> {\n    const orders = await this.orderRepo.find({ where: { userId } });\n    // 1 query for orders\n\n    for (const order of orders) {\n      // N additional queries - one per order!\n      order.items = await this.itemRepo.find({ where: { orderId: order.id } });\n    }\n\n    return orders;\n  }\n}\n\n// Accessing lazy relations without loading\n@Controller('users')\nexport class UsersController {\n  @Get()\n  async findAll(): Promise<User[]> {\n    const users = await this.userRepo.find();\n    // If User.posts is lazy-loaded, serializing triggers N queries\n    return users; // Each user.posts access = 1 query\n  }\n}\n```\n\n**Correct (use relations for eager loading):**\n\n```typescript\n// Use relations option for eager loading\n@Injectable()\nexport class OrdersService {\n  async getOrdersWithItems(userId: string): Promise<Order[]> {\n    // Single query with JOIN\n    return this.orderRepo.find({\n      where: { userId },\n      relations: ['items', 'items.product'],\n    });\n  }\n}\n\n// Use QueryBuilder for complex joins\n@Injectable()\nexport class UsersService {\n  async getUsersWithPostCounts(): Promise<UserWithPostCount[]> {\n    return this.userRepo\n      .createQueryBuilder('user')\n      .leftJoin('user.posts', 'post')\n      .select('user.id', 'id')\n      .addSelect('user.name', 'name')\n      .addSelect('COUNT(post.id)', 'postCount')\n      .groupBy('user.id')\n      .getRawMany();\n  }\n\n  async getActiveUsersWithPosts(): Promise<User[]> {\n    return this.userRepo\n      .createQueryBuilder('user')\n      .leftJoinAndSelect('user.posts', 'post')\n      .leftJoinAndSelect('post.comments', 'comment')\n      .where('user.isActive = :active', { active: true })\n      .andWhere('post.status = :status', { status: 'published' })\n      .getMany();\n  }\n}\n\n// Use find options for specific fields\nasync getOrderSummaries(userId: string): Promise<OrderSummary[]> {\n  return this.orderRepo.find({\n    where: { userId },\n    relations: ['items'],\n    select: {\n      id: true,\n      total: true,\n      status: true,\n      items: {\n        id: true,\n        quantity: true,\n        price: true,\n      },\n    },\n  });\n}\n\n// Use DataLoader for GraphQL to batch and cache queries\nimport DataLoader from 'dataloader';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class PostsLoader {\n  constructor(private postsService: PostsService) {}\n\n  readonly batchPosts = new DataLoader<string, Post[]>(async (userIds) => {\n    // Single query for all users' posts\n    const posts = await this.postsService.findByUserIds([...userIds]);\n\n    // Group by userId\n    const postsMap = new Map<string, Post[]>();\n    for (const post of posts) {\n      const userPosts = postsMap.get(post.userId) || [];\n      userPosts.push(post);\n      postsMap.set(post.userId, userPosts);\n    }\n\n    // Return in same order as input\n    return userIds.map((id) => postsMap.get(id) || []);\n  });\n}\n\n// In resolver\n@ResolveField()\nasync posts(@Parent() user: User): Promise<Post[]> {\n  // DataLoader batches multiple calls into single query\n  return this.postsLoader.batchPosts.load(user.id);\n}\n\n// Enable query logging in development to detect N+1\nTypeOrmModule.forRoot({\n  logging: ['query', 'error'],\n  logger: 'advanced-console',\n});\n```\n\nReference: [TypeORM Relations](https://typeorm.io/relations)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/db-use-migrations.md (Tokens: ~904 | Category: anthropic-skill) -->\n---\ntitle: Use Database Migrations\nimpact: HIGH\nimpactDescription: Enables safe, repeatable database schema changes\ntags: database, migrations, typeorm, schema\n---\n\n## Use Database Migrations\n\nNever use `synchronize: true` in production. Use migrations for all schema changes. Migrations provide version control for your database, enable safe rollbacks, and ensure consistency across all environments.\n\n**Incorrect (using synchronize or manual SQL):**\n\n```typescript\n// Use synchronize in production\nTypeOrmModule.forRoot({\n  type: 'postgres',\n  synchronize: true, // DANGEROUS in production!\n  // Can drop columns, tables, or data\n});\n\n// Manual SQL in production\n@Injectable()\nexport class DatabaseService {\n  async addColumn(): Promise<void> {\n    await this.dataSource.query('ALTER TABLE users ADD COLUMN age INT');\n    // No version control, no rollback, inconsistent across envs\n  }\n}\n\n// Modify entities without migration\n@Entity()\nexport class User {\n  @Column()\n  email: string;\n\n  @Column() // Added without migration\n  newField: string; // Will crash in production if synchronize is false\n}\n```\n\n**Correct (use migrations for all schema changes):**\n\n```typescript\n// Configure TypeORM for migrations\n// data-source.ts\nexport const dataSource = new DataSource({\n  type: 'postgres',\n  host: process.env.DB_HOST,\n  port: parseInt(process.env.DB_PORT),\n  username: process.env.DB_USERNAME,\n  password: process.env.DB_PASSWORD,\n  database: process.env.DB_NAME,\n  entities: ['dist/**/*.entity.js'],\n  migrations: ['dist/migrations/*.js'],\n  synchronize: false, // Always false in production\n  migrationsRun: true, // Run migrations on startup\n});\n\n// app.module.ts\nTypeOrmModule.forRootAsync({\n  inject: [ConfigService],\n  useFactory: (config: ConfigService) => ({\n    type: 'postgres',\n    host: config.get('DB_HOST'),\n    synchronize: config.get('NODE_ENV') === 'development', // Only in dev\n    migrations: ['dist/migrations/*.js'],\n    migrationsRun: true,\n  }),\n});\n\n// migrations/1705312800000-AddUserAge.ts\nimport { MigrationInterface, QueryRunner } from 'typeorm';\n\nexport class AddUserAge1705312800000 implements MigrationInterface {\n  name = 'AddUserAge1705312800000';\n\n  public async up(queryRunner: QueryRunner): Promise<void> {\n    // Add column with default to handle existing rows\n    await queryRunner.query(`\n      ALTER TABLE \"users\" ADD \"age\" integer DEFAULT 0\n    `);\n\n    // Add index for frequently queried columns\n    await queryRunner.query(`\n      CREATE INDEX \"IDX_users_age\" ON \"users\" (\"age\")\n    `);\n  }\n\n  public async down(queryRunner: QueryRunner): Promise<void> {\n    // Always implement down for rollback\n    await queryRunner.query(`DROP INDEX \"IDX_users_age\"`);\n    await queryRunner.query(`ALTER TABLE \"users\" DROP COLUMN \"age\"`);\n  }\n}\n\n// Safe column rename (two-step)\nexport class RenameNameToFullName1705312900000 implements MigrationInterface {\n  public async up(queryRunner: QueryRunner): Promise<void> {\n    // Step 1: Add new column\n    await queryRunner.query(`\n      ALTER TABLE \"users\" ADD \"full_name\" varchar(255)\n    `);\n\n    // Step 2: Copy data\n    await queryRunner.query(`\n      UPDATE \"users\" SET \"full_name\" = \"name\"\n    `);\n\n    // Step 3: Add NOT NULL constraint\n    await queryRunner.query(`\n      ALTER TABLE \"users\" ALTER COLUMN \"full_name\" SET NOT NULL\n    `);\n\n    // Step 4: Drop old column (after verifying app works)\n    await queryRunner.query(`\n      ALTER TABLE \"users\" DROP COLUMN \"name\"\n    `);\n  }\n\n  public async down(queryRunner: QueryRunner): Promise<void> {\n    await queryRunner.query(`ALTER TABLE \"users\" ADD \"name\" varchar(255)`);\n    await queryRunner.query(`UPDATE \"users\" SET \"name\" = \"full_name\"`);\n    await queryRunner.query(`ALTER TABLE \"users\" DROP COLUMN \"full_name\"`);\n  }\n}\n```\n\nReference: [TypeORM Migrations](https://typeorm.io/migrations)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/db-use-transactions.md (Tokens: ~782 | Category: anthropic-skill) -->\n---\ntitle: Use Transactions for Multi-Step Operations\nimpact: HIGH\nimpactDescription: Ensures data consistency in multi-step operations\ntags: database, transactions, typeorm, consistency\n---\n\n## Use Transactions for Multi-Step Operations\n\nWhen multiple database operations must succeed or fail together, wrap them in a transaction. This prevents partial updates that leave your data in an inconsistent state. Use TypeORM's transaction APIs or the DataSource query runner for complex scenarios.\n\n**Incorrect (multiple saves without transaction):**\n\n```typescript\n// Multiple saves without transaction\n@Injectable()\nexport class OrdersService {\n  async createOrder(userId: string, items: OrderItem[]): Promise<Order> {\n    // If any step fails, data is inconsistent\n    const order = await this.orderRepo.save({ userId, status: 'pending' });\n\n    for (const item of items) {\n      await this.orderItemRepo.save({ orderId: order.id, ...item });\n      await this.inventoryRepo.decrement({ productId: item.productId }, 'stock', item.quantity);\n    }\n\n    await this.paymentService.charge(order.id);\n    // If payment fails, order and inventory are already modified!\n\n    return order;\n  }\n}\n```\n\n**Correct (use DataSource.transaction for automatic rollback):**\n\n```typescript\n// Use DataSource.transaction() for automatic rollback\n@Injectable()\nexport class OrdersService {\n  constructor(private dataSource: DataSource) {}\n\n  async createOrder(userId: string, items: OrderItem[]): Promise<Order> {\n    return this.dataSource.transaction(async manager => {\n      // All operations use the same transactional manager\n      const order = await manager.save(Order, { userId, status: 'pending' });\n\n      for (const item of items) {\n        await manager.save(OrderItem, { orderId: order.id, ...item });\n        await manager.decrement(Inventory, { productId: item.productId }, 'stock', item.quantity);\n      }\n\n      // If this throws, everything rolls back\n      await this.paymentService.chargeWithManager(manager, order.id);\n\n      return order;\n    });\n  }\n}\n\n// QueryRunner for manual transaction control\n@Injectable()\nexport class TransferService {\n  constructor(private dataSource: DataSource) {}\n\n  async transfer(fromId: string, toId: string, amount: number): Promise<void> {\n    const queryRunner = this.dataSource.createQueryRunner();\n    await queryRunner.connect();\n    await queryRunner.startTransaction();\n\n    try {\n      // Debit source account\n      await queryRunner.manager.decrement(Account, { id: fromId }, 'balance', amount);\n\n      // Verify sufficient funds\n      const source = await queryRunner.manager.findOne(Account, {\n        where: { id: fromId },\n      });\n      if (source.balance < 0) {\n        throw new BadRequestException('Insufficient funds');\n      }\n\n      // Credit destination account\n      await queryRunner.manager.increment(Account, { id: toId }, 'balance', amount);\n\n      // Log the transaction\n      await queryRunner.manager.save(TransactionLog, {\n        fromId,\n        toId,\n        amount,\n        timestamp: new Date(),\n      });\n\n      await queryRunner.commitTransaction();\n    } catch (error) {\n      await queryRunner.rollbackTransaction();\n      throw error;\n    } finally {\n      await queryRunner.release();\n    }\n  }\n}\n\n// Repository method with transaction support\n@Injectable()\nexport class UsersRepository {\n  constructor(\n    @InjectRepository(User) private repo: Repository<User>,\n    private dataSource: DataSource,\n  ) {}\n\n  async createWithProfile(userData: CreateUserDto, profileData: CreateProfileDto): Promise<User> {\n    return this.dataSource.transaction(async manager => {\n      const user = await manager.save(User, userData);\n      await manager.save(Profile, { ...profileData, userId: user.id });\n      return user;\n    });\n  }\n}\n```\n\nReference: [TypeORM Transactions](https://typeorm.io/transactions)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/devops-graceful-shutdown.md (Tokens: ~1256 | Category: anthropic-skill) -->\n---\ntitle: Implement Graceful Shutdown\nimpact: MEDIUM-HIGH\nimpactDescription: Proper shutdown handling ensures zero-downtime deployments\ntags: devops, graceful-shutdown, lifecycle, kubernetes\n---\n\n## Implement Graceful Shutdown\n\nHandle SIGTERM and SIGINT signals to gracefully shutdown your NestJS application. Stop accepting new requests, wait for in-flight requests to complete, close database connections, and clean up resources. This prevents data loss and connection errors during deployments.\n\n**Incorrect (ignoring shutdown signals):**\n\n```typescript\n// Ignore shutdown signals\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  await app.listen(3000);\n  // App crashes immediately on SIGTERM\n  // In-flight requests fail\n  // Database connections are abruptly closed\n}\n\n// Long-running tasks without cancellation\n@Injectable()\nexport class ProcessingService {\n  async processLargeFile(file: File): Promise<void> {\n    // No way to interrupt this during shutdown\n    for (let i = 0; i < file.chunks.length; i++) {\n      await this.processChunk(file.chunks[i]);\n      // May run for minutes, blocking shutdown\n    }\n  }\n}\n```\n\n**Correct (enable shutdown hooks and handle cleanup):**\n\n```typescript\n// Enable shutdown hooks in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n\n  // Enable shutdown hooks\n  app.enableShutdownHooks();\n\n  // Optional: Add timeout for forced shutdown\n  const server = await app.listen(3000);\n  server.setTimeout(30000); // 30 second timeout\n\n  // Handle graceful shutdown\n  const signals = ['SIGTERM', 'SIGINT'];\n  signals.forEach(signal => {\n    process.on(signal, async () => {\n      console.log(`Received ${signal}, starting graceful shutdown...`);\n\n      // Stop accepting new connections\n      server.close(async () => {\n        console.log('HTTP server closed');\n        await app.close();\n        process.exit(0);\n      });\n\n      // Force exit after timeout\n      setTimeout(() => {\n        console.error('Forced shutdown after timeout');\n        process.exit(1);\n      }, 30000);\n    });\n  });\n}\n\n// Lifecycle hooks for cleanup\n@Injectable()\nexport class DatabaseService implements OnApplicationShutdown {\n  private readonly connections: Connection[] = [];\n\n  async onApplicationShutdown(signal?: string): Promise<void> {\n    console.log(`Database service shutting down on ${signal}`);\n\n    // Close all connections gracefully\n    await Promise.all(this.connections.map(conn => conn.close()));\n\n    console.log('All database connections closed');\n  }\n}\n\n// Queue processor with graceful shutdown\n@Injectable()\nexport class QueueService implements OnApplicationShutdown, OnModuleDestroy {\n  private isShuttingDown = false;\n\n  onModuleDestroy(): void {\n    this.isShuttingDown = true;\n  }\n\n  async onApplicationShutdown(): Promise<void> {\n    // Wait for current jobs to complete\n    await this.queue.close();\n  }\n\n  async processJob(job: Job): Promise<void> {\n    if (this.isShuttingDown) {\n      throw new Error('Service is shutting down');\n    }\n    await this.doWork(job);\n  }\n}\n\n// WebSocket gateway cleanup\n@WebSocketGateway()\nexport class EventsGateway implements OnApplicationShutdown {\n  @WebSocketServer()\n  server: Server;\n\n  async onApplicationShutdown(): Promise<void> {\n    // Notify all connected clients\n    this.server.emit('shutdown', { message: 'Server is shutting down' });\n\n    // Close all connections\n    this.server.disconnectSockets();\n  }\n}\n\n// Health check integration\n@Injectable()\nexport class ShutdownService {\n  private isShuttingDown = false;\n\n  startShutdown(): void {\n    this.isShuttingDown = true;\n  }\n\n  isShutdown(): boolean {\n    return this.isShuttingDown;\n  }\n}\n\n@Controller('health')\nexport class HealthController {\n  constructor(private shutdownService: ShutdownService) {}\n\n  @Get('ready')\n  @HealthCheck()\n  readiness(): Promise<HealthCheckResult> {\n    // Return 503 during shutdown - k8s stops sending traffic\n    if (this.shutdownService.isShutdown()) {\n      throw new ServiceUnavailableException('Shutting down');\n    }\n\n    return this.health.check([() => this.db.pingCheck('database')]);\n  }\n}\n\n// Integrate with shutdown\n@Injectable()\nexport class AppShutdownService implements OnApplicationShutdown {\n  constructor(private shutdownService: ShutdownService) {}\n\n  async onApplicationShutdown(): Promise<void> {\n    // Mark as unhealthy first\n    this.shutdownService.startShutdown();\n\n    // Wait for k8s to update endpoints\n    await this.sleep(5000);\n\n    // Then proceed with cleanup\n  }\n}\n\n// Request tracking for in-flight requests\n@Injectable()\nexport class RequestTracker implements NestMiddleware, OnApplicationShutdown {\n  private activeRequests = 0;\n  private isShuttingDown = false;\n  private shutdownPromise: Promise<void> | null = null;\n  private resolveShutdown: (() => void) | null = null;\n\n  use(req: Request, res: Response, next: NextFunction): void {\n    if (this.isShuttingDown) {\n      res.status(503).send('Service Unavailable');\n      return;\n    }\n\n    this.activeRequests++;\n\n    res.on('finish', () => {\n      this.activeRequests--;\n      if (this.isShuttingDown && this.activeRequests === 0 && this.resolveShutdown) {\n        this.resolveShutdown();\n      }\n    });\n\n    next();\n  }\n\n  async onApplicationShutdown(): Promise<void> {\n    this.isShuttingDown = true;\n\n    if (this.activeRequests > 0) {\n      console.log(`Waiting for ${this.activeRequests} requests to complete`);\n      this.shutdownPromise = new Promise(resolve => {\n        this.resolveShutdown = resolve;\n      });\n\n      // Wait with timeout\n      await Promise.race([this.shutdownPromise, new Promise(resolve => setTimeout(resolve, 30000))]);\n    }\n\n    console.log('All requests completed');\n  }\n}\n```\n\nReference: [NestJS Lifecycle Events](https://docs.nestjs.com/fundamentals/lifecycle-events)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/devops-use-config-module.md (Tokens: ~991 | Category: anthropic-skill) -->\n---\ntitle: Use ConfigModule for Environment Configuration\nimpact: LOW-MEDIUM\nimpactDescription: Proper configuration prevents deployment failures\ntags: devops, configuration, environment, validation\n---\n\n## Use ConfigModule for Environment Configuration\n\nUse `@nestjs/config` for environment-based configuration. Validate configuration at startup to fail fast on misconfigurations. Use namespaced configuration for organization and type safety.\n\n**Incorrect (accessing process.env directly):**\n\n```typescript\n// Access process.env directly\n@Injectable()\nexport class DatabaseService {\n  constructor() {\n    // No validation, can fail at runtime\n    this.connection = new Pool({\n      host: process.env.DB_HOST,\n      port: parseInt(process.env.DB_PORT), // NaN if missing\n      password: process.env.DB_PASSWORD, // undefined if missing\n    });\n  }\n}\n\n// Scattered env access\n@Injectable()\nexport class EmailService {\n  sendEmail() {\n    // Different services access env differently\n    const apiKey = process.env.SENDGRID_API_KEY || 'default';\n    // Typos go unnoticed: process.env.SENDGRID_API_KY\n  }\n}\n```\n\n**Correct (use @nestjs/config with validation):**\n\n```typescript\n// Setup validated configuration\nimport { ConfigModule, ConfigService, registerAs } from '@nestjs/config';\nimport * as Joi from 'joi';\n\n// config/database.config.ts\nexport const databaseConfig = registerAs('database', () => ({\n  host: process.env.DB_HOST,\n  port: parseInt(process.env.DB_PORT, 10),\n  username: process.env.DB_USERNAME,\n  password: process.env.DB_PASSWORD,\n  database: process.env.DB_NAME,\n}));\n\n// config/app.config.ts\nexport const appConfig = registerAs('app', () => ({\n  port: parseInt(process.env.PORT, 10) || 3000,\n  environment: process.env.NODE_ENV || 'development',\n  apiPrefix: process.env.API_PREFIX || 'api',\n}));\n\n// config/validation.schema.ts\nexport const validationSchema = Joi.object({\n  NODE_ENV: Joi.string().valid('development', 'production', 'test').default('development'),\n  PORT: Joi.number().default(3000),\n  DB_HOST: Joi.string().required(),\n  DB_PORT: Joi.number().default(5432),\n  DB_USERNAME: Joi.string().required(),\n  DB_PASSWORD: Joi.string().required(),\n  DB_NAME: Joi.string().required(),\n  JWT_SECRET: Joi.string().min(32).required(),\n  REDIS_URL: Joi.string().uri().required(),\n});\n\n// app.module.ts\n@Module({\n  imports: [\n    ConfigModule.forRoot({\n      isGlobal: true, // Available everywhere without importing\n      load: [databaseConfig, appConfig],\n      validationSchema,\n      validationOptions: {\n        abortEarly: true, // Stop on first error\n        allowUnknown: true, // Allow other env vars\n      },\n    }),\n    TypeOrmModule.forRootAsync({\n      inject: [ConfigService],\n      useFactory: (config: ConfigService) => ({\n        type: 'postgres',\n        host: config.get('database.host'),\n        port: config.get('database.port'),\n        username: config.get('database.username'),\n        password: config.get('database.password'),\n        database: config.get('database.database'),\n        autoLoadEntities: true,\n      }),\n    }),\n  ],\n})\nexport class AppModule {}\n\n// Type-safe configuration access\nexport interface AppConfig {\n  port: number;\n  environment: 'development' | 'production' | 'test';\n  apiPrefix: string;\n}\n\nexport interface DatabaseConfig {\n  host: string;\n  port: number;\n  username: string;\n  password: string;\n  database: string;\n}\n\n// Type-safe access\n@Injectable()\nexport class AppService {\n  constructor(private config: ConfigService) {}\n\n  getPort(): number {\n    // Type-safe with generic\n    return this.config.get<number>('app.port');\n  }\n\n  getDatabaseConfig(): DatabaseConfig {\n    return this.config.get<DatabaseConfig>('database');\n  }\n}\n\n// Inject namespaced config directly\n@Injectable()\nexport class DatabaseService {\n  constructor(\n    @Inject(databaseConfig.KEY)\n    private dbConfig: ConfigType<typeof databaseConfig>,\n  ) {\n    // Full type inference!\n    const host = this.dbConfig.host; // string\n    const port = this.dbConfig.port; // number\n  }\n}\n\n// Environment files support\nConfigModule.forRoot({\n  envFilePath: [`.env.${process.env.NODE_ENV}.local`, `.env.${process.env.NODE_ENV}`, '.env.local', '.env'],\n});\n\n// .env.development\n// DB_HOST=localhost\n// DB_PORT=5432\n\n// .env.production\n// DB_HOST=prod-db.example.com\n// DB_PORT=5432\n```\n\nReference: [NestJS Configuration](https://docs.nestjs.com/techniques/configuration)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/devops-use-logging.md (Tokens: ~1258 | Category: anthropic-skill) -->\n---\ntitle: Use Structured Logging\nimpact: MEDIUM-HIGH\nimpactDescription: Structured logging enables effective debugging and monitoring\ntags: devops, logging, structured-logs, pino\n---\n\n## Use Structured Logging\n\nUse NestJS Logger with structured JSON output in production. Include contextual information (request ID, user ID, operation) to trace requests across services. Avoid console.log and implement proper log levels.\n\n**Incorrect (using console.log in production):**\n\n```typescript\n// Use console.log in production\n@Injectable()\nexport class UsersService {\n  async createUser(dto: CreateUserDto): Promise<User> {\n    console.log('Creating user:', dto);\n    // Not structured, no levels, lost in production logs\n\n    try {\n      const user = await this.repo.save(dto);\n      console.log('User created:', user.id);\n      return user;\n    } catch (error) {\n      console.log('Error:', error); // Using log for errors\n      throw error;\n    }\n  }\n}\n\n// Log sensitive data\nconsole.log('Login attempt:', { email, password }); // SECURITY RISK!\n\n// Inconsistent log format\nlogger.log('User ' + userId + ' created at ' + new Date());\n// Hard to parse, no structure\n```\n\n**Correct (use structured logging with context):**\n\n```typescript\n// Configure logger in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule, {\n    logger:\n      process.env.NODE_ENV === 'production' ? ['error', 'warn', 'log'] : ['error', 'warn', 'log', 'debug', 'verbose'],\n  });\n}\n\n// Use NestJS Logger with context\n@Injectable()\nexport class UsersService {\n  private readonly logger = new Logger(UsersService.name);\n\n  async createUser(dto: CreateUserDto): Promise<User> {\n    this.logger.log('Creating user', { email: dto.email });\n\n    try {\n      const user = await this.repo.save(dto);\n      this.logger.log('User created', { userId: user.id });\n      return user;\n    } catch (error) {\n      this.logger.error('Failed to create user', error.stack, {\n        email: dto.email,\n      });\n      throw error;\n    }\n  }\n}\n\n// Custom logger for JSON output\n@Injectable()\nexport class JsonLogger implements LoggerService {\n  log(message: string, context?: object): void {\n    console.log(\n      JSON.stringify({\n        level: 'info',\n        timestamp: new Date().toISOString(),\n        message,\n        ...context,\n      }),\n    );\n  }\n\n  error(message: string, trace?: string, context?: object): void {\n    console.error(\n      JSON.stringify({\n        level: 'error',\n        timestamp: new Date().toISOString(),\n        message,\n        trace,\n        ...context,\n      }),\n    );\n  }\n\n  warn(message: string, context?: object): void {\n    console.warn(\n      JSON.stringify({\n        level: 'warn',\n        timestamp: new Date().toISOString(),\n        message,\n        ...context,\n      }),\n    );\n  }\n\n  debug(message: string, context?: object): void {\n    console.debug(\n      JSON.stringify({\n        level: 'debug',\n        timestamp: new Date().toISOString(),\n        message,\n        ...context,\n      }),\n    );\n  }\n}\n\n// Request context logging with ClsModule\nimport { ClsModule, ClsService } from 'nestjs-cls';\n\n@Module({\n  imports: [\n    ClsModule.forRoot({\n      global: true,\n      middleware: {\n        mount: true,\n        generateId: true,\n      },\n    }),\n  ],\n})\nexport class AppModule {}\n\n// Middleware to set request context\n@Injectable()\nexport class RequestContextMiddleware implements NestMiddleware {\n  constructor(private cls: ClsService) {}\n\n  use(req: Request, res: Response, next: NextFunction): void {\n    const requestId = req.headers['x-request-id'] || randomUUID();\n    this.cls.set('requestId', requestId);\n    this.cls.set('userId', req.user?.id);\n\n    res.setHeader('x-request-id', requestId);\n    next();\n  }\n}\n\n// Logger that includes request context\n@Injectable()\nexport class ContextLogger {\n  constructor(private cls: ClsService) {}\n\n  log(message: string, data?: object): void {\n    console.log(\n      JSON.stringify({\n        level: 'info',\n        timestamp: new Date().toISOString(),\n        requestId: this.cls.get('requestId'),\n        userId: this.cls.get('userId'),\n        message,\n        ...data,\n      }),\n    );\n  }\n\n  error(message: string, error: Error, data?: object): void {\n    console.error(\n      JSON.stringify({\n        level: 'error',\n        timestamp: new Date().toISOString(),\n        requestId: this.cls.get('requestId'),\n        userId: this.cls.get('userId'),\n        message,\n        error: error.message,\n        stack: error.stack,\n        ...data,\n      }),\n    );\n  }\n}\n\n// Pino integration for high-performance logging\nimport { LoggerModule } from 'nestjs-pino';\n\n@Module({\n  imports: [\n    LoggerModule.forRoot({\n      pinoHttp: {\n        level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',\n        transport: process.env.NODE_ENV !== 'production' ? { target: 'pino-pretty' } : undefined,\n        redact: ['req.headers.authorization', 'req.body.password'],\n        serializers: {\n          req: req => ({\n            method: req.method,\n            url: req.url,\n            query: req.query,\n          }),\n          res: res => ({\n            statusCode: res.statusCode,\n          }),\n        },\n      },\n    }),\n  ],\n})\nexport class AppModule {}\n\n// Usage with Pino\n@Injectable()\nexport class UsersService {\n  constructor(private logger: PinoLogger) {\n    this.logger.setContext(UsersService.name);\n  }\n\n  async findOne(id: string): Promise<User> {\n    this.logger.info({ userId: id }, 'Finding user');\n    // Pino uses first arg for data, second for message\n  }\n}\n```\n\nReference: [NestJS Logger](https://docs.nestjs.com/techniques/logger)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/di-avoid-service-locator.md (Tokens: ~614 | Category: anthropic-skill) -->\n---\ntitle: Avoid Service Locator Anti-Pattern\nimpact: HIGH\nimpactDescription: Hides dependencies and breaks testability\ntags: dependency-injection, anti-patterns, testing\n---\n\n## Avoid Service Locator Anti-Pattern\n\nAvoid using `ModuleRef.get()` or global containers to resolve dependencies at runtime. This hides dependencies, makes code harder to test, and breaks the benefits of dependency injection. Use constructor injection instead.\n\n**Incorrect (service locator anti-pattern):**\n\n```typescript\n// Use ModuleRef to get dependencies dynamically\n@Injectable()\nexport class OrdersService {\n  constructor(private moduleRef: ModuleRef) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    // Dependencies are hidden - not visible in constructor\n    const usersService = this.moduleRef.get(UsersService);\n    const inventoryService = this.moduleRef.get(InventoryService);\n    const paymentService = this.moduleRef.get(PaymentService);\n\n    const user = await usersService.findOne(dto.userId);\n    // ... rest of logic\n  }\n}\n\n// Global singleton container\nclass ServiceContainer {\n  private static instance: ServiceContainer;\n  private services = new Map<string, any>();\n\n  static getInstance(): ServiceContainer {\n    if (!this.instance) {\n      this.instance = new ServiceContainer();\n    }\n    return this.instance;\n  }\n\n  get<T>(key: string): T {\n    return this.services.get(key);\n  }\n}\n```\n\n**Correct (constructor injection with explicit dependencies):**\n\n```typescript\n// Use constructor injection - dependencies are explicit\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private usersService: UsersService,\n    private inventoryService: InventoryService,\n    private paymentService: PaymentService,\n  ) {}\n\n  async createOrder(dto: CreateOrderDto): Promise<Order> {\n    const user = await this.usersService.findOne(dto.userId);\n    const inventory = await this.inventoryService.check(dto.items);\n    // Dependencies are clear and testable\n  }\n}\n\n// Easy to test with mocks\ndescribe('OrdersService', () => {\n  let service: OrdersService;\n\n  beforeEach(async () => {\n    const module = await Test.createTestingModule({\n      providers: [\n        OrdersService,\n        { provide: UsersService, useValue: mockUsersService },\n        { provide: InventoryService, useValue: mockInventoryService },\n        { provide: PaymentService, useValue: mockPaymentService },\n      ],\n    }).compile();\n\n    service = module.get(OrdersService);\n  });\n});\n\n// VALID: Factory pattern for dynamic instantiation\n@Injectable()\nexport class HandlerFactory {\n  constructor(private moduleRef: ModuleRef) {}\n\n  getHandler(type: string): Handler {\n    switch (type) {\n      case 'email':\n        return this.moduleRef.get(EmailHandler);\n      case 'sms':\n        return this.moduleRef.get(SmsHandler);\n      default:\n        return this.moduleRef.get(DefaultHandler);\n    }\n  }\n}\n```\n\nReference: [NestJS Module Reference](https://docs.nestjs.com/fundamentals/module-ref)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/di-interface-segregation.md (Tokens: ~1127 | Category: anthropic-skill) -->\n---\ntitle: Apply Interface Segregation Principle\nimpact: HIGH\nimpactDescription: Reduces coupling and improves testability by 30-50%\ntags: dependency-injection, interfaces, solid, isp\n---\n\n## Apply Interface Segregation Principle\n\nClients should not be forced to depend on interfaces they don't use. In NestJS, this means keeping interfaces small and focused on specific capabilities rather than creating \"fat\" interfaces that bundle unrelated methods. When a service only needs to send emails, it shouldn't depend on an interface that also includes SMS, push notifications, and logging. Split large interfaces into role-based ones.\n\n**Incorrect (fat interface forcing unused dependencies):**\n\n```typescript\n// Fat interface - forces all consumers to depend on everything\ninterface NotificationService {\n  sendEmail(to: string, subject: string, body: string): Promise<void>;\n  sendSms(phone: string, message: string): Promise<void>;\n  sendPush(userId: string, notification: PushPayload): Promise<void>;\n  sendSlack(channel: string, message: string): Promise<void>;\n  logNotification(type: string, payload: any): Promise<void>;\n  getDeliveryStatus(id: string): Promise<DeliveryStatus>;\n  retryFailed(id: string): Promise<void>;\n  scheduleNotification(dto: ScheduleDto): Promise<string>;\n}\n\n// Consumer only needs email, but must mock everything for tests\n@Injectable()\nexport class OrdersService {\n  constructor(\n    private notifications: NotificationService, // Depends on 8 methods, uses 1\n  ) {}\n\n  async confirmOrder(order: Order): Promise<void> {\n    await this.notifications.sendEmail(\n      order.customer.email,\n      'Order Confirmed',\n      `Your order ${order.id} has been confirmed.`,\n    );\n  }\n}\n\n// Testing is painful - must mock unused methods\nconst mockNotificationService = {\n  sendEmail: jest.fn(),\n  sendSms: jest.fn(), // Never used, but required\n  sendPush: jest.fn(), // Never used, but required\n  sendSlack: jest.fn(), // Never used, but required\n  logNotification: jest.fn(), // Never used, but required\n  getDeliveryStatus: jest.fn(), // Never used, but required\n  retryFailed: jest.fn(), // Never used, but required\n  scheduleNotification: jest.fn(), // Never used, but required\n};\n```\n\n**Correct (segregated interfaces by capability):**\n\n```typescript\n// Segregated interfaces - each focused on one capability\ninterface EmailSender {\n  sendEmail(to: string, subject: string, body: string): Promise<void>;\n}\n\ninterface SmsSender {\n  sendSms(phone: string, message: string): Promise<void>;\n}\n\ninterface PushSender {\n  sendPush(userId: string, notification: PushPayload): Promise<void>;\n}\n\ninterface NotificationLogger {\n  logNotification(type: string, payload: any): Promise<void>;\n}\n\ninterface NotificationScheduler {\n  scheduleNotification(dto: ScheduleDto): Promise<string>;\n}\n\n// Implementation can implement multiple interfaces\n@Injectable()\nexport class NotificationService implements EmailSender, SmsSender, PushSender {\n  async sendEmail(to: string, subject: string, body: string): Promise<void> {\n    // Email implementation\n  }\n\n  async sendSms(phone: string, message: string): Promise<void> {\n    // SMS implementation\n  }\n\n  async sendPush(userId: string, notification: PushPayload): Promise<void> {\n    // Push implementation\n  }\n}\n\n// Or separate implementations\n@Injectable()\nexport class SendGridEmailService implements EmailSender {\n  async sendEmail(to: string, subject: string, body: string): Promise<void> {\n    // SendGrid-specific implementation\n  }\n}\n\n// Consumer depends only on what it needs\n@Injectable()\nexport class OrdersService {\n  constructor(\n    @Inject(EMAIL_SENDER) private emailSender: EmailSender, // Minimal dependency\n  ) {}\n\n  async confirmOrder(order: Order): Promise<void> {\n    await this.emailSender.sendEmail(\n      order.customer.email,\n      'Order Confirmed',\n      `Your order ${order.id} has been confirmed.`,\n    );\n  }\n}\n\n// Testing is simple - only mock what's used\nconst mockEmailSender: EmailSender = {\n  sendEmail: jest.fn(),\n};\n\n// Module registration with tokens\nexport const EMAIL_SENDER = Symbol('EMAIL_SENDER');\nexport const SMS_SENDER = Symbol('SMS_SENDER');\n\n@Module({\n  providers: [\n    { provide: EMAIL_SENDER, useClass: SendGridEmailService },\n    { provide: SMS_SENDER, useClass: TwilioSmsService },\n  ],\n  exports: [EMAIL_SENDER, SMS_SENDER],\n})\nexport class NotificationModule {}\n```\n\n**Combining interfaces when needed:**\n\n```typescript\n// Sometimes a consumer legitimately needs multiple capabilities\ninterface EmailAndSmsSender extends EmailSender, SmsSender {}\n\n// Or use intersection types\ntype MultiChannelSender = EmailSender & SmsSender & PushSender;\n\n// Consumer that genuinely needs multiple channels\n@Injectable()\nexport class AlertService {\n  constructor(\n    @Inject(MULTI_CHANNEL_SENDER)\n    private sender: EmailSender & SmsSender,\n  ) {}\n\n  async sendCriticalAlert(user: User, message: string): Promise<void> {\n    await Promise.all([\n      this.sender.sendEmail(user.email, 'Critical Alert', message),\n      this.sender.sendSms(user.phone, message),\n    ]);\n  }\n}\n```\n\nReference: [Interface Segregation Principle](https://en.wikipedia.org/wiki/Interface_segregation_principle)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/di-liskov-substitution.md (Tokens: ~1511 | Category: anthropic-skill) -->\n---\ntitle: Honor Liskov Substitution Principle\nimpact: HIGH\nimpactDescription: Ensures implementations are truly interchangeable without breaking callers\ntags: dependency-injection, inheritance, solid, lsp\n---\n\n## Honor Liskov Substitution Principle\n\nSubtypes must be substitutable for their base types without altering program correctness. In NestJS with dependency injection, this means any implementation of an interface or abstract class must honor the contract completely. A mock payment service used in tests must behave like a real payment service (return similar shapes, handle errors the same way). Violating LSP causes subtle bugs when swapping implementations.\n\n**Incorrect (implementation violates the contract):**\n\n```typescript\n// Base interface with clear contract\ninterface PaymentGateway {\n  /**\n   * Charges the specified amount.\n   * @returns PaymentResult on success\n   * @throws PaymentFailedException on payment failure\n   */\n  charge(amount: number, currency: string): Promise<PaymentResult>;\n}\n\n// Production implementation - follows the contract\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    const response = await this.stripe.charges.create({ amount, currency });\n    return { success: true, transactionId: response.id, amount };\n  }\n}\n\n// Mock that violates LSP - different behavior!\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    // VIOLATION 1: Throws for valid input (contract says return PaymentResult)\n    if (amount > 1000) {\n      throw new Error('Mock does not support large amounts');\n    }\n\n    // VIOLATION 2: Returns null instead of PaymentResult\n    if (currency !== 'USD') {\n      return null as any; // Real service would convert or reject properly\n    }\n\n    // VIOLATION 3: Missing required field\n    return { success: true } as PaymentResult; // Missing transactionId!\n  }\n}\n\n// Consumer trusts the contract\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async checkout(order: Order): Promise<void> {\n    const result = await this.payment.charge(order.total, order.currency);\n    // These fail with MockPaymentService:\n    await this.saveTransaction(result.transactionId); // undefined!\n    await this.sendReceipt(result); // might be null!\n  }\n}\n```\n\n**Correct (implementations honor the contract):**\n\n```typescript\n// Well-defined interface with documented behavior\ninterface PaymentGateway {\n  /**\n   * Charges the specified amount.\n   * @param amount - Amount in smallest currency unit (cents)\n   * @param currency - ISO 4217 currency code\n   * @returns PaymentResult with transactionId, success status, and amount\n   * @throws PaymentFailedException if charge is declined\n   * @throws InvalidCurrencyException if currency is not supported\n   */\n  charge(amount: number, currency: string): Promise<PaymentResult>;\n\n  /**\n   * Refunds a previous charge.\n   * @throws TransactionNotFoundException if transactionId is invalid\n   */\n  refund(transactionId: string, amount?: number): Promise<RefundResult>;\n}\n\n// Production implementation\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    try {\n      const response = await this.stripe.charges.create({ amount, currency });\n      return {\n        success: true,\n        transactionId: response.id,\n        amount: response.amount,\n      };\n    } catch (error) {\n      if (error.type === 'card_error') {\n        throw new PaymentFailedException(error.message);\n      }\n      throw error;\n    }\n  }\n\n  async refund(transactionId: string, amount?: number): Promise<RefundResult> {\n    // Implementation...\n  }\n}\n\n// Mock that honors LSP - same contract, same behavior shape\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  private transactions = new Map<string, PaymentResult>();\n\n  async charge(amount: number, currency: string): Promise<PaymentResult> {\n    // Honor the contract: validate currency like real service would\n    if (!['USD', 'EUR', 'GBP'].includes(currency)) {\n      throw new InvalidCurrencyException(`Unsupported currency: ${currency}`);\n    }\n\n    // Simulate decline for specific test scenarios\n    if (amount === 99999) {\n      throw new PaymentFailedException('Card declined (test scenario)');\n    }\n\n    // Return same shape as production\n    const result: PaymentResult = {\n      success: true,\n      transactionId: `mock_${Date.now()}_${Math.random().toString(36)}`,\n      amount,\n    };\n\n    this.transactions.set(result.transactionId, result);\n    return result;\n  }\n\n  async refund(transactionId: string, amount?: number): Promise<RefundResult> {\n    // Honor the contract: throw if transaction not found\n    if (!this.transactions.has(transactionId)) {\n      throw new TransactionNotFoundException(transactionId);\n    }\n\n    return {\n      success: true,\n      refundId: `refund_${transactionId}`,\n      amount: amount ?? this.transactions.get(transactionId)!.amount,\n    };\n  }\n}\n\n// Consumer can swap implementations safely\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async checkout(order: Order): Promise<Order> {\n    try {\n      const result = await this.payment.charge(order.total, order.currency);\n      // Works with both StripeService and MockPaymentService\n      order.transactionId = result.transactionId;\n      order.status = 'paid';\n      return order;\n    } catch (error) {\n      if (error instanceof PaymentFailedException) {\n        order.status = 'payment_failed';\n        return order;\n      }\n      throw error;\n    }\n  }\n}\n```\n\n**Testing LSP compliance:**\n\n```typescript\n// Shared test suite that any implementation must pass\nfunction testPaymentGatewayContract(createGateway: () => PaymentGateway) {\n  describe('PaymentGateway contract', () => {\n    let gateway: PaymentGateway;\n\n    beforeEach(() => {\n      gateway = createGateway();\n    });\n\n    it('returns PaymentResult with all required fields', async () => {\n      const result = await gateway.charge(1000, 'USD');\n      expect(result).toHaveProperty('success');\n      expect(result).toHaveProperty('transactionId');\n      expect(result).toHaveProperty('amount');\n      expect(typeof result.transactionId).toBe('string');\n    });\n\n    it('throws InvalidCurrencyException for unsupported currency', async () => {\n      await expect(gateway.charge(1000, 'INVALID')).rejects.toThrow(InvalidCurrencyException);\n    });\n\n    it('throws TransactionNotFoundException for invalid refund', async () => {\n      await expect(gateway.refund('nonexistent')).rejects.toThrow(TransactionNotFoundException);\n    });\n  });\n}\n\n// Run against all implementations\ndescribe('StripeService', () => {\n  testPaymentGatewayContract(() => new StripeService(mockStripeClient));\n});\n\ndescribe('MockPaymentService', () => {\n  testPaymentGatewayContract(() => new MockPaymentService());\n});\n```\n\nReference: [Liskov Substitution Principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/di-prefer-constructor-injection.md (Tokens: ~486 | Category: anthropic-skill) -->\n---\ntitle: Prefer Constructor Injection\nimpact: CRITICAL\nimpactDescription: Required for proper DI and testing\ntags: dependency-injection, constructor, testing\n---\n\n## Prefer Constructor Injection\n\nAlways use constructor injection over property injection. Constructor injection makes dependencies explicit, enables TypeScript type checking, ensures dependencies are available when the class is instantiated, and improves testability. This is required for proper DI, testing, and TypeScript support.\n\n**Incorrect (property injection with hidden dependencies):**\n\n```typescript\n// Property injection - avoid unless necessary\n@Injectable()\nexport class UsersService {\n  @Inject()\n  private userRepo: UserRepository; // Hidden dependency\n\n  @Inject('CONFIG')\n  private config: ConfigType; // Also hidden\n\n  async findAll() {\n    return this.userRepo.find();\n  }\n}\n\n// Problems:\n// 1. Dependencies not visible in constructor\n// 2. Service can be instantiated without dependencies in tests\n// 3. TypeScript can't enforce dependency types at instantiation\n```\n\n**Correct (constructor injection with explicit dependencies):**\n\n```typescript\n// Constructor injection - explicit and testable\n@Injectable()\nexport class UsersService {\n  constructor(\n    private readonly userRepo: UserRepository,\n    @Inject('CONFIG') private readonly config: ConfigType,\n  ) {}\n\n  async findAll(): Promise<User[]> {\n    return this.userRepo.find();\n  }\n}\n\n// Testing is straightforward\ndescribe('UsersService', () => {\n  let service: UsersService;\n  let mockRepo: jest.Mocked<UserRepository>;\n\n  beforeEach(() => {\n    mockRepo = {\n      find: jest.fn(),\n      save: jest.fn(),\n    } as any;\n\n    service = new UsersService(mockRepo, { dbUrl: 'test' });\n  });\n\n  it('should find all users', async () => {\n    mockRepo.find.mockResolvedValue([{ id: '1', name: 'Test' }]);\n    const result = await service.findAll();\n    expect(result).toHaveLength(1);\n  });\n});\n\n// Only use property injection for optional dependencies\n@Injectable()\nexport class LoggingService {\n  @Optional()\n  @Inject('ANALYTICS')\n  private analytics?: AnalyticsService;\n\n  log(message: string) {\n    console.log(message);\n    this.analytics?.track('log', message); // Optional enhancement\n  }\n}\n```\n\nReference: [NestJS Providers](https://docs.nestjs.com/providers)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/di-scope-awareness.md (Tokens: ~564 | Category: anthropic-skill) -->\n---\ntitle: Understand Provider Scopes\nimpact: CRITICAL\nimpactDescription: Prevents data leaks and performance issues\ntags: dependency-injection, scopes, request-context\n---\n\n## Understand Provider Scopes\n\nNestJS has three provider scopes: DEFAULT (singleton), REQUEST (per-request instance), and TRANSIENT (new instance for each injection). Most providers should be singletons. Request-scoped providers have performance implications as they bubble up through the dependency tree. Understanding scopes prevents memory leaks and incorrect data sharing.\n\n**Incorrect (wrong scope usage):**\n\n```typescript\n// Request-scoped when not needed (performance hit)\n@Injectable({ scope: Scope.REQUEST })\nexport class UsersService {\n  // This creates a new instance for EVERY request\n  // All dependencies also become request-scoped\n  async findAll() {\n    return this.userRepo.find();\n  }\n}\n\n// Singleton with mutable request state\n@Injectable() // Default: singleton\nexport class RequestContextService {\n  private userId: string; // DANGER: Shared across all requests!\n\n  setUser(userId: string) {\n    this.userId = userId; // Overwrites for all concurrent requests\n  }\n\n  getUser() {\n    return this.userId; // Returns wrong user!\n  }\n}\n```\n\n**Correct (appropriate scope for each use case):**\n\n```typescript\n// Singleton for stateless services (default, most common)\n@Injectable()\nexport class UsersService {\n  constructor(private readonly userRepo: UserRepository) {}\n\n  async findById(id: string): Promise<User> {\n    return this.userRepo.findOne({ where: { id } });\n  }\n}\n\n// Request-scoped ONLY when you need request context\n@Injectable({ scope: Scope.REQUEST })\nexport class RequestContextService {\n  private userId: string;\n\n  setUser(userId: string) {\n    this.userId = userId;\n  }\n\n  getUser(): string {\n    return this.userId;\n  }\n}\n\n// Better: Use NestJS built-in request context\nimport { REQUEST } from '@nestjs/core';\nimport { Request } from 'express';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class AuditService {\n  constructor(@Inject(REQUEST) private request: Request) {}\n\n  log(action: string) {\n    console.log(`User ${this.request.user?.id} performed ${action}`);\n  }\n}\n\n// Best: Use ClsModule for async context (no scope bubble-up)\nimport { ClsService } from 'nestjs-cls';\n\n@Injectable() // Stays singleton!\nexport class AuditService {\n  constructor(private cls: ClsService) {}\n\n  log(action: string) {\n    const userId = this.cls.get('userId');\n    console.log(`User ${userId} performed ${action}`);\n  }\n}\n```\n\nReference: [NestJS Injection Scopes](https://docs.nestjs.com/fundamentals/injection-scopes)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/di-use-interfaces-tokens.md (Tokens: ~536 | Category: anthropic-skill) -->\n---\ntitle: Use Injection Tokens for Interfaces\nimpact: HIGH\nimpactDescription: Enables interface-based DI at runtime\ntags: dependency-injection, tokens, interfaces\n---\n\n## Use Injection Tokens for Interfaces\n\nTypeScript interfaces are erased at compile time and can't be used as injection tokens. Use string tokens, symbols, or abstract classes when you want to inject implementations of interfaces. This enables swapping implementations for testing or different environments.\n\n**Incorrect (interface can't be used as token):**\n\n```typescript\n// Interface can't be used as injection token\ninterface PaymentGateway {\n  charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  charge(amount: number) {\n    /* ... */\n  }\n}\n\n@Injectable()\nexport class OrdersService {\n  // This WON'T work - PaymentGateway doesn't exist at runtime\n  constructor(private payment: PaymentGateway) {}\n}\n```\n\n**Correct (symbol tokens or abstract classes):**\n\n```typescript\n// Option 1: String/Symbol tokens (most flexible)\nexport const PAYMENT_GATEWAY = Symbol('PAYMENT_GATEWAY');\n\nexport interface PaymentGateway {\n  charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService implements PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    // Stripe implementation\n  }\n}\n\n@Injectable()\nexport class MockPaymentService implements PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    return { success: true, id: 'mock-id' };\n  }\n}\n\n// Module registration\n@Module({\n  providers: [\n    {\n      provide: PAYMENT_GATEWAY,\n      useClass: process.env.NODE_ENV === 'test' ? MockPaymentService : StripeService,\n    },\n  ],\n  exports: [PAYMENT_GATEWAY],\n})\nexport class PaymentModule {}\n\n// Injection\n@Injectable()\nexport class OrdersService {\n  constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}\n\n  async createOrder(dto: CreateOrderDto) {\n    await this.payment.charge(dto.amount);\n  }\n}\n\n// Option 2: Abstract class (carries runtime type info)\nexport abstract class PaymentGateway {\n  abstract charge(amount: number): Promise<PaymentResult>;\n}\n\n@Injectable()\nexport class StripeService extends PaymentGateway {\n  async charge(amount: number): Promise<PaymentResult> {\n    // Implementation\n  }\n}\n\n// No @Inject needed with abstract class\n@Injectable()\nexport class OrdersService {\n  constructor(private payment: PaymentGateway) {}\n}\n```\n\nReference: [NestJS Custom Providers](https://docs.nestjs.com/fundamentals/custom-providers)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/error-handle-async-errors.md (Tokens: ~773 | Category: anthropic-skill) -->\n---\ntitle: Handle Async Errors Properly\nimpact: HIGH\nimpactDescription: Prevents process crashes from unhandled rejections\ntags: error-handling, async, promises\n---\n\n## Handle Async Errors Properly\n\nNestJS automatically catches errors from async route handlers, but errors from background tasks, event handlers, and manually created promises can crash your application. Always handle async errors explicitly and use global handlers as a safety net.\n\n**Incorrect (fire-and-forget without error handling):**\n\n```typescript\n// Fire-and-forget without error handling\n@Injectable()\nexport class UsersService {\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Fire and forget - if this fails, error is unhandled!\n    this.emailService.sendWelcome(user.email);\n\n    return user;\n  }\n}\n\n// Unhandled promise in event handler\n@Injectable()\nexport class OrdersService {\n  @OnEvent('order.created')\n  handleOrderCreated(event: OrderCreatedEvent) {\n    // This returns a promise but it's not awaited!\n    this.processOrder(event);\n    // Errors will crash the process\n  }\n\n  private async processOrder(event: OrderCreatedEvent): Promise<void> {\n    await this.inventoryService.reserve(event.items);\n    await this.notificationService.send(event.userId);\n  }\n}\n\n// Missing try-catch in scheduled tasks\n@Cron('0 0 * * *')\nasync dailyCleanup(): Promise<void> {\n  await this.cleanupService.run();\n  // If this throws, no error handling\n}\n```\n\n**Correct (explicit async error handling):**\n\n```typescript\n// Handle fire-and-forget with explicit catch\n@Injectable()\nexport class UsersService {\n  private readonly logger = new Logger(UsersService.name);\n\n  async createUser(dto: CreateUserDto): Promise<User> {\n    const user = await this.repo.save(dto);\n\n    // Explicitly catch and log errors\n    this.emailService.sendWelcome(user.email).catch(error => {\n      this.logger.error('Failed to send welcome email', error.stack);\n      // Optionally queue for retry\n    });\n\n    return user;\n  }\n}\n\n// Properly handle async event handlers\n@Injectable()\nexport class OrdersService {\n  private readonly logger = new Logger(OrdersService.name);\n\n  @OnEvent('order.created')\n  async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {\n    try {\n      await this.processOrder(event);\n    } catch (error) {\n      this.logger.error('Failed to process order', { event, error });\n      // Don't rethrow - would crash the process\n      await this.deadLetterQueue.add('order.created', event);\n    }\n  }\n}\n\n// Safe scheduled tasks\n@Injectable()\nexport class CleanupService {\n  private readonly logger = new Logger(CleanupService.name);\n\n  @Cron('0 0 * * *')\n  async dailyCleanup(): Promise<void> {\n    try {\n      await this.cleanupService.run();\n      this.logger.log('Daily cleanup completed');\n    } catch (error) {\n      this.logger.error('Daily cleanup failed', error.stack);\n      // Alert or retry logic\n    }\n  }\n}\n\n// Global unhandled rejection handler in main.ts\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  const logger = new Logger('Bootstrap');\n\n  process.on('unhandledRejection', (reason, promise) => {\n    logger.error('Unhandled Rejection at:', promise, 'reason:', reason);\n  });\n\n  process.on('uncaughtException', error => {\n    logger.error('Uncaught Exception:', error);\n    process.exit(1);\n  });\n\n  await app.listen(3000);\n}\n```\n\nReference: [Node.js Unhandled Rejections](https://nodejs.org/api/process.html#event-unhandledrejection)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/error-throw-http-exceptions.md (Tokens: ~688 | Category: anthropic-skill) -->\n---\ntitle: Throw HTTP Exceptions from Services\nimpact: HIGH\nimpactDescription: Keeps controllers thin and simplifies error handling\ntags: error-handling, exceptions, services\n---\n\n## Throw HTTP Exceptions from Services\n\nIt's acceptable (and often preferable) to throw `HttpException` subclasses from services in HTTP applications. This keeps controllers thin and allows services to communicate appropriate error states. For truly layer-agnostic services, use domain exceptions that map to HTTP status codes.\n\n**Incorrect (return error objects instead of throwing):**\n\n```typescript\n// Return error objects instead of throwing\n@Injectable()\nexport class UsersService {\n  async findById(id: string): Promise<{ user?: User; error?: string }> {\n    const user = await this.repo.findOne({ where: { id } });\n    if (!user) {\n      return { error: 'User not found' }; // Controller must check this\n    }\n    return { user };\n  }\n}\n\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string) {\n    const result = await this.usersService.findById(id);\n    if (result.error) {\n      throw new NotFoundException(result.error);\n    }\n    return result.user;\n  }\n}\n```\n\n**Correct (throw exceptions directly from service):**\n\n```typescript\n// Throw exceptions directly from service\n@Injectable()\nexport class UsersService {\n  constructor(private readonly repo: UserRepository) {}\n\n  async findById(id: string): Promise<User> {\n    const user = await this.repo.findOne({ where: { id } });\n    if (!user) {\n      throw new NotFoundException(`User #${id} not found`);\n    }\n    return user;\n  }\n\n  async create(dto: CreateUserDto): Promise<User> {\n    const existing = await this.repo.findOne({\n      where: { email: dto.email },\n    });\n    if (existing) {\n      throw new ConflictException('Email already registered');\n    }\n    return this.repo.save(dto);\n  }\n\n  async update(id: string, dto: UpdateUserDto): Promise<User> {\n    const user = await this.findById(id); // Throws if not found\n    Object.assign(user, dto);\n    return this.repo.save(user);\n  }\n}\n\n// Controller stays thin\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  findOne(@Param('id') id: string): Promise<User> {\n    return this.usersService.findById(id);\n  }\n\n  @Post()\n  create(@Body() dto: CreateUserDto): Promise<User> {\n    return this.usersService.create(dto);\n  }\n}\n\n// For layer-agnostic services, use domain exceptions\nexport class EntityNotFoundException extends Error {\n  constructor(\n    public readonly entity: string,\n    public readonly id: string,\n  ) {\n    super(`${entity} with ID \"${id}\" not found`);\n  }\n}\n\n// Map to HTTP in exception filter\n@Catch(EntityNotFoundException)\nexport class EntityNotFoundFilter implements ExceptionFilter {\n  catch(exception: EntityNotFoundException, host: ArgumentsHost) {\n    const ctx = host.switchToHttp();\n    const response = ctx.getResponse<Response>();\n\n    response.status(404).json({\n      statusCode: 404,\n      message: exception.message,\n      entity: exception.entity,\n      id: exception.id,\n    });\n  }\n}\n```\n\nReference: [NestJS Exception Filters](https://docs.nestjs.com/exception-filters)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/error-use-exception-filters.md (Tokens: ~745 | Category: anthropic-skill) -->\n---\ntitle: Use Exception Filters for Error Handling\nimpact: HIGH\nimpactDescription: Consistent, centralized error handling\ntags: error-handling, exception-filters, consistency\n---\n\n## Use Exception Filters for Error Handling\n\nNever catch exceptions and manually format error responses in controllers. Use NestJS exception filters to handle errors consistently across your application. Create custom exception filters for specific error types and a global filter for unhandled exceptions.\n\n**Incorrect (manual error handling in controllers):**\n\n```typescript\n// Manual error handling in controllers\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string, @Res() res: Response) {\n    try {\n      const user = await this.usersService.findById(id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found',\n        });\n      }\n      return res.json(user);\n    } catch (error) {\n      console.error(error);\n      return res.status(500).json({\n        statusCode: 500,\n        message: 'Internal server error',\n      });\n    }\n  }\n}\n```\n\n**Correct (exception filters with consistent handling):**\n\n```typescript\n// Use built-in and custom exceptions\n@Controller('users')\nexport class UsersController {\n  @Get(':id')\n  async findOne(@Param('id') id: string): Promise<User> {\n    const user = await this.usersService.findById(id);\n    if (!user) {\n      throw new NotFoundException(`User #${id} not found`);\n    }\n    return user;\n  }\n}\n\n// Custom domain exception\nexport class UserNotFoundException extends NotFoundException {\n  constructor(userId: string) {\n    super({\n      statusCode: 404,\n      error: 'Not Found',\n      message: `User with ID \"${userId}\" not found`,\n      code: 'USER_NOT_FOUND',\n    });\n  }\n}\n\n// Custom exception filter for domain errors\n@Catch(DomainException)\nexport class DomainExceptionFilter implements ExceptionFilter {\n  catch(exception: DomainException, host: ArgumentsHost) {\n    const ctx = host.switchToHttp();\n    const response = ctx.getResponse<Response>();\n    const request = ctx.getRequest<Request>();\n\n    const status = exception.getStatus?.() || 400;\n\n    response.status(status).json({\n      statusCode: status,\n      code: exception.code,\n      message: exception.message,\n      timestamp: new Date().toISOString(),\n      path: request.url,\n    });\n  }\n}\n\n// Global exception filter for unhandled errors\n@Catch()\nexport class AllExceptionsFilter implements ExceptionFilter {\n  constructor(private readonly logger: Logger) {}\n\n  catch(exception: unknown, host: ArgumentsHost) {\n    const ctx = host.switchToHttp();\n    const response = ctx.getResponse<Response>();\n    const request = ctx.getRequest<Request>();\n\n    const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;\n\n    const message = exception instanceof HttpException ? exception.message : 'Internal server error';\n\n    this.logger.error(`${request.method} ${request.url}`, exception instanceof Error ? exception.stack : exception);\n\n    response.status(status).json({\n      statusCode: status,\n      message,\n      timestamp: new Date().toISOString(),\n      path: request.url,\n    });\n  }\n}\n\n// Register globally in main.ts\napp.useGlobalFilters(new AllExceptionsFilter(app.get(Logger)), new DomainExceptionFilter());\n\n// Or via module\n@Module({\n  providers: [\n    {\n      provide: APP_FILTER,\n      useClass: AllExceptionsFilter,\n    },\n  ],\n})\nexport class AppModule {}\n```\n\nReference: [NestJS Exception Filters](https://docs.nestjs.com/exception-filters)\n\n<!-- File: .agents/skills/nestjs-best-practices/rules/micro-use-health-checks.md (Tokens: ~1381 | Category: anthropic-skill) -->\n---\ntitle: Implement Health Checks for Microservices\nimpact: MEDIUM-HIGH\nimpactDescription: Health checks enable orchestrators to manage service lifecycle\ntags: microservices, health-checks, terminus, kubernetes\n---\n\n## Implement Health Checks for Microservices\n\nImplement liveness and readiness probes using `@nestjs/terminus`. Liveness checks determine if the service should be restarted. Readiness checks determine if the service can accept traffic. Proper health checks enable Kubernetes and load balancers to route traffic correctly.\n\n**Incorrect (simple ping that doesn't check dependencies):**\n\n```typescript\n// Simple ping that doesn't check dependencies\n@Controller('health')\nexport class HealthController {\n  @Get()\n  check(): string {\n    return 'OK'; // Service might be unhealthy but returns OK\n  }\n}\n\n// Health check that blocks on slow dependencies\n@Controller('health')\nexport class HealthController {\n  @Get()\n  async check(): Promise<string> {\n    // If database is slow, health check times out\n    await this.userRepo.findOne({ where: { id: '1' } });\n    await this.redis.ping();\n    await this.externalApi.healthCheck();\n    return 'OK';\n  }\n}\n```\n\n**Correct (use @nestjs/terminus for comprehensive health checks):**\n\n```typescript\n// Use @nestjs/terminus for comprehensive health checks\nimport {\n  HealthCheckService,\n  HttpHealthIndicator,\n  TypeOrmHealthIndicator,\n  HealthCheck,\n  DiskHealthIndicator,\n  MemoryHealthIndicator,\n} from '@nestjs/terminus';\n\n@Controller('health')\nexport class HealthController {\n  constructor(\n    private health: HealthCheckService,\n    private http: HttpHealthIndicator,\n    private db: TypeOrmHealthIndicator,\n    private disk: DiskHealthIndicator,\n    private memory: MemoryHealthIndicator,\n  ) {}\n\n  // Liveness probe - is the service alive?\n  @Get('live')\n  @HealthCheck()\n  liveness() {\n    return this.health.check([\n      // Basic checks only\n      () => this.memory.checkHeap('memory_heap', 200 * 1024 * 1024), // 200MB\n    ]);\n  }\n\n  // Readiness probe - can the service handle traffic?\n  @Get('ready')\n  @HealthCheck()\n  readiness() {\n    return this.health.check([\n      () => this.db.pingCheck('database'),\n      () =>\n        this.http.pingCheck('redis', 'http://redis:6379', { timeout: 1000 }),\n      () =>\n        this.disk.checkStorage('disk', { path: '/', thresholdPercent: 0.9 }),\n    ]);\n  }\n\n  // Deep health check for debugging\n  @Get('deep')\n  @HealthCheck()\n  deepCheck() {\n    return this.health.check([\n      () => this.db.pingCheck('database'),\n      () => this.memory.checkHeap('memory_heap', 200 * 1024 * 1024),\n      () => this.memory.checkRSS('memory_rss', 300 * 1024 * 1024),\n      () =>\n        this.disk.checkStorage('disk', { path: '/', thresholdPercent: 0.9 }),\n      () =>\n        this.http.pingCheck('external-api', 'https://api.example.com/health'),\n    ]);\n  }\n}\n\n// Custom indicator for business-specific health\n@Injectable()\nexport class QueueHealthIndicator extends HealthIndicator {\n  constructor(private queueService: QueueService) {\n    super();\n  }\n\n  async isHealthy(key: string): Promise<HealthIndicatorResult> {\n    const queueStats = await this.queueService.getStats();\n\n    const isHealthy = queueStats.failedCount < 100;\n    const result = this.getStatus(key, isHealthy, {\n      waiting: queueStats.waitingCount,\n      active: queueStats.activeCount,\n      failed: queueStats.failedCount,\n    });\n\n    if (!isHealthy) {\n      throw new HealthCheckError('Queue unhealthy', result);\n    }\n\n    return result;\n  }\n}\n\n// Redis health indicator\n@Injectable()\nexport class RedisHealthIndicator extends HealthIndicator {\n  constructor(@InjectRedis() private redis: Redis) {\n    super();\n  }\n\n  async isHealthy(key: string): Promise<HealthIndicatorResult> {\n    try {\n      const pong = await this.redis.ping();\n      return this.getStatus(key, pong === 'PONG');\n    } catch (error) {\n      throw new HealthCheckError('Redis check failed', this.getStatus(key, false));\n    }\n  }\n}\n\n// Use custom indicators\n@Get('ready')\n@HealthCheck()\nreadiness() {\n  return this.health.check([\n    () => this.db.pingCheck('database'),\n    () => this.redis.isHealthy('redis'),\n    () => this.queue.isHealthy('job-queue'),\n  ]);\n}\n\n// Graceful shutdown handling\n@Injectable()\nexport class GracefulShutdownService implements OnApplicationShutdown {\n  private isShuttingDown = false;\n\n  isShutdown(): boolean {\n    return this.isShuttingDown;\n  }\n\n  async onApplicationShutdown(signal: string): Promise<void> {\n    this.isShuttingDown = true;\n    console.log(`Shutting down on ${signal}`);\n\n    // Wait for in-flight requests\n    await new Promise((resolve) => setTimeout(resolve, 5000));\n  }\n}\n\n// Health check respects shutdown state\n@Get('ready')\n@HealthCheck()\nreadiness() {\n  if (this.shutdownService.isShutdown()) {\n    throw new ServiceUnavailableException('Shutting down');\n  }\n\n  return this.health.check([\n    () => this.db.pingCheck('database'),\n  ]);\n}\n```\n\n### Kubernetes Configuration\n\n```yaml\n# Kubernetes deployment with probes\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: api-service\nspec:\n  template:\n    spec:\n      containers:\n        - name: api\n          image: api-service:latest\n          ports:\n            - containerPort: 3000\n          livenessProbe:\n            httpGet:\n              path: /health/live\n              port: 3000\n            initialDelaySeconds: 30\n            periodSeconds: 10\n            timeoutSeconds: 5\n            failureThreshold: 3\n          readinessProbe:\n            httpGet:\n              path: /health/ready\n              port: 3000\n            initialDelaySeconds: 5\n            periodSeconds: 5\n            timeoutSeconds: 3\n            failureThreshold: 3\n          startupProbe:\n            httpGet:\n              path: /health/live\n              port: 3000\n            initialDelaySeconds: 0\n            periodSeconds: 5\n            failureThreshold: 30\n```\n\nReference: [NestJS Terminus](https://docs.nestjs.com/recipes/terminus)\n\n<!-- ... +19 more modular skills available via /api/skills/rolling-scopes/rsschool-app?path={path} -->\n</yakaai_skills>"}