@objectstack/core 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/ENHANCED_FEATURES.md +380 -0
- package/README.md +299 -12
- package/dist/contracts/data-engine.d.ts +39 -22
- package/dist/contracts/data-engine.d.ts.map +1 -1
- package/dist/contracts/logger.d.ts +63 -0
- package/dist/contracts/logger.d.ts.map +1 -0
- package/dist/contracts/logger.js +1 -0
- package/dist/enhanced-kernel.d.ts +103 -0
- package/dist/enhanced-kernel.d.ts.map +1 -0
- package/dist/enhanced-kernel.js +403 -0
- package/dist/enhanced-kernel.test.d.ts +2 -0
- package/dist/enhanced-kernel.test.d.ts.map +1 -0
- package/dist/enhanced-kernel.test.js +412 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -2
- package/dist/kernel-base.d.ts +84 -0
- package/dist/kernel-base.d.ts.map +1 -0
- package/dist/kernel-base.js +219 -0
- package/dist/kernel.d.ts +11 -18
- package/dist/kernel.d.ts.map +1 -1
- package/dist/kernel.js +43 -114
- package/dist/kernel.test.d.ts +2 -0
- package/dist/kernel.test.d.ts.map +1 -0
- package/dist/kernel.test.js +161 -0
- package/dist/logger.d.ts +70 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +268 -0
- package/dist/logger.test.d.ts +2 -0
- package/dist/logger.test.d.ts.map +1 -0
- package/dist/logger.test.js +92 -0
- package/dist/plugin-loader.d.ts +148 -0
- package/dist/plugin-loader.d.ts.map +1 -0
- package/dist/plugin-loader.js +287 -0
- package/dist/plugin-loader.test.d.ts +2 -0
- package/dist/plugin-loader.test.d.ts.map +1 -0
- package/dist/plugin-loader.test.js +339 -0
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/examples/enhanced-kernel-example.ts +309 -0
- package/package.json +19 -4
- package/src/contracts/data-engine.ts +46 -24
- package/src/contracts/logger.ts +70 -0
- package/src/enhanced-kernel.test.ts +535 -0
- package/src/enhanced-kernel.ts +496 -0
- package/src/index.ts +23 -2
- package/src/kernel-base.ts +256 -0
- package/src/kernel.test.ts +200 -0
- package/src/kernel.ts +55 -129
- package/src/logger.test.ts +116 -0
- package/src/logger.ts +306 -0
- package/src/plugin-loader.test.ts +412 -0
- package/src/plugin-loader.ts +435 -0
- package/src/types.ts +2 -1
- package/vitest.config.ts +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# Enhanced ObjectKernel - Advanced Plugin Features
|
|
2
|
+
|
|
3
|
+
This document describes the enhanced features added to ObjectKernel for production-grade plugin management.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `EnhancedObjectKernel` extends the basic `ObjectKernel` with enterprise-grade features for plugin lifecycle management, dependency injection, and operational resilience.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
### 1. Enhanced Plugin Loading
|
|
12
|
+
|
|
13
|
+
Async plugin loading with comprehensive validation:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { EnhancedObjectKernel, PluginMetadata } from '@objectstack/core';
|
|
17
|
+
|
|
18
|
+
const kernel = new EnhancedObjectKernel({
|
|
19
|
+
logger: { level: 'info' },
|
|
20
|
+
defaultStartupTimeout: 30000, // 30 seconds
|
|
21
|
+
gracefulShutdown: true,
|
|
22
|
+
shutdownTimeout: 60000, // 60 seconds
|
|
23
|
+
rollbackOnFailure: true, // Rollback on startup failure
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Plugin with version and timeout
|
|
27
|
+
const myPlugin: PluginMetadata = {
|
|
28
|
+
name: 'my-plugin',
|
|
29
|
+
version: '1.2.3', // Semantic version required
|
|
30
|
+
startupTimeout: 10000, // Override default timeout
|
|
31
|
+
|
|
32
|
+
async init(ctx) {
|
|
33
|
+
// Register services
|
|
34
|
+
ctx.registerService('my-service', serviceInstance);
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
async start(ctx) {
|
|
38
|
+
// Start business logic
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
async destroy() {
|
|
42
|
+
// Cleanup resources
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
// Optional: Health check
|
|
46
|
+
async healthCheck() {
|
|
47
|
+
return {
|
|
48
|
+
healthy: true,
|
|
49
|
+
message: 'Service is running',
|
|
50
|
+
details: { connections: 10 }
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
await kernel.use(myPlugin);
|
|
56
|
+
await kernel.bootstrap();
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 2. Advanced Dependency Injection
|
|
60
|
+
|
|
61
|
+
Factory-based service registration with lifecycle management:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { ServiceLifecycle } from '@objectstack/core';
|
|
65
|
+
|
|
66
|
+
// Singleton: Created once, shared across all requests
|
|
67
|
+
kernel.registerServiceFactory(
|
|
68
|
+
'database',
|
|
69
|
+
async (ctx) => {
|
|
70
|
+
const db = await connectToDatabase();
|
|
71
|
+
return db;
|
|
72
|
+
},
|
|
73
|
+
ServiceLifecycle.SINGLETON
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Transient: New instance on every request
|
|
77
|
+
kernel.registerServiceFactory(
|
|
78
|
+
'request-id',
|
|
79
|
+
() => generateUUID(),
|
|
80
|
+
ServiceLifecycle.TRANSIENT
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Scoped: One instance per scope (e.g., per HTTP request)
|
|
84
|
+
kernel.registerServiceFactory(
|
|
85
|
+
'user-session',
|
|
86
|
+
async (ctx) => {
|
|
87
|
+
return new UserSession();
|
|
88
|
+
},
|
|
89
|
+
ServiceLifecycle.SCOPED
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// Get service (async)
|
|
93
|
+
const db = await kernel.getServiceAsync('database');
|
|
94
|
+
|
|
95
|
+
// Get scoped service
|
|
96
|
+
const session = await kernel.getServiceAsync('user-session', 'request-123');
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 3. Service Dependencies
|
|
100
|
+
|
|
101
|
+
Declare service dependencies for proper initialization order:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
kernel.registerServiceFactory(
|
|
105
|
+
'api-client',
|
|
106
|
+
async (ctx) => {
|
|
107
|
+
const auth = await ctx.getService('auth-service');
|
|
108
|
+
return new ApiClient(auth);
|
|
109
|
+
},
|
|
110
|
+
ServiceLifecycle.SINGLETON,
|
|
111
|
+
['auth-service'] // Dependencies
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// Detect circular dependencies
|
|
115
|
+
const cycles = kernel['pluginLoader'].detectCircularDependencies();
|
|
116
|
+
if (cycles.length > 0) {
|
|
117
|
+
console.error('Circular dependencies detected:', cycles);
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### 4. Plugin Timeout Control
|
|
122
|
+
|
|
123
|
+
Prevent plugins from hanging during startup:
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
const plugin: PluginMetadata = {
|
|
127
|
+
name: 'slow-plugin',
|
|
128
|
+
version: '1.0.0',
|
|
129
|
+
startupTimeout: 5000, // 5 second timeout
|
|
130
|
+
|
|
131
|
+
async init(ctx) {
|
|
132
|
+
// If this takes longer than 5s, it will timeout
|
|
133
|
+
await slowInitialization();
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
await kernel.use(plugin);
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
await kernel.bootstrap();
|
|
141
|
+
} catch (error) {
|
|
142
|
+
// Error: Plugin slow-plugin init timeout after 5000ms
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 5. Startup Failure Rollback
|
|
147
|
+
|
|
148
|
+
Automatically rollback started plugins if any plugin fails:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
const plugin1: Plugin = {
|
|
152
|
+
name: 'plugin-1',
|
|
153
|
+
version: '1.0.0',
|
|
154
|
+
async init() {},
|
|
155
|
+
async start() {
|
|
156
|
+
// Starts successfully
|
|
157
|
+
},
|
|
158
|
+
async destroy() {
|
|
159
|
+
console.log('Rolling back plugin-1');
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const plugin2: Plugin = {
|
|
164
|
+
name: 'plugin-2',
|
|
165
|
+
version: '1.0.0',
|
|
166
|
+
async init() {},
|
|
167
|
+
async start() {
|
|
168
|
+
throw new Error('Startup failed!');
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
await kernel.use(plugin1);
|
|
173
|
+
await kernel.use(plugin2);
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
await kernel.bootstrap();
|
|
177
|
+
} catch (error) {
|
|
178
|
+
// plugin-1 will be automatically destroyed (rolled back)
|
|
179
|
+
// Error: Plugin plugin-2 failed to start - rollback complete
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### 6. Plugin Health Checks
|
|
184
|
+
|
|
185
|
+
Monitor plugin health at runtime:
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
const plugin: PluginMetadata = {
|
|
189
|
+
name: 'database-plugin',
|
|
190
|
+
version: '1.0.0',
|
|
191
|
+
|
|
192
|
+
async init(ctx) {
|
|
193
|
+
// Initialize database connection
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
async healthCheck() {
|
|
197
|
+
const isConnected = await checkDatabaseConnection();
|
|
198
|
+
return {
|
|
199
|
+
healthy: isConnected,
|
|
200
|
+
message: isConnected ? 'Connected' : 'Disconnected',
|
|
201
|
+
details: {
|
|
202
|
+
connections: 10,
|
|
203
|
+
responseTime: 50
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
await kernel.use(plugin);
|
|
210
|
+
await kernel.bootstrap();
|
|
211
|
+
|
|
212
|
+
// Check individual plugin health
|
|
213
|
+
const health = await kernel.checkPluginHealth('database-plugin');
|
|
214
|
+
console.log(health);
|
|
215
|
+
// { healthy: true, message: 'Connected', details: {...}, lastCheck: Date }
|
|
216
|
+
|
|
217
|
+
// Check all plugins health
|
|
218
|
+
const allHealth = await kernel.checkAllPluginsHealth();
|
|
219
|
+
for (const [pluginName, health] of allHealth) {
|
|
220
|
+
console.log(`${pluginName}: ${health.healthy ? '✅' : '❌'}`);
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### 7. Performance Metrics
|
|
225
|
+
|
|
226
|
+
Track plugin startup times:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
await kernel.bootstrap();
|
|
230
|
+
|
|
231
|
+
const metrics = kernel.getPluginMetrics();
|
|
232
|
+
for (const [pluginName, startTime] of metrics) {
|
|
233
|
+
console.log(`${pluginName}: ${startTime}ms`);
|
|
234
|
+
}
|
|
235
|
+
// plugin-1: 150ms
|
|
236
|
+
// plugin-2: 320ms
|
|
237
|
+
// plugin-3: 45ms
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### 8. Graceful Shutdown
|
|
241
|
+
|
|
242
|
+
Properly cleanup resources on shutdown:
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
const kernel = new EnhancedObjectKernel({
|
|
246
|
+
gracefulShutdown: true,
|
|
247
|
+
shutdownTimeout: 60000 // 60 second timeout
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// Register custom shutdown handler
|
|
251
|
+
kernel.onShutdown(async () => {
|
|
252
|
+
console.log('Closing database connections...');
|
|
253
|
+
await db.close();
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Graceful shutdown
|
|
257
|
+
process.on('SIGTERM', async () => {
|
|
258
|
+
await kernel.shutdown();
|
|
259
|
+
process.exit(0);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Manual shutdown
|
|
263
|
+
await kernel.shutdown();
|
|
264
|
+
// Triggers:
|
|
265
|
+
// 1. kernel:shutdown hook
|
|
266
|
+
// 2. Plugin destroy() in reverse order
|
|
267
|
+
// 3. Custom shutdown handlers
|
|
268
|
+
// 4. Logger cleanup
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### 9. Version Compatibility
|
|
272
|
+
|
|
273
|
+
Plugins must use semantic versioning:
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
// Valid versions
|
|
277
|
+
'1.0.0'
|
|
278
|
+
'2.3.4'
|
|
279
|
+
'1.0.0-alpha.1'
|
|
280
|
+
'1.0.0+20230101'
|
|
281
|
+
|
|
282
|
+
// Invalid versions (will be rejected)
|
|
283
|
+
'1.0'
|
|
284
|
+
'v1.0.0'
|
|
285
|
+
'latest'
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### 10. Plugin Configuration Validation
|
|
289
|
+
|
|
290
|
+
Use Zod schemas to validate plugin configuration:
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
import { z } from 'zod';
|
|
294
|
+
|
|
295
|
+
const MyPluginConfigSchema = z.object({
|
|
296
|
+
apiKey: z.string(),
|
|
297
|
+
timeout: z.number().min(1000).max(30000),
|
|
298
|
+
retries: z.number().int().min(0).default(3)
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const plugin: PluginMetadata = {
|
|
302
|
+
name: 'my-plugin',
|
|
303
|
+
version: '1.0.0',
|
|
304
|
+
configSchema: MyPluginConfigSchema,
|
|
305
|
+
|
|
306
|
+
async init(ctx) {
|
|
307
|
+
// Config is validated before init is called
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Migration from ObjectKernel
|
|
313
|
+
|
|
314
|
+
To migrate from `ObjectKernel` to `EnhancedObjectKernel`:
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
// Before
|
|
318
|
+
import { ObjectKernel } from '@objectstack/core';
|
|
319
|
+
const kernel = new ObjectKernel();
|
|
320
|
+
|
|
321
|
+
// After
|
|
322
|
+
import { EnhancedObjectKernel } from '@objectstack/core';
|
|
323
|
+
const kernel = new EnhancedObjectKernel({
|
|
324
|
+
logger: { level: 'info' },
|
|
325
|
+
gracefulShutdown: true,
|
|
326
|
+
rollbackOnFailure: true
|
|
327
|
+
});
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Both kernels are compatible - `EnhancedObjectKernel` is a superset of `ObjectKernel`.
|
|
331
|
+
|
|
332
|
+
## Best Practices
|
|
333
|
+
|
|
334
|
+
1. **Always set timeouts**: Configure `startupTimeout` to prevent hanging plugins
|
|
335
|
+
2. **Implement health checks**: Monitor plugin health at runtime
|
|
336
|
+
3. **Use semantic versioning**: Ensures compatibility and proper dependency resolution
|
|
337
|
+
4. **Enable rollback**: Set `rollbackOnFailure: true` to prevent partial startup states
|
|
338
|
+
5. **Handle shutdown**: Implement `destroy()` to cleanup resources properly
|
|
339
|
+
6. **Monitor metrics**: Track startup times to identify slow plugins
|
|
340
|
+
7. **Use service factories**: Prefer factories over static instances for better control
|
|
341
|
+
8. **Declare dependencies**: Use the dependencies array for proper initialization order
|
|
342
|
+
|
|
343
|
+
## API Reference
|
|
344
|
+
|
|
345
|
+
### EnhancedObjectKernel
|
|
346
|
+
|
|
347
|
+
- `constructor(config: EnhancedKernelConfig)`
|
|
348
|
+
- `async use(plugin: Plugin): Promise<this>`
|
|
349
|
+
- `registerServiceFactory<T>(name, factory, lifecycle, dependencies?): this`
|
|
350
|
+
- `async bootstrap(): Promise<void>`
|
|
351
|
+
- `async shutdown(): Promise<void>`
|
|
352
|
+
- `async checkPluginHealth(pluginName: string): Promise<PluginHealthStatus>`
|
|
353
|
+
- `async checkAllPluginsHealth(): Promise<Map<string, PluginHealthStatus>>`
|
|
354
|
+
- `getPluginMetrics(): Map<string, number>`
|
|
355
|
+
- `async getServiceAsync<T>(name: string, scopeId?: string): Promise<T>`
|
|
356
|
+
- `onShutdown(handler: () => Promise<void>): void`
|
|
357
|
+
- `getState(): string`
|
|
358
|
+
- `isRunning(): boolean`
|
|
359
|
+
|
|
360
|
+
### ServiceLifecycle
|
|
361
|
+
|
|
362
|
+
- `SINGLETON`: Single instance shared across all requests
|
|
363
|
+
- `TRANSIENT`: New instance created for each request
|
|
364
|
+
- `SCOPED`: New instance per scope (e.g., per HTTP request)
|
|
365
|
+
|
|
366
|
+
### PluginMetadata
|
|
367
|
+
|
|
368
|
+
Extended `Plugin` interface with:
|
|
369
|
+
- `version: string` - Semantic version
|
|
370
|
+
- `configSchema?: z.ZodSchema` - Configuration schema
|
|
371
|
+
- `signature?: string` - Plugin signature for verification
|
|
372
|
+
- `healthCheck?(): Promise<PluginHealthStatus>` - Health check function
|
|
373
|
+
- `startupTimeout?: number` - Startup timeout in milliseconds
|
|
374
|
+
- `hotReloadable?: boolean` - Whether plugin supports hot reload
|
|
375
|
+
|
|
376
|
+
## Examples
|
|
377
|
+
|
|
378
|
+
See the test files for comprehensive examples:
|
|
379
|
+
- `packages/core/src/enhanced-kernel.test.ts`
|
|
380
|
+
- `packages/core/src/plugin-loader.test.ts`
|