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

@perasite/tdi

v0.2.8

Published

A 1kb, zero-dependencies, immutable, type-safe IoC container for TypeScript

Downloads

1,056

Readme

🎯 tdi

npm package minimized gzipped size GitHub Actions Workflow Status Codecov NPM Version License: MIT

🚀 A tiny, zero-dependencies, immutable, type-safe IoC container for TypeScript.

🌟 Why tdi?

  • 📦 Tiny: < 1KB minified + gzipped
  • 🧩 Simple: No decorators, reflection, or magic strings
  • 🛡️ Type-safe: Full TypeScript support with type inference and compile-time checks
  • Async Support: First-class support for async dependencies
  • 🔒 Immutable: New container is created without mutating the original

📥 Installation

Choose your preferred package manager:

npm install @perasite/tdi    # npm
pnpm install @perasite/tdi   # pnpm
yarn add @perasite/tdi       # yarn

📘 Usage Examples

1️⃣ Basic DI Container

Create a container and add dependencies with automatic type inference. Access dependencies through the items property or get() method.

import { createContainer } from '@perasite/tdi';

// Create a container with configuration
const container = createContainer()
  .add({
    config: {
      apiUrl: 'https://api.example.com', 
      timeout: 5000
    }
  });

// Access dependencies with full type safety
container.items.config;    // { apiUrl: string; timeout: number }
container.get('config');  // does the same

Dependencies can be values, functions, or promises. Function dependencies are evaluated when accessed.

const container = createContainer()
  .add({
    lazyValue: () => 'Hello, world'
    asyncValue: async () => 'Hello, async world'
  });

container.items.lazyValue;  // 'Hello, world'
await container.items.asyncValue; // 'Hello, async world'

2️⃣ Compile-time Type Safety

The container prevents errors like duplicate dependencies and accessing non-existent values at compile time. Use upsert() to safely update existing values when needed.

const container = createContainer()
  .add({ 
    config: { apiUrl: 'https://api.example.com' }
  });

// ❌ Error: Unsafe overwrite. Use `upsert` instead
container.add({ 
  config: { timeout: 5000 }
});

// ❌ Error: Property `missing` does not exist
container.add((ctx) => ({
  service: () => ctx.missing 
}));

// ✅ Valid
container
  .upsert({ config: { apiUrl: 'https://new-api.com' } })
  .add((ctx) => ({
    newService: () => ctx.config.apiUrl
  }));

3️⃣ Dependency Resolution

Dependencies are lazily evaluated, ensuring they always reflect the current state when accessed through context.

const userContainer = createContainer()
    .add({
        name: 'John'
    });

const greetContainer = createContainer(userContainer)
    .add((ctx) => ({
        greet: () => `Hello, ${ctx.name}!`
    }))
    .add((ctx) => ({
        formal: () => `${ctx.greet} How are you?`
    }));

const janeContainer = greetContainer.upsert({
    name: 'Jane'
});

// greet, formal are now automatically updated
janeContainer.items.name;   // 'Jane'
janeContainer.items.greet;  // 'Hello, Jane!'
janeContainer.items.formal; // 'Hello, Jane! How are you?'

4️⃣ Container Operations

Compose containers using various operations to manage dependencies effectively:

  • addContainer(): Import all dependencies from another container
  • upsertContainer(): Override existing dependencies from another container
  • addTokens(): Import specific dependencies
  • upsertTokens(): Override specific dependencies
// Base container with configuration
const baseContainer = createContainer()
  .add({ config: { apiUrl: 'https://api.example.com' } });

// Add all dependencies
const extendedContainer = createContainer()
  .addContainer(baseContainer)
  .add({ additionalConfig: { timeout: 5000 } });

// Override existing dependencies
const updatedContainer = createContainer()
  .add({ config: { apiUrl: 'https://new-api.com' } })
  .upsertContainer(baseContainer)

// Import specific dependencies
const specificContainer = createContainer()
  .addTokens(baseContainer, 'config')

// Override specific dependencies
const specificUpdatedContainer = createContainer()
  .add({ config: { apiUrl: 'https://new-api.com' } })
  .upsertTokens(baseContainer, 'config');

5️⃣ Complex scenarios with testing

Create test environments by overriding production dependencies with mocks using upsert.

interface IUserRepository {
  getUser(id: number): Promise<string>;
}

class UserRepository implements IUserRepository {
  async getUser(id: number): Promise<string> {
    return `User ${id} from Database`;
  }
}

class UserService {
  constructor(private userRepository: IUserRepository) {
  }

  async printName(id: number) {
    console.log(await this.userRepository.getUser(id));
  }
}

// Production container with real implementation
const prodContainer = createContainer()
  .add({
    userRepository: (): IUserRepository => new UserRepository(),
  })
  .add(ctx => ({
    userService: new UserService(ctx.userRepository),
  }));

// Test container with mock implementation
const testContainer = prodContainer
  .upsert({
    userRepository: (): IUserRepository => ({
      getUser: async () => 'Mock User',
    }),
  });

await prodContainer.get('userService').printName(1); // User 1 from Database
await testContainer.get('userService').printName(1); // Mock User

💬 Support

📝 License

MIT © PeraSite