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

context-di

v1.0.1

Published

The Proxy context container that resolves dependencies and exposes them as context properties

Readme

context-di

A TypeScript library that creates lazy-loaded dependency injection containers using Proxy with circular dependency detection.

Purpose

The context-di library provides a type-safe way to create dependency injection containers where dependencies are resolved lazily and only when accessed. It automatically detects circular dependencies and provides clear error messages, making it ideal for code splitting scenarios where you want to defer expensive operations until they're actually needed.

Contract-Driven Development with IApiContract

The main reason for this library is to enable contract-driven development through the IApiContract pattern. This approach allows you to define a shared interface between components and providers, enabling true separation of concerns:

  • Components know about the runtime instance and expected API shape without needing the provider
  • Providers know about the component's expected results but nothing about the implementation details

Contract-Driven vs Provider-Driven Approaches

import createContextContainer from 'context-di';

// Define your API contract - this can be shared between components and providers
interface IApiContract {
  main: {
    status: string;
  };
}

// Contract-driven approach: Type-safe with compile-time guarantees
const contractContext = createContextContainer<IApiContract>({
  main() {
    return { status: 'OK' };
  }
});

// TypeScript knows exactly what's available
console.log(contractContext.main.status); // ✅ Type-safe access
// contractContext.settings; // ❌ TypeScript error - property doesn't exist

// Provider-driven approach: Flexible but less constrained
const providerContext = createContextContainer({
  main() {
    return { status: 'OK' };
  }
});

// Still type-safe, but based on implementation rather than contract
console.log(providerContext.main.status); // ✅ Works, but less explicit about API boundaries

Benefits of Contract-Driven Development

  1. API Boundaries: Clear separation between what components expect and how providers implement
  2. Type Safety: Compile-time guarantees that implementations match contracts
  3. Refactoring Safety: Changes to contracts are caught at compile time across all consumers
  4. Documentation: The contract serves as living documentation of your API
  5. Testing: Easy to mock and test components independently of providers

Features

  • Lazy Loading: Dependencies are only resolved when first accessed
  • Circular Dependency Detection: Automatically detects and prevents circular dependencies with detailed error messages
  • Type Safety: Full TypeScript support with generic types
  • Lightweight: Uses native JavaScript Proxy with minimal overhead

Installation

npm install context-di
# or
bun install context-di

Simple Usage Example

import createContextProxy from 'context-di';

// Define your context interface
interface AppContext {
  database: Promise<Database>;
  userService: Promise<UserService>;
  logger: Logger;
}

// Create the context proxy with providers
const context = createContextProxy<AppContext>({
  database: () => connectToDatabase(),
  userService: async (ctx) => new UserService(await ctx.database),
  logger: () => new Logger()
});

// Access dependencies lazily - they're only created when needed
const userService = await context.userService;
const user = await userService.getUser('123');

Redux Thunk Integration Example

The library works seamlessly with Redux Thunk for dependency injection in async actions:

import { configureStore, createAsyncThunk, createReducer } from '@reduxjs/toolkit';
import createContextProxy from 'context-di';

// Define your services context
interface ServicesContext {
  userService: Promise<{
    getUser(token: string): Promise<string | undefined>;
  }>;
}

// Create the context proxy
const extraArgument = createContextProxy<ServicesContext>({
  userService: () => Promise.resolve({
    getUser: (token: string) => Promise.resolve(token === 'abc' ? 'Dummy User' : undefined)
  })
});

// Configure Redux store with the context as extra argument
const store = configureStore({
  reducer: createReducer({}, () => void 0),
  middleware: (getDefaultMiddleware) => getDefaultMiddleware({
    thunk: {
      extraArgument
    }
  })
});

// Create typed async thunk
const createAppAsyncThunk = createAsyncThunk.withTypes<{
  state: ReturnType<typeof store.getState>;
  dispatch: typeof store.dispatch;
  extra: ServicesContext;
}>();

// Use in async actions
const loginUser = createAppAsyncThunk('auth/login',
  async (token: string, thunkAPI) => {
    const userService = await thunkAPI.extra.userService;
    return userService.getUser(token);
  }
);

// Dispatch the action
store.dispatch(loginUser('abc'));

Error Handling

The library provides clear error messages for common issues:

// Missing provider
const emptyContext = createContextProxy({});
emptyContext.missingService; // Error: Missing provider for property: missingService

// Circular dependency
const circularContext = createContextProxy({
  serviceA: (ctx) => ctx.serviceB,
  serviceB: (ctx) => ctx.serviceA
});
circularContext.serviceA; // Error: Circular dependency detected while resolving property "serviceA"
                          // Cause: Dependency chain: serviceA -> serviceB -> serviceA

Development

To install dependencies:

bun install

To run tests:

bun test

To build:

bun run build

License

MIT