A lightweight, decorator-based lifecycle and dependency injection framework for TypeScript applications.
# Using npm
npm install app-leaf
# Using Bun
bun add app-leafWhen using controllers, always use regular imports rather than type-only imports:
import type { SomeController } from "./SomeController"; // BAD: Will cause errors during dependency injection
import { SomeController } from "./SomeController"; // GOOD: Ensures proper controller registrationType-only imports prevent the controller class from being registered properly in the dependency injection system.
- Decorator-based controller registration
- Controlled lifecycle with initialization and start hooks
- Automatic dependency injection
- Module system for organizing controllers
- Prevention of cyclic dependencies
- Load order control
import { Controller, OnInit, OnStart, AppLeaf } from "app-leaf";
@Controller()
class HelloController {
@OnInit()
private init() {
console.log("HelloController initialized");
}
@OnStart()
private start() {
console.log("HelloController started");
}
sayHello() {
return "Hello, world!";
}
}
@Controller()
class AppController {
constructor(private readonly helloController: HelloController) {}
@OnStart()
private start() {
console.log(this.helloController.sayHello());
}
}
// Start the application lifecycle
AppLeaf.Start();import { Controller, Module, AppLeaf } from "app-leaf";
@Controller()
class UserController {
getUsers() {
return ["User1", "User2"];
}
}
@Controller()
class ProductController {
getProducts() {
return ["Product1", "Product2"];
}
}
@Module([UserController, ProductController])
class FeatureModule {}
// To ensure all controllers are imported
AppLeaf.LoadModules([FeatureModule]);
AppLeaf.Start();@Controller({ loadOrder: 1 })
class FirstController {}
@Controller({ loadOrder: 2 })
class SecondController {}import { Controller, OnStart, Dependency, AppLeaf } from "app-leaf";
@Controller()
class ServiceController {
getData() {
return "Service Data";
}
}
@Controller()
class ConsumerController {
@OnStart()
private start() {
// Get controller reference after initialization
const service = Dependency(ServiceController);
console.log(service.getData());
}
}
AppLeaf.Start();Registers a class as a controller in the application lifecycle.
loadOrder: Optional number that determines initialization order (lower values initialize first)
Marks a method to be called during the initialization phase, before dependency injection.
Marks a method to be called after all controllers are initialized and injected.
Registers a class as a module containing a list of controllers.
Gets an instance of a registered controller after initialization.
- Should primarily be used within
OnStartmethods or afterAppLeaf.Start() - Throws error if controller is not registered or not loaded yet
Ensures that all controllers in the provided modules are imported.
Starts the application lifecycle:
- Initializes all controllers in order of their
loadOrder - Resolves dependencies between controllers
- Calls
OnInitmethods - Calls all
OnStartmethods in parallel
- If a controller fails in constructor - app stops
- If a controller fails in OnInit - app stops
- If a controller fails in OnStart - it logs a warning but the app continues running
- Cyclic dependencies are detected and prevented