@abdokouta/react-application
v0.0.1
Published
Application kernel for orchestrating @abdokouta packages with boot lifecycle, dependency ordering, and cross-package communication
Maintainers
Readme
@abdokouta/application
Application kernel for orchestrating @abdokouta packages with boot lifecycle, dependency ordering, and cross-package communication.
Features
- 🚀 Fluent Builder API - Chain package registration with
.use() - 📦 Automatic Dependency Resolution - Topological sort ensures correct boot order
- 🔄 Lifecycle Hooks -
onBoot(),onReady(),onShutdown()for each package - 📡 Event System - Monitor boot progress with typed events
- ⚛️ React Integration - Provider and hooks for component access
- 💉 DI Container - Seamless integration with
@abdokouta/react-di
Installation
pnpm add @abdokouta/applicationQuick Start
1. Create and Boot the Application
// main.tsx
import "reflect-metadata";
import { Application, ApplicationProvider } from "@abdokouta/application";
import { AuthPackage } from "@abdokouta/auth";
import { RefinePackage } from "@abdokouta/refine";
// Create and boot BEFORE React renders
const app = Application.create({
name: "MNGO Platform",
logLevel: import.meta.env.DEV ? "debug" : "info",
})
.use(AuthPackage.forRoot({ session: { lockAfterSeconds: 900 } }))
.use(RefinePackage.forRoot({ dataProvider: myDataProvider }))
.boot();
// Wrap React with the provider
ReactDOM.createRoot(root).render(
<ApplicationProvider app={app}>
<BrowserRouter>
<App />
</BrowserRouter>
</ApplicationProvider>
);2. Access in Components
import { useApplication, usePackage, useBootPhase } from "@abdokouta/application";
import { useInject } from "@abdokouta/react-di";
function MyComponent() {
// Access the Application
const { app } = useApplication();
// Check boot phase
const phase = useBootPhase();
// Get package info
const { isBooted, version } = usePackage("@abdokouta/auth");
// Inject services (from @abdokouta/react-di)
const sessionService = useInject(SessionService);
return (
<div>
<p>Phase: {phase}</p>
<p>
Auth v{version}: {isBooted ? "Ready" : "Loading"}
</p>
</div>
);
}Creating a Package
Packages can participate in the Application lifecycle by implementing the IBootable interface:
// packages/my-package/src/my-package.package.ts
import {
Package,
IBootable,
IPackageRegistration,
Application,
} from "@abdokouta/application";
import { MyModule } from "./my.module";
@Package({
name: "@abdokouta/my-package",
version: "1.0.0",
dependencies: ["@abdokouta/react-di"],
})
export class MyPackage implements IBootable {
private options: MyOptions;
constructor(options: MyOptions = {}) {
this.options = options;
}
/**
* Factory method for registration
*/
static forRoot(options?: MyOptions): IPackageRegistration {
return {
manifest: {
name: "@abdokouta/my-package",
version: "1.0.0",
dependencies: ["@abdokouta/react-di"],
module: MyModule,
},
dynamicModule: MyModule.forRoot(options),
bootable: new MyPackage(options),
};
}
/**
* Called when the package is being initialized.
* Other packages may not be ready yet.
*/
onBoot(app: Application): void {
// Register built-in components
myRegistry.register("default", DefaultComponent);
console.log("[MyPackage] Booted");
}
/**
* Called after ALL packages have booted.
* Safe to resolve cross-package dependencies.
*/
onReady(app: Application): void {
// Access services from other packages
const authConfig = app.resolve(AUTH_MODULE_CONFIG);
console.log("[MyPackage] Ready");
}
/**
* Called when the application is shutting down.
*/
onShutdown(app: Application): void {
// Cleanup resources
console.log("[MyPackage] Shutdown");
}
}API Reference
Application
Application.create(options?)
Creates a new Application instance.
const app = Application.create({
name: "My App", // Display name for logging
version: "1.0.0", // App version
logLevel: "debug", // 'none' | 'info' | 'debug'
defaultScope: "Singleton", // DI scope
strict: true, // Throw on errors
bootTimeout: 30000, // Timeout for async operations
onError: (error, pkg) => {}, // Error handler
});.use(registration)
Register a package.
app.use(AuthPackage.forRoot({ ... }));.on(event, callback)
Subscribe to lifecycle events.
app.on("packageBooted", ({ manifest, duration }) => {
console.log(`${manifest.name} booted in ${duration}ms`);
});.boot()
Boot the application. Must be called before React renders.
app.boot();.resolve(identifier)
Resolve a service from the DI container.
const service = app.resolve(MyService);
const config = app.resolve(MY_CONFIG_TOKEN);.shutdown(reason?)
Shutdown the application gracefully.
await app.shutdown("User logged out");Hooks
useApplication()
Access the Application instance.
const { app } = useApplication();useBootPhase()
Get the current boot phase.
const phase = useBootPhase();
// BootPhase.READYusePackage(name)
Get information about a registered package.
const { isBooted, version, bootDuration } = usePackage("@abdokouta/auth");Events
| Event | Payload | Description |
| ------------------- | ------------------------------------------ | ----------------------------- |
| phaseChange | { from, to, timestamp } | Boot phase changed |
| packageRegistered | { manifest, totalRegistered } | Package registered |
| packageBooting | { manifest, order, total } | Package starting to boot |
| packageBooted | { manifest, duration } | Package finished booting |
| packageFailed | { manifest, error } | Package failed to boot |
| ready | { totalPackages, totalDuration } | All packages ready |
| shuttingDown | { reason? } | Shutdown started |
| terminated | { duration } | Shutdown complete |
| error | { error, phase, packageName? } | Error occurred |
Boot Phases
enum BootPhase {
IDLE = "idle", // Initial state
REGISTERING = "registering", // Packages being registered
RESOLVING = "resolving", // Dependency graph being resolved
BOOTING = "booting", // Packages executing onBoot()
READY = "ready", // All packages ready
SHUTTING_DOWN = "shutting_down", // Shutdown in progress
TERMINATED = "terminated", // Shutdown complete
}Dependency Resolution
The Application automatically resolves package dependencies using topological sort:
// These can be registered in any order
app
.use(AuthPackage.forRoot()) // depends on: ['@abdokouta/react-di']
.use(RefinePackage.forRoot()) // depends on: ['@abdokouta/react-di']
.use(TenantPackage.forRoot()) // depends on: ['@abdokouta/auth']
.boot();
// Boot order will be:
// 1. @abdokouta/react-di (no deps)
// 2. @abdokouta/auth (depends on react-di)
// 3. @abdokouta/refine (depends on react-di)
// 4. @abdokouta/tenant (depends on auth)Circular dependencies are detected and throw an error:
DependencyResolverError: Circular dependency detected: A → B → C → ALicense
MIT © Abdo Kouta
