iocello
v2.0.0
Published
Inversion of Control
Downloads
242
Maintainers
Readme
iocello
Lightweight IoC / DI container with decorator-based service registration, lazy loading, domains, and graph introspection.
Requirements
- TypeScript 5+ (standard decorator pipeline).
corello1.x.@abraham/reflection.
TypeScript Config (Stage 3 decorators)
Use the standard decorator pipeline. Do not enable legacy decorator mode.
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"useDefineForClassFields": true,
"moduleResolution": "bundler",
"isolatedModules": true,
"strict": true
}
}Important:
- Keep
experimentalDecoratorsunset/false (legacy mode). - Keep
emitDecoratorMetadataunset/false unless your own app explicitly needs it.
Quick Start
import { ioc, Service, Inject, Singleton } from 'iocello'
@Service({ tag: 'Pet' })
class Pet {
constructor(public name = 'Milo') {}
dispose() {}
}
@Singleton
@Service({ tag: 'Person' })
class Person {
@Inject({ tag: 'Pet' }) pet!: Pet
dispose() {}
}
ioc.add(Pet)
ioc.add(Person)
const person = await ioc.construct<Person>('Person')
console.log(person.pet.name) // MiloCore Concepts
- tag: unique service identifier (example:
Person). - domain: hierarchical scope (
0is base). Lookup falls back downward (current -> ... -> 0). - app context: optional namespace for independent registries.
- lazy service: service loaded only when first needed.
API Guide
Registration
ioc.add(ServiceClass)- Registers an eagerly-available class decorated with
@Service.
- Registers an eagerly-available class decorated with
ioc.addLazy(meta, loader)- Registers a lazy service.
meta:{ tag, domain?, declaredDeps?, enforce?, ctorArgs? }loader: async function returning module that exports the class (default export recommended).
Construction
await ioc.construct<T>(tag, domain?, ...ctorArgs)- Main async resolver.
- Loads lazy services on demand.
- Applies runtime ctor args for this construction call.
ioc.tryConstructSync<T>(tag, domain?, ...ctorArgs)- Sync fast path.
- Returns instance when class is already available synchronously, otherwise
undefined.
Graph / Preload / Manifest
await ioc.buildGraph({ tag, domain?, ctorArgs?, enforce? })- Returns the transitive dependency tree from a root service.
- Includes resolved domain for every edge.
- Marks cycles via
cycle: true. - Useful for diagnostics and dependency visualization.
await ioc.bootstrap<T>({ tag, domain?, ctorArgs?, enforce? })- Builds dependency graph, then warms/constructs reachable nodes.
- Finally returns the requested root instance (
constructresult). - Useful to avoid runtime lazy waterfalls before route/app startup.
await ioc.manifest(tag, domain?)- Returns only direct dependencies (shallow list).
- Fast for prefetch hints, CI checks, and bundle planning.
Context and Domain Controls
ioc.instance.setContainerDomain(domain)- Sets active default domain for lookups.
ioc.instance.setAppContext(appName)- Switches active app context namespace.
Decorators
@Service({ tag, domain?, enforce?, ctorArgs? })- Marks class as IoC service and stores metadata.
@Inject({ tag?, domain?, ctorArgs? }, executor?)- Property injection entry.
- If
tagis omitted, it falls back to property name. - Custom
executorcan override how dependency is resolved.
@Singleton- Ensures a single class instance per decorated class constructor.
Notes and Best Practices
- Register all core services before first
construct/bootstrapcall. - With
@Inject, passtagexplicitly when property name differs from service tag. - Use
manifestfor fast direct deps,buildGraphfor full dependency analysis. - In frontend apps (React/Vue), perform registration during app initialization.
