@objectstack/core 0.9.1 → 1.0.0

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.
Files changed (94) hide show
  1. package/{ENHANCED_FEATURES.md → ADVANCED_FEATURES.md} +13 -13
  2. package/CHANGELOG.md +21 -0
  3. package/PHASE2_IMPLEMENTATION.md +388 -0
  4. package/README.md +12 -341
  5. package/REFACTORING_SUMMARY.md +40 -0
  6. package/dist/api-registry-plugin.test.js +23 -21
  7. package/dist/api-registry.test.js +2 -2
  8. package/dist/dependency-resolver.d.ts +62 -0
  9. package/dist/dependency-resolver.d.ts.map +1 -0
  10. package/dist/dependency-resolver.js +317 -0
  11. package/dist/dependency-resolver.test.d.ts +2 -0
  12. package/dist/dependency-resolver.test.d.ts.map +1 -0
  13. package/dist/dependency-resolver.test.js +241 -0
  14. package/dist/health-monitor.d.ts +65 -0
  15. package/dist/health-monitor.d.ts.map +1 -0
  16. package/dist/health-monitor.js +269 -0
  17. package/dist/health-monitor.test.d.ts +2 -0
  18. package/dist/health-monitor.test.d.ts.map +1 -0
  19. package/dist/health-monitor.test.js +68 -0
  20. package/dist/hot-reload.d.ts +79 -0
  21. package/dist/hot-reload.d.ts.map +1 -0
  22. package/dist/hot-reload.js +313 -0
  23. package/dist/index.d.ts +4 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +5 -1
  26. package/dist/kernel-base.d.ts +2 -2
  27. package/dist/kernel-base.js +2 -2
  28. package/dist/kernel.d.ts +89 -31
  29. package/dist/kernel.d.ts.map +1 -1
  30. package/dist/kernel.js +430 -73
  31. package/dist/kernel.test.js +375 -122
  32. package/dist/lite-kernel.d.ts +55 -0
  33. package/dist/lite-kernel.d.ts.map +1 -0
  34. package/dist/lite-kernel.js +112 -0
  35. package/dist/lite-kernel.test.d.ts +2 -0
  36. package/dist/lite-kernel.test.d.ts.map +1 -0
  37. package/dist/lite-kernel.test.js +161 -0
  38. package/dist/logger.d.ts +2 -2
  39. package/dist/logger.d.ts.map +1 -1
  40. package/dist/logger.js +26 -7
  41. package/dist/plugin-loader.d.ts +15 -0
  42. package/dist/plugin-loader.d.ts.map +1 -1
  43. package/dist/plugin-loader.js +40 -10
  44. package/dist/plugin-loader.test.js +9 -0
  45. package/dist/security/index.d.ts +3 -0
  46. package/dist/security/index.d.ts.map +1 -1
  47. package/dist/security/index.js +4 -0
  48. package/dist/security/permission-manager.d.ts +96 -0
  49. package/dist/security/permission-manager.d.ts.map +1 -0
  50. package/dist/security/permission-manager.js +235 -0
  51. package/dist/security/permission-manager.test.d.ts +2 -0
  52. package/dist/security/permission-manager.test.d.ts.map +1 -0
  53. package/dist/security/permission-manager.test.js +220 -0
  54. package/dist/security/plugin-permission-enforcer.d.ts +1 -1
  55. package/dist/security/sandbox-runtime.d.ts +115 -0
  56. package/dist/security/sandbox-runtime.d.ts.map +1 -0
  57. package/dist/security/sandbox-runtime.js +310 -0
  58. package/dist/security/security-scanner.d.ts +92 -0
  59. package/dist/security/security-scanner.d.ts.map +1 -0
  60. package/dist/security/security-scanner.js +273 -0
  61. package/examples/{enhanced-kernel-example.ts → kernel-features-example.ts} +6 -6
  62. package/examples/phase2-integration.ts +355 -0
  63. package/package.json +3 -2
  64. package/src/api-registry-plugin.test.ts +23 -21
  65. package/src/api-registry.test.ts +2 -2
  66. package/src/dependency-resolver.test.ts +287 -0
  67. package/src/dependency-resolver.ts +388 -0
  68. package/src/health-monitor.test.ts +81 -0
  69. package/src/health-monitor.ts +316 -0
  70. package/src/hot-reload.ts +388 -0
  71. package/src/index.ts +6 -1
  72. package/src/kernel-base.ts +2 -2
  73. package/src/kernel.test.ts +471 -134
  74. package/src/kernel.ts +518 -76
  75. package/src/lite-kernel.test.ts +200 -0
  76. package/src/lite-kernel.ts +135 -0
  77. package/src/logger.ts +28 -7
  78. package/src/plugin-loader.test.ts +10 -1
  79. package/src/plugin-loader.ts +49 -13
  80. package/src/security/index.ts +19 -0
  81. package/src/security/permission-manager.test.ts +256 -0
  82. package/src/security/permission-manager.ts +336 -0
  83. package/src/security/plugin-permission-enforcer.test.ts +1 -1
  84. package/src/security/plugin-permission-enforcer.ts +1 -1
  85. package/src/security/sandbox-runtime.ts +432 -0
  86. package/src/security/security-scanner.ts +365 -0
  87. package/dist/enhanced-kernel.d.ts +0 -103
  88. package/dist/enhanced-kernel.d.ts.map +0 -1
  89. package/dist/enhanced-kernel.js +0 -403
  90. package/dist/enhanced-kernel.test.d.ts +0 -2
  91. package/dist/enhanced-kernel.test.d.ts.map +0 -1
  92. package/dist/enhanced-kernel.test.js +0 -412
  93. package/src/enhanced-kernel.test.ts +0 -535
  94. package/src/enhanced-kernel.ts +0 -496
package/dist/kernel.js CHANGED
@@ -1,112 +1,469 @@
1
1
  import { createLogger } from './logger.js';
2
- import { ObjectKernelBase } from './kernel-base.js';
2
+ import { ServiceRequirementDef } from '@objectstack/spec/system';
3
+ import { PluginLoader, ServiceLifecycle } from './plugin-loader.js';
3
4
  /**
4
- * ObjectKernel - MiniKernel Architecture
5
+ * Enhanced ObjectKernel with Advanced Plugin Management
5
6
  *
6
- * A highly modular, plugin-based microkernel that:
7
- * - Manages plugin lifecycle (init, start, destroy)
8
- * - Provides dependency injection via service registry
9
- * - Implements event/hook system for inter-plugin communication
10
- * - Handles dependency resolution (topological sort)
11
- * - Provides configurable logging for server and browser
12
- *
13
- * Core philosophy:
14
- * - Business logic is completely separated into plugins
15
- * - Kernel only manages lifecycle, DI, and hooks
16
- * - Plugins are loaded as equal building blocks
7
+ * Extends the basic ObjectKernel with:
8
+ * - Async plugin loading with validation
9
+ * - Version compatibility checking
10
+ * - Plugin signature verification
11
+ * - Configuration validation (Zod)
12
+ * - Factory-based dependency injection
13
+ * - Service lifecycle management (singleton/transient/scoped)
14
+ * - Circular dependency detection
15
+ * - Lazy loading services
16
+ * - Graceful shutdown
17
+ * - Plugin startup timeout control
18
+ * - Startup failure rollback
19
+ * - Plugin health checks
17
20
  */
18
- export class ObjectKernel extends ObjectKernelBase {
19
- constructor(config) {
20
- const logger = createLogger(config?.logger);
21
- super(logger);
22
- // Initialize context after logger is created
23
- this.context = this.createContext();
21
+ export class ObjectKernel {
22
+ constructor(config = {}) {
23
+ this.plugins = new Map();
24
+ this.services = new Map();
25
+ this.hooks = new Map();
26
+ this.state = 'idle';
27
+ this.startedPlugins = new Set();
28
+ this.pluginStartTimes = new Map();
29
+ this.shutdownHandlers = [];
30
+ this.config = {
31
+ defaultStartupTimeout: 30000, // 30 seconds
32
+ gracefulShutdown: true,
33
+ shutdownTimeout: 60000, // 60 seconds
34
+ rollbackOnFailure: true,
35
+ ...config,
36
+ };
37
+ this.logger = createLogger(config.logger);
38
+ this.pluginLoader = new PluginLoader(this.logger);
39
+ // Initialize context
40
+ this.context = {
41
+ registerService: (name, service) => {
42
+ this.registerService(name, service);
43
+ },
44
+ getService: (name) => {
45
+ // 1. Try direct service map first (synchronous cache)
46
+ const service = this.services.get(name);
47
+ if (service) {
48
+ return service;
49
+ }
50
+ // 2. Try to get from plugin loader cache (Sync access to factories)
51
+ const loaderService = this.pluginLoader.getServiceInstance(name);
52
+ if (loaderService) {
53
+ // Cache it locally for faster next access
54
+ this.services.set(name, loaderService);
55
+ return loaderService;
56
+ }
57
+ // 3. Try to get from plugin loader (support async factories)
58
+ try {
59
+ const service = this.pluginLoader.getService(name);
60
+ if (service instanceof Promise) {
61
+ // If we found it in the loader but not in the sync map, it's likely a factory-based service or still loading
62
+ throw new Error(`Service '${name}' is async - use await`);
63
+ }
64
+ return service;
65
+ }
66
+ catch (error) {
67
+ if (error.message?.includes('is async')) {
68
+ throw error;
69
+ }
70
+ // Re-throw critical factory errors instead of masking them as "not found"
71
+ // If the error came from the factory execution (e.g. database connection failed), we must see it.
72
+ // "Service '${name}' not found" comes from PluginLoader.getService fallback.
73
+ const isNotFoundError = error.message === `Service '${name}' not found`;
74
+ if (!isNotFoundError) {
75
+ throw error;
76
+ }
77
+ throw new Error(`[Kernel] Service '${name}' not found`);
78
+ }
79
+ },
80
+ hook: (name, handler) => {
81
+ if (!this.hooks.has(name)) {
82
+ this.hooks.set(name, []);
83
+ }
84
+ this.hooks.get(name).push(handler);
85
+ },
86
+ trigger: async (name, ...args) => {
87
+ const handlers = this.hooks.get(name) || [];
88
+ for (const handler of handlers) {
89
+ await handler(...args);
90
+ }
91
+ },
92
+ getServices: () => {
93
+ return new Map(this.services);
94
+ },
95
+ logger: this.logger,
96
+ getKernel: () => this, // Type compatibility
97
+ };
98
+ this.pluginLoader.setContext(this.context);
99
+ // Register shutdown handler
100
+ if (this.config.gracefulShutdown) {
101
+ this.registerShutdownSignals();
102
+ }
103
+ }
104
+ /**
105
+ * Register a plugin with enhanced validation
106
+ */
107
+ async use(plugin) {
108
+ if (this.state !== 'idle') {
109
+ throw new Error('[Kernel] Cannot register plugins after bootstrap has started');
110
+ }
111
+ // Load plugin through enhanced loader
112
+ const result = await this.pluginLoader.loadPlugin(plugin);
113
+ if (!result.success || !result.plugin) {
114
+ throw new Error(`Failed to load plugin: ${plugin.name} - ${result.error?.message}`);
115
+ }
116
+ const pluginMeta = result.plugin;
117
+ this.plugins.set(pluginMeta.name, pluginMeta);
118
+ this.logger.info(`Plugin registered: ${pluginMeta.name}@${pluginMeta.version}`, {
119
+ plugin: pluginMeta.name,
120
+ version: pluginMeta.version,
121
+ });
122
+ return this;
24
123
  }
25
124
  /**
26
- * Register a plugin
27
- * @param plugin - Plugin instance
125
+ * Register a service instance directly
28
126
  */
29
- use(plugin) {
30
- this.validateIdle();
31
- const pluginName = plugin.name;
32
- if (this.plugins.has(pluginName)) {
33
- throw new Error(`[Kernel] Plugin '${pluginName}' already registered`);
127
+ registerService(name, service) {
128
+ if (this.services.has(name)) {
129
+ throw new Error(`[Kernel] Service '${name}' already registered`);
34
130
  }
35
- this.plugins.set(pluginName, plugin);
131
+ this.services.set(name, service);
132
+ this.pluginLoader.registerService(name, service);
133
+ this.logger.info(`Service '${name}' registered`, { service: name });
36
134
  return this;
37
135
  }
38
136
  /**
39
- * Bootstrap the kernel
40
- * 1. Resolve dependencies (topological sort)
41
- * 2. Init phase - plugins register services
42
- * 3. Start phase - plugins execute business logic
43
- * 4. Trigger 'kernel:ready' hook
137
+ * Register a service factory with lifecycle management
138
+ */
139
+ registerServiceFactory(name, factory, lifecycle = ServiceLifecycle.SINGLETON, dependencies) {
140
+ this.pluginLoader.registerServiceFactory({
141
+ name,
142
+ factory,
143
+ lifecycle,
144
+ dependencies,
145
+ });
146
+ return this;
147
+ }
148
+ /**
149
+ * Validate Critical System Requirements
150
+ */
151
+ validateSystemRequirements() {
152
+ if (this.config.skipSystemValidation) {
153
+ this.logger.debug('System requirement validation skipped');
154
+ return;
155
+ }
156
+ this.logger.debug('Validating system service requirements...');
157
+ const missingServices = [];
158
+ const missingCoreServices = [];
159
+ // Iterate through all defined requirements
160
+ for (const [serviceName, criticality] of Object.entries(ServiceRequirementDef)) {
161
+ const hasService = this.services.has(serviceName) || this.pluginLoader.hasService(serviceName);
162
+ if (!hasService) {
163
+ if (criticality === 'required') {
164
+ this.logger.error(`CRITICAL: Required service missing: ${serviceName}`);
165
+ missingServices.push(serviceName);
166
+ }
167
+ else if (criticality === 'core') {
168
+ this.logger.warn(`CORE: Core service missing, functionality may be degraded: ${serviceName}`);
169
+ missingCoreServices.push(serviceName);
170
+ }
171
+ else {
172
+ this.logger.info(`Info: Optional service not present: ${serviceName}`);
173
+ }
174
+ }
175
+ }
176
+ if (missingServices.length > 0) {
177
+ const errorMsg = `System failed to start. Missing critical services: ${missingServices.join(', ')}`;
178
+ this.logger.error(errorMsg);
179
+ throw new Error(errorMsg);
180
+ }
181
+ if (missingCoreServices.length > 0) {
182
+ this.logger.warn(`System started with degraded capabilities. Missing core services: ${missingCoreServices.join(', ')}`);
183
+ }
184
+ this.logger.info('System requirement check passed');
185
+ }
186
+ /**
187
+ * Bootstrap the kernel with enhanced features
44
188
  */
45
189
  async bootstrap() {
46
- this.validateState('idle');
190
+ if (this.state !== 'idle') {
191
+ throw new Error('[Kernel] Kernel already bootstrapped');
192
+ }
47
193
  this.state = 'initializing';
48
194
  this.logger.info('Bootstrap started');
49
- // Resolve dependencies
50
- const orderedPlugins = this.resolveDependencies();
51
- // Phase 1: Init - Plugins register services
52
- this.logger.info('Phase 1: Init plugins');
53
- for (const plugin of orderedPlugins) {
54
- await this.runPluginInit(plugin);
195
+ try {
196
+ // Check for circular dependencies
197
+ const cycles = this.pluginLoader.detectCircularDependencies();
198
+ if (cycles.length > 0) {
199
+ this.logger.warn('Circular service dependencies detected:', { cycles });
200
+ }
201
+ // Resolve plugin dependencies
202
+ const orderedPlugins = this.resolveDependencies();
203
+ // Phase 1: Init - Plugins register services
204
+ this.logger.info('Phase 1: Init plugins');
205
+ for (const plugin of orderedPlugins) {
206
+ await this.initPluginWithTimeout(plugin);
207
+ }
208
+ // Phase 2: Start - Plugins execute business logic
209
+ this.logger.info('Phase 2: Start plugins');
210
+ this.state = 'running';
211
+ for (const plugin of orderedPlugins) {
212
+ const result = await this.startPluginWithTimeout(plugin);
213
+ if (!result.success) {
214
+ this.logger.error(`Plugin startup failed: ${plugin.name}`, result.error);
215
+ if (this.config.rollbackOnFailure) {
216
+ this.logger.warn('Rolling back started plugins...');
217
+ await this.rollbackStartedPlugins();
218
+ throw new Error(`Plugin ${plugin.name} failed to start - rollback complete`);
219
+ }
220
+ }
221
+ }
222
+ // Phase 3: Trigger kernel:ready hook
223
+ this.validateSystemRequirements(); // Final check before ready
224
+ this.logger.debug('Triggering kernel:ready hook');
225
+ await this.context.trigger('kernel:ready');
226
+ this.logger.info('✅ Bootstrap complete');
55
227
  }
56
- // Phase 2: Start - Plugins execute business logic
57
- this.logger.info('Phase 2: Start plugins');
58
- this.state = 'running';
59
- for (const plugin of orderedPlugins) {
60
- await this.runPluginStart(plugin);
228
+ catch (error) {
229
+ this.state = 'stopped';
230
+ throw error;
61
231
  }
62
- // Trigger ready hook
63
- await this.triggerHook('kernel:ready');
64
- this.logger.info('✅ Bootstrap complete', {
65
- pluginCount: this.plugins.size
66
- });
67
232
  }
68
233
  /**
69
- * Shutdown the kernel
70
- * Calls destroy on all plugins in reverse order
234
+ * Graceful shutdown with timeout
71
235
  */
72
236
  async shutdown() {
73
- await this.destroy();
74
- }
75
- /**
76
- * Graceful shutdown - destroy all plugins in reverse order
77
- */
78
- async destroy() {
79
- if (this.state === 'stopped') {
80
- this.logger.warn('Kernel already stopped');
237
+ if (this.state === 'stopped' || this.state === 'stopping') {
238
+ this.logger.warn('Kernel already stopped or stopping');
81
239
  return;
82
240
  }
241
+ if (this.state !== 'running') {
242
+ throw new Error('[Kernel] Kernel not running');
243
+ }
83
244
  this.state = 'stopping';
84
- this.logger.info('Shutdown started');
85
- // Trigger shutdown hook
86
- await this.triggerHook('kernel:shutdown');
87
- // Destroy plugins in reverse order
88
- const orderedPlugins = this.resolveDependencies();
89
- for (const plugin of orderedPlugins.reverse()) {
90
- await this.runPluginDestroy(plugin);
91
- }
92
- this.state = 'stopped';
93
- this.logger.info('✅ Shutdown complete');
94
- // Cleanup logger resources
95
- if (this.logger && typeof this.logger.destroy === 'function') {
245
+ this.logger.info('Graceful shutdown started');
246
+ try {
247
+ // Create shutdown promise with timeout
248
+ const shutdownPromise = this.performShutdown();
249
+ const timeoutPromise = new Promise((_, reject) => {
250
+ setTimeout(() => {
251
+ reject(new Error('Shutdown timeout exceeded'));
252
+ }, this.config.shutdownTimeout);
253
+ });
254
+ // Race between shutdown and timeout
255
+ await Promise.race([shutdownPromise, timeoutPromise]);
256
+ this.state = 'stopped';
257
+ this.logger.info('✅ Graceful shutdown complete');
258
+ }
259
+ catch (error) {
260
+ this.logger.error('Shutdown error - forcing stop', error);
261
+ this.state = 'stopped';
262
+ throw error;
263
+ }
264
+ finally {
265
+ // Cleanup logger resources
96
266
  await this.logger.destroy();
97
267
  }
98
268
  }
99
269
  /**
100
- * Get a service from the registry
101
- * Convenience method for external access
270
+ * Check health of a specific plugin
271
+ */
272
+ async checkPluginHealth(pluginName) {
273
+ return await this.pluginLoader.checkPluginHealth(pluginName);
274
+ }
275
+ /**
276
+ * Check health of all plugins
277
+ */
278
+ async checkAllPluginsHealth() {
279
+ const results = new Map();
280
+ for (const pluginName of this.plugins.keys()) {
281
+ const health = await this.checkPluginHealth(pluginName);
282
+ results.set(pluginName, health);
283
+ }
284
+ return results;
285
+ }
286
+ /**
287
+ * Get plugin startup metrics
288
+ */
289
+ getPluginMetrics() {
290
+ return new Map(this.pluginStartTimes);
291
+ }
292
+ /**
293
+ * Get a service (sync helper)
102
294
  */
103
295
  getService(name) {
104
296
  return this.context.getService(name);
105
297
  }
298
+ /**
299
+ * Get a service asynchronously (supports factories)
300
+ */
301
+ async getServiceAsync(name, scopeId) {
302
+ return await this.pluginLoader.getService(name, scopeId);
303
+ }
106
304
  /**
107
305
  * Check if kernel is running
108
306
  */
109
307
  isRunning() {
110
308
  return this.state === 'running';
111
309
  }
310
+ /**
311
+ * Get kernel state
312
+ */
313
+ getState() {
314
+ return this.state;
315
+ }
316
+ // Private methods
317
+ async initPluginWithTimeout(plugin) {
318
+ const timeout = plugin.startupTimeout || this.config.defaultStartupTimeout;
319
+ this.logger.debug(`Init: ${plugin.name}`, { plugin: plugin.name });
320
+ const initPromise = plugin.init(this.context);
321
+ const timeoutPromise = new Promise((_, reject) => {
322
+ setTimeout(() => {
323
+ reject(new Error(`Plugin ${plugin.name} init timeout after ${timeout}ms`));
324
+ }, timeout);
325
+ });
326
+ await Promise.race([initPromise, timeoutPromise]);
327
+ }
328
+ async startPluginWithTimeout(plugin) {
329
+ if (!plugin.start) {
330
+ return { success: true, pluginName: plugin.name };
331
+ }
332
+ const timeout = plugin.startupTimeout || this.config.defaultStartupTimeout;
333
+ const startTime = Date.now();
334
+ this.logger.debug(`Start: ${plugin.name}`, { plugin: plugin.name });
335
+ try {
336
+ const startPromise = plugin.start(this.context);
337
+ const timeoutPromise = new Promise((_, reject) => {
338
+ setTimeout(() => {
339
+ reject(new Error(`Plugin ${plugin.name} start timeout after ${timeout}ms`));
340
+ }, timeout);
341
+ });
342
+ await Promise.race([startPromise, timeoutPromise]);
343
+ const duration = Date.now() - startTime;
344
+ this.startedPlugins.add(plugin.name);
345
+ this.pluginStartTimes.set(plugin.name, duration);
346
+ this.logger.debug(`Plugin started: ${plugin.name} (${duration}ms)`);
347
+ return {
348
+ success: true,
349
+ pluginName: plugin.name,
350
+ startTime: duration,
351
+ };
352
+ }
353
+ catch (error) {
354
+ const duration = Date.now() - startTime;
355
+ const isTimeout = error.message.includes('timeout');
356
+ return {
357
+ success: false,
358
+ pluginName: plugin.name,
359
+ error: error,
360
+ startTime: duration,
361
+ timedOut: isTimeout,
362
+ };
363
+ }
364
+ }
365
+ async rollbackStartedPlugins() {
366
+ const pluginsToRollback = Array.from(this.startedPlugins).reverse();
367
+ for (const pluginName of pluginsToRollback) {
368
+ const plugin = this.plugins.get(pluginName);
369
+ if (plugin?.destroy) {
370
+ try {
371
+ this.logger.debug(`Rollback: ${pluginName}`);
372
+ await plugin.destroy();
373
+ }
374
+ catch (error) {
375
+ this.logger.error(`Rollback failed for ${pluginName}`, error);
376
+ }
377
+ }
378
+ }
379
+ this.startedPlugins.clear();
380
+ }
381
+ async performShutdown() {
382
+ // Trigger shutdown hook
383
+ await this.context.trigger('kernel:shutdown');
384
+ // Destroy plugins in reverse order
385
+ const orderedPlugins = Array.from(this.plugins.values()).reverse();
386
+ for (const plugin of orderedPlugins) {
387
+ if (plugin.destroy) {
388
+ this.logger.debug(`Destroy: ${plugin.name}`, { plugin: plugin.name });
389
+ try {
390
+ await plugin.destroy();
391
+ }
392
+ catch (error) {
393
+ this.logger.error(`Error destroying plugin ${plugin.name}`, error);
394
+ }
395
+ }
396
+ }
397
+ // Execute custom shutdown handlers
398
+ for (const handler of this.shutdownHandlers) {
399
+ try {
400
+ await handler();
401
+ }
402
+ catch (error) {
403
+ this.logger.error('Shutdown handler error', error);
404
+ }
405
+ }
406
+ }
407
+ resolveDependencies() {
408
+ const resolved = [];
409
+ const visited = new Set();
410
+ const visiting = new Set();
411
+ const visit = (pluginName) => {
412
+ if (visited.has(pluginName))
413
+ return;
414
+ if (visiting.has(pluginName)) {
415
+ throw new Error(`[Kernel] Circular dependency detected: ${pluginName}`);
416
+ }
417
+ const plugin = this.plugins.get(pluginName);
418
+ if (!plugin) {
419
+ throw new Error(`[Kernel] Plugin '${pluginName}' not found`);
420
+ }
421
+ visiting.add(pluginName);
422
+ // Visit dependencies first
423
+ const deps = plugin.dependencies || [];
424
+ for (const dep of deps) {
425
+ if (!this.plugins.has(dep)) {
426
+ throw new Error(`[Kernel] Dependency '${dep}' not found for plugin '${pluginName}'`);
427
+ }
428
+ visit(dep);
429
+ }
430
+ visiting.delete(pluginName);
431
+ visited.add(pluginName);
432
+ resolved.push(plugin);
433
+ };
434
+ // Visit all plugins
435
+ for (const pluginName of this.plugins.keys()) {
436
+ visit(pluginName);
437
+ }
438
+ return resolved;
439
+ }
440
+ registerShutdownSignals() {
441
+ const signals = ['SIGINT', 'SIGTERM', 'SIGQUIT'];
442
+ let shutdownInProgress = false;
443
+ const handleShutdown = async (signal) => {
444
+ if (shutdownInProgress) {
445
+ this.logger.warn(`Shutdown already in progress, ignoring ${signal}`);
446
+ return;
447
+ }
448
+ shutdownInProgress = true;
449
+ this.logger.info(`Received ${signal} - initiating graceful shutdown`);
450
+ try {
451
+ await this.shutdown();
452
+ process.exit(0);
453
+ }
454
+ catch (error) {
455
+ this.logger.error('Shutdown failed', error);
456
+ process.exit(1);
457
+ }
458
+ };
459
+ for (const signal of signals) {
460
+ process.on(signal, () => handleShutdown(signal));
461
+ }
462
+ }
463
+ /**
464
+ * Register a custom shutdown handler
465
+ */
466
+ onShutdown(handler) {
467
+ this.shutdownHandlers.push(handler);
468
+ }
112
469
  }