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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@khapu2906/treasure-chest

v1.4.1

Published

A lightweight TypeScript dependency injection container for managing service dependencies

Downloads

389

Readme

Treasure Chest

npm version CI License: DIB TypeScript Node.js

A lightweight and powerful TypeScript dependency injection container for managing service dependencies with support for transient, singleton, scoped, lazy, conditional, and contextual bindings.

Features

Core Features

  • Lightweight: Zero runtime dependencies, minimal footprint
  • TypeScript First: Full TypeScript support with complete type safety
  • Flexible Lifecycle: Transient, singleton, and scoped bindings
  • Lazy Loading: Deferred initialization for performance optimization
  • Container Hierarchy: Child containers with inheritance
  • Nested Dependencies: Automatic resolution of dependency chains
  • Circular Detection: Automatic circular dependency detection
  • Conditional & Contextual: Environment and context-aware bindings
  • Alias Support: Multiple names for the same service

Performance (v1.2.0)

  • Map-based Storage: O(1) lookup complexity (10-100x faster)
  • Binding Cache: Memoized results for non-conditional bindings
  • High-Speed Resolution: 3.67M ops/sec (worst case), 24.7M ops/sec (cached)
  • Auto-Dispose: IDisposable interface with zero overhead
  • Modular Architecture: Better tree-shaking and code splitting

Developer Experience

  • Intuitive API: Fluent API with comprehensive JSDoc
  • Well Tested: 62+ test cases with full coverage
  • Dual Module Support: Both CommonJS and ES Modules (ESM)
  • Type Safe: Full TypeScript declarations
  • Production Ready: Battle-tested patterns
  • Modular Codebase: Clean separation of concerns

Installation

npm install @khapu2906/treasure-chest

Quick Start

import { Container } from '@khapu2906/treasure-chest';

const container = new Container();

// Register a service
container.bind('logger', () => ({
  log: (message: string) => console.log(`[LOG] ${message}`),
}));

// Use the service
const logger = container.resolve('logger');
logger.log('Hello World!');

Key Features

1. Bind (Transient)

Creates a new instance on each resolve:

container.bind('service', () => ({ id: Math.random() }));

const instance1 = container.resolve('service');
const instance2 = container.resolve('service');
console.log(instance1.id !== instance2.id); // true

2. Singleton

Creates only one instance:

container.singleton('cache', () => ({ data: {} }));

const cache1 = container.resolve('cache');
const cache2 = container.resolve('cache');
console.log(cache1 === cache2); // true

3. Alias

Create aliases for services:

container.singleton('logger', () => ({ log: console.log }));
container.alias('appLogger', 'logger');

const logger = container.resolve('appLogger'); // Same as 'logger'

4. Conditional Binding

Register services based on conditions:

const env = 'production';

container.bind(
  'storage',
  () => ({ type: 'local' }),
  () => env === 'development'
);
container.bind(
  'storage',
  () => ({ type: 's3' }),
  () => env === 'production'
);

const storage = container.resolve('storage'); // { type: 's3' }

5. Contextual Binding

Register different implementations for different contexts:

container
  .when('UserService')
  .needs('repository')
  .give(() => new UserRepository());
container
  .when('AdminService')
  .needs('repository')
  .give(() => new AdminRepository());

const userRepo = container.resolve('repository', 'UserService'); // UserRepository
const adminRepo = container.resolve('repository', 'AdminService'); // AdminRepository

6. Container Composition (NEW in v1.3.0)

Mix services from different domains without inheritance:

import { Container } from '@khapu2906/treasure-chest';

// Create domain-specific containers
const infra = new Container();
infra.singleton('db', () => new Database());

const services = new Container();
services.singleton('userService', () => new UserService());

const controllers = new Container();
controllers.singleton('userController', () => new UserController());

// Compose them together
const app = Container.compose([infra, services, controllers]);

// All services available in one container
const db = app.resolve('db');
const userService = app.resolve('userService');
const userController = app.resolve('userController');

7. Scoped Lifecycle (NEW in v1.2.0)

Per-scope instances with automatic cleanup:

import { Container } from '@khapu2906/treasure-chest';

const container = new Container();

// Register scoped service with cleanup
container.scoped(
  'dbConnection',
  () => new DbConnection(),
  function () {
    this.close();
  } // Cleanup function
);

// Create a scope (e.g., per HTTP request)
const scope = container.createScope();

const conn1 = container.resolve('dbConnection');
const conn2 = container.resolve('dbConnection');
console.log(conn1 === conn2); // true - same instance within scope

// Cleanup when done
await scope.dispose(); // Calls cleanup functions

7. Lazy Loading (NEW in v1.2.0)

Defer expensive initialization:

import { Container, Lazy } from '@khapu2906/treasure-chest';

const container = new Container();

// Register lazy service
container.lazy('heavyService', () => new HeavyMLModel());

// Resolve as Lazy wrapper
const lazy = container.resolve<Lazy<HeavyMLModel>>('heavyService');

console.log(lazy.isInitialized); // false - not loaded yet

// Access when needed
const model = lazy.value; // NOW it's initialized

console.log(lazy.isInitialized); // true

8. Child Containers (NEW in v1.2.0)

Container hierarchy for multi-tenant apps:

const parent = new Container();
parent.singleton('config', () => new Config());

// Create child container
const tenant1 = parent.createChild();
tenant1.singleton('logger', () => new TenantLogger('tenant1'));

const tenant2 = parent.createChild();
tenant2.singleton('logger', () => new TenantLogger('tenant2'));

// Each tenant has its own logger but shares config
tenant1.resolve('config'); // From parent
tenant1.resolve('logger'); // From tenant1

9. Circular Dependency Detection (NEW in v1.2.0)

Automatic detection with clear error messages:

container.bind('A', (c) => {
  const b = c.resolve('B');
  return { name: 'A', dep: b };
});

container.bind('B', (c) => {
  const a = c.resolve('A'); // Circular!
  return { name: 'B', dep: a };
});

try {
  container.resolve('A');
} catch (error) {
  console.log(error.message);
  // "Circular dependency detected: A -> B -> A"
}

10. IDisposable Interface & Auto-Dispose (NEW in v1.2.0)

Automatic cleanup detection with zero overhead:

import { Container, IDisposable } from '@khapu2906/treasure-chest';

// Implement IDisposable for auto-cleanup
class DbConnection implements IDisposable {
  async connect() {
    console.log('Connected');
  }

  async dispose() {
    console.log('Disconnected');
  }
}

const container = new Container();

// Auto-detection: No need to pass dispose function!
container.scoped('db', () => new DbConnection());

// Using withScope() - C# using pattern
await container.withScope(async (scope) => {
  const db = container.resolve<DbConnection>('db');
  await db.connect();
  // Auto-disposed when scope exits
});
// Dispose called automatically here

11. Web Framework Integration Pattern

Pattern for automatic per-request scope management. See examples/10-middleware.ts for full implementation.

Note: Middleware helpers are not exported from the core package to keep it framework-agnostic. For production use, consider creating separate adapter packages.

Pattern Example:

import { Container } from '@khapu2906/treasure-chest';

const container = new Container();
container.scoped('db', () => new DbConnection());

// Express pattern
function expressScope(container: Container) {
  return (req, res, next) => {
    const scope = container.createScope();
    req.scope = scope;
    res.on('finish', () => scope.dispose());
    next();
  };
}

app.use(expressScope(container));

Supported Patterns:

  • ✅ Express middleware
  • ✅ Fastify hooks (onRequest/onResponse)
  • ✅ Koa middleware

See examples/10-middleware.ts for complete implementations.

API Reference

Container Class

Lifecycle Methods

bind<T>(key: ServiceKey, factory: FactoryFn<T>, condition?: ConditionFn)

  • Register a transient service (new instance each time)

singleton<T>(key: ServiceKey, factory: FactoryFn<T>, condition?: ConditionFn)

  • Register a singleton service (single instance shared)

scoped<T>(key: ServiceKey, factory: FactoryFn<T>, dispose?: DisposeFn) ⭐ NEW

  • Register a scoped service (instance per scope)
  • Optional cleanup function called on dispose

lazy<T>(key: ServiceKey, factory: FactoryFn<T>, lifecycle?: Lifecycle) ⭐ NEW

  • Register a lazy service (deferred initialization)
  • Returns Lazy<T> wrapper
  • Default lifecycle: singleton

Resolution Methods

resolve<T>(key: ServiceKey, context?: ServiceKey): T

  • Resolve a service from the container
  • Supports circular dependency detection

has(key: ServiceKey): boolean ⭐ NEW

  • Check if a binding exists for a key

keys(): ServiceKey[] ⭐ NEW

  • Get all registered service keys

Contextual Binding

when(context: ServiceKey).needs(key: ServiceKey).give(factory: FactoryFn)

  • Create context-specific bindings
  • Fluent API for readability

alias(aliasKey: ServiceKey, originalKey: ServiceKey)

  • Create an alias for a service

Container Composition

Container.compose(containers: Container[]): Container ⭐ NEW v1.3.0

  • Combine multiple containers into one
  • Allows mixing services from different domains
  • First-wins conflict resolution
  • Maintains container isolation

Container Hierarchy

createChild(): Container ⭐ NEW

  • Create a child container that inherits from parent
  • Children can override parent bindings

createScope(): Scope ⭐ NEW

  • Create a new scope for scoped instances
  • Returns a Scope object

withScope<T>(callback: (scope: Scope) => T | Promise<T>): Promise<T> ⭐ NEW v1.2.0

  • Execute callback with auto-managed scope
  • C#-style using statement pattern
  • Automatic disposal even on errors
  • Exception-safe cleanup

Cleanup

reset()

  • Clear all bindings and instances

dispose(): Promise<void> ⭐ NEW

  • Dispose container and all scoped instances
  • Calls cleanup functions

Exported Types

import {
  Container,
  Lazy,
  Scope,
  Lifecycle,
  IDisposable,
  DisposeFn,
  ServiceKey,
} from '@khapu2906/treasure-chest';

// ServiceKey: 'string' | 'Symbol' | 'Constructor' ⭐ NEW v1.4.1
// Lifecycle: 'transient' | 'singleton' | 'scoped'
// Lazy<T>: Wrapper with .value and .isInitialized
// Scope: Scope management with .dispose()
// IDisposable: Interface for auto-cleanup detection ⭐ NEW v1.2.0
// DisposeFn: Type for cleanup functions

Global Container

import { Container } from '@khapu2906/treasure-chest';
const container = new Container();
// Use global container
container.bind('config', () => ({ env: 'production' }));

Real-world Example

import { Container } from '@khapu2906/treasure-chest';

class Database {
  connect() {
    return { status: 'connected' };
  }
}

class UserRepository {
  constructor(private db: Database) {}

  findUser(id: number) {
    return { id, name: 'John Doe' };
  }
}

class UserService {
  constructor(private repo: UserRepository) {}

  getUser(id: number) {
    return this.repo.findUser(id);
  }
}

// Setup container
const container = new Container();

container.singleton('db', () => new Database());
container.singleton('userRepo', (c) => new UserRepository(c.resolve('db')));
container.singleton(
  'userService',
  (c) => new UserService(c.resolve('userRepo'))
);

// Contextual binding
container
  .when('AdminService')
  .needs('userRepo')
  .give((c) => ({
    ...c.resolve('userRepo'),
    adminAccess: true,
  }));

// Usage
const userService = container.resolve<UserService>('userService');
console.log(userService.getUser(1)); // { id: 1, name: 'John Doe' }

Examples

Check out the examples directory for comprehensive real-world usage patterns:

Basic Features

  1. Basic Usage: Transient and singleton bindings
  2. Dependency Injection: Nested dependency resolution
  3. Conditional Binding: Environment-based configuration
  4. Contextual Binding: Context-aware service resolution
  5. Alias: Multiple names for the same service

Advanced Features (v1.2.0)

  1. Scoped Lifecycle: Per-request services with cleanup
  2. Lazy Loading: Performance optimization with deferred init
  3. Child Containers: Multi-tenant and plugin systems
  4. Circular Dependency: Detection and best practices
  5. Container Composition: Mix services from different domains

Performance & Integration (v1.2.0) ⭐ NEW

  1. Web Framework Middleware: Express, Fastify, Koa integration
    • Auto-scoping per HTTP request
    • IDisposable auto-detection
    • withScope() pattern demos

Each example includes detailed comments and runnable code.

Development

Install Dependencies

npm install

Running Tests

# Run all tests
npm test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage
npm run test:coverage

# Run tests with UI
npm run test:ui

Linting and Formatting

# Run ESLint
npm run lint

# Fix linting issues
npm run lint:fix

# Format code with Prettier
npm run format

# Check formatting
npm run format:check

Building

# Build for production (generates CJS, ESM, and type declarations)
npm run build

# Clean build artifacts
npm run clean

Running Benchmarks ⭐ NEW v1.2.0

# Run performance benchmarks
npm run benchmark

# Results auto-saved to:
# - benchmarks/results/run-YYYY-MM-DD-HH-MM-SS.json (timestamped)
# - benchmarks/results/history.jsonl (append-only log)
# - benchmarks/results/LATEST.md (human-readable report)

See benchmarks/README.md for detailed documentation on:

  • Benchmark categories
  • Result formats
  • Comparing performance over time
  • Expected performance metrics

Running Examples

# Run a specific example
npm run example:01  # Basic usage
npm run example:02  # Dependency injection
npm run example:10  # Middleware (v1.2.0)

# Run all examples sequentially
npm run examples

Contributing

Contributions are welcome! Please read the CONTRIBUTING.md for details on our code of conduct and development process.

Changelog

See CHANGELOG.md for a list of changes and version history.

License

This project is licensed under the DIB License - see the LICENSE file for details.

Author

Kent Phung

Support

If you encounter any issues or have questions:


Made with ❤️ by Kent Phung