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

@bosker-labs/di-assemble

v0.1.0

Published

Composable DI modules and providers with an Inversify-backed container; optional React bindings (@bosker/di-assemble/react).

Readme

di-assemble

Composable dependency injection for TypeScript apps: module-style @Module, explicit providers (useClass, useValue, useFactory, useExisting), and an Inversify container behind a small API. Optional React bindings live under @bosker/di-assemble/react.

Install

yarn add @bosker/di-assemble inversify reflect-metadata
# or: npm install @bosker/di-assemble inversify reflect-metadata

If you use createAppProvider / useInjection, also install react (peer dependency).

TypeScript

Enable legacy decorators for @Module, @Injectable, and @Inject (you do not need emitDecoratorMetadata if every injected constructor parameter has @Inject(token)):

{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}

If you prefer Inversify’s reflection-based parameter types, set emitDecoratorMetadata: true and ensure reflect-metadata is loaded (see Inversify docs). The supported path in this library is explicit @Inject(createInjectionToken(...)) on each parameter.

App entry

Import reflect-metadata once before any module that uses @Module or @Injectable / @Inject (e.g. your root index.js or layout). The library’s injection entry also imports it, but a single top-level import keeps load order predictable.

Package exports

| Import | Purpose | |--------|---------| | @bosker/di-assemble | Module, bootstrapApplication, Injectable, Inject, providers, createInjectionToken, … | | @bosker/di-assemble/react | createAppProvider, useInjection, useApplicationContext |

Quick start (core only)

import 'reflect-metadata';
import { Module, bootstrapApplication, createInjectionToken } from '@bosker/di-assemble';

const GREETING = createInjectionToken<string>('greeting');

@Module({
  providers: [{ provide: GREETING, useValue: 'hello' }],
  exports: [GREETING],
})
class AppModule {}

const app = bootstrapApplication({ modules: [AppModule] });
app.get(GREETING); // 'hello'

createInjectionToken<T>() keeps TypeScript inference for app.get(...) and useInjection(...).

Constructor injection (useClass)

useClass is bound with Inversify’s to(Class): the container constructs the class and resolves constructor dependencies.

  • Use @Injectable() on the class and @Inject(TOKEN) on each injected constructor parameter.
  • Zero-argument classes do not require @Injectable().
import 'reflect-metadata';
import {
  Inject,
  Injectable,
  Module,
  bootstrapApplication,
  createInjectionToken,
} from '@bosker/di-assemble';

const DEP = createInjectionToken<string>('dep');
const CONSUMER = createInjectionToken<{ msg: string }>('consumer');

@Injectable()
class ConsumerImpl {
  constructor(@Inject(DEP) readonly msg: string) {}
}

@Module({
  providers: [
    { provide: DEP, useValue: 'wired' },
    { provide: CONSUMER, useClass: ConsumerImpl },
  ],
  exports: [CONSUMER],
})
class AppModule {}

const app = bootstrapApplication({ modules: [AppModule] });
app.get(CONSUMER).msg; // 'wired'

React (optional)

import { Slot } from 'expo-router';
import { createAppProvider } from '@bosker/di-assemble/react';
import { AppModule } from './app.module';

const { AppProvider } = createAppProvider({ modules: [AppModule] });

export default function RootLayout() {
  return (
    <AppProvider>
      <Slot />
    </AppProvider>
  );
}
import { useInjection } from '@bosker/di-assemble/react';
import { FEATURE_TOKENS } from './feature.tokens';

// Type inferred when FEATURE_TOKENS.SomeUseCase is createInjectionToken<SomeUseCase>(...)
const useCase = useInjection(FEATURE_TOKENS.SomeUseCase);

Testing / storybook

Pass a pre-built context to avoid double bootstrap:

import { bootstrapApplication } from '@bosker/di-assemble';
import { createAppProvider } from '@bosker/di-assemble/react';

const app = bootstrapApplication({ modules: [AppModule], overrides: [...] });
const { AppProvider } = createAppProvider({ modules: [AppModule] });

<AppProvider app={app}>{children}</AppProvider>;

Providers

  • useClass — Inversify instantiates the class (see Constructor injection above). Default singleton; use scope: 'transient' for a new instance per get.
  • useValue — constant.
  • useFactory(injector) => T with access to injector.get(...).
  • useExisting — alias another token.

Module rules

  • imports — other module classes.
  • providers — bindings registered when the app boots (child-first order by the internal graph).
  • exports — tokens that must be provided locally or re-exported from an import; invalid exports throw at bootstrap.

CLI (module scaffold)

After install, di-assemble is on PATH via node_modules/.bin.

npx di-assemble generate module <kebab-name> [options]
# or: yarn di-assemble generate module billing

| Option | Default | Meaning | |--------|---------|--------| | --out-dir | ./src/features | Directory that will contain <kebab-name>/ | | --import-di | @bosker/di-assemble | Import path for Module, createInjectionToken, Inject, … | | --no-presentation | off | Skip presentation/hooks and hook export | | --force | off | Overwrite existing files | | --dry-run | off | Print paths only |

Generated layout matches the sample feature: services/ (request, response, contract, impl), models/ (presentation types + mapper stubs), usecases/, *.module.ts, *.tokens.ts, *.types.ts, index.ts. Replace throw new Error('… not implemented') stubs with real types and logic, then register \*Module in your app module imports.

Agent skills (skills CLI)

This package includes an Agent Skill at skills/di-assemble/SKILL.md (name: di-assemble). It is discoverable from the repo root via the open-ecosystem skills tool (vercel-labs/skills), which can install into Cursor, Claude Code, Codex, and many other agents.

After yarn add @bosker/di-assemble (or npm install @bosker/di-assemble):

# List the skill(s) shipped in the package
npx skills add ./node_modules/@bosker/di-assemble --list

# Install into Cursor (project scope; non-interactive)
npx skills add ./node_modules/@bosker/di-assemble --skill di-assemble -a cursor -y

# Or global install for your user
npx skills add ./node_modules/@bosker/di-assemble --skill di-assemble -a cursor -g -y

From a Git clone or monorepo path (this library or a fork):

npx skills add /path/to/di-assemble --skill di-assemble -a cursor -y

From GitHub (once published there):

npx skills add https://github.com/<org>/di-assemble --skill di-assemble -a cursor -y

The CLI symlinks or copies SKILL.md into each agent’s configured skills directory (for Cursor, typically .agents/skills/ in the project, or ~/.cursor/skills/ when using -g). You can still copy skills/di-assemble/ manually if you prefer not to use the CLI.

Monorepo / this repository

Library source lives under src/:

src/
  index.ts              # public entry (re-exports di)
  react.ts              # public entry for @bosker/di-assemble/react
  di/
    index.ts            # core + tokens barrel
    core/               # bootstrap, container, decorators, injection, …
    tokens/
  example/sample/       # optional reference layout (not published)
    services/           # *.request.ts, *.response.ts, service contract + impl
    models/             # presentation-oriented types + mappers (use case boundary)
    usecases/
    presentation/
  bin/                  # di-assemble CLI (published)
  skills/di-assemble/   # Agent SKILL.md (skills CLI + Cursor-compatible)

The published package includes dist/, bin/, skills/, README.md, LICENSE (run yarn build before publish).

Develop & test (contributors)

yarn install
yarn test        # Vitest
yarn build       # tsdown → dist/

Limits (today)

  • No request scope, async lifecycle hooks, or auto file scanning.
  • Circular module imports are rejected; circular service graphs depend on Inversify behavior.

License

MIT