Dependency Injection
HeronJS 4.0.0 uses application-local containers and registries. Use the container and registry on the application returned by HeronJS.create(). Decorators record metadata; explicitly declare application components in modules.
import 'reflect-metadata';
import {Inject, injectable, Module} from '@heronjs/common';
import {Rest, Get} from '@heronjs/express';
@injectable()
class Greeting {
message() {
return 'Hello Heron';
}
}
@Rest('/hello')
class HelloController {
constructor(@Inject(Greeting) private greeting: Greeting) {}
@Get({uri: '/'})
hello() {
return {message: this.greeting.message()};
}
}
@Module({providers: [Greeting], controllers: [HelloController]})
export class AppModule {}Use imports to compose modules and declare export visibility explicitly. Module traversal uses constructor identity and handles cycles and repeated imports. Importing a decorator does not register global runtime state.
Typed tokens
Constructors and Token<T> preserve the relationship between registration and resolution. String and symbol lookups return unknown and require validation.
import {Token, createContainer} from '@heronjs/common';
interface Clock {
now(): number;
}
const clock = new Token<Clock>('clock');
const container = createContainer();
container.registerInstance(clock, {now: () => Date.now()});
const timestamp: number = container.resolve(clock).now();
await container.dispose();registerInstance leaves ownership with the caller. Use registerOwnedInstance or onDispose when the container should clean up a resource. Disposal is asynchronous and idempotent and attempts all owned cleanup. Factory-created resources need explicit cleanup registration.
Use the exported GATEKEEPER and GATEKEEPER_RESOLVER token identities for authentication dependencies. Class and method guards both apply; asynchronous requirements and predicates are awaited.
See migration for removed globals, typed metadata and renamed contracts, and worker lifetimes for worker-local containers and job scopes.