npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

sdijs

v2.1.0

Published

Modern Dependency Injection (DI) for Node.js with fluent API and advanced features.

Readme

SDIJS v2.1 🚀

Modern Dependency Injection for Node.js - A powerful, enterprise-ready DI container with fluent API, decorators, and universal validation while maintaining the elegant {a,b,c} destructuring syntax.

Version Node.js ES Modules TypeScript Tests Examples Security

Features

Core DI Features:

  • Fluent/Chainable API - Modern, readable service registration
  • Destructuring Support - Keep your elegant {service, config} syntax
  • Tag-based Discovery - Find services by tags with AND/OR logic
  • Scoped Dependencies - Request/session scoped services
  • Factory Functions - With full dependency injection
  • Circular Dependency Detection - Automatic detection and helpful errors
  • Lifecycle Hooks - beforeCreate, afterCreate, etc.

NEW in v2.1 - Advanced Decorator System:

  • Service Decorators - Services that decorate other services with DI
  • Custom Function Decorators - Flexible function-based decoration
  • Universal Method Validation - Works with ANY method names automatically
  • Batch Registration - Declarative service configuration
  • Interface Preservation - Automatic validation of service interfaces
  • Smart Error Detection - Intelligent signature change warnings

Enterprise Features:

  • TypeScript Ready - Full type definitions included
  • Zero Dependencies - Lightweight and secure
  • Security Hardened - Prototype pollution protection & memory limits
  • Verbose Logging - Clear [SDIJS:CATEGORY] logging for debugging
  • Performance Optimized - Efficient caching and tag discovery

Why Dependency Injection?

The Dependency Injection pattern separates object instantiation from business logic, providing:

  • Explicit dependencies - Clear understanding of service relationships
  • Code reuse - Services decoupled from specific implementations
  • Easy testing - Mock dependencies effortlessly
  • Better architecture - Clean, maintainable code structure

📦 Installation

# Install from npm (recommended)
npm install sdijs

# Or clone the repository for development
git clone https://github.com/mau-io/sdijs.git
cd sdijs

# Install dependencies
npm install

# Run tests
npm test

# Run example
node example.js

Requirements: Node.js 16+ (ES Modules support)

🎯 Quick Start

import { createContainer } from 'sdijs';

// Create container with modern API
const container = createContainer({ 
  verbose: true, 
  autoBinding: true,
  strictMode: false
});

// Sample services with {destructuring} - MAINTAINED!
class Database {
  constructor({config, logger}) {  // ← Still works!
    this.config = config;
    this.logger = logger;
  }
  
  async query(sql) {
    this.logger.info(`Executing: ${sql}`);
    return [];
  }
}

class UserService {
  constructor({database, logger}) {  // ← Destructuring preserved!
    this.database = database;
    this.logger = logger;
  }
  
  async findUser(id) {
    return this.database.query(`SELECT * FROM users WHERE id = ${id}`);
  }
}

// Fluent API - Much more powerful!
container
  .value('config', { dbUrl: 'postgresql://...' })
  .factory('logger', ({config}) => ({
    info: (msg) => console.log(`[INFO] ${msg}`),
    error: (msg) => console.error(`[ERROR] ${msg}`)
  }))
  .singleton(Database)
  .singleton(UserService);

// Use your services
const userService = container.resolve('userService');
const user = await userService.findUser(123);

🎨 Decorator System (NEW in v2.1)

The decorator system allows you to add cross-cutting concerns like logging, caching, and metrics to any service without modifying the original code.

Basic Decorator Usage

// Create decorator services
class LoggingDecorator {
  decorate(serviceInstance) {
    return {
      ...serviceInstance, // Preserve all properties
      findUser: async (id) => {
        console.log(`Finding user ${id}`);
        const result = await serviceInstance.findUser(id);
        console.log(`User ${id} found`);
        return result;
      }
    };
  }
}

class TimingDecorator {
  decorate(serviceInstance) {
    return {
      ...serviceInstance,
      findUser: async (id) => {
        const start = Date.now();
        const result = await serviceInstance.findUser(id);
        console.log(`findUser took ${Date.now() - start}ms`);
        return result;
      }
    };
  }
}

// Register decorators and apply them
container
  .register(LoggingDecorator, 'loggingDecorator').asSingleton()
  .register(TimingDecorator, 'timingDecorator').asSingleton()
  .register(UserService, 'userService')
  .decorateWith(['loggingDecorator', 'timingDecorator'])
  .asSingleton();

Batch Registration

Register multiple services with decorators using declarative configuration:

const serviceConfigs = [
  {
    class: OrderService,
    name: 'orderService',
    decorators: ['logging', 'metrics', 'cache'],
    lifecycle: 'singleton',
    tags: ['business', 'critical']
  },
  {
    class: UserService,
    name: 'userService', 
    decorators: ['logging', 'metrics'],
    lifecycle: 'singleton',
    tags: ['business', 'user']
  },
  {
    class: ReportingService,
    name: 'reportingService',
    decorators: ['logging', 'cache'],
    lifecycle: 'singleton',
    tags: ['analytics', 'heavy']
  }
];

container.batchRegister(serviceConfigs);

Custom Function Decorators

For more flexibility, use custom function decorators:

const cacheDecorator = (serviceInstance) => {
  const cache = new Map();
  
  return {
    ...serviceInstance,
    findUser: async (id) => {
      if (cache.has(id)) {
        return cache.get(id);
      }
      
      const result = await serviceInstance.findUser(id);
      cache.set(id, result);
      return result;
    }
  };
};

container
  .register(UserService, 'userService')
  .decorate(cacheDecorator)
  .decorateWith(['logging'])  // Mix function and service decorators
  .asSingleton();

Smart Decorators with Dependency Injection

Decorators can receive their own dependencies:

class UniversalCacheDecorator {
  constructor({ cache, logger }) {
    this.cache = cache;
    this.logger = logger;
  }

  decorate(serviceInstance) {
    const decoratedMethods = {};
    
    // Get ALL public methods dynamically
    const publicMethods = this._getPublicMethods(serviceInstance);
    
    for (const methodName of publicMethods) {
      const originalMethod = serviceInstance[methodName].bind(serviceInstance);
      decoratedMethods[methodName] = async (...args) => {
        const cacheKey = `${serviceInstance.constructor.name}.${methodName}:${JSON.stringify(args)}`;
        
        const cached = await this.cache.get(cacheKey);
        if (cached) {
          this.logger.info(`Cache hit for ${methodName}`);
          return cached;
        }
        
        const result = await originalMethod(...args);
        await this.cache.set(cacheKey, result);
        return result;
      };
    }

    return { ...serviceInstance, ...decoratedMethods };
  }
  
  _getPublicMethods(serviceInstance) {
    // Implementation to discover all public methods
    // Works with ANY method names automatically
  }
}

📚 API Reference

Container Creation

import { createContainer } from 'sdijs';

// Factory function (RECOMMENDED)
const container = createContainer({
  verbose: false,        // Enable/disable logging
  autoBinding: true,     // Auto-bind class methods
  strictMode: false,     // Strict registration rules
  maxServices: 1000,     // Memory limit for services
  maxInstances: 5000,    // Memory limit for instances
  maxScopes: 100,        // Memory limit for scopes
  maxHooksPerEvent: 50   // Memory limit for hooks
});

Service Registration

Fluent Registration API

// Basic registration with lifecycle
container.singleton(UserService);           // Singleton class
container.transient(NotificationService);   // New instance each time
container.value('config', configObject);    // Direct value

// Advanced registration with builder pattern
container
  .register(AdminService)
  .withTag('admin')
  .withTag('security')
  .when(() => process.env.NODE_ENV === 'production')
  .asSingleton();

// Factory functions with DI
container.factory('emailService', ({config, logger}) => {
  return {
    send: async (to, subject, body) => {
      logger.info(`Sending email to ${to}`);
      return { sent: true, to, subject };
    }
  };
}).asSingleton();

// Decorator registration
container
  .register(UserService, 'userService')
  .decorateWith(['loggingDecorator', 'cachingDecorator'])
  .decorate(customTimingDecorator)
  .withTags('business', 'critical')
  .asSingleton();

// Batch registration
container.batchRegister([
  { class: OrderService, name: 'orderService', decorators: ['logging'] },
  { class: UserService, name: 'userService', decorators: ['logging', 'cache'] }
]);

Service Lifecycles

| Lifecycle | Description | Method | |-----------|-------------|---------| | Singleton | One instance, cached globally | .singleton() | | Transient | New instance every time | .transient() | | Scoped | One instance per scope | .register().asScoped() | | Value | Direct value, no instantiation | .value() |

Service Resolution

// Basic resolution
const userService = container.resolve('userService');

// Multiple resolution
const {database, logger, config} = container.resolveAll([
  'database', 'logger', 'config'
]);

// Lazy resolution
const getUserService = container.getResolver('userService');
const service = getUserService(); // Resolved when called

// Scoped resolution
const requestService = requestScope.resolve('requestService');

Scoped Dependencies

Perfect for web applications with request/session-specific data:

// Create scopes
const requestScope = container.createScope('request');
const sessionScope = container.createScope('session');

// Register scoped services
class RequestContext {
  constructor({}) {
    this.requestId = Math.random().toString(36).substr(2, 9);
    this.startTime = Date.now();
  }
  
  getElapsed() {
    return Date.now() - this.startTime;
  }
}

container.register(RequestContext).asScoped();

// Use in different scopes
const ctx1 = requestScope.resolve('requestContext');
const ctx2 = requestScope.resolve('requestContext');
console.log(ctx1 === ctx2); // true - same instance within scope

// Clean up when done
requestScope.dispose();

Lifecycle Hooks

container
  .hook('beforeCreate', ({service}) => {
    console.log(`Creating ${service.name}`);
  })
  .hook('afterCreate', ({service, instance}) => {
    console.log(`Created ${service.name}`);
  });

// Clean up hooks when needed
container.clearHooks('beforeCreate');

Tag-based Service Discovery

Tag-based service discovery enables powerful architectural patterns like plugin systems, environment-specific services, and layered architectures:

// Register services with multiple tags
container
  .register(DatabaseRepository)
  .withTag('repository')
  .withTag('persistence')
  .withTag('production')
  .withTag('database')
  .asSingleton();

container
  .register(ApiRepository)
  .withTag('repository')
  .withTag('http')
  .withTag('external')
  .withTag('api')
  .asSingleton();

container
  .register(CacheService)
  .withTag('cache')
  .withTag('performance')
  .withTag('memory')
  .asSingleton();

// POWERFUL TAG-BASED SERVICE DISCOVERY

// 1. Find services with ALL specified tags (AND mode - default)
const prodRepositories = container.getServicesByTags(['repository', 'production']);
// Returns: [{ name: 'databaseRepository', service: {...}, tags: [...], lifecycle: 'singleton' }]

// 2. Find services with ANY specified tags (OR mode)  
const dataServices = container.getServicesByTags(['repository', 'cache'], 'OR');
// Returns: All repositories AND cache services

// 3. Get just the service names (simplified)
const repoNames = container.getServiceNamesByTags(['repository']);
// Returns: ['databaseRepository', 'apiRepository']

// 4. Resolve services directly by tags
const resolvedRepos = container.resolveServicesByTags(['repository']);
// Returns: [{ name: 'databaseRepository', instance: <resolved>, tags: [...] }]

// 5. Get all available tags (sorted)
const allTags = container.getAllTags();
console.log(allTags); 
// ['api', 'cache', 'database', 'external', 'http', 'memory', 'persistence', 'production', 'repository']

// 6. Get services grouped by tag
const grouped = container.getServicesByTag();
console.log(grouped.repository); // ['databaseRepository', 'apiRepository']
console.log(grouped.cache);      // ['cacheService']

Tag Discovery Methods:

| Method | Purpose | Returns | |--------|---------|---------| | getServicesByTags(tags, mode) | Full service metadata | Array<{name, service, tags, lifecycle}> | | getServiceNamesByTags(tags, mode) | Just service names | string[] | | resolveServicesByTags(tags, mode, scope) | Resolved instances | Array<{name, instance, tags}> | | getAllTags() | All unique tags | string[] (sorted) | | getServicesByTag() | Services grouped by tag | Record<string, string[]> |

Real-World Tag Use Cases

Tags enable enterprise-grade architectural patterns:

// 1. ENVIRONMENT-SPECIFIC SERVICES
// Register different implementations for different environments
container.register(MockPaymentService).withTags('payment', 'development').asSingleton();
container.register(StripePaymentService).withTags('payment', 'production').asSingleton();
container.register(TestEmailService).withTags('email', 'test').asSingleton();
container.register(SendGridEmailService).withTags('email', 'production').asSingleton();

// Load environment-specific services
const env = process.env.NODE_ENV || 'development';
const paymentService = container.getServicesByTags(['payment', env])[0];
const emailServices = container.resolveServicesByTags(['email', env]);

// 2. LAYERED ARCHITECTURE
// Organize services by architectural layers
container.register(UserRepository).withTags('repository', 'data-layer').asSingleton();
container.register(ProductRepository).withTags('repository', 'data-layer').asSingleton();
container.register(UserService).withTags('service', 'business-layer').asSingleton();
container.register(OrderService).withTags('service', 'business-layer').asSingleton();
container.register(UserController).withTags('controller', 'presentation-layer').asSingleton();

// Load entire layers
const dataLayer = container.resolveServicesByTags(['data-layer']);
const businessLayer = container.resolveServicesByTags(['business-layer']);

// 3. PLUGIN SYSTEM
// Dynamic plugin discovery and initialization
container.register(AuthPlugin).withTags('plugin', 'security').asSingleton();
container.register(LoggingPlugin).withTags('plugin', 'monitoring').asSingleton();
container.register(CachePlugin).withTags('plugin', 'performance').asSingleton();

// Initialize all plugins
const plugins = container.resolveServicesByTags(['plugin']);
plugins.forEach(plugin => {
  console.log(`Initializing plugin: ${plugin.name}`);
  plugin.instance.initialize();
});

// 4. STRATEGY PATTERN IMPLEMENTATION
// Multiple payment strategies
container.register(CreditCardPayment).withTags('payment', 'strategy', 'card').asSingleton();
container.register(PayPalPayment).withTags('payment', 'strategy', 'paypal').asSingleton();
container.register(CryptoPayment).withTags('payment', 'strategy', 'crypto').asSingleton();

// Get all payment strategies
const allPaymentStrategies = container.getServicesByTags(['payment', 'strategy']);
console.log(`Available payment methods: ${allPaymentStrategies.map(s => s.name).join(', ')}`);

// Get specific payment type
const cardPayments = container.resolveServicesByTags(['payment', 'card']);

Pro Tips for Tag Usage:

  • Use hierarchical tags: ['service', 'user-service'] for better organization
  • Combine environment + feature: ['cache', 'redis', 'production']
  • Use descriptive names: ['repository', 'read-only'] vs ['repo', 'ro']
  • Group by capability: ['serializer', 'json'], ['serializer', 'xml']
  • Version your APIs: ['api', 'v1'], ['api', 'v2']

Conditional Registration

container
  .register(MockEmailService)
  .when(() => process.env.NODE_ENV === 'test')
  .asSingleton();

container
  .register(SendGridEmailService)
  .when(() => process.env.NODE_ENV === 'production')
  .asSingleton();

🔒 Security Features

SDIJS includes enterprise-grade security features:

// Prototype pollution protection
class VulnerableService {
  constructor(deps) {
    deps.__proto__; // Throws: Dangerous property access blocked
    deps.constructor; // Throws: Dangerous property access blocked
  }
}

// Memory limits prevent DoS attacks
const limitedContainer = createContainer({
  maxServices: 100,      // Max registered services
  maxInstances: 500,     // Max cached instances
  maxScopes: 10,         // Max concurrent scopes
  maxHooksPerEvent: 20   // Max hooks per event
});

// Input validation on all methods
container.value('', 'test'); // Throws: Service name must be non-empty
container.factory('test', 'not-a-function'); // Throws: Factory must be a function

💎 Destructuring Support

The key feature that makes SDIJS special - your destructuring syntax remains unchanged:

// All of these still work exactly the same!

class UserService {
  constructor({database, logger, config, emailService}) {
    this.database = database;
    this.logger = logger;
    this.config = config;
    this.emailService = emailService;
  }
}

class PaymentService {
  constructor({userService, database, logger}) {
    // Your existing code doesn't change!
  }
}

// Factory functions too
const utils = ({config, logger}) => {
  return {
    formatUser: (user) => `${user.name} <${user.email}>`,
    logAction: (action) => logger.info(action)
  };
};

🛠️ Utility Methods

// Service management
container.has('serviceName');           // Check if registered
container.unregister('serviceName');    // Remove service
container.clear();                      // Clear all services
container.getServiceNames();            // List all service names

// Scope management
const scope = container.createScope('myScope');
scope.dispose();                        // Clean up scope
scope.getInstances();                   // Get all instances in scope

// Hook management
container.clearHooks('beforeCreate');   // Remove all hooks for event

🔍 Error Handling & Debugging

SDIJS provides helpful error messages and verbose logging:

// Verbose logging with clear categories
const container = createContainer({ verbose: true });

// Logs show clear categories:
// [SDIJS:REGISTER] Service 'userService' [singleton] with decorators
// [SDIJS:RESOLVE] Resolving dependency: database
// [SDIJS:DECORATOR] Applied decorator 'logging' to service 'userService'
// [SDIJS:VALIDATION] Decorator 'logging' successfully preserved service interface...

// Service not found
container.resolve('nonExistent');
// Error: Service 'nonExistent' not found. Did you forget to register it?

// Circular dependency
container.singleton(ServiceA).singleton(ServiceB);
// Error: Circular dependency detected: serviceA → serviceB → serviceA

// Decorator validation errors
// Error: Decorator 'badDecorator' removed public method(s) from service 'userService': findUser
// Warning: Decorator 'timingDecorator' changed 'findUser' method signature. Original: 1 params, Decorated: 3 params

// Memory limits exceeded
container.value('service1000', {}); 
// Error: Memory limit exceeded for services. Max: 1000

// Security violations
class BadService {
  constructor(deps) {
    deps.__proto__; // Error: Dangerous property access blocked: '__proto__'
  }
}

🚀 Migration from v2.0

New Features in v2.1 (Non-breaking):

  • Service decorators with .decorateWith() and .decorate()
  • Universal method validation (works with any method names)
  • Batch registration with container.batchRegister()
  • Enhanced verbose logging with clear categories
  • Interface preservation validation
  • Smart signature change detection

Your existing v2.0 code works unchanged!

📝 Examples

Main Example

Check out example.js for a comprehensive demonstration of all features.

Comprehensive Examples Directory

Explore the examples/ directory with specialized examples:

# If you cloned the repository
cd examples

# Run individual examples
node 01-basic/simple-di.js              # Basic DI concepts
node 02-advanced/factory-functions.js   # Factory functions
node 02-advanced/scopes-and-hooks.js    # Scoped dependencies & hooks
node 03-patterns/strategy-pattern.js    # Strategy pattern with tags
node 03-patterns/decorator-pattern.js   # Decorator pattern
node 04-enterprise/microservices-communication.js # Enterprise microservices
node 05-web-app/express-integration.js  # Express.js integration
node 06-testing/mocking-and-testing.js  # Testing with mocks
node 07-advanced/tag-discovery.js       # Advanced tag-based discovery
node 09-decorators/basic-decorator-usage.js        # NEW: Basic decorators
node 09-decorators/advanced-decorator-features.js  # NEW: Advanced decorators

NEW Decorator Examples:

  • Basic Decorator Usage - Simple logging, timing, and caching decorators
  • Advanced Decorator Features - Universal validation, batch registration, enterprise patterns

🔗 TypeScript

Full TypeScript support included with comprehensive type definitions:

import { createContainer, ServiceConfig } from 'sdijs';

interface IUserService {
  findUser(id: number): Promise<User>;
}

interface User {
  id: number;
  name: string;
  email: string;
}

const container = createContainer({ verbose: true });

// Batch registration with types
const configs: ServiceConfig[] = [
  { 
    class: UserService, 
    name: 'userService',
    decorators: ['logging', 'cache'],
    lifecycle: 'singleton',
    tags: ['business']
  }
];

container.batchRegister(configs);
const userService = container.resolve<IUserService>('userService');

📊 Performance

SDIJS v2.1 is designed for enterprise performance:

  • Lazy resolution - Services created only when needed
  • Efficient caching - Singleton instances cached with WeakMap
  • Memory limits - Configurable limits prevent memory leaks
  • Auto-binding - Optional method binding with caching
  • Minimal overhead - Zero dependencies, pure JavaScript
  • Fast tag discovery - Optimized Set operations for tag matching
  • Scalable architecture - Performance tested with 55+ services and 100+ tags
  • Universal validation - Efficient method discovery with prototype chain walking
  • Smart decorator caching - Optimized decorator application and validation

Performance Results:

  • Tag discovery: <100ms for 100+ tags
  • Service registration: <1000ms for 55+ services
  • Universal method validation: <10ms per service
  • Decorator application: <5ms per decorator

SDIJS v2.1 - Modern DI with advanced decorators that grows with your application while keeping your code clean, secure, and testable. 🚀