@objectstack/core 1.0.4 → 1.0.6
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/.turbo/turbo-build.log +22 -0
- package/CHANGELOG.md +19 -0
- package/dist/index.cjs +4304 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1777 -0
- package/dist/index.d.ts +1776 -21
- package/dist/index.js +4246 -23
- package/dist/index.js.map +1 -0
- package/package.json +5 -5
- package/src/logger.ts +2 -2
- package/src/security/plugin-signature-verifier.ts +12 -11
- package/tsconfig.json +1 -3
- package/dist/api-registry-plugin.d.ts +0 -54
- package/dist/api-registry-plugin.d.ts.map +0 -1
- package/dist/api-registry-plugin.js +0 -53
- package/dist/api-registry-plugin.test.d.ts +0 -2
- package/dist/api-registry-plugin.test.d.ts.map +0 -1
- package/dist/api-registry-plugin.test.js +0 -334
- package/dist/api-registry.d.ts +0 -259
- package/dist/api-registry.d.ts.map +0 -1
- package/dist/api-registry.js +0 -600
- package/dist/api-registry.test.d.ts +0 -2
- package/dist/api-registry.test.d.ts.map +0 -1
- package/dist/api-registry.test.js +0 -957
- package/dist/contracts/data-engine.d.ts +0 -62
- package/dist/contracts/data-engine.d.ts.map +0 -1
- package/dist/contracts/data-engine.js +0 -1
- package/dist/contracts/http-server.d.ts +0 -119
- package/dist/contracts/http-server.d.ts.map +0 -1
- package/dist/contracts/http-server.js +0 -11
- package/dist/contracts/logger.d.ts +0 -63
- package/dist/contracts/logger.d.ts.map +0 -1
- package/dist/contracts/logger.js +0 -1
- package/dist/dependency-resolver.d.ts +0 -62
- package/dist/dependency-resolver.d.ts.map +0 -1
- package/dist/dependency-resolver.js +0 -317
- package/dist/dependency-resolver.test.d.ts +0 -2
- package/dist/dependency-resolver.test.d.ts.map +0 -1
- package/dist/dependency-resolver.test.js +0 -241
- package/dist/health-monitor.d.ts +0 -65
- package/dist/health-monitor.d.ts.map +0 -1
- package/dist/health-monitor.js +0 -269
- package/dist/health-monitor.test.d.ts +0 -2
- package/dist/health-monitor.test.d.ts.map +0 -1
- package/dist/health-monitor.test.js +0 -68
- package/dist/hot-reload.d.ts +0 -79
- package/dist/hot-reload.d.ts.map +0 -1
- package/dist/hot-reload.js +0 -313
- package/dist/index.d.ts.map +0 -1
- package/dist/kernel-base.d.ts +0 -84
- package/dist/kernel-base.d.ts.map +0 -1
- package/dist/kernel-base.js +0 -219
- package/dist/kernel.d.ts +0 -113
- package/dist/kernel.d.ts.map +0 -1
- package/dist/kernel.js +0 -472
- package/dist/kernel.test.d.ts +0 -2
- package/dist/kernel.test.d.ts.map +0 -1
- package/dist/kernel.test.js +0 -414
- package/dist/lite-kernel.d.ts +0 -55
- package/dist/lite-kernel.d.ts.map +0 -1
- package/dist/lite-kernel.js +0 -112
- package/dist/lite-kernel.test.d.ts +0 -2
- package/dist/lite-kernel.test.d.ts.map +0 -1
- package/dist/lite-kernel.test.js +0 -161
- package/dist/logger.d.ts +0 -71
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js +0 -312
- package/dist/logger.test.d.ts +0 -2
- package/dist/logger.test.d.ts.map +0 -1
- package/dist/logger.test.js +0 -92
- package/dist/plugin-loader.d.ts +0 -164
- package/dist/plugin-loader.d.ts.map +0 -1
- package/dist/plugin-loader.js +0 -319
- package/dist/plugin-loader.test.d.ts +0 -2
- package/dist/plugin-loader.test.d.ts.map +0 -1
- package/dist/plugin-loader.test.js +0 -348
- package/dist/qa/adapter.d.ts +0 -14
- package/dist/qa/adapter.d.ts.map +0 -1
- package/dist/qa/adapter.js +0 -1
- package/dist/qa/http-adapter.d.ts +0 -16
- package/dist/qa/http-adapter.d.ts.map +0 -1
- package/dist/qa/http-adapter.js +0 -107
- package/dist/qa/index.d.ts +0 -4
- package/dist/qa/index.d.ts.map +0 -1
- package/dist/qa/index.js +0 -3
- package/dist/qa/runner.d.ts +0 -27
- package/dist/qa/runner.d.ts.map +0 -1
- package/dist/qa/runner.js +0 -157
- package/dist/security/index.d.ts +0 -17
- package/dist/security/index.d.ts.map +0 -1
- package/dist/security/index.js +0 -17
- package/dist/security/permission-manager.d.ts +0 -96
- package/dist/security/permission-manager.d.ts.map +0 -1
- package/dist/security/permission-manager.js +0 -235
- package/dist/security/permission-manager.test.d.ts +0 -2
- package/dist/security/permission-manager.test.d.ts.map +0 -1
- package/dist/security/permission-manager.test.js +0 -220
- package/dist/security/plugin-config-validator.d.ts +0 -79
- package/dist/security/plugin-config-validator.d.ts.map +0 -1
- package/dist/security/plugin-config-validator.js +0 -166
- package/dist/security/plugin-config-validator.test.d.ts +0 -2
- package/dist/security/plugin-config-validator.test.d.ts.map +0 -1
- package/dist/security/plugin-config-validator.test.js +0 -223
- package/dist/security/plugin-permission-enforcer.d.ts +0 -154
- package/dist/security/plugin-permission-enforcer.d.ts.map +0 -1
- package/dist/security/plugin-permission-enforcer.js +0 -323
- package/dist/security/plugin-permission-enforcer.test.d.ts +0 -2
- package/dist/security/plugin-permission-enforcer.test.d.ts.map +0 -1
- package/dist/security/plugin-permission-enforcer.test.js +0 -205
- package/dist/security/plugin-signature-verifier.d.ts +0 -96
- package/dist/security/plugin-signature-verifier.d.ts.map +0 -1
- package/dist/security/plugin-signature-verifier.js +0 -250
- package/dist/security/sandbox-runtime.d.ts +0 -115
- package/dist/security/sandbox-runtime.d.ts.map +0 -1
- package/dist/security/sandbox-runtime.js +0 -311
- package/dist/security/security-scanner.d.ts +0 -92
- package/dist/security/security-scanner.d.ts.map +0 -1
- package/dist/security/security-scanner.js +0 -273
- package/dist/types.d.ts +0 -89
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -1
- package/dist/utils/env.d.ts +0 -20
- package/dist/utils/env.d.ts.map +0 -1
- package/dist/utils/env.js +0 -46
- package/dist/utils/env.test.d.ts +0 -2
- package/dist/utils/env.test.d.ts.map +0 -1
- package/dist/utils/env.test.js +0 -52
package/dist/kernel.test.js
DELETED
|
@@ -1,414 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { ObjectKernel } from './kernel';
|
|
3
|
-
import { ServiceLifecycle } from './plugin-loader';
|
|
4
|
-
describe('ObjectKernel', () => {
|
|
5
|
-
let kernel;
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
kernel = new ObjectKernel({
|
|
8
|
-
logger: { level: 'error' }, // Suppress logs in tests
|
|
9
|
-
gracefulShutdown: false, // Disable for tests
|
|
10
|
-
skipSystemValidation: true,
|
|
11
|
-
});
|
|
12
|
-
});
|
|
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 () => { },
|
|
19
|
-
};
|
|
20
|
-
await kernel.use(plugin);
|
|
21
|
-
await kernel.bootstrap();
|
|
22
|
-
expect(kernel.isRunning()).toBe(true);
|
|
23
|
-
await kernel.shutdown();
|
|
24
|
-
});
|
|
25
|
-
it('should validate plugin during registration', async () => {
|
|
26
|
-
const invalidPlugin = {
|
|
27
|
-
name: '',
|
|
28
|
-
init: async () => { },
|
|
29
|
-
};
|
|
30
|
-
await expect(async () => {
|
|
31
|
-
await kernel.use(invalidPlugin);
|
|
32
|
-
}).rejects.toThrow();
|
|
33
|
-
});
|
|
34
|
-
it('should reject plugin registration after bootstrap', async () => {
|
|
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');
|
|
43
|
-
await kernel.shutdown();
|
|
44
|
-
});
|
|
45
|
-
});
|
|
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 () => {
|
|
91
|
-
const plugin = {
|
|
92
|
-
name: 'slow-init',
|
|
93
|
-
version: '1.0.0',
|
|
94
|
-
init: async () => {
|
|
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));
|
|
125
|
-
},
|
|
126
|
-
start: async () => {
|
|
127
|
-
await new Promise(resolve => setTimeout(resolve, 10));
|
|
128
|
-
},
|
|
129
|
-
startupTimeout: 1000,
|
|
130
|
-
};
|
|
131
|
-
await kernel.use(plugin);
|
|
132
|
-
await kernel.bootstrap();
|
|
133
|
-
expect(kernel.isRunning()).toBe(true);
|
|
134
|
-
await kernel.shutdown();
|
|
135
|
-
});
|
|
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 () => {
|
|
200
|
-
const plugin = {
|
|
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',
|
|
280
|
-
init: async () => { },
|
|
281
|
-
destroy: async () => {
|
|
282
|
-
plugin1Destroyed = true;
|
|
283
|
-
},
|
|
284
|
-
};
|
|
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);
|
|
295
|
-
await kernel.bootstrap();
|
|
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();
|
|
319
|
-
expect(kernel.getState()).toBe('stopped');
|
|
320
|
-
});
|
|
321
|
-
it('should trigger shutdown hook', async () => {
|
|
322
|
-
let hookCalled = false;
|
|
323
|
-
const plugin = {
|
|
324
|
-
name: 'hook-plugin',
|
|
325
|
-
version: '1.0.0',
|
|
326
|
-
init: async (ctx) => {
|
|
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;
|
|
341
|
-
});
|
|
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
|
-
},
|
|
364
|
-
};
|
|
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');
|
|
412
|
-
});
|
|
413
|
-
});
|
|
414
|
-
});
|
package/dist/lite-kernel.d.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
package/dist/lite-kernel.js
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { createLogger } from './logger.js';
|
|
2
|
-
import { ObjectKernelBase } from './kernel-base.js';
|
|
3
|
-
/**
|
|
4
|
-
* ObjectKernel - MiniKernel Architecture
|
|
5
|
-
*
|
|
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
|
|
17
|
-
*/
|
|
18
|
-
export class LiteKernel 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();
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Register a plugin
|
|
27
|
-
* @param plugin - Plugin instance
|
|
28
|
-
*/
|
|
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`);
|
|
34
|
-
}
|
|
35
|
-
this.plugins.set(pluginName, plugin);
|
|
36
|
-
return this;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
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
|
|
44
|
-
*/
|
|
45
|
-
async bootstrap() {
|
|
46
|
-
this.validateState('idle');
|
|
47
|
-
this.state = 'initializing';
|
|
48
|
-
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);
|
|
55
|
-
}
|
|
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);
|
|
61
|
-
}
|
|
62
|
-
// Trigger ready hook
|
|
63
|
-
await this.triggerHook('kernel:ready');
|
|
64
|
-
this.logger.info('✅ Bootstrap complete', {
|
|
65
|
-
pluginCount: this.plugins.size
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Shutdown the kernel
|
|
70
|
-
* Calls destroy on all plugins in reverse order
|
|
71
|
-
*/
|
|
72
|
-
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');
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
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') {
|
|
96
|
-
await this.logger.destroy();
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Get a service from the registry
|
|
101
|
-
* Convenience method for external access
|
|
102
|
-
*/
|
|
103
|
-
getService(name) {
|
|
104
|
-
return this.context.getService(name);
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Check if kernel is running
|
|
108
|
-
*/
|
|
109
|
-
isRunning() {
|
|
110
|
-
return this.state === 'running';
|
|
111
|
-
}
|
|
112
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lite-kernel.test.d.ts","sourceRoot":"","sources":["../src/lite-kernel.test.ts"],"names":[],"mappings":""}
|