@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
@@ -1,161 +1,414 @@
1
1
  import { describe, it, expect, beforeEach } from 'vitest';
2
2
  import { ObjectKernel } from './kernel';
3
- describe('ObjectKernel with Configurable Logger', () => {
3
+ import { ServiceLifecycle } from './plugin-loader';
4
+ describe('ObjectKernel', () => {
4
5
  let kernel;
5
6
  beforeEach(() => {
6
- kernel = new ObjectKernel();
7
- });
8
- describe('Logger Configuration', () => {
9
- it('should create kernel with default logger', () => {
10
- expect(kernel).toBeDefined();
11
- });
12
- it('should create kernel with custom logger config', async () => {
13
- const customKernel = new ObjectKernel({
14
- logger: {
15
- level: 'debug',
16
- format: 'pretty',
17
- sourceLocation: true
18
- }
19
- });
20
- expect(customKernel).toBeDefined();
21
- // Cleanup
22
- await customKernel.bootstrap();
23
- await customKernel.shutdown();
24
- });
25
- it('should create kernel with file logging config', async () => {
26
- const fileKernel = new ObjectKernel({
27
- logger: {
28
- level: 'info',
29
- format: 'json',
30
- file: '/tmp/test-kernel.log'
31
- }
32
- });
33
- expect(fileKernel).toBeDefined();
34
- // Cleanup
35
- await fileKernel.bootstrap();
36
- await fileKernel.shutdown();
7
+ kernel = new ObjectKernel({
8
+ logger: { level: 'error' }, // Suppress logs in tests
9
+ gracefulShutdown: false, // Disable for tests
10
+ skipSystemValidation: true,
37
11
  });
38
12
  });
39
- describe('Plugin Context Logger', () => {
40
- it('should provide logger to plugins', async () => {
41
- let loggerReceived = false;
42
- const testPlugin = {
43
- name: 'test-plugin',
44
- init: async (ctx) => {
45
- if (ctx.logger) {
46
- loggerReceived = true;
47
- ctx.logger.info('Plugin initialized', { plugin: 'test-plugin' });
48
- }
49
- }
13
+ describe('Plugin Registration and Loading', () => {
14
+ it('should register a plugin with version', async () => {
15
+ const plugin = {
16
+ name: 'versioned-plugin',
17
+ version: '1.2.3',
18
+ init: async () => { },
50
19
  };
51
- kernel.use(testPlugin);
20
+ await kernel.use(plugin);
52
21
  await kernel.bootstrap();
53
- expect(loggerReceived).toBe(true);
22
+ expect(kernel.isRunning()).toBe(true);
54
23
  await kernel.shutdown();
55
24
  });
56
- it('should allow plugins to use all log levels', async () => {
57
- const logCalls = [];
58
- const loggingPlugin = {
59
- name: 'logging-plugin',
60
- init: async (ctx) => {
61
- ctx.logger.debug('Debug message');
62
- logCalls.push('debug');
63
- ctx.logger.info('Info message');
64
- logCalls.push('info');
65
- ctx.logger.warn('Warning message');
66
- logCalls.push('warn');
67
- ctx.logger.error('Error message');
68
- logCalls.push('error');
69
- }
70
- };
71
- kernel.use(loggingPlugin);
72
- await kernel.bootstrap();
73
- expect(logCalls).toContain('debug');
74
- expect(logCalls).toContain('info');
75
- expect(logCalls).toContain('warn');
76
- expect(logCalls).toContain('error');
77
- await kernel.shutdown();
78
- });
79
- it('should support metadata in logs', async () => {
80
- const metadataPlugin = {
81
- name: 'metadata-plugin',
82
- init: async (ctx) => {
83
- ctx.logger.info('User action', {
84
- userId: '123',
85
- action: 'create',
86
- resource: 'document'
87
- });
88
- }
25
+ it('should validate plugin during registration', async () => {
26
+ const invalidPlugin = {
27
+ name: '',
28
+ init: async () => { },
89
29
  };
90
- kernel.use(metadataPlugin);
30
+ await expect(async () => {
31
+ await kernel.use(invalidPlugin);
32
+ }).rejects.toThrow();
33
+ });
34
+ it('should reject plugin registration after bootstrap', async () => {
91
35
  await kernel.bootstrap();
36
+ const plugin = {
37
+ name: 'late-plugin',
38
+ init: async () => { },
39
+ };
40
+ await expect(async () => {
41
+ await kernel.use(plugin);
42
+ }).rejects.toThrow('Cannot register plugins after bootstrap');
92
43
  await kernel.shutdown();
93
44
  });
94
45
  });
95
- describe('Kernel Lifecycle Logging', () => {
96
- it('should log bootstrap process', async () => {
46
+ describe('Service Factory Registration', () => {
47
+ it('should register singleton service factory', async () => {
48
+ let callCount = 0;
49
+ kernel.registerServiceFactory('counter', () => {
50
+ callCount++;
51
+ return { count: callCount };
52
+ }, ServiceLifecycle.SINGLETON);
53
+ await kernel.bootstrap();
54
+ const service1 = await kernel.getServiceAsync('counter');
55
+ const service2 = await kernel.getServiceAsync('counter');
56
+ expect(callCount).toBe(1);
57
+ expect(service1).toBe(service2);
58
+ await kernel.shutdown();
59
+ });
60
+ it('should register transient service factory', async () => {
61
+ let callCount = 0;
62
+ kernel.registerServiceFactory('transient', () => {
63
+ callCount++;
64
+ return { count: callCount };
65
+ }, ServiceLifecycle.TRANSIENT);
66
+ await kernel.bootstrap();
67
+ const service1 = await kernel.getServiceAsync('transient');
68
+ const service2 = await kernel.getServiceAsync('transient');
69
+ expect(callCount).toBe(2);
70
+ expect(service1).not.toBe(service2);
71
+ await kernel.shutdown();
72
+ });
73
+ it('should register scoped service factory', async () => {
74
+ let callCount = 0;
75
+ kernel.registerServiceFactory('scoped', () => {
76
+ callCount++;
77
+ return { count: callCount };
78
+ }, ServiceLifecycle.SCOPED);
79
+ await kernel.bootstrap();
80
+ const service1 = await kernel.getServiceAsync('scoped', 'request-1');
81
+ const service2 = await kernel.getServiceAsync('scoped', 'request-1');
82
+ const service3 = await kernel.getServiceAsync('scoped', 'request-2');
83
+ expect(callCount).toBe(2); // Once per scope
84
+ expect(service1).toBe(service2); // Same within scope
85
+ expect(service1).not.toBe(service3); // Different across scopes
86
+ await kernel.shutdown();
87
+ });
88
+ });
89
+ describe('Plugin Lifecycle with Timeout', () => {
90
+ it('should timeout plugin init if it takes too long', async () => {
97
91
  const plugin = {
98
- name: 'lifecycle-test',
92
+ name: 'slow-init',
93
+ version: '1.0.0',
99
94
  init: async () => {
100
- // Init logic
95
+ await new Promise(resolve => setTimeout(resolve, 5000)); // 5 seconds
96
+ },
97
+ startupTimeout: 100, // 100ms timeout
98
+ };
99
+ await kernel.use(plugin);
100
+ await expect(async () => {
101
+ await kernel.bootstrap();
102
+ }).rejects.toThrow('timeout');
103
+ }, 1000); // Test should complete in 1 second
104
+ it('should timeout plugin start if it takes too long', async () => {
105
+ const plugin = {
106
+ name: 'slow-start',
107
+ version: '1.0.0',
108
+ init: async () => { },
109
+ start: async () => {
110
+ await new Promise(resolve => setTimeout(resolve, 5000)); // 5 seconds
111
+ },
112
+ startupTimeout: 100, // 100ms timeout
113
+ };
114
+ await kernel.use(plugin);
115
+ await expect(async () => {
116
+ await kernel.bootstrap();
117
+ }).rejects.toThrow();
118
+ }, 1000); // Test should complete in 1 second
119
+ it('should complete plugin startup within timeout', async () => {
120
+ const plugin = {
121
+ name: 'fast-plugin',
122
+ version: '1.0.0',
123
+ init: async () => {
124
+ await new Promise(resolve => setTimeout(resolve, 10));
101
125
  },
102
126
  start: async () => {
103
- // Start logic
104
- }
127
+ await new Promise(resolve => setTimeout(resolve, 10));
128
+ },
129
+ startupTimeout: 1000,
105
130
  };
106
- kernel.use(plugin);
131
+ await kernel.use(plugin);
107
132
  await kernel.bootstrap();
108
133
  expect(kernel.isRunning()).toBe(true);
109
134
  await kernel.shutdown();
110
135
  });
111
- it('should log shutdown process', async () => {
136
+ });
137
+ describe('Startup Failure Rollback', () => {
138
+ it('should rollback started plugins on failure', async () => {
139
+ let plugin1Destroyed = false;
140
+ const plugin1 = {
141
+ name: 'plugin-1',
142
+ version: '1.0.0',
143
+ init: async () => { },
144
+ start: async () => { },
145
+ destroy: async () => {
146
+ plugin1Destroyed = true;
147
+ },
148
+ };
149
+ const plugin2 = {
150
+ name: 'plugin-2',
151
+ version: '1.0.0',
152
+ init: async () => { },
153
+ start: async () => {
154
+ throw new Error('Startup failed');
155
+ },
156
+ };
157
+ await kernel.use(plugin1);
158
+ await kernel.use(plugin2);
159
+ await expect(async () => {
160
+ await kernel.bootstrap();
161
+ }).rejects.toThrow('failed to start');
162
+ // Plugin 1 should be rolled back
163
+ expect(plugin1Destroyed).toBe(true);
164
+ });
165
+ it('should not rollback if disabled', async () => {
166
+ const noRollbackKernel = new ObjectKernel({
167
+ logger: { level: 'error' },
168
+ rollbackOnFailure: false,
169
+ gracefulShutdown: false,
170
+ skipSystemValidation: true,
171
+ });
172
+ let plugin1Destroyed = false;
173
+ const plugin1 = {
174
+ name: 'plugin-1',
175
+ version: '1.0.0',
176
+ init: async () => { },
177
+ start: async () => { },
178
+ destroy: async () => {
179
+ plugin1Destroyed = true;
180
+ },
181
+ };
182
+ const plugin2 = {
183
+ name: 'plugin-2',
184
+ version: '1.0.0',
185
+ init: async () => { },
186
+ start: async () => {
187
+ throw new Error('Startup failed');
188
+ },
189
+ };
190
+ await noRollbackKernel.use(plugin1);
191
+ await noRollbackKernel.use(plugin2);
192
+ // Should not throw since rollback is disabled
193
+ await noRollbackKernel.bootstrap();
194
+ // Plugin 1 should NOT be destroyed
195
+ expect(plugin1Destroyed).toBe(false);
196
+ });
197
+ });
198
+ describe('Plugin Health Checks', () => {
199
+ it('should check individual plugin health', async () => {
112
200
  const plugin = {
113
- name: 'shutdown-test',
201
+ name: 'healthy-plugin',
202
+ version: '1.0.0',
203
+ init: async () => { },
204
+ };
205
+ await kernel.use(plugin);
206
+ await kernel.bootstrap();
207
+ const health = await kernel.checkPluginHealth('healthy-plugin');
208
+ expect(health.healthy).toBe(true);
209
+ expect(health.lastCheck).toBeInstanceOf(Date);
210
+ await kernel.shutdown();
211
+ });
212
+ it('should check all plugins health', async () => {
213
+ const plugin1 = {
214
+ name: 'plugin-1',
215
+ version: '1.0.0',
216
+ init: async () => { },
217
+ };
218
+ const plugin2 = {
219
+ name: 'plugin-2',
220
+ version: '1.0.0',
221
+ init: async () => { },
222
+ };
223
+ await kernel.use(plugin1);
224
+ await kernel.use(plugin2);
225
+ await kernel.bootstrap();
226
+ const allHealth = await kernel.checkAllPluginsHealth();
227
+ expect(allHealth.size).toBe(2);
228
+ expect(allHealth.get('plugin-1').healthy).toBe(true);
229
+ expect(allHealth.get('plugin-2').healthy).toBe(true);
230
+ await kernel.shutdown();
231
+ });
232
+ });
233
+ describe('Plugin Metrics', () => {
234
+ it('should track plugin startup times', async () => {
235
+ const plugin1 = {
236
+ name: 'plugin-1',
237
+ version: '1.0.0',
238
+ init: async () => { },
239
+ start: async () => {
240
+ await new Promise(resolve => setTimeout(resolve, 50));
241
+ },
242
+ };
243
+ const plugin2 = {
244
+ name: 'plugin-2',
245
+ version: '1.0.0',
246
+ init: async () => { },
247
+ start: async () => {
248
+ await new Promise(resolve => setTimeout(resolve, 30));
249
+ },
250
+ };
251
+ await kernel.use(plugin1);
252
+ await kernel.use(plugin2);
253
+ await kernel.bootstrap();
254
+ const metrics = kernel.getPluginMetrics();
255
+ expect(metrics.size).toBe(2);
256
+ expect(metrics.get('plugin-1')).toBeGreaterThan(0);
257
+ expect(metrics.get('plugin-2')).toBeGreaterThan(0);
258
+ await kernel.shutdown();
259
+ });
260
+ it('should not track metrics for plugins without start', async () => {
261
+ const plugin = {
262
+ name: 'no-start',
263
+ version: '1.0.0',
264
+ init: async () => { },
265
+ };
266
+ await kernel.use(plugin);
267
+ await kernel.bootstrap();
268
+ const metrics = kernel.getPluginMetrics();
269
+ expect(metrics.has('no-start')).toBe(false);
270
+ await kernel.shutdown();
271
+ });
272
+ });
273
+ describe('Graceful Shutdown', () => {
274
+ it('should call destroy on all plugins', async () => {
275
+ let plugin1Destroyed = false;
276
+ let plugin2Destroyed = false;
277
+ const plugin1 = {
278
+ name: 'plugin-1',
279
+ version: '1.0.0',
114
280
  init: async () => { },
115
281
  destroy: async () => {
116
- // Cleanup
117
- }
282
+ plugin1Destroyed = true;
283
+ },
118
284
  };
119
- kernel.use(plugin);
285
+ const plugin2 = {
286
+ name: 'plugin-2',
287
+ version: '1.0.0',
288
+ init: async () => { },
289
+ destroy: async () => {
290
+ plugin2Destroyed = true;
291
+ },
292
+ };
293
+ await kernel.use(plugin1);
294
+ await kernel.use(plugin2);
120
295
  await kernel.bootstrap();
121
296
  await kernel.shutdown();
297
+ expect(plugin1Destroyed).toBe(true);
298
+ expect(plugin2Destroyed).toBe(true);
299
+ });
300
+ it('should handle plugin destroy errors gracefully', async () => {
301
+ const plugin1 = {
302
+ name: 'error-destroy',
303
+ version: '1.0.0',
304
+ init: async () => { },
305
+ destroy: async () => {
306
+ throw new Error('Destroy failed');
307
+ },
308
+ };
309
+ const plugin2 = {
310
+ name: 'normal-plugin',
311
+ version: '1.0.0',
312
+ init: async () => { },
313
+ };
314
+ await kernel.use(plugin1);
315
+ await kernel.use(plugin2);
316
+ await kernel.bootstrap();
317
+ // Should not throw even if one plugin fails to destroy
318
+ await kernel.shutdown();
122
319
  expect(kernel.getState()).toBe('stopped');
123
320
  });
124
- });
125
- describe('Environment Compatibility', () => {
126
- it('should work in Node.js environment', async () => {
127
- const nodeKernel = new ObjectKernel({
128
- logger: {
129
- level: 'info',
130
- format: 'json'
131
- }
132
- });
321
+ it('should trigger shutdown hook', async () => {
322
+ let hookCalled = false;
133
323
  const plugin = {
134
- name: 'node-test',
324
+ name: 'hook-plugin',
325
+ version: '1.0.0',
135
326
  init: async (ctx) => {
136
- ctx.logger.info('Running in Node.js');
137
- }
138
- };
139
- nodeKernel.use(plugin);
140
- await nodeKernel.bootstrap();
141
- await nodeKernel.shutdown();
142
- });
143
- it('should support browser-friendly logging', async () => {
144
- const browserKernel = new ObjectKernel({
145
- logger: {
146
- level: 'info',
147
- format: 'pretty'
148
- }
327
+ ctx.hook('kernel:shutdown', async () => {
328
+ hookCalled = true;
329
+ });
330
+ },
331
+ };
332
+ await kernel.use(plugin);
333
+ await kernel.bootstrap();
334
+ await kernel.shutdown();
335
+ expect(hookCalled).toBe(true);
336
+ });
337
+ it('should execute custom shutdown handlers', async () => {
338
+ let handlerCalled = false;
339
+ kernel.onShutdown(async () => {
340
+ handlerCalled = true;
149
341
  });
150
- const plugin = {
151
- name: 'browser-test',
152
- init: async (ctx) => {
153
- ctx.logger.info('Browser-friendly format');
154
- }
342
+ await kernel.bootstrap();
343
+ await kernel.shutdown();
344
+ expect(handlerCalled).toBe(true);
345
+ });
346
+ });
347
+ describe('Dependency Resolution', () => {
348
+ it('should resolve plugin dependencies in correct order', async () => {
349
+ const initOrder = [];
350
+ const pluginA = {
351
+ name: 'plugin-a',
352
+ version: '1.0.0',
353
+ dependencies: ['plugin-b'],
354
+ init: async () => {
355
+ initOrder.push('plugin-a');
356
+ },
357
+ };
358
+ const pluginB = {
359
+ name: 'plugin-b',
360
+ version: '1.0.0',
361
+ init: async () => {
362
+ initOrder.push('plugin-b');
363
+ },
155
364
  };
156
- browserKernel.use(plugin);
157
- await browserKernel.bootstrap();
158
- await browserKernel.shutdown();
365
+ await kernel.use(pluginA);
366
+ await kernel.use(pluginB);
367
+ await kernel.bootstrap();
368
+ expect(initOrder).toEqual(['plugin-b', 'plugin-a']);
369
+ await kernel.shutdown();
370
+ });
371
+ it('should detect circular plugin dependencies', async () => {
372
+ const pluginA = {
373
+ name: 'plugin-a',
374
+ version: '1.0.0',
375
+ dependencies: ['plugin-b'],
376
+ init: async () => { },
377
+ };
378
+ const pluginB = {
379
+ name: 'plugin-b',
380
+ version: '1.0.0',
381
+ dependencies: ['plugin-a'],
382
+ init: async () => { },
383
+ };
384
+ await kernel.use(pluginA);
385
+ await kernel.use(pluginB);
386
+ await expect(async () => {
387
+ await kernel.bootstrap();
388
+ }).rejects.toThrow('Circular dependency');
389
+ });
390
+ });
391
+ describe('State Management', () => {
392
+ it('should track kernel state correctly', async () => {
393
+ expect(kernel.getState()).toBe('idle');
394
+ await kernel.bootstrap();
395
+ expect(kernel.getState()).toBe('running');
396
+ expect(kernel.isRunning()).toBe(true);
397
+ await kernel.shutdown();
398
+ expect(kernel.getState()).toBe('stopped');
399
+ expect(kernel.isRunning()).toBe(false);
400
+ });
401
+ it('should not allow double bootstrap', async () => {
402
+ await kernel.bootstrap();
403
+ await expect(async () => {
404
+ await kernel.bootstrap();
405
+ }).rejects.toThrow('already bootstrapped');
406
+ await kernel.shutdown();
407
+ });
408
+ it('should not allow shutdown before bootstrap', async () => {
409
+ await expect(async () => {
410
+ await kernel.shutdown();
411
+ }).rejects.toThrow('not running');
159
412
  });
160
413
  });
161
414
  });
@@ -0,0 +1,55 @@
1
+ import { Plugin } from './types.js';
2
+ import type { LoggerConfig } from '@objectstack/spec/system';
3
+ import { ObjectKernelBase } from './kernel-base.js';
4
+ /**
5
+ * ObjectKernel - MiniKernel Architecture
6
+ *
7
+ * A highly modular, plugin-based microkernel that:
8
+ * - Manages plugin lifecycle (init, start, destroy)
9
+ * - Provides dependency injection via service registry
10
+ * - Implements event/hook system for inter-plugin communication
11
+ * - Handles dependency resolution (topological sort)
12
+ * - Provides configurable logging for server and browser
13
+ *
14
+ * Core philosophy:
15
+ * - Business logic is completely separated into plugins
16
+ * - Kernel only manages lifecycle, DI, and hooks
17
+ * - Plugins are loaded as equal building blocks
18
+ */
19
+ export declare class LiteKernel extends ObjectKernelBase {
20
+ constructor(config?: {
21
+ logger?: Partial<LoggerConfig>;
22
+ });
23
+ /**
24
+ * Register a plugin
25
+ * @param plugin - Plugin instance
26
+ */
27
+ use(plugin: Plugin): this;
28
+ /**
29
+ * Bootstrap the kernel
30
+ * 1. Resolve dependencies (topological sort)
31
+ * 2. Init phase - plugins register services
32
+ * 3. Start phase - plugins execute business logic
33
+ * 4. Trigger 'kernel:ready' hook
34
+ */
35
+ bootstrap(): Promise<void>;
36
+ /**
37
+ * Shutdown the kernel
38
+ * Calls destroy on all plugins in reverse order
39
+ */
40
+ shutdown(): Promise<void>;
41
+ /**
42
+ * Graceful shutdown - destroy all plugins in reverse order
43
+ */
44
+ destroy(): Promise<void>;
45
+ /**
46
+ * Get a service from the registry
47
+ * Convenience method for external access
48
+ */
49
+ getService<T>(name: string): T;
50
+ /**
51
+ * Check if kernel is running
52
+ */
53
+ isRunning(): boolean;
54
+ }
55
+ //# sourceMappingURL=lite-kernel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lite-kernel.d.ts","sourceRoot":"","sources":["../src/lite-kernel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,UAAW,SAAQ,gBAAgB;gBAChC,MAAM,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;KAAE;IAQvD;;;OAGG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAYzB;;;;;;OAMG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IA8BhC;;;OAGG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B9B;;;OAGG;IACH,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC;IAI9B;;OAEG;IACH,SAAS,IAAI,OAAO;CAGvB"}