| name | reflect-metadata-injection |
| description | Runtime metadata reflection for TypeScript dependency injection. Decorator-based class metadata, design:type/paramtypes annotation, custom metadata keys, and DI container bootstrap. Sources: rbuckton/reflect-metadata (Apache-2.0). |
/reflect-metadata-injection
When to Use
- Build lightweight dependency injection containers for agent service registries
- Attach metadata to classes at decoration time for runtime introspection
- Auto-wire agent dependencies without explicit factory functions
- Implement decorators that record audit trails or capability declarations
Do NOT use for
- Simple config injection (use environment variables or [[cli-config-persistence]])
- Environments where
experimentalDecorators is not enabled
tsconfig requirements
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Core metadata API
import 'reflect-metadata'
Reflect.defineMetadata('role', 'executor', MyClass)
const role = Reflect.getMetadata('role', MyClass)
Reflect.hasMetadata('role', MyClass)
Reflect.getMetadataKeys(MyClass)
Reflect.deleteMetadata('role', MyClass)
Injectable decorator + DI container
import 'reflect-metadata'
const INJECTABLE_KEY = Symbol('injectable')
const registry = new Map<Function, unknown>()
function Injectable(): ClassDecorator {
return (target) => {
Reflect.defineMetadata(INJECTABLE_KEY, true, target)
}
}
function Inject(token: symbol): ParameterDecorator {
return (target, _key, index) => {
const tokens = Reflect.getMetadata('inject:tokens', target) ?? []
tokens[index] = token
Reflect.defineMetadata('inject:tokens', tokens, target)
}
}
function resolve<T>(cls: new (...args: any[]) => T): T {
if (!Reflect.getMetadata(, cls)) {
()
}
: [] = .(, cls) ?? []
args = tokens.( registry.(tok))
(...args)
}
= ()
registry.(, { : (: ) => [] })
()
{
() {}
() { ..() }
}
svc = ()
Read design:paramtypes (emitDecoratorMetadata)
@Injectable()
class OrderService {
constructor(
private db: DatabaseService,
private logger: LoggerService,
) {}
}
const types = Reflect.getMetadata('design:paramtypes', OrderService)
Audit capability decorator
function Capability(name: string): MethodDecorator {
return (target, key) => {
const caps: string[] = Reflect.getMetadata('capabilities', target) ?? []
caps.push(name)
Reflect.defineMetadata('capabilities', caps, target)
}
}
class SandboxAgent {
@Capability('file-read')
readFile(path: string) { }
@Capability('network-fetch')
fetch(url: string) { }
}
const caps = Reflect.getMetadata('capabilities', SandboxAgent.prototype)
Anti-Fake-Pass Checklist
❌ Missing `import 'reflect-metadata'` at entry point → Reflect.defineMetadata is undefined
❌ emitDecoratorMetadata: false → design:paramtypes always undefined, DI auto-wiring fails
❌ Metadata defined on instance vs prototype → Reflect.getMetadata(key, instance) ≠ Reflect.getMetadata(key, Class.prototype)
❌ Symbol tokens not shared between Inject() and registry → tokens don't match, injection returns undefined
❌ resolve() before registry.set() → dependency is undefined, constructor receives undefined silently
❌ Circular dependencies → resolve() hangs in infinite recursion without detection