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

lite-di-container

v1.0.4

Published

A lightweight, zero-magic Dependency Injection container for modern JavaScript and TypeScript with boot-time graph validation.

Readme

Lite DI Container

A lightweight, zero-magic Dependency Injection container for modern JavaScript and TypeScript.

npm version npm bundle size npm downloads npm total downloads TypeScript Zero Dependencies License: MIT

Unlike heavy frameworks that rely on decorators, reflection, or magic string parsing, this container embraces explicit registration. You tell it exactly what a dependency is (a value, a singleton, a transient, or a factory), and it handles the rest.

Features

  • Zero Magic: Explicit API (value, singleton, transient, factory). No guessing game.
  • Fail-Fast Boot Validation: Validates the entire dependency graph at startup. Catches typos and misconfigurations before your app even begins serving traffic.
  • Advanced Cycle Detection: Detects circular dependencies (e.g., A → B → A) both at runtime and via a dry-run Depth-First Search during the boot() phase.
  • Boot Lock: After boot(), the container is sealed — no accidental runtime registrations.
  • Test-Driven Design: Built-in methods to lock, unlock, reset, and selectively unregister services for easy mocking in integration tests.
  • First-Class TypeScript Support: Includes comprehensive .d.ts types with generic support for perfect IDE autocomplete.

Installation

npm install lite-di-container

Quick Start

1. Register your services

import { Container } from 'lite-di-container';

const container = new Container();

// 1. Register a raw value (returned as-is)
container.value('config', { port: 3000 });

// 2. Register a Singleton (instantiated once lazily, cached forever)
class Database {
    constructor(config) { this.port = config.port; }
}
container.singleton('db', Database, ['config']);

// 3. Register a Transient (new instance created on every get)
class Logger {
    log(msg) { console.log(msg); }
}
container.transient('logger', Logger);

// 4. Register a Factory (function called on every get)
container.factory('requestId', () => crypto.randomUUID());

2. Validate and Lock the Graph

Always call boot() before starting your application. This locks the container (preventing accidental runtime registrations), checks that all required dependencies exist, and scans for circular dependency loops.

container.boot();

3. Resolve Dependencies

const db = container.get('db'); // Instantiates Database and injects 'config'

If you are using TypeScript, you can pass generics to get for full type inference:

const db = container.get<Database>('db');

API

Registration

| Method | Description | |--------|-------------| | .value(name, val) | Register a raw value — returned as-is on every get() | | .singleton(name, Class, deps?) | Register a class — instantiated once on first get(), cached thereafter | | .transient(name, Class, deps?) | Register a class — new instance on every get() | | .factory(name, fn) | Register a function — called on every get(), receives the container |

Resolution

| Method | Description | |--------|-------------| | .get(name) | Resolve a service by name | | .has(name) | Check if a service is registered (no resolution) |

Validation & Lifecycle

| Method | Description | |--------|-------------| | .boot() | Validate wiring + lock the container | | .reset() | Flush singleton caches + unlock for re-registration | | .unregister(name) | Remove a single service (must call reset() first if booted) | | .clear() | Remove everything — full teardown |

Testing & Mocking

The container is designed to make integration testing painless. Use reset() to unlock the container and flush cached instances, then swap dependencies freely:

import { describe, it, expect, beforeEach } from 'vitest';
import container from './my-app-container.js';
import { MockDatabase } from './mocks.js';

describe('My App', () => {
    beforeEach(() => {
        // Unlock the container and clear cached singletons
        container.reset();

        // Swap the real DB for a mock
        container.unregister('db');
        container.singleton('db', MockDatabase);

        // Re-validate the graph
        container.boot();
    });

    it('should use the mocked database', () => {
        const db = container.get('db');
        expect(db).toBeInstanceOf(MockDatabase);
    });
});

Safety Features

Boot Lock

After boot(), any attempt to register, unregister, or modify services throws immediately:

container.boot();
container.value('late', 123); // Error: cannot modify registrations after boot()

Call reset() or clear() to unlock.

Circular Dependency Detection

Detected both statically (at boot() time via DFS) and at runtime (during get() resolution):

class A { constructor(b) {} }
class B { constructor(a) {} }

container.transient('a', A, ['b']);
container.transient('b', B, ['a']);
container.boot(); // Error: circular dependency detected: a → b → a

Factory-based cycles are caught at runtime:

container.factory('ping', (c) => c.get('pong'));
container.factory('pong', (c) => c.get('ping'));
container.get('ping'); // Error: circular dependency detected: ping → pong → ping

Arrow Function Guard

Arrow functions can't be instantiated with new. The container catches this at registration time instead of letting it crash at runtime:

container.singleton('bad', () => {});
// Error: "bad" is an arrow function and cannot be instantiated with `new`.
// Use .factory() for arrow functions or .value() for static utilities.

Known Limitations

  • Factory dependencies are dynamic: boot() validates declared dependency arrays but cannot inspect factory function bodies. A typo inside c.get('dataBse') will only surface when the factory is first called.
  • Arrow function detection is heuristic: Uses .prototype check, which covers all practical cases but could theoretically be fooled by Object.defineProperty.

License

MIT