This document describes the advanced features of ObjectKernel for production-grade plugin management.
The ObjectKernel (formerly EnhancedObjectKernel) provides enterprise-grade features for plugin lifecycle management, dependency injection, and operational resilience.
Async plugin loading with comprehensive validation:
import { ObjectKernel, PluginMetadata } from '@objectstack/core';
const kernel = new ObjectKernel({
logger: { level: 'info' },
defaultStartupTimeout: 30000, // 30 seconds
gracefulShutdown: true,
shutdownTimeout: 60000, // 60 seconds
rollbackOnFailure: true, // Rollback on startup failure
});
// Plugin with version and timeout
const myPlugin: PluginMetadata = {
name: 'my-plugin',
version: '1.2.3', // Semantic version required
startupTimeout: 10000, // Override default timeout
async init(ctx) {
// Register services
ctx.registerService('my-service', serviceInstance);
},
async start(ctx) {
// Start business logic
},
async destroy() {
// Cleanup resources
},
// Optional: Health check
async healthCheck() {
return {
healthy: true,
message: 'Service is running',
details: { connections: 10 }
};
}
};
await kernel.use(myPlugin);
await kernel.bootstrap();Factory-based service registration with lifecycle management:
import { ServiceLifecycle } from '@objectstack/core';
// Singleton: Created once, shared across all requests
kernel.registerServiceFactory(
'database',
async (ctx) => {
const db = await connectToDatabase();
return db;
},
ServiceLifecycle.SINGLETON
);
// Transient: New instance on every request
kernel.registerServiceFactory(
'request-id',
() => generateUUID(),
ServiceLifecycle.TRANSIENT
);
// Scoped: One instance per scope (e.g., per HTTP request)
kernel.registerServiceFactory(
'user-session',
async (ctx) => {
return new UserSession();
},
ServiceLifecycle.SCOPED
);
// Get service (async)
const db = await kernel.getServiceAsync('database');
// Get scoped service
const session = await kernel.getServiceAsync('user-session', 'request-123');Declare service dependencies for proper initialization order:
kernel.registerServiceFactory(
'api-client',
async (ctx) => {
const auth = await ctx.getService('auth-service');
return new ApiClient(auth);
},
ServiceLifecycle.SINGLETON,
['auth-service'] // Dependencies
);
// Detect circular dependencies
const cycles = kernel['pluginLoader'].detectCircularDependencies();
if (cycles.length > 0) {
console.error('Circular dependencies detected:', cycles);
}Prevent plugins from hanging during startup:
const plugin: PluginMetadata = {
name: 'slow-plugin',
version: '1.0.0',
startupTimeout: 5000, // 5 second timeout
async init(ctx) {
// If this takes longer than 5s, it will timeout
await slowInitialization();
}
};
await kernel.use(plugin);
try {
await kernel.bootstrap();
} catch (error) {
// Error: Plugin slow-plugin init timeout after 5000ms
}Automatically rollback started plugins if any plugin fails:
const plugin1: Plugin = {
name: 'plugin-1',
version: '1.0.0',
async init() {},
async start() {
// Starts successfully
},
async destroy() {
console.log('Rolling back plugin-1');
}
};
const plugin2: Plugin = {
name: 'plugin-2',
version: '1.0.0',
async init() {},
async start() {
throw new Error('Startup failed!');
}
};
await kernel.use(plugin1);
await kernel.use(plugin2);
try {
await kernel.bootstrap();
} catch (error) {
// plugin-1 will be automatically destroyed (rolled back)
// Error: Plugin plugin-2 failed to start - rollback complete
}Monitor plugin health at runtime:
const plugin: PluginMetadata = {
name: 'database-plugin',
version: '1.0.0',
async init(ctx) {
// Initialize database connection
},
async healthCheck() {
const isConnected = await checkDatabaseConnection();
return {
healthy: isConnected,
message: isConnected ? 'Connected' : 'Disconnected',
details: {
connections: 10,
responseTime: 50
}
};
}
};
await kernel.use(plugin);
await kernel.bootstrap();
// Check individual plugin health
const health = await kernel.checkPluginHealth('database-plugin');
console.log(health);
// { healthy: true, message: 'Connected', details: {...}, lastCheck: Date }
// Check all plugins health
const allHealth = await kernel.checkAllPluginsHealth();
for (const [pluginName, health] of allHealth) {
console.log(`${pluginName}: ${health.healthy ? '✅' : '❌'}`);
}Track plugin startup times:
await kernel.bootstrap();
const metrics = kernel.getPluginMetrics();
for (const [pluginName, startTime] of metrics) {
console.log(`${pluginName}: ${startTime}ms`);
}
// plugin-1: 150ms
// plugin-2: 320ms
// plugin-3: 45msProperly cleanup resources on shutdown:
const kernel = new ObjectKernel({
gracefulShutdown: true,
shutdownTimeout: 60000 // 60 second timeout
});
// Register custom shutdown handler
kernel.onShutdown(async () => {
console.log('Closing database connections...');
await db.close();
});
// Graceful shutdown
process.on('SIGTERM', async () => {
await kernel.shutdown();
process.exit(0);
});
// Manual shutdown
await kernel.shutdown();
// Triggers:
// 1. kernel:shutdown hook
// 2. Plugin destroy() in reverse order
// 3. Custom shutdown handlers
// 4. Logger cleanupPlugins must use semantic versioning:
// Valid versions
'1.0.0'
'2.3.4'
'1.0.0-alpha.1'
'1.0.0+20230101'
// Invalid versions (will be rejected)
'1.0'
'v1.0.0'
'latest'Use Zod schemas to validate plugin configuration:
import { z } from 'zod';
const MyPluginConfigSchema = z.object({
apiKey: z.string(),
timeout: z.number().min(1000).max(30000),
retries: z.number().int().min(0).default(3)
});
const plugin: PluginMetadata = {
name: 'my-plugin',
version: '1.0.0',
configSchema: MyPluginConfigSchema,
async init(ctx) {
// Config is validated before init is called
}
};To migrate from LiteKernel to ObjectKernel:
// Before
import { LiteKernel } from '@objectstack/core';
const kernel = new LiteKernel();
// After
import { ObjectKernel } from '@objectstack/core';
const kernel = new ObjectKernel({
logger: { level: 'info' },
gracefulShutdown: true,
rollbackOnFailure: true
});Both kernels adhere to the same Plugin interface, but ObjectKernel supports advanced features like Rollback, Sandbox, and Hot Reload.
- Always set timeouts: Configure
startupTimeoutto prevent hanging plugins - Implement health checks: Monitor plugin health at runtime
- Use semantic versioning: Ensures compatibility and proper dependency resolution
- Enable rollback: Set
rollbackOnFailure: trueto prevent partial startup states - Handle shutdown: Implement
destroy()to cleanup resources properly - Monitor metrics: Track startup times to identify slow plugins
- Use service factories: Prefer factories over static instances for better control
- Declare dependencies: Use the dependencies array for proper initialization order
constructor(config: EnhancedKernelConfig)async use(plugin: Plugin): Promise<this>registerServiceFactory<T>(name, factory, lifecycle, dependencies?): thisasync bootstrap(): Promise<void>async shutdown(): Promise<void>async checkPluginHealth(pluginName: string): Promise<PluginHealthStatus>async checkAllPluginsHealth(): Promise<Map<string, PluginHealthStatus>>getPluginMetrics(): Map<string, number>async getServiceAsync<T>(name: string, scopeId?: string): Promise<T>onShutdown(handler: () => Promise<void>): voidgetState(): stringisRunning(): boolean
SINGLETON: Single instance shared across all requestsTRANSIENT: New instance created for each requestSCOPED: New instance per scope (e.g., per HTTP request)
Extended Plugin interface with:
version: string- Semantic versionconfigSchema?: z.ZodSchema- Configuration schemasignature?: string- Plugin signature for verificationhealthCheck?(): Promise<PluginHealthStatus>- Health check functionstartupTimeout?: number- Startup timeout in millisecondshotReloadable?: boolean- Whether plugin supports hot reload
See the test files for comprehensive examples:
packages/core/src/enhanced-kernel.test.tspackages/core/src/plugin-loader.test.ts