@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,193 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- import { z } from 'zod';
4
- import type { Logger } from '@objectstack/spec/contracts';
5
- import type { PluginMetadata } from '../plugin-loader.js';
6
-
7
- /**
8
- * Plugin Configuration Validator
9
- *
10
- * Validates plugin configurations against Zod schemas to ensure:
11
- * 1. Type safety - all config values have correct types
12
- * 2. Business rules - values meet constraints (min/max, regex, etc.)
13
- * 3. Required fields - all mandatory configuration is provided
14
- * 4. Default values - missing optional fields get defaults
15
- *
16
- * Architecture:
17
- * - Uses Zod for runtime validation
18
- * - Provides detailed error messages with field paths
19
- * - Supports nested configuration objects
20
- * - Allows partial validation for incremental updates
21
- *
22
- * Usage:
23
- * ```typescript
24
- * const validator = new PluginConfigValidator(logger);
25
- * const validConfig = validator.validatePluginConfig(plugin, userConfig);
26
- * ```
27
- */
28
- export class PluginConfigValidator {
29
- private logger: Logger;
30
-
31
- constructor(logger: Logger) {
32
- this.logger = logger;
33
- }
34
-
35
- /**
36
- * Validate plugin configuration against its Zod schema
37
- *
38
- * @param plugin - Plugin metadata with configSchema
39
- * @param config - User-provided configuration
40
- * @returns Validated and typed configuration
41
- * @throws Error with detailed validation errors
42
- */
43
- validatePluginConfig<T = any>(plugin: PluginMetadata, config: any): T {
44
- if (!plugin.configSchema) {
45
- this.logger.debug(`Plugin ${plugin.name} has no config schema - skipping validation`);
46
- return config as T;
47
- }
48
-
49
- try {
50
- // Use Zod to parse and validate
51
- const validatedConfig = plugin.configSchema.parse(config);
52
-
53
- this.logger.debug(`✅ Plugin config validated: ${plugin.name}`, {
54
- plugin: plugin.name,
55
- configKeys: Object.keys(config || {}).length,
56
- });
57
-
58
- return validatedConfig as T;
59
- } catch (error) {
60
- if (error instanceof z.ZodError) {
61
- const formattedErrors = this.formatZodErrors(error);
62
- const errorMessage = [
63
- `Plugin ${plugin.name} configuration validation failed:`,
64
- ...formattedErrors.map(e => ` - ${e.path}: ${e.message}`),
65
- ].join('\n');
66
-
67
- this.logger.error(errorMessage, undefined, {
68
- plugin: plugin.name,
69
- errors: formattedErrors,
70
- });
71
-
72
- throw new Error(errorMessage);
73
- }
74
-
75
- // Re-throw other errors
76
- throw error;
77
- }
78
- }
79
-
80
- /**
81
- * Validate partial configuration (for incremental updates)
82
- *
83
- * @param plugin - Plugin metadata
84
- * @param partialConfig - Partial configuration to validate
85
- * @returns Validated partial configuration
86
- */
87
- validatePartialConfig<T = any>(plugin: PluginMetadata, partialConfig: any): Partial<T> {
88
- if (!plugin.configSchema) {
89
- return partialConfig as Partial<T>;
90
- }
91
-
92
- try {
93
- // Use Zod's partial() method for partial validation
94
- // Cast to ZodObject to access partial() method
95
- const partialSchema = (plugin.configSchema as any).partial();
96
- const validatedConfig = partialSchema.parse(partialConfig);
97
-
98
- this.logger.debug(`✅ Partial config validated: ${plugin.name}`);
99
- return validatedConfig as Partial<T>;
100
- } catch (error) {
101
- if (error instanceof z.ZodError) {
102
- const formattedErrors = this.formatZodErrors(error);
103
- const errorMessage = [
104
- `Plugin ${plugin.name} partial configuration validation failed:`,
105
- ...formattedErrors.map(e => ` - ${e.path}: ${e.message}`),
106
- ].join('\n');
107
-
108
- throw new Error(errorMessage);
109
- }
110
-
111
- throw error;
112
- }
113
- }
114
-
115
- /**
116
- * Get default configuration from schema
117
- *
118
- * @param plugin - Plugin metadata
119
- * @returns Default configuration object
120
- */
121
- getDefaultConfig<T = any>(plugin: PluginMetadata): T | undefined {
122
- if (!plugin.configSchema) {
123
- return undefined;
124
- }
125
-
126
- try {
127
- // Parse empty object to get defaults
128
- const defaults = plugin.configSchema.parse({});
129
- this.logger.debug(`Default config extracted: ${plugin.name}`);
130
- return defaults as T;
131
- } catch (error) {
132
- // Schema may require some fields - return undefined
133
- this.logger.debug(`No default config available: ${plugin.name}`);
134
- return undefined;
135
- }
136
- }
137
-
138
- /**
139
- * Check if configuration is valid without throwing
140
- *
141
- * @param plugin - Plugin metadata
142
- * @param config - Configuration to check
143
- * @returns True if valid, false otherwise
144
- */
145
- isConfigValid(plugin: PluginMetadata, config: any): boolean {
146
- if (!plugin.configSchema) {
147
- return true;
148
- }
149
-
150
- const result = plugin.configSchema.safeParse(config);
151
- return result.success;
152
- }
153
-
154
- /**
155
- * Get configuration errors without throwing
156
- *
157
- * @param plugin - Plugin metadata
158
- * @param config - Configuration to check
159
- * @returns Array of validation errors, or empty array if valid
160
- */
161
- getConfigErrors(plugin: PluginMetadata, config: any): Array<{path: string; message: string}> {
162
- if (!plugin.configSchema) {
163
- return [];
164
- }
165
-
166
- const result = plugin.configSchema.safeParse(config);
167
-
168
- if (result.success) {
169
- return [];
170
- }
171
-
172
- return this.formatZodErrors(result.error);
173
- }
174
-
175
- // Private methods
176
-
177
- private formatZodErrors(error: z.ZodError<any>): Array<{path: string; message: string}> {
178
- return error.issues.map((e: z.ZodIssue) => ({
179
- path: e.path.join('.') || 'root',
180
- message: e.message,
181
- }));
182
- }
183
- }
184
-
185
- /**
186
- * Create a plugin config validator
187
- *
188
- * @param logger - Logger instance
189
- * @returns Plugin config validator
190
- */
191
- export function createPluginConfigValidator(logger: Logger): PluginConfigValidator {
192
- return new PluginConfigValidator(logger);
193
- }
@@ -1,251 +0,0 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
2
- import { PluginPermissionEnforcer, SecurePluginContext } from './plugin-permission-enforcer.js';
3
- import { createLogger } from '../logger.js';
4
- import type { PluginCapability } from '@objectstack/spec/kernel';
5
- import type { PluginContext } from '../types.js';
6
-
7
- describe('PluginPermissionEnforcer', () => {
8
- let enforcer: PluginPermissionEnforcer;
9
- let logger: ReturnType<typeof createLogger>;
10
-
11
- beforeEach(() => {
12
- logger = createLogger({ level: 'error' });
13
- enforcer = new PluginPermissionEnforcer(logger);
14
- });
15
-
16
- describe('registerPluginPermissions', () => {
17
- it('should register plugin capabilities', () => {
18
- const capabilities: PluginCapability[] = [
19
- {
20
- protocol: {
21
- id: 'com.objectstack.protocol.service.database.v1',
22
- label: 'Database Service',
23
- version: { major: 1, minor: 0, patch: 0 },
24
- },
25
- conformance: 'full',
26
- certified: false,
27
- },
28
- ];
29
-
30
- enforcer.registerPluginPermissions('com.test.plugin', capabilities);
31
-
32
- const registeredCapabilities = enforcer.getPluginCapabilities('com.test.plugin');
33
- expect(registeredCapabilities).toEqual(capabilities);
34
- });
35
- });
36
-
37
- describe('enforceServiceAccess', () => {
38
- it('should allow access to declared services', () => {
39
- const capabilities: PluginCapability[] = [
40
- {
41
- protocol: {
42
- id: 'com.objectstack.protocol.service.database.v1',
43
- label: 'Database Service',
44
- version: { major: 1, minor: 0, patch: 0 },
45
- },
46
- conformance: 'full',
47
- certified: false,
48
- },
49
- ];
50
-
51
- enforcer.registerPluginPermissions('com.test.plugin', capabilities);
52
-
53
- // Should not throw
54
- expect(() => {
55
- enforcer.enforceServiceAccess('com.test.plugin', 'database');
56
- }).not.toThrow();
57
- });
58
-
59
- it('should deny access to undeclared services', () => {
60
- const capabilities: PluginCapability[] = [
61
- {
62
- protocol: {
63
- id: 'com.objectstack.protocol.service.database.v1',
64
- label: 'Database Service',
65
- version: { major: 1, minor: 0, patch: 0 },
66
- },
67
- conformance: 'full',
68
- certified: false,
69
- },
70
- ];
71
-
72
- enforcer.registerPluginPermissions('com.test.plugin', capabilities);
73
-
74
- expect(() => {
75
- enforcer.enforceServiceAccess('com.test.plugin', 'network');
76
- }).toThrow(/Permission denied/);
77
- });
78
-
79
- it('should allow wildcard service access', () => {
80
- const capabilities: PluginCapability[] = [
81
- {
82
- protocol: {
83
- id: 'com.objectstack.protocol.service.all.v1',
84
- label: 'All Services',
85
- version: { major: 1, minor: 0, patch: 0 },
86
- },
87
- conformance: 'full',
88
- certified: false,
89
- },
90
- ];
91
-
92
- enforcer.registerPluginPermissions('com.test.plugin', capabilities);
93
-
94
- // Should allow any service
95
- expect(() => {
96
- enforcer.enforceServiceAccess('com.test.plugin', 'database');
97
- enforcer.enforceServiceAccess('com.test.plugin', 'network');
98
- enforcer.enforceServiceAccess('com.test.plugin', 'filesystem');
99
- }).not.toThrow();
100
- });
101
- });
102
-
103
- describe('enforceHookTrigger', () => {
104
- it('should allow triggering declared hooks', () => {
105
- const capabilities: PluginCapability[] = [
106
- {
107
- protocol: {
108
- id: 'com.objectstack.protocol.hook.data.v1',
109
- label: 'Data Hooks',
110
- version: { major: 1, minor: 0, patch: 0 },
111
- },
112
- conformance: 'full',
113
- certified: false,
114
- },
115
- ];
116
-
117
- enforcer.registerPluginPermissions('com.test.plugin', capabilities);
118
-
119
- expect(() => {
120
- enforcer.enforceHookTrigger('com.test.plugin', 'data:beforeCreate');
121
- }).not.toThrow();
122
- });
123
-
124
- it('should deny triggering undeclared hooks', () => {
125
- const capabilities: PluginCapability[] = [
126
- {
127
- protocol: {
128
- id: 'com.objectstack.protocol.hook.data.v1',
129
- label: 'Data Hooks',
130
- version: { major: 1, minor: 0, patch: 0 },
131
- },
132
- conformance: 'full',
133
- certified: false,
134
- },
135
- ];
136
-
137
- enforcer.registerPluginPermissions('com.test.plugin', capabilities);
138
-
139
- expect(() => {
140
- enforcer.enforceHookTrigger('com.test.plugin', 'kernel:shutdown');
141
- }).toThrow(/Permission denied/);
142
- });
143
- });
144
-
145
- describe('revokePermissions', () => {
146
- it('should revoke plugin permissions', () => {
147
- const capabilities: PluginCapability[] = [
148
- {
149
- protocol: {
150
- id: 'com.objectstack.protocol.service.database.v1',
151
- label: 'Database Service',
152
- version: { major: 1, minor: 0, patch: 0 },
153
- },
154
- conformance: 'full',
155
- certified: false,
156
- },
157
- ];
158
-
159
- enforcer.registerPluginPermissions('com.test.plugin', capabilities);
160
- enforcer.revokePermissions('com.test.plugin');
161
-
162
- expect(() => {
163
- enforcer.enforceServiceAccess('com.test.plugin', 'database');
164
- }).toThrow(/Permission denied/);
165
- });
166
- });
167
- });
168
-
169
- describe('SecurePluginContext', () => {
170
- let enforcer: PluginPermissionEnforcer;
171
- let logger: ReturnType<typeof createLogger>;
172
- let mockBaseContext: PluginContext;
173
-
174
- beforeEach(() => {
175
- logger = createLogger({ level: 'error' });
176
- enforcer = new PluginPermissionEnforcer(logger);
177
-
178
- mockBaseContext = {
179
- registerService: () => {},
180
- getService: <T>(name: string): T => ({ name } as any),
181
- getServices: () => new Map(),
182
- hook: () => {},
183
- trigger: async () => {},
184
- logger,
185
- getKernel: () => ({} as any),
186
- };
187
- });
188
-
189
- describe('getService', () => {
190
- it('should check permission before accessing service', () => {
191
- const capabilities: PluginCapability[] = [
192
- {
193
- protocol: {
194
- id: 'com.objectstack.protocol.service.database.v1',
195
- label: 'Database Service',
196
- version: { major: 1, minor: 0, patch: 0 },
197
- },
198
- conformance: 'full',
199
- certified: false,
200
- },
201
- ];
202
-
203
- enforcer.registerPluginPermissions('com.test.plugin', capabilities);
204
-
205
- const secureContext = new SecurePluginContext(
206
- 'com.test.plugin',
207
- enforcer,
208
- mockBaseContext
209
- );
210
-
211
- // Should succeed with permission
212
- const service = secureContext.getService('database');
213
- expect(service).toBeDefined();
214
-
215
- // Should fail without permission
216
- expect(() => {
217
- secureContext.getService('network');
218
- }).toThrow(/Permission denied/);
219
- });
220
- });
221
-
222
- describe('trigger', () => {
223
- it('should check permission before triggering hook', async () => {
224
- const capabilities: PluginCapability[] = [
225
- {
226
- protocol: {
227
- id: 'com.objectstack.protocol.hook.data.v1',
228
- label: 'Data Hooks',
229
- version: { major: 1, minor: 0, patch: 0 },
230
- },
231
- conformance: 'full',
232
- certified: false,
233
- },
234
- ];
235
-
236
- enforcer.registerPluginPermissions('com.test.plugin', capabilities);
237
-
238
- const secureContext = new SecurePluginContext(
239
- 'com.test.plugin',
240
- enforcer,
241
- mockBaseContext
242
- );
243
-
244
- // Should succeed with permission
245
- await expect(secureContext.trigger('data:beforeCreate')).resolves.not.toThrow();
246
-
247
- // Should fail without permission
248
- await expect(secureContext.trigger('kernel:shutdown')).rejects.toThrow(/Permission denied/);
249
- });
250
- });
251
- });