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

@n2js/container

v0.1.0

Published

TypeScript-first module runtime and dependency injection container with scoped resolution and typed module features.

Readme

@n2js/container

简体中文 | English

@n2js/container 是一个 TypeScript-first 模块运行时与依赖注入容器。它的重点不只是实例化 class,而是把 Provider、模块可见性、request scope、只读 Definition、模块 Feature 和生命周期放进同一张类型安全的模块图里。适合在服务端、CLI、worker 或测试工具中独立组织复杂依赖。

当前包提供 ESM-only 构建产物,运行入口为 ./dist/index.js,类型声明入口为 ./dist/index.d.ts

与普通 DI 容器的不同

  • 模块图优先: 依赖不只是全局注册表,Provider 属于明确的模块,跨模块访问需要 exported()reexport
  • 类型优先: createToken()createMultiToken() 和 class token 都保留 TypeScript 推断,减少手写类型断言。
  • 声明与运行时分离: Definition 用于收集命令、schema、任务配置等只读元数据,不会混进运行时依赖解析。
  • Feature 可扩展: defineModuleFeature() 可以把只读 artifact 挂到模块实例上,让模块既能解析依赖,也能携带派生能力。
  • 作用域明确: singletontransientrequest 三种 scope 都是显式行为,request scope 需要主动创建和释放。
  • 运行环境中立: 不绑定 HTTP、CLI 或 worker,你可以把它用在任意需要模块化依赖组织的 TypeScript 项目里。

安装

npm install @n2js/container

也可以使用 Bun:

bun add @n2js/container

快速开始

import {
  Container,
  createToken,
  defineModule,
  provide,
} from '@n2js/container';

const appConfigToken = createToken<{ readonly region: string }>('app.config');

class UserService {
  getName(): string {
    return 'Ada';
  }
}

const AppModule = defineModule({
  displayName: 'AppModule',
  providers: [
    UserService,
    provide(appConfigToken).useValue({ region: 'cn' }).register(),
  ],
});

const container = new Container(AppModule);
const userService = await container.root.resolve(UserService);
const appConfig = await container.root.resolve(appConfigToken);

console.log(userService?.getName());
console.log(appConfig?.region);

能力概览

  • 使用 defineModule() 描述模块的 Provider、imports、features 和 lifecycle。
  • 使用 createToken()createMultiToken() 或 class token 解析运行时依赖。
  • 使用 provide() 注册 class、value、factory 或只读 Definition。
  • 支持 singletontransientrequest 三种 Provider scope。
  • 支持构造函数注入、属性注入、可选依赖和 CURRENT_MODULE_REF
  • 使用 defineModuleFeature() 为模块实例附加类型安全的只读 artifact。
  • 使用 container.init()container.dispose() 管理模块生命周期。
  • 使用 forwardRef() 延迟引用模块或 token,解决声明顺序问题。

模块与可见性

模块通过 imports 组合。use() 表示普通导入,mount() 表示带路径的导入,并会把路径记录到模块快照中。

默认情况下,子模块 Provider 不会自动暴露给父模块。需要向上暴露时,可以在 Provider 上调用 exported(),或在导入关系上声明 reexport

import {
  Container,
  createToken,
  defineModule,
  mount,
  provide,
  use,
} from '@n2js/container';

const sharedToken = createToken<string>('shared');

const SharedModule = defineModule({
  displayName: 'SharedModule',
  providers: [provide(sharedToken).useValue('shared').exported()],
});

const FeatureModule = defineModule({
  displayName: 'FeatureModule',
  imports: [use(SharedModule, { reexport: true })],
});

const RootModule = defineModule({
  displayName: 'RootModule',
  imports: [mount('/feature', FeatureModule)],
});

const container = new Container(RootModule);

await container.root.resolve(sharedToken);
container.findMount('/feature');
container.snapshot();

Provider Scope

| Scope | 行为 | | --- | --- | | singleton | 默认 scope,同一个 Container runtime 内复用实例。 | | transient | 每次解析创建新实例。 | | request | 在同一个 request scope 内复用,不同 scope 之间隔离。 |

import {
  Container,
  createToken,
  defineModule,
  provide,
} from '@n2js/container';

const requestIdToken = createToken<string>('request.id');

class RequestService {
  static readonly inject = [requestIdToken] as const;

  constructor(readonly requestId: string) {}
}

const RequestModule = defineModule({
  displayName: 'RequestModule',
  providers: [
    provide(requestIdToken)
      .useFactory(() => crypto.randomUUID(), [], { scope: 'request' })
      .register(),
    provide(RequestService)
      .useClass(RequestService, { scope: 'request' })
      .register(),
  ],
});

const container = new Container(RequestModule);
const scope = container.createRequestScope();

await container.root.resolveWithScope(RequestService, scope);
scope.dispose();

注入装饰器

可以使用 @Injectable()@Inject()@Optional() 描述构造函数或属性注入。若使用属性默认类型推断,请确保运行环境已经配置 decorator metadata。

import {
  Container,
  Inject,
  Injectable,
  Optional,
  createToken,
  defineModule,
  provide,
} from '@n2js/container';

const nameToken = createToken<string>('name');
const missingToken = createToken<string>('missing');

@Injectable()
class GreetingService {
  constructor(
    @Inject(nameToken) readonly name: string,
    @Optional() @Inject(missingToken) readonly fallback: string | undefined,
  ) {}
}

const GreetingModule = defineModule({
  displayName: 'GreetingModule',
  providers: [
    provide(nameToken).useValue('Ada').register(),
    GreetingService,
  ],
});

const container = new Container(GreetingModule);
await container.root.resolve(GreetingService);

Definition 与 Feature

Definition 是启动期收集的只读声明,不参与运行时 resolve()。它适合保存命令、schema、任务配置或其它声明式元数据。

import {
  Container,
  createDefinitionToken,
  defineModule,
  provide,
} from '@n2js/container';

const commandToken = createDefinitionToken<{
  readonly name: string;
  readonly description: string;
}>('commands');

const CommandModule = defineModule({
  displayName: 'CommandModule',
  providers: [
    provide(commandToken)
      .useDefinition({ name: 'sync', description: 'Sync records' })
      .register(),
  ],
});

const container = new Container(CommandModule);
const commands = container.definitions.entries('commands');

defineModuleFeature() 可以把派生出的只读 artifact 挂到模块实例上,并保留精确类型。

import {
  Container,
  defineModule,
  defineModuleFeature,
} from '@n2js/container';

const configFeature = defineModuleFeature({
  name: 'config',
  create() {
    return {
      config: Object.freeze({
        region: 'cn',
      }),
    };
  },
});

const ConfigModule = defineModule({
  displayName: 'ConfigModule',
  features: [configFeature],
});

const container = new Container(ConfigModule);
container.root.config.region;

生命周期

container.init() 会按依赖优先顺序执行 onModuleInitcontainer.dispose() 会按反向顺序执行 onModuleDispose。重复调用会复用同一次状态,销毁失败会抛出 ContainerError

import { Container, defineModule } from '@n2js/container';

const AppModule = defineModule({
  displayName: 'LifecycleModule',
  lifecycle: {
    async onModuleInit() {
      await Promise.resolve();
    },
    async onModuleDispose() {
      await Promise.resolve();
    },
  },
});

const container = new Container(AppModule);

await container.init();
await container.dispose();

公共 API

| API | 说明 | | --- | --- | | Container | 创建模块 runtime,提供 rootselect()resolve()snapshot()init()dispose()。 | | defineModule() | 定义模块 token,描述 Provider、imports、features 和 lifecycle。 | | mount() | 按路径挂载模块,并保留 mount path 快照。 | | use() | 导入模块,不产生新的 path 段。 | | provide() | 创建 class、value、factory 或 definition provider。 | | createToken() | 创建单值运行时 token。 | | createMultiToken() | 创建多值运行时 token,解析结果为冻结数组。 | | createDefinitionToken() | 创建 Definition token,仅用于声明收集。 | | defineModuleFeature() | 定义模块 artifact 扩展。 | | forwardRef() | 延迟解析模块或 runtime token。 | | Injectable() | 标记 class 的默认 Provider 选项。 | | Inject() | 标记构造函数参数或属性注入 token。 | | Optional() | 标记依赖缺失时返回 undefined。 | | CURRENT_MODULE_REF | 内置 token,用于在 Provider 中注入当前模块引用。 | | ContainerError | 容器错误类型,带稳定错误码和结构化 details。 |

许可证

MIT