@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.
- package/README.md +95 -10
- package/dist/index.cjs +172 -507
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -223
- package/dist/index.d.ts +24 -223
- package/dist/index.js +178 -505
- package/dist/index.js.map +1 -1
- package/dist/logger.cjs +177 -0
- package/dist/logger.cjs.map +1 -0
- package/dist/logger.d.cts +26 -0
- package/dist/logger.d.ts +26 -0
- package/dist/logger.js +158 -0
- package/dist/logger.js.map +1 -0
- package/package.json +36 -15
- package/.turbo/turbo-build.log +0 -22
- package/ADVANCED_FEATURES.md +0 -380
- package/API_REGISTRY.md +0 -392
- package/CHANGELOG.md +0 -472
- package/PHASE2_IMPLEMENTATION.md +0 -388
- package/REFACTORING_SUMMARY.md +0 -40
- package/examples/api-registry-example.ts +0 -559
- package/examples/kernel-features-example.ts +0 -311
- package/examples/phase2-integration.ts +0 -357
- package/src/api-registry-plugin.test.ts +0 -393
- package/src/api-registry-plugin.ts +0 -89
- package/src/api-registry.test.ts +0 -1089
- package/src/api-registry.ts +0 -739
- package/src/contracts/data-engine.ts +0 -57
- package/src/contracts/http-server.ts +0 -151
- package/src/contracts/logger.ts +0 -72
- package/src/dependency-resolver.test.ts +0 -287
- package/src/dependency-resolver.ts +0 -390
- package/src/fallbacks/fallbacks.test.ts +0 -281
- package/src/fallbacks/index.ts +0 -26
- package/src/fallbacks/memory-cache.ts +0 -34
- package/src/fallbacks/memory-i18n.ts +0 -112
- package/src/fallbacks/memory-job.ts +0 -23
- package/src/fallbacks/memory-metadata.ts +0 -50
- package/src/fallbacks/memory-queue.ts +0 -28
- package/src/health-monitor.test.ts +0 -81
- package/src/health-monitor.ts +0 -318
- package/src/hot-reload.ts +0 -382
- package/src/index.ts +0 -50
- package/src/kernel-base.ts +0 -273
- package/src/kernel.test.ts +0 -624
- package/src/kernel.ts +0 -631
- package/src/lite-kernel.test.ts +0 -248
- package/src/lite-kernel.ts +0 -137
- package/src/logger.test.ts +0 -116
- package/src/logger.ts +0 -355
- package/src/namespace-resolver.test.ts +0 -130
- package/src/namespace-resolver.ts +0 -188
- package/src/package-manager.test.ts +0 -225
- package/src/package-manager.ts +0 -428
- package/src/plugin-loader.test.ts +0 -421
- package/src/plugin-loader.ts +0 -484
- package/src/qa/adapter.ts +0 -16
- package/src/qa/http-adapter.ts +0 -116
- package/src/qa/index.ts +0 -5
- package/src/qa/runner.ts +0 -189
- package/src/security/index.ts +0 -50
- package/src/security/permission-manager.test.ts +0 -256
- package/src/security/permission-manager.ts +0 -338
- package/src/security/plugin-config-validator.test.ts +0 -276
- package/src/security/plugin-config-validator.ts +0 -193
- package/src/security/plugin-permission-enforcer.test.ts +0 -251
- package/src/security/plugin-permission-enforcer.ts +0 -436
- package/src/security/plugin-signature-verifier.ts +0 -403
- package/src/security/sandbox-runtime.ts +0 -462
- package/src/security/security-scanner.ts +0 -367
- package/src/types.ts +0 -120
- package/src/utils/env.test.ts +0 -62
- package/src/utils/env.ts +0 -53
- package/tsconfig.json +0 -10
- package/vitest.config.ts +0 -10
|
@@ -1,367 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import type {
|
|
4
|
-
KernelSecurityVulnerability,
|
|
5
|
-
KernelSecurityScanResult
|
|
6
|
-
} from '@objectstack/spec/kernel';
|
|
7
|
-
import type { ObjectLogger } from '../logger.js';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Scan Target
|
|
11
|
-
*/
|
|
12
|
-
export interface ScanTarget {
|
|
13
|
-
pluginId: string;
|
|
14
|
-
version: string;
|
|
15
|
-
files?: string[];
|
|
16
|
-
dependencies?: Record<string, string>;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Security Issue
|
|
21
|
-
*/
|
|
22
|
-
export interface SecurityIssue {
|
|
23
|
-
id: string;
|
|
24
|
-
severity: 'critical' | 'high' | 'medium' | 'low' | 'info';
|
|
25
|
-
category: 'vulnerability' | 'malware' | 'license' | 'code-quality' | 'configuration';
|
|
26
|
-
title: string;
|
|
27
|
-
description: string;
|
|
28
|
-
location?: {
|
|
29
|
-
file?: string;
|
|
30
|
-
line?: number;
|
|
31
|
-
column?: number;
|
|
32
|
-
};
|
|
33
|
-
remediation?: string;
|
|
34
|
-
cve?: string;
|
|
35
|
-
cvss?: number;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Plugin Security Scanner
|
|
40
|
-
*
|
|
41
|
-
* Scans plugins for security vulnerabilities, malware, and license issues
|
|
42
|
-
*/
|
|
43
|
-
export class PluginSecurityScanner {
|
|
44
|
-
private logger: ObjectLogger;
|
|
45
|
-
|
|
46
|
-
// Known vulnerabilities database (CVE cache)
|
|
47
|
-
private vulnerabilityDb = new Map<string, KernelSecurityVulnerability>();
|
|
48
|
-
|
|
49
|
-
// Scan results cache
|
|
50
|
-
private scanResults = new Map<string, KernelSecurityScanResult>();
|
|
51
|
-
|
|
52
|
-
private passThreshold: number = 70;
|
|
53
|
-
|
|
54
|
-
constructor(logger: ObjectLogger, config?: { passThreshold?: number }) {
|
|
55
|
-
this.logger = logger.child({ component: 'SecurityScanner' });
|
|
56
|
-
if (config?.passThreshold !== undefined) {
|
|
57
|
-
this.passThreshold = config.passThreshold;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Perform a comprehensive security scan on a plugin
|
|
63
|
-
*/
|
|
64
|
-
async scan(target: ScanTarget): Promise<KernelSecurityScanResult> {
|
|
65
|
-
this.logger.info('Starting security scan', {
|
|
66
|
-
pluginId: target.pluginId,
|
|
67
|
-
version: target.version
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
const issues: SecurityIssue[] = [];
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
// 1. Scan for code vulnerabilities
|
|
74
|
-
const codeIssues = await this.scanCode(target);
|
|
75
|
-
issues.push(...codeIssues);
|
|
76
|
-
|
|
77
|
-
// 2. Scan dependencies for known vulnerabilities
|
|
78
|
-
const depIssues = await this.scanDependencies(target);
|
|
79
|
-
issues.push(...depIssues);
|
|
80
|
-
|
|
81
|
-
// 3. Scan for malware patterns
|
|
82
|
-
const malwareIssues = await this.scanMalware(target);
|
|
83
|
-
issues.push(...malwareIssues);
|
|
84
|
-
|
|
85
|
-
// 4. Check license compliance
|
|
86
|
-
const licenseIssues = await this.scanLicenses(target);
|
|
87
|
-
issues.push(...licenseIssues);
|
|
88
|
-
|
|
89
|
-
// 5. Check configuration security
|
|
90
|
-
const configIssues = await this.scanConfiguration(target);
|
|
91
|
-
issues.push(...configIssues);
|
|
92
|
-
|
|
93
|
-
// Calculate security score (0-100, higher is better)
|
|
94
|
-
const score = this.calculateSecurityScore(issues);
|
|
95
|
-
|
|
96
|
-
const result: KernelSecurityScanResult = {
|
|
97
|
-
timestamp: new Date().toISOString(),
|
|
98
|
-
scanner: { name: 'ObjectStack Security Scanner', version: '1.0.0' },
|
|
99
|
-
status: score >= this.passThreshold ? 'passed' : 'failed',
|
|
100
|
-
vulnerabilities: issues.map(issue => ({
|
|
101
|
-
id: issue.id,
|
|
102
|
-
severity: issue.severity,
|
|
103
|
-
category: issue.category,
|
|
104
|
-
title: issue.title,
|
|
105
|
-
description: issue.description,
|
|
106
|
-
location: issue.location ? `${issue.location.file}:${issue.location.line}` : undefined,
|
|
107
|
-
remediation: issue.remediation,
|
|
108
|
-
affectedVersions: [],
|
|
109
|
-
exploitAvailable: false,
|
|
110
|
-
patchAvailable: false,
|
|
111
|
-
})),
|
|
112
|
-
summary: {
|
|
113
|
-
totalVulnerabilities: issues.length,
|
|
114
|
-
criticalCount: issues.filter(i => i.severity === 'critical').length,
|
|
115
|
-
highCount: issues.filter(i => i.severity === 'high').length,
|
|
116
|
-
mediumCount: issues.filter(i => i.severity === 'medium').length,
|
|
117
|
-
lowCount: issues.filter(i => i.severity === 'low').length,
|
|
118
|
-
infoCount: issues.filter(i => i.severity === 'info').length,
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
this.scanResults.set(`${target.pluginId}:${target.version}`, result);
|
|
123
|
-
|
|
124
|
-
this.logger.info('Security scan complete', {
|
|
125
|
-
pluginId: target.pluginId,
|
|
126
|
-
score,
|
|
127
|
-
status: result.status,
|
|
128
|
-
summary: result.summary
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
return result;
|
|
132
|
-
} catch (error) {
|
|
133
|
-
this.logger.error('Security scan failed', {
|
|
134
|
-
pluginId: target.pluginId,
|
|
135
|
-
error
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
throw error;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Scan code for vulnerabilities
|
|
144
|
-
*/
|
|
145
|
-
private async scanCode(target: ScanTarget): Promise<SecurityIssue[]> {
|
|
146
|
-
const issues: SecurityIssue[] = [];
|
|
147
|
-
|
|
148
|
-
// In a real implementation, this would:
|
|
149
|
-
// - Parse code with AST (e.g., using @typescript-eslint/parser)
|
|
150
|
-
// - Check for dangerous patterns (eval, Function constructor, etc.)
|
|
151
|
-
// - Check for XSS vulnerabilities
|
|
152
|
-
// - Check for SQL injection patterns
|
|
153
|
-
// - Check for insecure crypto usage
|
|
154
|
-
// - Check for path traversal vulnerabilities
|
|
155
|
-
|
|
156
|
-
this.logger.debug('Code scan complete', {
|
|
157
|
-
pluginId: target.pluginId,
|
|
158
|
-
issuesFound: issues.length
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
return issues;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Scan dependencies for known vulnerabilities
|
|
166
|
-
*/
|
|
167
|
-
private async scanDependencies(target: ScanTarget): Promise<SecurityIssue[]> {
|
|
168
|
-
const issues: SecurityIssue[] = [];
|
|
169
|
-
|
|
170
|
-
if (!target.dependencies) {
|
|
171
|
-
return issues;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// In a real implementation, this would:
|
|
175
|
-
// - Query npm audit API
|
|
176
|
-
// - Check GitHub Advisory Database
|
|
177
|
-
// - Check Snyk vulnerability database
|
|
178
|
-
// - Check OSV (Open Source Vulnerabilities)
|
|
179
|
-
|
|
180
|
-
for (const [depName, version] of Object.entries(target.dependencies)) {
|
|
181
|
-
const vulnKey = `${depName}@${version}`;
|
|
182
|
-
const vulnerability = this.vulnerabilityDb.get(vulnKey);
|
|
183
|
-
|
|
184
|
-
if (vulnerability) {
|
|
185
|
-
issues.push({
|
|
186
|
-
id: `vuln-${vulnerability.cve || depName}`,
|
|
187
|
-
severity: vulnerability.severity,
|
|
188
|
-
category: 'vulnerability',
|
|
189
|
-
title: `Vulnerable dependency: ${depName}`,
|
|
190
|
-
description: `${depName}@${version} has known security vulnerabilities`,
|
|
191
|
-
remediation: vulnerability.fixedIn
|
|
192
|
-
? `Upgrade to ${vulnerability.fixedIn.join(' or ')}`
|
|
193
|
-
: 'No fix available',
|
|
194
|
-
cve: vulnerability.cve,
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
this.logger.debug('Dependency scan complete', {
|
|
200
|
-
pluginId: target.pluginId,
|
|
201
|
-
dependencies: Object.keys(target.dependencies).length,
|
|
202
|
-
vulnerabilities: issues.length
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
return issues;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Scan for malware patterns
|
|
210
|
-
*/
|
|
211
|
-
private async scanMalware(target: ScanTarget): Promise<SecurityIssue[]> {
|
|
212
|
-
const issues: SecurityIssue[] = [];
|
|
213
|
-
|
|
214
|
-
// In a real implementation, this would:
|
|
215
|
-
// - Check for obfuscated code
|
|
216
|
-
// - Check for suspicious network activity patterns
|
|
217
|
-
// - Check for crypto mining patterns
|
|
218
|
-
// - Check for data exfiltration patterns
|
|
219
|
-
// - Use ML-based malware detection
|
|
220
|
-
// - Check file hashes against known malware databases
|
|
221
|
-
|
|
222
|
-
this.logger.debug('Malware scan complete', {
|
|
223
|
-
pluginId: target.pluginId,
|
|
224
|
-
issuesFound: issues.length
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
return issues;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Check license compliance
|
|
232
|
-
*/
|
|
233
|
-
private async scanLicenses(target: ScanTarget): Promise<SecurityIssue[]> {
|
|
234
|
-
const issues: SecurityIssue[] = [];
|
|
235
|
-
|
|
236
|
-
if (!target.dependencies) {
|
|
237
|
-
return issues;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// In a real implementation, this would:
|
|
241
|
-
// - Check license compatibility
|
|
242
|
-
// - Detect GPL contamination
|
|
243
|
-
// - Flag proprietary dependencies
|
|
244
|
-
// - Check for missing licenses
|
|
245
|
-
// - Verify SPDX identifiers
|
|
246
|
-
|
|
247
|
-
this.logger.debug('License scan complete', {
|
|
248
|
-
pluginId: target.pluginId,
|
|
249
|
-
issuesFound: issues.length
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
return issues;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Check configuration security
|
|
257
|
-
*/
|
|
258
|
-
private async scanConfiguration(target: ScanTarget): Promise<SecurityIssue[]> {
|
|
259
|
-
const issues: SecurityIssue[] = [];
|
|
260
|
-
|
|
261
|
-
// In a real implementation, this would:
|
|
262
|
-
// - Check for hardcoded secrets
|
|
263
|
-
// - Check for weak permissions
|
|
264
|
-
// - Check for insecure defaults
|
|
265
|
-
// - Check for missing security headers
|
|
266
|
-
// - Check CSP policies
|
|
267
|
-
|
|
268
|
-
this.logger.debug('Configuration scan complete', {
|
|
269
|
-
pluginId: target.pluginId,
|
|
270
|
-
issuesFound: issues.length
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
return issues;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Calculate security score based on issues
|
|
278
|
-
*/
|
|
279
|
-
private calculateSecurityScore(issues: SecurityIssue[]): number {
|
|
280
|
-
// Start with perfect score
|
|
281
|
-
let score = 100;
|
|
282
|
-
|
|
283
|
-
// Deduct points based on severity
|
|
284
|
-
for (const issue of issues) {
|
|
285
|
-
switch (issue.severity) {
|
|
286
|
-
case 'critical':
|
|
287
|
-
score -= 20;
|
|
288
|
-
break;
|
|
289
|
-
case 'high':
|
|
290
|
-
score -= 10;
|
|
291
|
-
break;
|
|
292
|
-
case 'medium':
|
|
293
|
-
score -= 5;
|
|
294
|
-
break;
|
|
295
|
-
case 'low':
|
|
296
|
-
score -= 2;
|
|
297
|
-
break;
|
|
298
|
-
case 'info':
|
|
299
|
-
score -= 0;
|
|
300
|
-
break;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Ensure score doesn't go below 0
|
|
305
|
-
return Math.max(0, score);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Add a vulnerability to the database
|
|
310
|
-
*/
|
|
311
|
-
addVulnerability(
|
|
312
|
-
packageName: string,
|
|
313
|
-
version: string,
|
|
314
|
-
vulnerability: KernelSecurityVulnerability
|
|
315
|
-
): void {
|
|
316
|
-
const key = `${packageName}@${version}`;
|
|
317
|
-
this.vulnerabilityDb.set(key, vulnerability);
|
|
318
|
-
|
|
319
|
-
this.logger.debug('Vulnerability added to database', {
|
|
320
|
-
package: packageName,
|
|
321
|
-
version,
|
|
322
|
-
cve: vulnerability.cve
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* Get scan result from cache
|
|
328
|
-
*/
|
|
329
|
-
getScanResult(pluginId: string, version: string): KernelSecurityScanResult | undefined {
|
|
330
|
-
return this.scanResults.get(`${pluginId}:${version}`);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* Clear scan results cache
|
|
335
|
-
*/
|
|
336
|
-
clearCache(): void {
|
|
337
|
-
this.scanResults.clear();
|
|
338
|
-
this.logger.debug('Scan results cache cleared');
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Update vulnerability database from external source
|
|
343
|
-
*/
|
|
344
|
-
async updateVulnerabilityDatabase(): Promise<void> {
|
|
345
|
-
this.logger.info('Updating vulnerability database');
|
|
346
|
-
|
|
347
|
-
// In a real implementation, this would:
|
|
348
|
-
// - Fetch from GitHub Advisory Database
|
|
349
|
-
// - Fetch from npm audit
|
|
350
|
-
// - Fetch from NVD (National Vulnerability Database)
|
|
351
|
-
// - Parse and cache vulnerability data
|
|
352
|
-
|
|
353
|
-
this.logger.info('Vulnerability database updated', {
|
|
354
|
-
entries: this.vulnerabilityDb.size
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* Shutdown security scanner
|
|
360
|
-
*/
|
|
361
|
-
shutdown(): void {
|
|
362
|
-
this.vulnerabilityDb.clear();
|
|
363
|
-
this.scanResults.clear();
|
|
364
|
-
|
|
365
|
-
this.logger.info('Security scanner shutdown complete');
|
|
366
|
-
}
|
|
367
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import { ObjectKernel } from './kernel.js';
|
|
4
|
-
import type { Logger } from '@objectstack/spec/contracts';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* PluginContext - Runtime context available to plugins
|
|
8
|
-
*
|
|
9
|
-
* Provides access to:
|
|
10
|
-
* - Service registry (registerService/getService)
|
|
11
|
-
* - Event/Hook system (hook/trigger)
|
|
12
|
-
* - Logger
|
|
13
|
-
* - Kernel instance (for advanced use cases)
|
|
14
|
-
*/
|
|
15
|
-
export interface PluginContext {
|
|
16
|
-
/**
|
|
17
|
-
* Register a service that can be consumed by other plugins
|
|
18
|
-
* @param name - Service name (e.g., 'db', 'http-server', 'objectql')
|
|
19
|
-
* @param service - Service instance
|
|
20
|
-
*/
|
|
21
|
-
registerService(name: string, service: any): void;
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Get a service registered by another plugin
|
|
25
|
-
* @param name - Service name
|
|
26
|
-
* @returns Service instance
|
|
27
|
-
* @throws Error if service not found
|
|
28
|
-
*/
|
|
29
|
-
getService<T>(name: string): T;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Replace an existing service with a new implementation.
|
|
33
|
-
* Useful for optimization plugins that wrap or swap kernel internals
|
|
34
|
-
* (e.g., metadata registry, connection pooling).
|
|
35
|
-
*
|
|
36
|
-
* @param name - Service name to replace
|
|
37
|
-
* @param implementation - New service implementation
|
|
38
|
-
* @throws Error if the service does not exist
|
|
39
|
-
*/
|
|
40
|
-
replaceService<T>(name: string, implementation: T): void;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Get all registered services
|
|
44
|
-
*/
|
|
45
|
-
getServices(): Map<string, any>;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Register a hook handler
|
|
49
|
-
* @param name - Hook name (e.g., 'kernel:ready', 'data:beforeInsert')
|
|
50
|
-
* @param handler - Hook handler function
|
|
51
|
-
*/
|
|
52
|
-
hook(name: string, handler: (...args: any[]) => void | Promise<void>): void;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Trigger a hook
|
|
56
|
-
* @param name - Hook name
|
|
57
|
-
* @param args - Arguments to pass to hook handlers
|
|
58
|
-
*/
|
|
59
|
-
trigger(name: string, ...args: any[]): Promise<void>;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Logger instance
|
|
63
|
-
*/
|
|
64
|
-
logger: Logger;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Get the kernel instance (for advanced use cases)
|
|
68
|
-
* @returns Kernel instance
|
|
69
|
-
*/
|
|
70
|
-
getKernel(): ObjectKernel;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Plugin Interface
|
|
75
|
-
*
|
|
76
|
-
* All ObjectStack plugins must implement this interface.
|
|
77
|
-
*/
|
|
78
|
-
export interface Plugin {
|
|
79
|
-
/**
|
|
80
|
-
* Unique plugin name (e.g., 'com.objectstack.engine.objectql')
|
|
81
|
-
*/
|
|
82
|
-
name: string;
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Plugin version
|
|
86
|
-
*/
|
|
87
|
-
version?: string;
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Plugin type (standard, ui, driver, server, app, theme, agent)
|
|
91
|
-
* @default 'standard'
|
|
92
|
-
*/
|
|
93
|
-
type?: string;
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* List of other plugin names that this plugin depends on.
|
|
97
|
-
* The kernel ensures these plugins are initialized before this one.
|
|
98
|
-
*/
|
|
99
|
-
dependencies?: string[];
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Init Phase: Register services
|
|
103
|
-
* Called when kernel is initializing.
|
|
104
|
-
* Use this to register services that other plugins might need.
|
|
105
|
-
*/
|
|
106
|
-
init(ctx: PluginContext): Promise<void> | void;
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Start Phase: Execute business logic
|
|
110
|
-
* Called after all plugins have been initialized.
|
|
111
|
-
* Use this to start servers, connect to DBs, or execute main logic.
|
|
112
|
-
*/
|
|
113
|
-
start?(ctx: PluginContext): Promise<void> | void;
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Destroy Phase: Cleanup
|
|
117
|
-
* Called when kernel is shutting down.
|
|
118
|
-
*/
|
|
119
|
-
destroy?(): Promise<void> | void;
|
|
120
|
-
}
|
package/src/utils/env.test.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
2
|
-
import * as envUtils from './env';
|
|
3
|
-
|
|
4
|
-
describe('Environment Utilities', () => {
|
|
5
|
-
|
|
6
|
-
// Save original process
|
|
7
|
-
const originalProcess = globalThis.process;
|
|
8
|
-
|
|
9
|
-
afterEach(() => {
|
|
10
|
-
// Restore process after each test
|
|
11
|
-
globalThis.process = originalProcess;
|
|
12
|
-
vi.restoreAllMocks();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
describe('isNode', () => {
|
|
16
|
-
it('should detect Node environment', () => {
|
|
17
|
-
// Since we are running in Vitest (Node), this should be true
|
|
18
|
-
expect(envUtils.isNode).toBe(true);
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
describe('getEnv', () => {
|
|
23
|
-
it('should retrieve environment variable in Node', () => {
|
|
24
|
-
process.env.TEST_VAR = 'test_value';
|
|
25
|
-
expect(envUtils.getEnv('TEST_VAR')).toBe('test_value');
|
|
26
|
-
delete process.env.TEST_VAR;
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('should return default value if variable not found', () => {
|
|
30
|
-
expect(envUtils.getEnv('NON_EXISTENT_VAR', 'default')).toBe('default');
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should access globalThis.process.env if process is not available directly', () => {
|
|
34
|
-
// This is tricky to test in Node because 'process' is globally available.
|
|
35
|
-
// We can't easily delete global.process in strict mode or without breaking tooling.
|
|
36
|
-
// But we can verify it works via globalThis
|
|
37
|
-
|
|
38
|
-
// @ts-ignore
|
|
39
|
-
globalThis.process.env.TEST_GLOBAL_VAR = 'global_value';
|
|
40
|
-
expect(envUtils.getEnv('TEST_GLOBAL_VAR')).toBe('global_value');
|
|
41
|
-
// @ts-ignore
|
|
42
|
-
delete globalThis.process.env.TEST_GLOBAL_VAR;
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
describe('getMemoryUsage', () => {
|
|
47
|
-
it('should return memory usage in Node', () => {
|
|
48
|
-
const usage = envUtils.getMemoryUsage();
|
|
49
|
-
expect(usage).toHaveProperty('heapUsed');
|
|
50
|
-
expect(usage).toHaveProperty('heapTotal');
|
|
51
|
-
expect(usage.heapUsed).toBeGreaterThan(0);
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
describe('safeExit', () => {
|
|
56
|
-
it('should call process.exit in Node', () => {
|
|
57
|
-
const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {}) as any);
|
|
58
|
-
envUtils.safeExit(1);
|
|
59
|
-
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
});
|
package/src/utils/env.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Environment utilities for universal (Node/Browser) compatibility.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// Check if running in a Node.js environment
|
|
8
|
-
export const isNode = typeof process !== 'undefined' &&
|
|
9
|
-
process.versions != null &&
|
|
10
|
-
process.versions.node != null;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Safely access environment variables
|
|
14
|
-
*/
|
|
15
|
-
export function getEnv(key: string, defaultValue?: string): string | undefined {
|
|
16
|
-
// Node.js
|
|
17
|
-
if (typeof process !== 'undefined' && process.env) {
|
|
18
|
-
return process.env[key] || defaultValue;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Browser (Vite/Webpack replacement usually handles process.env,
|
|
22
|
-
// but if not, we check safe global access)
|
|
23
|
-
try {
|
|
24
|
-
// @ts-ignore
|
|
25
|
-
if (typeof globalThis !== 'undefined' && globalThis.process?.env) {
|
|
26
|
-
// @ts-ignore
|
|
27
|
-
return globalThis.process.env[key] || defaultValue;
|
|
28
|
-
}
|
|
29
|
-
} catch (e) {
|
|
30
|
-
// Ignore access errors
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return defaultValue;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Safely exit the process if in Node.js
|
|
38
|
-
*/
|
|
39
|
-
export function safeExit(code: number = 0): void {
|
|
40
|
-
if (isNode) {
|
|
41
|
-
process.exit(code);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Safely get memory usage
|
|
47
|
-
*/
|
|
48
|
-
export function getMemoryUsage(): { heapUsed: number; heapTotal: number } {
|
|
49
|
-
if (isNode) {
|
|
50
|
-
return process.memoryUsage();
|
|
51
|
-
}
|
|
52
|
-
return { heapUsed: 0, heapTotal: 0 };
|
|
53
|
-
}
|
package/tsconfig.json
DELETED