@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
@@ -0,0 +1,256 @@
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
+ });
@@ -0,0 +1,336 @@
1
+ import type {
2
+ Permission,
3
+ PermissionSet,
4
+ PermissionAction,
5
+ ResourceType
6
+ } from '@objectstack/spec/kernel';
7
+ import type { ObjectLogger } from '../logger.js';
8
+
9
+ /**
10
+ * Permission Grant
11
+ * Represents a granted permission at runtime
12
+ */
13
+ export interface PermissionGrant {
14
+ permissionId: string;
15
+ pluginId: string;
16
+ grantedAt: Date;
17
+ grantedBy?: string;
18
+ expiresAt?: Date;
19
+ conditions?: Record<string, any>;
20
+ }
21
+
22
+ /**
23
+ * Permission Check Result
24
+ */
25
+ export interface PermissionCheckResult {
26
+ allowed: boolean;
27
+ reason?: string;
28
+ requiredPermission?: string;
29
+ grantedPermissions?: string[];
30
+ }
31
+
32
+ /**
33
+ * Plugin Permission Manager
34
+ *
35
+ * Manages fine-grained permissions for plugin security and access control
36
+ */
37
+ export class PluginPermissionManager {
38
+ private logger: ObjectLogger;
39
+
40
+ // Plugin permission definitions
41
+ private permissionSets = new Map<string, PermissionSet>();
42
+
43
+ // Granted permissions (pluginId -> Set of permission IDs)
44
+ private grants = new Map<string, Set<string>>();
45
+
46
+ // Permission grant details
47
+ private grantDetails = new Map<string, PermissionGrant>();
48
+
49
+ constructor(logger: ObjectLogger) {
50
+ this.logger = logger.child({ component: 'PermissionManager' });
51
+ }
52
+
53
+ /**
54
+ * Register permission requirements for a plugin
55
+ */
56
+ registerPermissions(pluginId: string, permissionSet: PermissionSet): void {
57
+ this.permissionSets.set(pluginId, permissionSet);
58
+
59
+ this.logger.info('Permissions registered for plugin', {
60
+ pluginId,
61
+ permissionCount: permissionSet.permissions.length
62
+ });
63
+ }
64
+
65
+ /**
66
+ * Grant a permission to a plugin
67
+ */
68
+ grantPermission(
69
+ pluginId: string,
70
+ permissionId: string,
71
+ grantedBy?: string,
72
+ expiresAt?: Date
73
+ ): void {
74
+ // Verify permission exists in plugin's declared permissions
75
+ const permissionSet = this.permissionSets.get(pluginId);
76
+ if (!permissionSet) {
77
+ throw new Error(`No permissions registered for plugin: ${pluginId}`);
78
+ }
79
+
80
+ const permission = permissionSet.permissions.find(p => p.id === permissionId);
81
+ if (!permission) {
82
+ throw new Error(`Permission ${permissionId} not declared by plugin ${pluginId}`);
83
+ }
84
+
85
+ // Create grant
86
+ if (!this.grants.has(pluginId)) {
87
+ this.grants.set(pluginId, new Set());
88
+ }
89
+ this.grants.get(pluginId)!.add(permissionId);
90
+
91
+ // Store grant details
92
+ const grantKey = `${pluginId}:${permissionId}`;
93
+ this.grantDetails.set(grantKey, {
94
+ permissionId,
95
+ pluginId,
96
+ grantedAt: new Date(),
97
+ grantedBy,
98
+ expiresAt,
99
+ });
100
+
101
+ this.logger.info('Permission granted', {
102
+ pluginId,
103
+ permissionId,
104
+ grantedBy
105
+ });
106
+ }
107
+
108
+ /**
109
+ * Revoke a permission from a plugin
110
+ */
111
+ revokePermission(pluginId: string, permissionId: string): void {
112
+ const grants = this.grants.get(pluginId);
113
+ if (grants) {
114
+ grants.delete(permissionId);
115
+
116
+ const grantKey = `${pluginId}:${permissionId}`;
117
+ this.grantDetails.delete(grantKey);
118
+
119
+ this.logger.info('Permission revoked', { pluginId, permissionId });
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Grant all permissions for a plugin
125
+ */
126
+ grantAllPermissions(pluginId: string, grantedBy?: string): void {
127
+ const permissionSet = this.permissionSets.get(pluginId);
128
+ if (!permissionSet) {
129
+ throw new Error(`No permissions registered for plugin: ${pluginId}`);
130
+ }
131
+
132
+ for (const permission of permissionSet.permissions) {
133
+ this.grantPermission(pluginId, permission.id, grantedBy);
134
+ }
135
+
136
+ this.logger.info('All permissions granted', { pluginId, grantedBy });
137
+ }
138
+
139
+ /**
140
+ * Check if a plugin has a specific permission
141
+ */
142
+ hasPermission(pluginId: string, permissionId: string): boolean {
143
+ const grants = this.grants.get(pluginId);
144
+ if (!grants) {
145
+ return false;
146
+ }
147
+
148
+ // Check if granted
149
+ if (!grants.has(permissionId)) {
150
+ return false;
151
+ }
152
+
153
+ // Check expiration
154
+ const grantKey = `${pluginId}:${permissionId}`;
155
+ const grantDetails = this.grantDetails.get(grantKey);
156
+ if (grantDetails?.expiresAt && grantDetails.expiresAt < new Date()) {
157
+ this.revokePermission(pluginId, permissionId);
158
+ return false;
159
+ }
160
+
161
+ return true;
162
+ }
163
+
164
+ /**
165
+ * Check if plugin can perform an action on a resource
166
+ */
167
+ checkAccess(
168
+ pluginId: string,
169
+ resource: ResourceType,
170
+ action: PermissionAction,
171
+ resourceId?: string
172
+ ): PermissionCheckResult {
173
+ const permissionSet = this.permissionSets.get(pluginId);
174
+ if (!permissionSet) {
175
+ return {
176
+ allowed: false,
177
+ reason: 'No permissions registered for plugin',
178
+ };
179
+ }
180
+
181
+ // Find matching permissions
182
+ const matchingPermissions = permissionSet.permissions.filter(p => {
183
+ // Check resource type
184
+ if (p.resource !== resource) {
185
+ return false;
186
+ }
187
+
188
+ // Check action
189
+ if (!p.actions.includes(action)) {
190
+ return false;
191
+ }
192
+
193
+ // Check resource filter if specified
194
+ if (resourceId && p.filter?.resourceIds) {
195
+ if (!p.filter.resourceIds.includes(resourceId)) {
196
+ return false;
197
+ }
198
+ }
199
+
200
+ return true;
201
+ });
202
+
203
+ if (matchingPermissions.length === 0) {
204
+ return {
205
+ allowed: false,
206
+ reason: `No permission found for ${action} on ${resource}`,
207
+ };
208
+ }
209
+
210
+ // Check if any matching permission is granted
211
+ const grantedPermissions = matchingPermissions.filter(p =>
212
+ this.hasPermission(pluginId, p.id)
213
+ );
214
+
215
+ if (grantedPermissions.length === 0) {
216
+ return {
217
+ allowed: false,
218
+ reason: 'Required permissions not granted',
219
+ requiredPermission: matchingPermissions[0].id,
220
+ };
221
+ }
222
+
223
+ return {
224
+ allowed: true,
225
+ grantedPermissions: grantedPermissions.map(p => p.id),
226
+ };
227
+ }
228
+
229
+ /**
230
+ * Get all permissions for a plugin
231
+ */
232
+ getPluginPermissions(pluginId: string): Permission[] {
233
+ const permissionSet = this.permissionSets.get(pluginId);
234
+ return permissionSet?.permissions || [];
235
+ }
236
+
237
+ /**
238
+ * Get granted permissions for a plugin
239
+ */
240
+ getGrantedPermissions(pluginId: string): string[] {
241
+ const grants = this.grants.get(pluginId);
242
+ return grants ? Array.from(grants) : [];
243
+ }
244
+
245
+ /**
246
+ * Get required but not granted permissions
247
+ */
248
+ getMissingPermissions(pluginId: string): Permission[] {
249
+ const permissionSet = this.permissionSets.get(pluginId);
250
+ if (!permissionSet) {
251
+ return [];
252
+ }
253
+
254
+ const granted = this.grants.get(pluginId) || new Set();
255
+
256
+ return permissionSet.permissions.filter(p =>
257
+ p.required && !granted.has(p.id)
258
+ );
259
+ }
260
+
261
+ /**
262
+ * Check if all required permissions are granted
263
+ */
264
+ hasAllRequiredPermissions(pluginId: string): boolean {
265
+ return this.getMissingPermissions(pluginId).length === 0;
266
+ }
267
+
268
+ /**
269
+ * Get permission grant details
270
+ */
271
+ getGrantDetails(pluginId: string, permissionId: string): PermissionGrant | undefined {
272
+ const grantKey = `${pluginId}:${permissionId}`;
273
+ return this.grantDetails.get(grantKey);
274
+ }
275
+
276
+ /**
277
+ * Validate permission against scope constraints
278
+ */
279
+ validatePermissionScope(
280
+ permission: Permission,
281
+ context: {
282
+ tenantId?: string;
283
+ userId?: string;
284
+ resourceId?: string;
285
+ }
286
+ ): boolean {
287
+ switch (permission.scope) {
288
+ case 'global':
289
+ return true;
290
+
291
+ case 'tenant':
292
+ return !!context.tenantId;
293
+
294
+ case 'user':
295
+ return !!context.userId;
296
+
297
+ case 'resource':
298
+ return !!context.resourceId;
299
+
300
+ case 'plugin':
301
+ return true;
302
+
303
+ default:
304
+ return false;
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Clear all permissions for a plugin
310
+ */
311
+ clearPluginPermissions(pluginId: string): void {
312
+ this.permissionSets.delete(pluginId);
313
+
314
+ const grants = this.grants.get(pluginId);
315
+ if (grants) {
316
+ for (const permissionId of grants) {
317
+ const grantKey = `${pluginId}:${permissionId}`;
318
+ this.grantDetails.delete(grantKey);
319
+ }
320
+ this.grants.delete(pluginId);
321
+ }
322
+
323
+ this.logger.info('All permissions cleared', { pluginId });
324
+ }
325
+
326
+ /**
327
+ * Shutdown permission manager
328
+ */
329
+ shutdown(): void {
330
+ this.permissionSets.clear();
331
+ this.grants.clear();
332
+ this.grantDetails.clear();
333
+
334
+ this.logger.info('Permission manager shutdown complete');
335
+ }
336
+ }
@@ -1,7 +1,7 @@
1
1
  import { describe, it, expect, beforeEach } from 'vitest';
2
2
  import { PluginPermissionEnforcer, SecurePluginContext } from './plugin-permission-enforcer.js';
3
3
  import { createLogger } from '../logger.js';
4
- import type { PluginCapability } from '@objectstack/spec/system';
4
+ import type { PluginCapability } from '@objectstack/spec/kernel';
5
5
  import type { PluginContext } from '../types.js';
6
6
 
7
7
  describe('PluginPermissionEnforcer', () => {
@@ -1,5 +1,5 @@
1
1
  import type { Logger } from '@objectstack/spec/contracts';
2
- import type { PluginCapability } from '@objectstack/spec/system';
2
+ import type { PluginCapability } from '@objectstack/spec/kernel';
3
3
  import type { PluginContext } from '../types.js';
4
4
 
5
5
  /**