Ultra-lightweight plugin orchestration with exceptional developer experience
Features β’ Quick Start β’ Documentation β’ Examples β’ API Reference
Zern Kernel is a next-generation plugin system designed for exceptional developer experience. It provides a minimal, type-safe core that enables plugins to work naturally like independent libraries, with automatic dependency resolution, transparent API augmentation, and powerful method interception.
- π― Natural API Design - Plugins feel like native libraries, not framework components
- π Complete Type Safety - Full TypeScript support with autocomplete everywhere
- π Zero Boilerplate - Fluent API eliminates ceremonial code
- π Intelligent Resolution - Automatic dependency ordering with version validation
- β‘ Runtime Flexibility - Extend, intercept, and modify plugin behavior dynamically
| Feature | Description |
|---|---|
| πͺΆ Minimal Core | Only essential functionality - register, initialize, shutdown |
| π Fluent API | Clean, chainable interface for plugin and kernel configuration |
| π€ Auto Dependency Resolution | Topological sorting with intelligent cycle detection |
| π§ API Extensions | Plugins can seamlessly extend other plugins' APIs |
| π Method Proxying | Intercept and modify behavior with before/after/around hooks |
| β±οΈ Lifecycle Hooks | onInit, onReady, onShutdown, onError for resource management |
| ποΈ Reactive Store | Automatic reactive state with watchers, computed values, and transactions |
| π·οΈ Complete Plugin Access | kernel.get() returns API + $meta + $store for full plugin inspection |
| π¦ Direct Exports | Import plugin methods directly like a normal library |
| π‘οΈ Error Handling | Hierarchical typed errors with stack traces, solutions, and severities |
| π Version Control | Semantic versioning with flexible constraint matching |
- β 4 Proxy Modes: Self-proxy, single plugin, dependencies, and global
- β
Kernel-Level Proxies: Application-level interception via
createKernel().proxy() - β Type-Safe Context: Plugin dependencies and metadata are fully typed
- β Priority-Based Execution: Control proxy execution order with priorities
- β Conditional Proxies: Apply interceptors based on runtime conditions
- β Method Selectors: Fine-grained control with include/exclude patterns
npm install @zern/kernelimport { createKernel, plugin } from '@zern/kernel';
// 1οΈβ£ Create a database plugin
const databasePlugin = plugin('database', '1.0.0')
.metadata({
author: 'Zern Team',
category: 'data',
})
.setup(() => ({
async connect(url: string) {
console.log(`Connected to: ${url}`);
return { connected: true };
},
users: {
async create(userData: { name: string; email: string }) {
const id = Math.random().toString(36).slice(2);
console.log(`User created: ${id}`);
return { id, ...userData };
},
},
}));
// 2οΈβ£ Create auth plugin with dependency
const authPlugin = plugin('auth', '1.0.0')
.depends(databasePlugin, '^1.0.0')
.onInit(({ plugins }) => {
console.log('Auth initializing...');
console.log('Database author:', plugins.database.$meta.author);
})
.setup(({ plugins }) => ({
async validateToken(token: string) {
console.log(`Validating token: ${token}`);
return token === 'valid-token';
},
}));
// 3οΈβ£ Initialize kernel and use plugins
const kernel = await createKernel().use(databasePlugin).use(authPlugin).start();
// β
Type-safe plugin access with $meta and $store
const db = kernel.get('database');
await db.connect('postgresql://localhost:5432/mydb');
// Access metadata
console.log(db.$meta.author); // "Zern Team"
console.log(db.$meta.category); // "data"
const user = await db.users.create({
name: 'John Doe',
email: 'john@example.com',
});
const auth = kernel.get('auth');
const isValid = await auth.validateToken('valid-token');
// Cleanup
await kernel.shutdown();const mathPlugin = plugin('math', '1.0.0')
.metadata({ author: 'Zern Team' })
.setup(() => ({
add: (a: number, b: number) => a + b,
multiply: (a: number, b: number) => a * b,
}));const calculatorPlugin = plugin('calculator', '1.0.0')
.depends(mathPlugin, '^1.0.0') // Semantic versioning
.setup(({ plugins }) => ({
calculate: (expr: string) => {
// Access math plugin with full type safety
return plugins.math.add(1, 2);
},
}));Create automatically reactive type-safe state accessible across all plugin stages:
const databasePlugin = plugin('database', '1.0.0')
.store(() => ({
connection: null as Connection | null,
queryCount: 0,
startTime: Date.now(),
}))
.onInit(async ({ store }) => {
// Initialize connection in store
store.connection = await createConnection();
// π₯ Watch for changes automatically
store.watch('queryCount', change => {
console.log(`Queries: ${change.oldValue} β ${change.newValue}`);
});
})
.proxy({
include: ['*'],
before: ctx => {
// Track queries in store (triggers watchers automatically)
ctx.store.queryCount++;
},
})
.setup(({ store }) => ({
query: async (sql: string) => {
// Access store in methods
if (!store.connection) throw new Error('Not connected');
return await store.connection.execute(sql);
},
getStats: () => ({
queries: store.queryCount,
uptime: Date.now() - store.startTime,
}),
}))
.onReady(({ store, api }) => {
// Access both store and api in hooks
console.log(`Database ready. Stats:`, api.getStats());
});Reactive Features:
- β Automatic reactivity - No manual setup required
- β
Watch changes -
watch(),watchAll(),watchBatch() - β
Computed values - Memoized derived state with
computed() - β
Batch updates - Group changes with
batch() - β Transactions - Atomic updates with automatic rollback
- β Performance - ~10x faster with optimized cloning and indexed watchers
- β Type inference - No generics needed, full autocomplete
- β Isolated - Each plugin has its own store
π See Store System for complete documentation
Extend another plugin's API transparently:
const advancedMathPlugin = plugin('advancedMath', '1.0.0')
.depends(mathPlugin, '^1.0.0')
.extend(mathPlugin, api => ({
// Add new methods to math plugin
power: (base: number, exp: number) => Math.pow(base, exp),
sqrt: (x: number) => Math.sqrt(x),
}))
.setup(() => ({}));
// After kernel initialization:
const math = kernel.get('math');
math.power(2, 3); // β
Extended method available!
math.sqrt(16); // β
All extensions are mergedIntercept and modify plugin behavior:
const loggingPlugin = plugin('logging', '1.0.0')
.depends(mathPlugin, '^1.0.0')
.proxy(mathPlugin, {
include: ['add'], // Intercept specific method
before: ctx => {
console.log(`[LOG] Calling ${ctx.method} with:`, ctx.args);
},
after: (result, ctx) => {
console.log(`[LOG] ${ctx.method} returned:`, result);
return result;
},
})
.setup(() => ({}));Proxy Modes:
// 1. Self-proxy: Intercept own methods
.proxy({ include: ['add'], before: ctx => console.log('self') })
// 2. Single plugin proxy: Intercept specific plugin
.depends(mathPlugin, '^1.0.0')
.proxy(mathPlugin, { before: ctx => console.log('single') })
// 3. Dependencies proxy: Intercept all dependencies
.proxy('*', { before: ctx => console.log('all deps') })
// 4. Global proxy: Intercept ALL plugins
.proxy('**', { before: ctx => console.log('global') })Apply proxies at the application level:
const kernel = await createKernel()
.use(mathPlugin)
.use(apiPlugin)
// Global logging for all plugins
.proxy('**', {
priority: 80,
before: ctx => {
console.log(`[LOG] ${ctx.plugin}.${ctx.method}() called`);
},
})
// Specific auth for API plugin
.proxy(apiPlugin, {
priority: 100,
include: ['create*', 'update*', 'delete*'],
before: ctx => checkPermissions(ctx.method),
})
.start();Manage plugin initialization, readiness, and cleanup:
const databasePlugin = plugin('database', '1.0.0')
.metadata({ connectionString: 'postgresql://localhost:5432/db' })
.onInit(({ plugins }) => {
console.log('Initializing database...');
})
.onReady(({ plugins }) => {
console.log('Database ready!');
})
.onShutdown(({ plugins }) => {
console.log('Closing connections...');
})
.onError(({ error, plugins }) => {
console.error('Database error:', error);
})
.setup(() => ({
query: async (sql: string) => {
/* ... */
},
}));kernel.get() returns complete plugin access - API methods, metadata, and reactive store:
const mathPlugin = plugin('math', '1.0.0')
.metadata({
author: 'Zern Team',
category: 'utilities',
precision: 'high',
})
.store(() => ({
operationCount: 0,
lastResult: 0,
}))
.setup(({ store }) => ({
add: (a: number, b: number) => {
store.operationCount++;
store.lastResult = a + b;
return a + b;
},
}));
const kernel = await createKernel().use(mathPlugin).start();
const math = kernel.get('math');
// β
Use API methods
const result = math.add(10, 5); // 15
// β
Access metadata (read-only)
console.log(math.$meta.name); // "math"
console.log(math.$meta.version); // "1.0.0"
console.log(math.$meta.author); // "Zern Team"
console.log(math.$meta.precision); // "high"
// β
Access and modify reactive store
console.log(math.$store.operationCount); // 1
console.log(math.$store.lastResult); // 15
// β
Watch store changes
math.$store.watch('operationCount', change => {
console.log(`Operations: ${change.oldValue} β ${change.newValue}`);
});
// β
Use all Store methods
math.$store.batch(() => {
math.$store.operationCount++;
math.$store.lastResult = 100;
});
const doubled = math.$store.computed(s => s.operationCount * 2);
console.log(doubled.value); // 4
// Next operation triggers watcher
math.add(20, 30); // Logs: "Operations: 2 β 3"Available on every plugin:
| Property | Description | Example |
|---|---|---|
| API methods | All plugin methods with full type safety | math.add(1, 2) |
$meta.name |
Plugin name | "math" |
$meta.version |
Plugin version | "1.0.0" |
$meta.* |
Custom metadata | math.$meta.author |
$store.* |
Reactive state properties | math.$store.count |
$store.watch() |
Watch specific property | math.$store.watch('count', fn) |
$store.watchAll() |
Watch all changes | math.$store.watchAll(fn) |
$store.batch() |
Batch multiple updates | math.$store.batch(() => {...}) |
$store.computed() |
Create computed values | math.$store.computed(s => s.x * 2) |
$store.transaction() |
Atomic updates with rollback | await math.$store.transaction(async () => {...}) |
π See Kernel Layer and Store System for complete documentation
Use plugins like normal libraries:
// In your plugin file:
export const mathPlugin = plugin('math', '1.0.0').setup(() => ({
add: (a: number, b: number) => a + b,
multiply: (a: number, b: number) => a * b,
}));
// Export direct methods
export const { add, multiply } = createDirectExports('math', {
add: (a: number, b: number): number => 0,
multiply: (a: number, b: number): number => 0,
});
// Usage in other files:
import { add, multiply } from './math-plugin';
console.log(add(2, 3)); // β
Full type safety!
console.log(multiply(4, 5)); // β
Autocomplete works!Professional error handling with typed errors, stack traces, and actionable solutions:
import { ValidationError, ErrorSeverity, solution } from '@zern/kernel';
const apiPlugin = plugin('api', '1.0.0')
.config({
errors: {
showStackTrace: true,
stackTraceLimit: 10,
formatErrors: true,
severity: ErrorSeverity.ERROR,
},
})
.onError(async ({ error, phase, method }) => {
// Global error handler for all plugin phases
console.error(`[${phase}] Error in ${method}:`, error);
// Send to monitoring service
if (error.severity === ErrorSeverity.FATAL) {
await alertTeam(error);
}
})
.setup(() => ({
async fetchUser(userId: string) {
if (!userId) {
throw new ValidationError(
{ userId },
{
severity: ErrorSeverity.ERROR,
solutions: [
solution(
'Provide a valid user ID',
'The userId parameter cannot be empty',
'api.fetchUser("user-123")'
),
],
}
);
}
return { id: userId, name: 'John' };
},
}));Error Features:
- β Typed Errors - Hierarchical error classes with full type safety
- β Stack Traces - Automatic parsing with file, line, and column
- β Solutions - Actionable suggestions for resolving errors
- β
Severities -
INFO,WARN,ERROR,FATALlevels - β Context - Rich metadata about where and why errors occurred
- β
Global Hooks - Capture errors from any phase (
init,setup,runtime,shutdown) - β Formatting - Beautiful console output with colors and structure
π See Error Handling for complete documentation
Comprehensive documentation is available in the docs/ directory:
| Document | Description |
|---|---|
| Architecture Overview | System design and layer architecture |
| Getting Started | Installation and first steps |
| Plugin System | Creating and managing plugins |
| Kernel Layer | Kernel initialization and lifecycle |
| Extension System | Extending plugin APIs |
| Direct Exports | Library-like method exports |
| Lifecycle Hooks | Plugin lifecycle management |
| Metadata System | Custom metadata with type safety |
| API Reference | Complete API documentation |
| Best Practices | Patterns and guidelines |
| Proxy System | Method interception and proxying |
| Store System | Reactive state with watchers, computed values, and transactions |
| Error Handling | Typed errors with stack traces, solutions, and severities |
Explore complete examples in the examples/ directory:
- Basic Usage - Plugin creation, dependencies, and kernel initialization
- Direct Usage - Direct method exports and library-like usage
- Store Demo - Comprehensive reactive store features (watch, computed, batch, transaction)
- Store Example - Store usage with lifecycle hooks and plugin integration
- Store Benchmark - Performance benchmarks (~10x faster with optimizations)
- Proxy Demo - Method interception with multiple proxies
- Proxy Complete Demo - All 4 proxy modes in action
- Kernel Proxy Demo - Kernel-level proxy examples
- Error Handling Demo - Comprehensive error handling with telemetry, retry, and error boundaries
- Simple Plugin - Minimalist plugin boilerplate
- Math Plugin - Opinionated, scalable plugin architecture
// Global logging
const loggingPlugin = plugin('logging', '1.0.0')
.proxy('**', {
before: ctx => console.log(`[LOG] ${ctx.plugin}.${ctx.method}()`),
})
.setup(() => ({}));
// Performance monitoring (using plugin store)
const timingPlugin = plugin('timing', '1.0.0')
.store(() => new Map<string, number>()) // Map to store start times by method
.proxy('**', {
before: ctx => {
const key = `${ctx.plugin}.${ctx.method}`;
ctx.store.set(key, Date.now());
},
after: (result, ctx) => {
const key = `${ctx.plugin}.${ctx.method}`;
const startTime = ctx.store.get(key);
if (startTime) {
console.log(`β±οΈ ${key} took ${Date.now() - startTime}ms`);
ctx.store.delete(key);
}
return result;
},
})
.setup(() => ({}));const authPlugin = plugin('auth', '1.0.0')
.depends(apiPlugin, '^1.0.0')
.proxy(apiPlugin, {
include: ['create*', 'update*', 'delete*'],
priority: 100, // Execute first
before: ctx => {
if (!isAuthenticated()) {
ctx.skip();
throw new Error('Unauthorized');
}
},
})
.setup(() => ({
/* ... */
}));const cachePlugin = plugin('cache', '1.0.0')
.depends(apiPlugin, '^1.0.0')
.proxy(apiPlugin, {
include: ['get*', 'find*'],
priority: 90,
around: async (ctx, next) => {
const key = `${ctx.method}:${JSON.stringify(ctx.args)}`;
const cached = cache.get(key);
if (cached) return cached;
const result = await next();
cache.set(key, result);
return result;
},
})
.setup(() => ({}));See examples/plugins/ for complete implementations:
// Telemetry: Track all errors across plugins
const telemetryPlugin = plugin('telemetry', '1.0.0')
.proxy('**', {
onError: async (error, ctx) => {
// Capture and send to monitoring service
console.log(`[TELEMETRY] Error in ${ctx.plugin}.${ctx.method}:`, error);
await sendToDatadog({ error, context: ctx });
throw error; // Re-throw to allow other handlers
},
})
.setup(() => ({
/* ... */
}));
// Retry: Automatic retry with exponential backoff
const retryPlugin = plugin('retry', '1.0.0')
.proxy('**', {
around: async (ctx, next) => {
const maxRetries = 3;
for (let i = 0; i < maxRetries; i++) {
try {
return await next();
} catch (error) {
if (i === maxRetries - 1) throw error;
await sleep(Math.pow(2, i) * 1000);
}
}
},
})
.setup(() => ({
/* ... */
}));
// Error Boundary: Graceful fallbacks
const errorBoundaryPlugin = plugin('errorBoundary', '1.0.0')
.proxy('**', {
onError: async (error, ctx) => {
console.error(`[BOUNDARY] Caught error in ${ctx.plugin}.${ctx.method}`);
return getFallbackValue(ctx.method); // Return fallback instead of throwing
},
})
.setup(() => ({
/* ... */
}));π‘ Pro Tip: These patterns demonstrate how Zern's proxy system enables powerful cross-cutting concerns without modifying plugin code!
const kernel = await createKernel()
.use(myPlugin)
.config({
autoGlobal: true, // Auto-register as global kernel
strictVersioning: true, // Enforce strict version matching
circularDependencies: false, // Disallow circular dependencies
initializationTimeout: 30000, // Timeout in milliseconds
extensionsEnabled: true, // Enable plugin extensions
logLevel: 'info', // Log level: debug | info | warn | error
errors: {
showStackTrace: true, // Show stack traces in errors
stackTraceLimit: 10, // Limit stack trace depth
formatErrors: true, // Format errors for console output
},
})
.start();const authPlugin = plugin('auth', '1.0.0')
.depends(databasePlugin, '^1.0.0') // Compatible with 1.x.x
.depends(cachePlugin, '>=2.0.0') // Requires 2.0.0 or higher
.depends(utilsPlugin, '~1.2.3') // Compatible with 1.2.x
.setup(({ plugins }) => ({
/* ... */
}));plugin(name: string, version: string)
.metadata(data: Record<string, unknown>)
.store(factory: () => state) // Reactive store (watch, computed, batch, transaction)
.config(options: Partial<PluginConfig>) // Plugin-level configuration (errors, etc)
.depends(plugin: BuiltPlugin, versionRange?: string)
.extend(target: BuiltPlugin, fn: (api) => extensions)
.proxy(config: ProxyConfig) // Self-proxy
.proxy(target: BuiltPlugin, config: ProxyConfig) // Single plugin
.proxy('*', config: ProxyConfig) // All dependencies
.proxy('**', config: ProxyConfig) // All plugins
.onInit(hook: (ctx) => void)
.onReady(hook: (ctx) => void)
.onShutdown(hook: (ctx) => void)
.onError(hook: (ctx) => void) // Captures errors from all phases
.setup(fn: (ctx) => api)createKernel()
.use(plugin: BuiltPlugin)
.config(config: Partial<KernelConfig>) // Kernel-level configuration
.proxy(target: BuiltPlugin, config: ProxyConfig)
.proxy('**', config: ProxyConfig)
.build()
.start()kernel.get(name: string) // Get plugin API + $meta + $store
kernel.shutdown() // Shutdown all pluginsWhat kernel.get() returns:
const plugin = kernel.get('myPlugin');
// β
API methods (fully typed)
plugin.myMethod();
// β
Metadata access
plugin.$meta.name; // Plugin name
plugin.$meta.version; // Plugin version
plugin.$meta.author; // Custom metadata
// β
Reactive store access
plugin.$store.count; // Read state
plugin.$store.count++; // Update state
plugin.$store.watch('count', change => {
console.log(`Changed: ${change.newValue}`);
});import { createKernel, plugin } from '@zern/kernel';
describe('MyPlugin', () => {
it('should work correctly', async () => {
const kernel = await createKernel().use(myPlugin).start();
const api = kernel.get('myPlugin');
expect(api.myMethod()).toBe('expected');
await kernel.shutdown();
});
});Contributions are welcome! Please read our contributing guidelines before submitting pull requests.
MIT Β© ZernJS
Made with β€οΈ by the Zern Team