@objectstack/core 4.0.3 → 4.0.5

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 (75) hide show
  1. package/README.md +95 -10
  2. package/dist/index.cjs +169 -507
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +24 -223
  5. package/dist/index.d.ts +24 -223
  6. package/dist/index.js +175 -505
  7. package/dist/index.js.map +1 -1
  8. package/dist/logger.cjs +177 -0
  9. package/dist/logger.cjs.map +1 -0
  10. package/dist/logger.d.cts +26 -0
  11. package/dist/logger.d.ts +26 -0
  12. package/dist/logger.js +158 -0
  13. package/dist/logger.js.map +1 -0
  14. package/package.json +36 -15
  15. package/.turbo/turbo-build.log +0 -22
  16. package/ADVANCED_FEATURES.md +0 -380
  17. package/API_REGISTRY.md +0 -392
  18. package/CHANGELOG.md +0 -465
  19. package/PHASE2_IMPLEMENTATION.md +0 -388
  20. package/REFACTORING_SUMMARY.md +0 -40
  21. package/examples/api-registry-example.ts +0 -559
  22. package/examples/kernel-features-example.ts +0 -311
  23. package/examples/phase2-integration.ts +0 -357
  24. package/src/api-registry-plugin.test.ts +0 -393
  25. package/src/api-registry-plugin.ts +0 -89
  26. package/src/api-registry.test.ts +0 -1089
  27. package/src/api-registry.ts +0 -739
  28. package/src/contracts/data-engine.ts +0 -57
  29. package/src/contracts/http-server.ts +0 -151
  30. package/src/contracts/logger.ts +0 -72
  31. package/src/dependency-resolver.test.ts +0 -287
  32. package/src/dependency-resolver.ts +0 -390
  33. package/src/fallbacks/fallbacks.test.ts +0 -281
  34. package/src/fallbacks/index.ts +0 -26
  35. package/src/fallbacks/memory-cache.ts +0 -34
  36. package/src/fallbacks/memory-i18n.ts +0 -112
  37. package/src/fallbacks/memory-job.ts +0 -23
  38. package/src/fallbacks/memory-metadata.ts +0 -50
  39. package/src/fallbacks/memory-queue.ts +0 -28
  40. package/src/health-monitor.test.ts +0 -81
  41. package/src/health-monitor.ts +0 -318
  42. package/src/hot-reload.ts +0 -382
  43. package/src/index.ts +0 -50
  44. package/src/kernel-base.ts +0 -273
  45. package/src/kernel.test.ts +0 -624
  46. package/src/kernel.ts +0 -631
  47. package/src/lite-kernel.test.ts +0 -248
  48. package/src/lite-kernel.ts +0 -137
  49. package/src/logger.test.ts +0 -116
  50. package/src/logger.ts +0 -355
  51. package/src/namespace-resolver.test.ts +0 -130
  52. package/src/namespace-resolver.ts +0 -188
  53. package/src/package-manager.test.ts +0 -225
  54. package/src/package-manager.ts +0 -428
  55. package/src/plugin-loader.test.ts +0 -421
  56. package/src/plugin-loader.ts +0 -484
  57. package/src/qa/adapter.ts +0 -16
  58. package/src/qa/http-adapter.ts +0 -116
  59. package/src/qa/index.ts +0 -5
  60. package/src/qa/runner.ts +0 -189
  61. package/src/security/index.ts +0 -50
  62. package/src/security/permission-manager.test.ts +0 -256
  63. package/src/security/permission-manager.ts +0 -338
  64. package/src/security/plugin-config-validator.test.ts +0 -276
  65. package/src/security/plugin-config-validator.ts +0 -193
  66. package/src/security/plugin-permission-enforcer.test.ts +0 -251
  67. package/src/security/plugin-permission-enforcer.ts +0 -436
  68. package/src/security/plugin-signature-verifier.ts +0 -403
  69. package/src/security/sandbox-runtime.ts +0 -462
  70. package/src/security/security-scanner.ts +0 -367
  71. package/src/types.ts +0 -120
  72. package/src/utils/env.test.ts +0 -62
  73. package/src/utils/env.ts +0 -53
  74. package/tsconfig.json +0 -10
  75. package/vitest.config.ts +0 -10
@@ -1,421 +0,0 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
2
- import { PluginLoader, ServiceLifecycle, PluginMetadata } from './plugin-loader';
3
- import { createLogger } from './logger';
4
- import type { Plugin, PluginContext } from './types';
5
-
6
- describe('PluginLoader', () => {
7
- let loader: PluginLoader;
8
-
9
- beforeEach(() => {
10
- const logger = createLogger({ level: 'error' }); // Suppress logs in tests
11
- loader = new PluginLoader(logger);
12
- loader.setContext({
13
- registerService: () => {},
14
- getService: () => { throw new Error('Mock service not found'); },
15
- hook: () => {},
16
- trigger: async () => {},
17
- getServices: () => new Map(),
18
- logger: logger,
19
- getKernel: () => ({}) as any
20
- } as PluginContext);
21
- });
22
-
23
- describe('Plugin Loading', () => {
24
- it('should load a valid plugin', async () => {
25
- const plugin: Plugin = {
26
- name: 'test-plugin',
27
- version: '1.0.0',
28
- init: async () => {},
29
- };
30
-
31
- const result = await loader.loadPlugin(plugin);
32
-
33
- expect(result.success).toBe(true);
34
- expect(result.plugin?.name).toBe('test-plugin');
35
- expect(result.plugin?.version).toBe('1.0.0');
36
- expect(result.loadTime).toBeGreaterThanOrEqual(0);
37
- });
38
-
39
- it('should reject plugin with invalid name', async () => {
40
- const plugin: Plugin = {
41
- name: '',
42
- init: async () => {},
43
- };
44
-
45
- const result = await loader.loadPlugin(plugin);
46
-
47
- expect(result.success).toBe(false);
48
- expect(result.error?.message).toContain('name is required');
49
- });
50
-
51
- it('should reject plugin without init function', async () => {
52
- const plugin: any = {
53
- name: 'invalid-plugin',
54
- };
55
-
56
- const result = await loader.loadPlugin(plugin);
57
-
58
- expect(result.success).toBe(false);
59
- expect(result.error?.message).toContain('init function is required');
60
- });
61
-
62
- it('should use default version 0.0.0 if not provided', async () => {
63
- const plugin: Plugin = {
64
- name: 'no-version',
65
- init: async () => {},
66
- };
67
-
68
- const result = await loader.loadPlugin(plugin);
69
-
70
- expect(result.success).toBe(true);
71
- expect(result.plugin?.version).toBe('0.0.0');
72
- });
73
- });
74
-
75
- describe('Version Compatibility', () => {
76
- it('should accept valid semantic versions', async () => {
77
- const validVersions = ['1.0.0', '2.3.4', '0.0.1', '10.20.30'];
78
-
79
- for (const version of validVersions) {
80
- const plugin: Plugin = {
81
- name: `plugin-${version}`,
82
- version,
83
- init: async () => {},
84
- };
85
-
86
- const result = await loader.loadPlugin(plugin);
87
- expect(result.success).toBe(true);
88
- }
89
- });
90
-
91
- it('should accept versions with pre-release tags', async () => {
92
- const plugin: Plugin = {
93
- name: 'prerelease',
94
- version: '1.0.0-alpha.1',
95
- init: async () => {},
96
- };
97
-
98
- const result = await loader.loadPlugin(plugin);
99
- expect(result.success).toBe(true);
100
- });
101
-
102
- it('should accept versions with build metadata', async () => {
103
- const plugin: Plugin = {
104
- name: 'build-meta',
105
- version: '1.0.0+20230101',
106
- init: async () => {},
107
- };
108
-
109
- const result = await loader.loadPlugin(plugin);
110
- expect(result.success).toBe(true);
111
- });
112
-
113
- it('should reject invalid semantic versions', async () => {
114
- const invalidVersions = ['1.0', 'v1.0.0', '1', 'invalid'];
115
-
116
- for (const version of invalidVersions) {
117
- const plugin: Plugin = {
118
- name: `invalid-${version}`,
119
- version,
120
- init: async () => {},
121
- };
122
-
123
- const result = await loader.loadPlugin(plugin);
124
- expect(result.success).toBe(false);
125
- }
126
- });
127
- });
128
-
129
- describe('Service Factory Registration', () => {
130
- it('should register a singleton service factory', () => {
131
- let callCount = 0;
132
- const factory = () => {
133
- callCount++;
134
- return { value: callCount };
135
- };
136
-
137
- loader.registerServiceFactory({
138
- name: 'singleton-service',
139
- factory,
140
- lifecycle: ServiceLifecycle.SINGLETON,
141
- });
142
-
143
- expect(() => {
144
- loader.registerServiceFactory({
145
- name: 'singleton-service',
146
- factory,
147
- lifecycle: ServiceLifecycle.SINGLETON,
148
- });
149
- }).toThrow('already registered');
150
- });
151
-
152
- it('should register multiple service factories with different names', () => {
153
- loader.registerServiceFactory({
154
- name: 'service-1',
155
- factory: () => ({ id: 1 }),
156
- lifecycle: ServiceLifecycle.SINGLETON,
157
- });
158
-
159
- loader.registerServiceFactory({
160
- name: 'service-2',
161
- factory: () => ({ id: 2 }),
162
- lifecycle: ServiceLifecycle.TRANSIENT,
163
- });
164
-
165
- // Should not throw
166
- expect(true).toBe(true);
167
- });
168
- });
169
-
170
- describe('Service Retrieval with Lifecycle', () => {
171
- it('should create singleton service only once', async () => {
172
- let callCount = 0;
173
- loader.registerServiceFactory({
174
- name: 'counter',
175
- factory: () => {
176
- callCount++;
177
- return { count: callCount };
178
- },
179
- lifecycle: ServiceLifecycle.SINGLETON,
180
- });
181
-
182
- const service1 = await loader.getService('counter');
183
- const service2 = await loader.getService('counter');
184
-
185
- expect(callCount).toBe(1);
186
- expect(service1).toBe(service2);
187
- });
188
-
189
- it('should create new transient service on each request', async () => {
190
- let callCount = 0;
191
- loader.registerServiceFactory({
192
- name: 'transient',
193
- factory: () => {
194
- callCount++;
195
- return { count: callCount };
196
- },
197
- lifecycle: ServiceLifecycle.TRANSIENT,
198
- });
199
-
200
- const service1 = await loader.getService('transient');
201
- const service2 = await loader.getService('transient');
202
-
203
- expect(callCount).toBe(2);
204
- expect(service1).not.toBe(service2);
205
- expect((service1 as any).count).toBe(1);
206
- expect((service2 as any).count).toBe(2);
207
- });
208
-
209
- it('should create scoped service once per scope', async () => {
210
- let callCount = 0;
211
- loader.registerServiceFactory({
212
- name: 'scoped',
213
- factory: () => {
214
- callCount++;
215
- return { count: callCount };
216
- },
217
- lifecycle: ServiceLifecycle.SCOPED,
218
- });
219
-
220
- const scope1Service1 = await loader.getService('scoped', 'scope-1');
221
- const scope1Service2 = await loader.getService('scoped', 'scope-1');
222
- const scope2Service1 = await loader.getService('scoped', 'scope-2');
223
-
224
- expect(callCount).toBe(2); // Once per scope
225
- expect(scope1Service1).toBe(scope1Service2); // Same within scope
226
- expect(scope1Service1).not.toBe(scope2Service1); // Different across scopes
227
- });
228
-
229
- it('should throw error for scoped service without scope ID', async () => {
230
- loader.registerServiceFactory({
231
- name: 'scoped-no-id',
232
- factory: () => ({ value: 'test' }),
233
- lifecycle: ServiceLifecycle.SCOPED,
234
- });
235
-
236
- await expect(async () => {
237
- await loader.getService('scoped-no-id');
238
- }).rejects.toThrow('Scope ID required');
239
- });
240
-
241
- it('should throw error for non-existent service', async () => {
242
- await expect(async () => {
243
- await loader.getService('non-existent');
244
- }).rejects.toThrow('not found');
245
- });
246
- });
247
-
248
- describe('Circular Dependency Detection', () => {
249
- it('should detect simple circular dependency', () => {
250
- loader.registerServiceFactory({
251
- name: 'service-a',
252
- factory: () => ({}),
253
- lifecycle: ServiceLifecycle.SINGLETON,
254
- dependencies: ['service-b'],
255
- });
256
-
257
- loader.registerServiceFactory({
258
- name: 'service-b',
259
- factory: () => ({}),
260
- lifecycle: ServiceLifecycle.SINGLETON,
261
- dependencies: ['service-a'],
262
- });
263
-
264
- const cycles = loader.detectCircularDependencies();
265
- expect(cycles.length).toBeGreaterThan(0);
266
- expect(cycles[0]).toContain('service-a');
267
- expect(cycles[0]).toContain('service-b');
268
- });
269
-
270
- it('should detect complex circular dependency', () => {
271
- loader.registerServiceFactory({
272
- name: 'service-a',
273
- factory: () => ({}),
274
- lifecycle: ServiceLifecycle.SINGLETON,
275
- dependencies: ['service-b'],
276
- });
277
-
278
- loader.registerServiceFactory({
279
- name: 'service-b',
280
- factory: () => ({}),
281
- lifecycle: ServiceLifecycle.SINGLETON,
282
- dependencies: ['service-c'],
283
- });
284
-
285
- loader.registerServiceFactory({
286
- name: 'service-c',
287
- factory: () => ({}),
288
- lifecycle: ServiceLifecycle.SINGLETON,
289
- dependencies: ['service-a'],
290
- });
291
-
292
- const cycles = loader.detectCircularDependencies();
293
- expect(cycles.length).toBeGreaterThan(0);
294
- });
295
-
296
- it('should not report false positives for valid dependency chains', () => {
297
- loader.registerServiceFactory({
298
- name: 'service-a',
299
- factory: () => ({}),
300
- lifecycle: ServiceLifecycle.SINGLETON,
301
- dependencies: ['service-b'],
302
- });
303
-
304
- loader.registerServiceFactory({
305
- name: 'service-b',
306
- factory: () => ({}),
307
- lifecycle: ServiceLifecycle.SINGLETON,
308
- dependencies: ['service-c'],
309
- });
310
-
311
- loader.registerServiceFactory({
312
- name: 'service-c',
313
- factory: () => ({}),
314
- lifecycle: ServiceLifecycle.SINGLETON,
315
- });
316
-
317
- const cycles = loader.detectCircularDependencies();
318
- expect(cycles.length).toBe(0);
319
- });
320
- });
321
-
322
- describe('Plugin Health Checks', () => {
323
- it('should return healthy for plugin without health check', async () => {
324
- const plugin: Plugin = {
325
- name: 'no-health-check',
326
- version: '1.0.0',
327
- init: async () => {},
328
- };
329
-
330
- await loader.loadPlugin(plugin);
331
- const health = await loader.checkPluginHealth('no-health-check');
332
-
333
- expect(health.healthy).toBe(true);
334
- expect(health.message).toContain('No health check');
335
- });
336
-
337
- it('should execute plugin health check', async () => {
338
- const plugin: PluginMetadata = {
339
- name: 'with-health-check',
340
- version: '1.0.0',
341
- init: async () => {},
342
- healthCheck: async () => ({
343
- healthy: true,
344
- message: 'All systems operational',
345
- }),
346
- };
347
-
348
- await loader.loadPlugin(plugin);
349
- const health = await loader.checkPluginHealth('with-health-check');
350
-
351
- expect(health.healthy).toBe(true);
352
- expect(health.message).toBe('All systems operational');
353
- expect(health.lastCheck).toBeInstanceOf(Date);
354
- });
355
-
356
- it('should handle failing health check', async () => {
357
- const plugin: PluginMetadata = {
358
- name: 'failing-health',
359
- version: '1.0.0',
360
- init: async () => {},
361
- healthCheck: async () => {
362
- throw new Error('Service unavailable');
363
- },
364
- };
365
-
366
- await loader.loadPlugin(plugin);
367
- const health = await loader.checkPluginHealth('failing-health');
368
-
369
- expect(health.healthy).toBe(false);
370
- expect(health.message).toContain('Health check failed');
371
- });
372
-
373
- it('should return not found for unknown plugin', async () => {
374
- const health = await loader.checkPluginHealth('unknown-plugin');
375
-
376
- expect(health.healthy).toBe(false);
377
- expect(health.message).toContain('not found');
378
- });
379
- });
380
-
381
- describe('Scope Management', () => {
382
- it('should clear scoped services', async () => {
383
- let callCount = 0;
384
- loader.registerServiceFactory({
385
- name: 'scoped-clear',
386
- factory: () => {
387
- callCount++;
388
- return { count: callCount };
389
- },
390
- lifecycle: ServiceLifecycle.SCOPED,
391
- });
392
-
393
- const service1 = await loader.getService('scoped-clear', 'scope-1');
394
- expect((service1 as any).count).toBe(1);
395
-
396
- loader.clearScope('scope-1');
397
-
398
- const service2 = await loader.getService('scoped-clear', 'scope-1');
399
- expect((service2 as any).count).toBe(2); // New instance created
400
- });
401
- });
402
-
403
- describe('Static Service Registration', () => {
404
- it('should register static service instance', () => {
405
- const service = { value: 'test' };
406
- loader.registerService('static-service', service);
407
-
408
- expect(() => {
409
- loader.registerService('static-service', service);
410
- }).toThrow('already registered');
411
- });
412
-
413
- it('should retrieve static service', async () => {
414
- const service = { value: 'static' };
415
- loader.registerService('static', service);
416
-
417
- const retrieved = await loader.getService('static');
418
- expect(retrieved).toBe(service);
419
- });
420
- });
421
- });