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

monopole

v0.4.1

Published

A modern, module-first DI container for TypeScript/JavaScript. Monopole supports async resolution, TC39 Stage 3 decorator property injection, and comprehensive lifecycle management for flexible, production-ready apps.

Readme

monopole

A powerful and flexible dependency injection container for TypeScript/JavaScript applications. Monopole provides a modern, module-based approach to dependency injection with support for async resolution, property injection using TC39 Stage 3 decorators, and comprehensive lifecycle management.

Features

  • Module-based architecture - Organize dependencies with modules that support imports/exports
  • Multiple provider types - Class, value, factory, and existing providers
  • Property injection - Using TC39 Stage 3 decorators (@inject)
  • Async resolution - Full support for async providers and initialization
  • Circular dependency support - Automatic resolution of circular dependencies
  • Lifecycle management - Module boot and dispose hooks
  • TypeScript first - Full TypeScript support with type inference
  • Framework agnostic - Works with Deno, Node.js, and browsers (Node/Bun/browsers require a build step; see below)

Runtime compatibility & build requirements

Monopole relies on the TC39 Stage 3 decorators proposal. Today, only Deno (v1.40+)/Deno Deploy ship this syntax natively.

| Runtime | Native Stage 3 decorators | What you need | | --------------------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | Deno | ✅ (TS/TSX/JSX) | Works out of the box. Make sure you run deno test/deno run directly against the source files. | | Node.js / Bun | ❌ (syntax error or legacy decorators) | Compile with TypeScript 5+ or Babel before running. The emitted JS no longer contains raw @decorator syntax. | | Browsers / Edge Functions / Workers | ❌ | Bundle/transpile with your existing toolchain (Vite, Webpack, Rollup, etc.) so the shipped JS is decorator-free. |

Using TypeScript

TypeScript 5.0 implements the new decorators proposal and accepts the syntax without --experimentalDecorators. A minimal tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "experimentalDecorators": false,
    "emitDecoratorMetadata": false,
    "moduleResolution": "bundler"
  }
}

Compile your sources (tsc -p tsconfig.json or via ts-node --transpile-only) and run the generated JS with Node/Bun.

Using Babel (via Vite/Webpack/Rollup)

If you stay in JavaScript, enable the official Stage 3 transform:

{
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "version": "2023-11" }],
    "@babel/plugin-transform-class-properties"
  ]
}

Ensure your bundler (Vite, Next.js, etc.) runs Babel on Monopole-using files so the output no longer contains raw decorators before it reaches browsers/Node runtimes.

Installation

Deno

deno add @denostack/monopole
import { createContainer } from "@denostack/monopole";

Node.js & Browser (after transpiling)

npm install monopole
import { createContainer } from "monopole";

ℹ️ Remember: runtimes other than Deno must load the transpiled output from the "Runtime compatibility" section above. Install the package, run it through TypeScript/Babel in your build, and execute/bundle the generated JavaScript.

Quick Start

import { createContainer, inject, type Module } from "monopole";

// Define services
class Logger {
  log(message: string) {
    console.log(`[LOG] ${message}`);
  }
}

class UserService {
  @inject(Logger)
  logger!: Logger;

  getUser(id: string) {
    this.logger.log(`Fetching user ${id}`);
    return { id, name: "John Doe" };
  }
}

// Create a module
const appModule: Module = {
  providers: [
    Logger,
    UserService,
  ],
  exports: [UserService],
};

// Create and use container
const container = await createContainer(appModule);
const userService = container.get(UserService);
userService.getUser("123");

Core Concepts

Modules

Modules are the building blocks of your application. They encapsulate providers and can import other modules to compose your dependency graph.

import type { Container, Module } from "monopole";

const databaseModule: Module = {
  providers: [
    { id: "dbConfig", useValue: { host: "localhost", port: 5432 } },
    {
      id: DatabaseConnection,
      useFactory: async (config) => {
        const conn = new DatabaseConnection(config);
        await conn.connect();
        return conn;
      },
      inject: ["dbConfig"],
    },
  ],
  exports: [DatabaseConnection],
  async dispose(container: Container) {
    const conn = container.get(DatabaseConnection);
    await conn.disconnect();
  },
};

const appModule: Module = {
  imports: [databaseModule],
  providers: [UserRepository],
  exports: [UserRepository],
};

Providers

Monopole supports four types of providers:

Class Provider

// Direct class registration
providers: [MyService];

// With explicit ID
providers: [{
  id: "myService",
  useClass: MyService,
}];

Value Provider

providers: [
  { id: "apiUrl", useValue: "https://api.example.com" },
  { id: "config", useValue: Promise.resolve({ key: "value" }) },
];

Factory Provider

providers: [{
  id: HttpClient,
  useFactory: (apiUrl: string) => new HttpClient(apiUrl),
  inject: ["apiUrl"],
}];

Existing Provider (Alias)

providers: [
  { id: Logger, useClass: ConsoleLogger },
  { id: "logger", useExisting: Logger },
];

Property Injection

Use the @inject decorator with TC39 Stage 3 decorator syntax:

import { inject } from "monopole";

class OrderService {
  @inject(Logger)
  logger!: Logger;

  @inject(DatabaseConnection)
  db!: DatabaseConnection;

  @inject("config")
  config!: Config;

  // With transformation
  @inject(UserService, (service) => service.getUser.bind(service))
  getUser!: (id: string) => User;
}

Optional Dependencies

Factory providers can specify optional dependencies:

providers: [{
  id: Service,
  useFactory: (required, optional) => {
    return new Service(required, optional ?? defaultValue);
  },
  inject: [
    RequiredDep,
    [OptionalDep, true], // true marks it as optional
  ],
}];

Advanced Usage

Circular Dependencies

Monopole automatically handles circular dependencies:

class Parent {
  @inject(Child)
  child!: Child;
}

class Child {
  @inject(Parent)
  parent!: Parent;
}

const module: Module = {
  providers: [Parent, Child],
  exports: [Parent, Child],
};

const container = await createContainer(module);
const parent = container.get(Parent);
const child = container.get(Child);

console.log(parent.child === child); // true
console.log(child.parent === parent); // true

Module Composition

Compose complex applications from smaller modules:

// Feature modules
const authModule: Module = {
  providers: [AuthService, JwtService],
  exports: [AuthService],
};

const dataModule: Module = {
  providers: [Database, UserRepository],
  exports: [UserRepository],
};

// Application module
const appModule: Module = {
  imports: [authModule, dataModule],
  providers: [
    {
      id: AppService,
      useFactory: (auth, repo) => new AppService(auth, repo),
      inject: [AuthService, UserRepository],
    },
  ],
  exports: [AppService],
  async boot(container) {
    // Initialize application
    const app = container.get(AppService);
    await app.initialize();
  },
  async dispose(container) {
    // Cleanup
    const app = container.get(AppService);
    await app.shutdown();
  },
};

// Create application
const container = await createContainer(appModule);
const app = container.get(AppService);

Async Disposal

Containers support the async disposal pattern:

// Using async disposal
await using container = await createContainer(appModule);
// Container will be automatically disposed when going out of scope

// Manual disposal
const container = await createContainer(appModule);
try {
  // Use container
} finally {
  await container.dispose();
}

Examples

API Reference

createContainer(module: Module): Promise<Container>

Creates a new container from a module definition.

Container

  • get<T>(id: ServiceIdentifier<T>): T - Get a resolved instance
  • has(id: ServiceIdentifier): boolean - Check if a service exists
  • entries(): IterableIterator<[ServiceIdentifier, unknown]> - Get all entries
  • dispose(): Promise<void> - Dispose the container and all modules

Module

  • imports?: Module[] - Modules to import
  • providers?: Provider[] - Service providers
  • exports?: ServiceIdentifier[] - Exported service identifiers
  • boot?(container: Container): MaybePromise<void> - Initialization hook
  • dispose?(container: Container): MaybePromise<void> - Cleanup hook

@inject(id: ServiceIdentifier, transformer?: (instance: T) => any)

Property decorator for dependency injection.