@objectstack/core 4.0.4 → 4.1.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 (75) hide show
  1. package/README.md +95 -10
  2. package/dist/index.cjs +172 -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 +178 -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 -472
  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
package/src/qa/runner.ts DELETED
@@ -1,189 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- import { QA } from '@objectstack/spec';
4
- import { TestExecutionAdapter } from './adapter.js';
5
-
6
- export interface TestResult {
7
- scenarioId: string;
8
- passed: boolean;
9
- steps: StepResult[];
10
- error?: unknown;
11
- duration: number;
12
- }
13
-
14
- export interface StepResult {
15
- stepName: string;
16
- passed: boolean;
17
- error?: unknown;
18
- output?: unknown;
19
- duration: number;
20
- }
21
-
22
- export class TestRunner {
23
- constructor(private adapter: TestExecutionAdapter) {}
24
-
25
- async runSuite(suite: QA.TestSuite): Promise<TestResult[]> {
26
- const results: TestResult[] = [];
27
- for (const scenario of suite.scenarios) {
28
- results.push(await this.runScenario(scenario));
29
- }
30
- return results;
31
- }
32
-
33
- async runScenario(scenario: QA.TestScenario): Promise<TestResult> {
34
- const startTime = Date.now();
35
- const context: Record<string, unknown> = {}; // Variable context
36
-
37
- // Initialize context from initial payload if needed? Currently schema doesn't have initial context prop on Scenario
38
- // But we defined TestContextSchema separately.
39
-
40
- // Setup
41
- if (scenario.setup) {
42
- for (const step of scenario.setup) {
43
- try {
44
- await this.runStep(step, context);
45
- } catch (e) {
46
- return {
47
- scenarioId: scenario.id,
48
- passed: false,
49
- steps: [],
50
- error: `Setup failed: ${e instanceof Error ? e.message : String(e)}`,
51
- duration: Date.now() - startTime
52
- };
53
- }
54
- }
55
- }
56
-
57
- const stepResults: StepResult[] = [];
58
- let scenarioPassed = true;
59
- let scenarioError: unknown = undefined;
60
-
61
- // Main Steps
62
- for (const step of scenario.steps) {
63
- const stepStartTime = Date.now();
64
- try {
65
- const output = await this.runStep(step, context);
66
- stepResults.push({
67
- stepName: step.name,
68
- passed: true,
69
- output,
70
- duration: Date.now() - stepStartTime
71
- });
72
- } catch (e) {
73
- scenarioPassed = false;
74
- scenarioError = e;
75
- stepResults.push({
76
- stepName: step.name,
77
- passed: false,
78
- error: e,
79
- duration: Date.now() - stepStartTime
80
- });
81
- break; // Stop on first failure
82
- }
83
- }
84
-
85
- // Teardown (run even if failed)
86
- if (scenario.teardown) {
87
- for (const step of scenario.teardown) {
88
- try {
89
- await this.runStep(step, context);
90
- } catch (e) {
91
- // Log teardown failure but don't override main failure if it exists
92
- if (scenarioPassed) {
93
- scenarioPassed = false;
94
- scenarioError = `Teardown failed: ${e instanceof Error ? e.message : String(e)}`;
95
- }
96
- }
97
- }
98
- }
99
-
100
- return {
101
- scenarioId: scenario.id,
102
- passed: scenarioPassed,
103
- steps: stepResults,
104
- error: scenarioError,
105
- duration: Date.now() - startTime
106
- };
107
- }
108
-
109
- private async runStep(step: QA.TestStep, context: Record<string, unknown>): Promise<unknown> {
110
- // 1. Resolve Variables with Context (Simple interpolation or just pass context?)
111
- // For now, assume adpater handles context resolution or we do basic replacement
112
- const resolvedAction = this.resolveVariables(step.action, context);
113
-
114
- // 2. Execute Action
115
- const result = await this.adapter.execute(resolvedAction, context);
116
-
117
- // 3. Capture Outputs
118
- if (step.capture) {
119
- for (const [varName, path] of Object.entries(step.capture)) {
120
- context[varName] = this.getValueByPath(result, path);
121
- }
122
- }
123
-
124
- // 4. Run Assertions
125
- if (step.assertions) {
126
- for (const assertion of step.assertions) {
127
- this.assert(result, assertion, context);
128
- }
129
- }
130
-
131
- return result;
132
- }
133
-
134
- private resolveVariables(action: QA.TestAction, context: Record<string, unknown>): QA.TestAction {
135
- const actionStr = JSON.stringify(action);
136
- const resolved = actionStr.replace(/\{\{([^}]+)\}\}/g, (_match, varPath: string) => {
137
- const value = this.getValueByPath(context, varPath.trim());
138
- if (value === undefined) return _match; // Keep unresolved
139
- return typeof value === 'string' ? value : JSON.stringify(value);
140
- });
141
- try {
142
- return JSON.parse(resolved) as QA.TestAction;
143
- } catch {
144
- return action; // Fallback to original if parse fails
145
- }
146
- }
147
-
148
- private getValueByPath(obj: unknown, path: string): unknown {
149
- if (!path) return obj;
150
- const parts = path.split('.');
151
- let current: any = obj;
152
- for (const part of parts) {
153
- if (current === null || current === undefined) return undefined;
154
- current = current[part];
155
- }
156
- return current;
157
- }
158
-
159
- private assert(result: unknown, assertion: QA.TestAssertion, _context: Record<string, unknown>) {
160
- const actual = this.getValueByPath(result, assertion.field);
161
- // Resolve expected value if it's a variable ref?
162
- const expected = assertion.expectedValue; // Simplify for now
163
-
164
- switch (assertion.operator) {
165
- case 'equals':
166
- if (actual !== expected) throw new Error(`Assertion failed: ${assertion.field} expected ${expected}, got ${actual}`);
167
- break;
168
- case 'not_equals':
169
- if (actual === expected) throw new Error(`Assertion failed: ${assertion.field} expected not ${expected}, got ${actual}`);
170
- break;
171
- case 'contains':
172
- if (Array.isArray(actual)) {
173
- if (!actual.includes(expected)) throw new Error(`Assertion failed: ${assertion.field} array does not contain ${expected}`);
174
- } else if (typeof actual === 'string') {
175
- if (!actual.includes(String(expected))) throw new Error(`Assertion failed: ${assertion.field} string does not contain ${expected}`);
176
- }
177
- break;
178
- case 'not_null':
179
- if (actual === null || actual === undefined) throw new Error(`Assertion failed: ${assertion.field} is null`);
180
- break;
181
- case 'is_null':
182
- if (actual !== null && actual !== undefined) throw new Error(`Assertion failed: ${assertion.field} is not null`);
183
- break;
184
- // ... Add other operators
185
- default:
186
- throw new Error(`Unknown assertion operator: ${assertion.operator}`);
187
- }
188
- }
189
- }
@@ -1,50 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- /**
4
- * Security Module
5
- *
6
- * Provides security features for the ObjectStack microkernel:
7
- * - Plugin signature verification
8
- * - Plugin configuration validation
9
- * - Permission and capability enforcement
10
- *
11
- * @module @objectstack/core/security
12
- */
13
-
14
- export {
15
- PluginSignatureVerifier,
16
- type PluginSignatureConfig,
17
- type SignatureVerificationResult,
18
- } from './plugin-signature-verifier.js';
19
-
20
- export {
21
- PluginConfigValidator,
22
- createPluginConfigValidator,
23
- } from './plugin-config-validator.js';
24
-
25
- export {
26
- PluginPermissionEnforcer,
27
- SecurePluginContext,
28
- createPluginPermissionEnforcer,
29
- type PluginPermissions,
30
- type PermissionCheckResult,
31
- } from './plugin-permission-enforcer.js';
32
-
33
- // Advanced security components (Phase 2)
34
- export {
35
- PluginPermissionManager,
36
- type PermissionGrant,
37
- type PermissionCheckResult as PluginPermissionCheckResult,
38
- } from './permission-manager.js';
39
-
40
- export {
41
- PluginSandboxRuntime,
42
- type SandboxContext,
43
- type ResourceUsage,
44
- } from './sandbox-runtime.js';
45
-
46
- export {
47
- PluginSecurityScanner,
48
- type ScanTarget,
49
- type SecurityIssue,
50
- } from './security-scanner.js';
@@ -1,256 +0,0 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
2
- import { PluginPermissionManager } from './permission-manager.js';
3
- import { createLogger } from '../logger.js';
4
- import type { PermissionSet } from '@objectstack/spec/kernel';
5
-
6
- describe('PluginPermissionManager', () => {
7
- let manager: PluginPermissionManager;
8
- let logger: ReturnType<typeof createLogger>;
9
-
10
- beforeEach(() => {
11
- logger = createLogger({ level: 'silent' });
12
- manager = new PluginPermissionManager(logger);
13
- });
14
-
15
- describe('registerPermissions', () => {
16
- it('should register permissions for a plugin', () => {
17
- const permissionSet: PermissionSet = {
18
- permissions: [
19
- {
20
- id: 'read-data',
21
- resource: 'data.object',
22
- actions: ['read'],
23
- scope: 'plugin',
24
- description: 'Read object data',
25
- required: true,
26
- },
27
- ],
28
- defaultGrant: 'prompt',
29
- };
30
-
31
- manager.registerPermissions('test-plugin', permissionSet);
32
-
33
- const permissions = manager.getPluginPermissions('test-plugin');
34
- expect(permissions).toHaveLength(1);
35
- expect(permissions[0].id).toBe('read-data');
36
- });
37
- });
38
-
39
- describe('grantPermission', () => {
40
- it('should grant a permission to a plugin', () => {
41
- const permissionSet: PermissionSet = {
42
- permissions: [
43
- {
44
- id: 'read-data',
45
- resource: 'data.object',
46
- actions: ['read'],
47
- scope: 'plugin',
48
- description: 'Read object data',
49
- required: true,
50
- },
51
- ],
52
- defaultGrant: 'prompt',
53
- };
54
-
55
- manager.registerPermissions('test-plugin', permissionSet);
56
- manager.grantPermission('test-plugin', 'read-data');
57
-
58
- expect(manager.hasPermission('test-plugin', 'read-data')).toBe(true);
59
- });
60
-
61
- it('should throw error for non-existent permission', () => {
62
- const permissionSet: PermissionSet = {
63
- permissions: [],
64
- defaultGrant: 'prompt',
65
- };
66
-
67
- manager.registerPermissions('test-plugin', permissionSet);
68
-
69
- expect(() => {
70
- manager.grantPermission('test-plugin', 'invalid-permission');
71
- }).toThrow();
72
- });
73
- });
74
-
75
- describe('revokePermission', () => {
76
- it('should revoke a permission from a plugin', () => {
77
- const permissionSet: PermissionSet = {
78
- permissions: [
79
- {
80
- id: 'read-data',
81
- resource: 'data.object',
82
- actions: ['read'],
83
- scope: 'plugin',
84
- description: 'Read object data',
85
- required: true,
86
- },
87
- ],
88
- defaultGrant: 'prompt',
89
- };
90
-
91
- manager.registerPermissions('test-plugin', permissionSet);
92
- manager.grantPermission('test-plugin', 'read-data');
93
- manager.revokePermission('test-plugin', 'read-data');
94
-
95
- expect(manager.hasPermission('test-plugin', 'read-data')).toBe(false);
96
- });
97
- });
98
-
99
- describe('checkAccess', () => {
100
- it('should allow access when permission is granted', () => {
101
- const permissionSet: PermissionSet = {
102
- permissions: [
103
- {
104
- id: 'read-data',
105
- resource: 'data.object',
106
- actions: ['read'],
107
- scope: 'plugin',
108
- description: 'Read object data',
109
- required: true,
110
- },
111
- ],
112
- defaultGrant: 'prompt',
113
- };
114
-
115
- manager.registerPermissions('test-plugin', permissionSet);
116
- manager.grantPermission('test-plugin', 'read-data');
117
-
118
- const result = manager.checkAccess('test-plugin', 'data.object', 'read');
119
- expect(result.allowed).toBe(true);
120
- });
121
-
122
- it('should deny access when permission is not granted', () => {
123
- const permissionSet: PermissionSet = {
124
- permissions: [
125
- {
126
- id: 'read-data',
127
- resource: 'data.object',
128
- actions: ['read'],
129
- scope: 'plugin',
130
- description: 'Read object data',
131
- required: true,
132
- },
133
- ],
134
- defaultGrant: 'prompt',
135
- };
136
-
137
- manager.registerPermissions('test-plugin', permissionSet);
138
-
139
- const result = manager.checkAccess('test-plugin', 'data.object', 'read');
140
- expect(result.allowed).toBe(false);
141
- });
142
-
143
- it('should deny access when permission does not exist', () => {
144
- const permissionSet: PermissionSet = {
145
- permissions: [],
146
- defaultGrant: 'prompt',
147
- };
148
-
149
- manager.registerPermissions('test-plugin', permissionSet);
150
-
151
- const result = manager.checkAccess('test-plugin', 'data.object', 'read');
152
- expect(result.allowed).toBe(false);
153
- });
154
- });
155
-
156
- describe('getMissingPermissions', () => {
157
- it('should return missing required permissions', () => {
158
- const permissionSet: PermissionSet = {
159
- permissions: [
160
- {
161
- id: 'read-data',
162
- resource: 'data.object',
163
- actions: ['read'],
164
- scope: 'plugin',
165
- description: 'Read object data',
166
- required: true,
167
- },
168
- {
169
- id: 'write-data',
170
- resource: 'data.object',
171
- actions: ['create', 'update'],
172
- scope: 'plugin',
173
- description: 'Write object data',
174
- required: true,
175
- },
176
- ],
177
- defaultGrant: 'prompt',
178
- };
179
-
180
- manager.registerPermissions('test-plugin', permissionSet);
181
- manager.grantPermission('test-plugin', 'read-data');
182
-
183
- const missing = manager.getMissingPermissions('test-plugin');
184
- expect(missing).toHaveLength(1);
185
- expect(missing[0].id).toBe('write-data');
186
- });
187
- });
188
-
189
- describe('hasAllRequiredPermissions', () => {
190
- it('should return true when all required permissions are granted', () => {
191
- const permissionSet: PermissionSet = {
192
- permissions: [
193
- {
194
- id: 'read-data',
195
- resource: 'data.object',
196
- actions: ['read'],
197
- scope: 'plugin',
198
- description: 'Read object data',
199
- required: true,
200
- },
201
- ],
202
- defaultGrant: 'prompt',
203
- };
204
-
205
- manager.registerPermissions('test-plugin', permissionSet);
206
- manager.grantPermission('test-plugin', 'read-data');
207
-
208
- expect(manager.hasAllRequiredPermissions('test-plugin')).toBe(true);
209
- });
210
-
211
- it('should return false when required permissions are missing', () => {
212
- const permissionSet: PermissionSet = {
213
- permissions: [
214
- {
215
- id: 'read-data',
216
- resource: 'data.object',
217
- actions: ['read'],
218
- scope: 'plugin',
219
- description: 'Read object data',
220
- required: true,
221
- },
222
- ],
223
- defaultGrant: 'prompt',
224
- };
225
-
226
- manager.registerPermissions('test-plugin', permissionSet);
227
-
228
- expect(manager.hasAllRequiredPermissions('test-plugin')).toBe(false);
229
- });
230
- });
231
-
232
- describe('clearPluginPermissions', () => {
233
- it('should clear all permissions for a plugin', () => {
234
- const permissionSet: PermissionSet = {
235
- permissions: [
236
- {
237
- id: 'read-data',
238
- resource: 'data.object',
239
- actions: ['read'],
240
- scope: 'plugin',
241
- description: 'Read object data',
242
- required: true,
243
- },
244
- ],
245
- defaultGrant: 'prompt',
246
- };
247
-
248
- manager.registerPermissions('test-plugin', permissionSet);
249
- manager.grantPermission('test-plugin', 'read-data');
250
- manager.clearPluginPermissions('test-plugin');
251
-
252
- expect(manager.getPluginPermissions('test-plugin')).toHaveLength(0);
253
- expect(manager.getGrantedPermissions('test-plugin')).toHaveLength(0);
254
- });
255
- });
256
- });