Skip to content

caffeine-projects/dicaf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

430 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

DiCaf - Fast and Powerful Dependency Injection Container for JS/TS


A fast, feature-rich dependency injection container for JavaScript and TypeScript.
Works with ECMAScript decorators, legacy TypeScript decorators, or plain configuration — no decorators required.

dicaf.dev

Contributing Guidelines · Submit an Issue


CI codecov Node.js


Core Features

DiCaf is built for speed and developer experience — zero dependencies and written in vanilla TypeScript. The only exception is reflect-metadata, which is opt-in and only needed if you choose legacy TypeScript decorators over the standard ECMAScript ones.

  • Three usage modes — ECMAScript decorators, legacy experimentalDecorators, or fully programmatic.
  • Binding targets — classes, abstract classes, interfaces, functions, and arbitrary values.
  • Profiles — segregate components by environment or configuration profile.
  • Injection options — single, array, object, map, and optional injection descriptors.
  • Conditionals — conditionally register components based on runtime conditions.
  • Scopes — singleton, transient, request, container, and refreshable scopes.
  • Mixed scopes — mix different scopes in the same dependency graph without extra configuration.
  • Async factories — resolve dependencies that require asynchronous initialization.
  • Scan — automatically scan and register decorated types; no manual module configuration needed.
  • Testing utilities — first-class support for overrides and isolated containers in tests.
  • Extensible — custom scopes, injection resolvers, metadata readers, post-processors, and hooks.
  • ESM + CJS — dual-format package.
  • Runtime agnostic — works in Node.js, browser, Deno, and Bun.

Install

npm install @caffeine-projects/dicaf

For legacy TypeScript decorators, also install:

npm install reflect-metadata # only when using TypeScript legacy decorators

Getting Started

Three rules to keep in mind:

  1. Initialize before resolving. Call await container.init() before any get() call.
  2. Bind before initializing. The container is sealed after init() — registering new bindings afterwards is not allowed.
  3. Decorated files must be loaded first. Decorators are evaluated when the module is imported. If a file hasn't been imported at least once before init(), its components are invisible to the container. The Scan feature makes this task straightforward.
import { DiCaf } from '@caffeine-projects/dicaf'
import { Injectable } from '@caffeine-projects/dicaf/decorators'

@Injectable()
class GreetingService {
  greet(name: string) {
    return `Hello, ${name}!`
  }
}

const container = new DiCaf()
// All bindings must be registered before this line
await container.init()

// Safe to resolve now
const svc = container.get(GreetingService)
console.log(svc.greet('World')) // Hello, World!

Imports

The library is split into focused entry points ensuring that each surface stays compatible with its specific toolchain requirements.

Import path Description
@caffeine-projects/dicaf Core components. Start here.
@caffeine-projects/dicaf/decorators ECMAScript decorators (stage 3). No tsconfig changes or extra dependencies required.
@caffeine-projects/dicaf/decorators/legacy Legacy TypeScript decorators. Requires experimentalDecorators, emitDecoratorMetadata, and reflect-metadata.
@caffeine-projects/dicaf/testing Testing utilities.
@caffeine-projects/dicaf/internals Low-level decorator registrar. Intended for library/framework authors building on top of DiCaf.

Examples

ECMAScript Decorators

No additional dependencies or tsconfig flags required.

import { DiCaf } from '@caffeine-projects/dicaf'
import { Injectable, Extends } from '@caffeine-projects/dicaf/decorators'

abstract class Notifier {
  abstract send(msg: string): void
}

@Injectable()
@Extends()
class EmailNotifier extends Notifier {
  send(msg: string) {
    console.log(`Email: ${msg}`)
  }
}

const kRepository = Symbol('app:repository')

interface Repository {
  save(item: string): void
}

@Injectable(kRepository)
class MySQLRepository implements Repository {
  save(): void { ... }
}

@Injectable([Notifier, kRepository])
class OrderService {
  constructor(readonly notifier: Notifier, readonly repository: Repository) {}

  place(item: string) {
    this.repository.save(item)
    this.notifier.send(`Order placed: ${item}`)
  }
}

const container = new DiCaf()
await container.init()

const orderService = container.get(OrderService)
orderService.place('Laptop')
// Email: Order placed: Laptop

Legacy TypeScript Decorators

Enable in tsconfig.json:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

Import reflect-metadata once at your entry point:

import 'reflect-metadata'
import { DiCaf } from '@caffeine-projects/dicaf'
import { Injectable, Extends } from '@caffeine-projects/dicaf/decorators/legacy'

abstract class Notifier {
  abstract send(msg: string): void
}

@Injectable()
@Extends()
class EmailNotifier extends Notifier {
  send(msg: string) {
    console.log(`Email: ${msg}`)
  }
}

const kRepository = Symbol('app:repository')

interface Repository {
  save(item: string): void
}

@Injectable(kRepository)
class MySQLRepository implements Repository {
  save(): void { ... }
}

@Injectable()
class OrderService {
  constructor(readonly notifier: Notifier, @Inject(kRepository) readonly repository: Repository) {}

  place(item: string) {
    this.repository.save(item)
    this.notifier.send(`Order placed: ${item}`)
  }
}

const container = new DiCaf()
await container.init()

const orderService = container.get(OrderService)
orderService.place('Laptop')
// Email: Order placed: Laptop

Plain Configuration

No decorators, no tsconfig changes.

import { DiCaf } from '@caffeine-projects/dicaf'

abstract class Notifier {
  abstract send(msg: string): void
}

class EmailNotifier extends Notifier {
  send(msg: string) {
    console.log(`Email: ${msg}`)
  }
}

class OrderService {
  constructor(readonly notifier: Notifier) {}

  place(item: string) {
    this.notifier.send(`Order placed: ${item}`)
  }
}

const container = new DiCaf()
container.bind(EmailNotifier).toSelf().extends()
container.bind(OrderService).toSelf([Notifier])

await container.init()

const orderService = container.get(OrderService)
orderService.place('Laptop')
// Email: Order placed: Laptop

Documentation

Full documentation is available in the docs/ directory and on the documentation site:

Benchmarks

Measured with mitata.

vs. Third-party Libraries

Resolution latency against a 7-class dependency graph.

Transient

Benchmark Latency
Raw (new) 9.64 ns
DiCaf (provider) 158.11 ns
DiCaf 165.17 ns
Awilix 481.83 ns
injection-js 569.86 ns
TSyringe 1.15 µs
Inversify 1.93 µs
TypeDI 2.28 µs
LoopBack 2.71 µs
NestJS 4.91 µs

Singleton

Benchmark Latency
DiCaf (provider) 16.37 ns
DiCaf 17.85 ns
Awilix 17.98 ns
TypeDI 27.23 ns
Awilix (cradle) 31.16 ns
injection-js 40.21 ns
TSyringe 68.56 ns
NestJS 81.12 ns
Inversify 101.72 ns
LoopBack 195.07 ns

Run the benchmarks locally with: npm run bench:compare.

License

This project is MIT licensed.


Credits

About

Fast and Feature Rich Dependency Injection Container for Javascript/Typescript

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Contributors