@objectstack/core 1.0.2 → 1.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 (125) hide show
  1. package/.turbo/turbo-build.log +58 -0
  2. package/CHANGELOG.md +25 -0
  3. package/dist/index.cjs +4294 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +1777 -0
  6. package/dist/index.d.ts +1776 -21
  7. package/dist/index.js +4246 -23
  8. package/dist/index.js.map +1 -0
  9. package/package.json +4 -4
  10. package/tsconfig.json +1 -3
  11. package/dist/api-registry-plugin.d.ts +0 -54
  12. package/dist/api-registry-plugin.d.ts.map +0 -1
  13. package/dist/api-registry-plugin.js +0 -53
  14. package/dist/api-registry-plugin.test.d.ts +0 -2
  15. package/dist/api-registry-plugin.test.d.ts.map +0 -1
  16. package/dist/api-registry-plugin.test.js +0 -334
  17. package/dist/api-registry.d.ts +0 -259
  18. package/dist/api-registry.d.ts.map +0 -1
  19. package/dist/api-registry.js +0 -600
  20. package/dist/api-registry.test.d.ts +0 -2
  21. package/dist/api-registry.test.d.ts.map +0 -1
  22. package/dist/api-registry.test.js +0 -957
  23. package/dist/contracts/data-engine.d.ts +0 -62
  24. package/dist/contracts/data-engine.d.ts.map +0 -1
  25. package/dist/contracts/data-engine.js +0 -1
  26. package/dist/contracts/http-server.d.ts +0 -119
  27. package/dist/contracts/http-server.d.ts.map +0 -1
  28. package/dist/contracts/http-server.js +0 -11
  29. package/dist/contracts/logger.d.ts +0 -63
  30. package/dist/contracts/logger.d.ts.map +0 -1
  31. package/dist/contracts/logger.js +0 -1
  32. package/dist/dependency-resolver.d.ts +0 -62
  33. package/dist/dependency-resolver.d.ts.map +0 -1
  34. package/dist/dependency-resolver.js +0 -317
  35. package/dist/dependency-resolver.test.d.ts +0 -2
  36. package/dist/dependency-resolver.test.d.ts.map +0 -1
  37. package/dist/dependency-resolver.test.js +0 -241
  38. package/dist/health-monitor.d.ts +0 -65
  39. package/dist/health-monitor.d.ts.map +0 -1
  40. package/dist/health-monitor.js +0 -269
  41. package/dist/health-monitor.test.d.ts +0 -2
  42. package/dist/health-monitor.test.d.ts.map +0 -1
  43. package/dist/health-monitor.test.js +0 -68
  44. package/dist/hot-reload.d.ts +0 -79
  45. package/dist/hot-reload.d.ts.map +0 -1
  46. package/dist/hot-reload.js +0 -313
  47. package/dist/index.d.ts.map +0 -1
  48. package/dist/kernel-base.d.ts +0 -84
  49. package/dist/kernel-base.d.ts.map +0 -1
  50. package/dist/kernel-base.js +0 -219
  51. package/dist/kernel.d.ts +0 -113
  52. package/dist/kernel.d.ts.map +0 -1
  53. package/dist/kernel.js +0 -472
  54. package/dist/kernel.test.d.ts +0 -2
  55. package/dist/kernel.test.d.ts.map +0 -1
  56. package/dist/kernel.test.js +0 -414
  57. package/dist/lite-kernel.d.ts +0 -55
  58. package/dist/lite-kernel.d.ts.map +0 -1
  59. package/dist/lite-kernel.js +0 -112
  60. package/dist/lite-kernel.test.d.ts +0 -2
  61. package/dist/lite-kernel.test.d.ts.map +0 -1
  62. package/dist/lite-kernel.test.js +0 -161
  63. package/dist/logger.d.ts +0 -71
  64. package/dist/logger.d.ts.map +0 -1
  65. package/dist/logger.js +0 -312
  66. package/dist/logger.test.d.ts +0 -2
  67. package/dist/logger.test.d.ts.map +0 -1
  68. package/dist/logger.test.js +0 -92
  69. package/dist/plugin-loader.d.ts +0 -164
  70. package/dist/plugin-loader.d.ts.map +0 -1
  71. package/dist/plugin-loader.js +0 -319
  72. package/dist/plugin-loader.test.d.ts +0 -2
  73. package/dist/plugin-loader.test.d.ts.map +0 -1
  74. package/dist/plugin-loader.test.js +0 -348
  75. package/dist/qa/adapter.d.ts +0 -14
  76. package/dist/qa/adapter.d.ts.map +0 -1
  77. package/dist/qa/adapter.js +0 -1
  78. package/dist/qa/http-adapter.d.ts +0 -16
  79. package/dist/qa/http-adapter.d.ts.map +0 -1
  80. package/dist/qa/http-adapter.js +0 -107
  81. package/dist/qa/index.d.ts +0 -4
  82. package/dist/qa/index.d.ts.map +0 -1
  83. package/dist/qa/index.js +0 -3
  84. package/dist/qa/runner.d.ts +0 -27
  85. package/dist/qa/runner.d.ts.map +0 -1
  86. package/dist/qa/runner.js +0 -157
  87. package/dist/security/index.d.ts +0 -17
  88. package/dist/security/index.d.ts.map +0 -1
  89. package/dist/security/index.js +0 -17
  90. package/dist/security/permission-manager.d.ts +0 -96
  91. package/dist/security/permission-manager.d.ts.map +0 -1
  92. package/dist/security/permission-manager.js +0 -235
  93. package/dist/security/permission-manager.test.d.ts +0 -2
  94. package/dist/security/permission-manager.test.d.ts.map +0 -1
  95. package/dist/security/permission-manager.test.js +0 -220
  96. package/dist/security/plugin-config-validator.d.ts +0 -79
  97. package/dist/security/plugin-config-validator.d.ts.map +0 -1
  98. package/dist/security/plugin-config-validator.js +0 -166
  99. package/dist/security/plugin-config-validator.test.d.ts +0 -2
  100. package/dist/security/plugin-config-validator.test.d.ts.map +0 -1
  101. package/dist/security/plugin-config-validator.test.js +0 -223
  102. package/dist/security/plugin-permission-enforcer.d.ts +0 -154
  103. package/dist/security/plugin-permission-enforcer.d.ts.map +0 -1
  104. package/dist/security/plugin-permission-enforcer.js +0 -323
  105. package/dist/security/plugin-permission-enforcer.test.d.ts +0 -2
  106. package/dist/security/plugin-permission-enforcer.test.d.ts.map +0 -1
  107. package/dist/security/plugin-permission-enforcer.test.js +0 -205
  108. package/dist/security/plugin-signature-verifier.d.ts +0 -96
  109. package/dist/security/plugin-signature-verifier.d.ts.map +0 -1
  110. package/dist/security/plugin-signature-verifier.js +0 -250
  111. package/dist/security/sandbox-runtime.d.ts +0 -115
  112. package/dist/security/sandbox-runtime.d.ts.map +0 -1
  113. package/dist/security/sandbox-runtime.js +0 -311
  114. package/dist/security/security-scanner.d.ts +0 -92
  115. package/dist/security/security-scanner.d.ts.map +0 -1
  116. package/dist/security/security-scanner.js +0 -273
  117. package/dist/types.d.ts +0 -89
  118. package/dist/types.d.ts.map +0 -1
  119. package/dist/types.js +0 -1
  120. package/dist/utils/env.d.ts +0 -20
  121. package/dist/utils/env.d.ts.map +0 -1
  122. package/dist/utils/env.js +0 -46
  123. package/dist/utils/env.test.d.ts +0 -2
  124. package/dist/utils/env.test.d.ts.map +0 -1
  125. package/dist/utils/env.test.js +0 -52
package/dist/index.cjs ADDED
@@ -0,0 +1,4294 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ApiRegistry: () => ApiRegistry,
24
+ DependencyResolver: () => DependencyResolver,
25
+ HotReloadManager: () => HotReloadManager,
26
+ LiteKernel: () => LiteKernel,
27
+ ObjectKernel: () => ObjectKernel,
28
+ ObjectKernelBase: () => ObjectKernelBase,
29
+ ObjectLogger: () => ObjectLogger,
30
+ PluginConfigValidator: () => PluginConfigValidator,
31
+ PluginHealthMonitor: () => PluginHealthMonitor,
32
+ PluginLoader: () => PluginLoader,
33
+ PluginPermissionEnforcer: () => PluginPermissionEnforcer,
34
+ PluginPermissionManager: () => PluginPermissionManager,
35
+ PluginSandboxRuntime: () => PluginSandboxRuntime,
36
+ PluginSecurityScanner: () => PluginSecurityScanner,
37
+ PluginSignatureVerifier: () => PluginSignatureVerifier,
38
+ QA: () => qa_exports,
39
+ SecurePluginContext: () => SecurePluginContext,
40
+ SemanticVersionManager: () => SemanticVersionManager,
41
+ ServiceLifecycle: () => ServiceLifecycle,
42
+ createApiRegistryPlugin: () => createApiRegistryPlugin,
43
+ createLogger: () => createLogger,
44
+ createPluginConfigValidator: () => createPluginConfigValidator,
45
+ createPluginPermissionEnforcer: () => createPluginPermissionEnforcer,
46
+ getEnv: () => getEnv,
47
+ getMemoryUsage: () => getMemoryUsage,
48
+ isNode: () => isNode,
49
+ safeExit: () => safeExit
50
+ });
51
+ module.exports = __toCommonJS(index_exports);
52
+
53
+ // src/kernel-base.ts
54
+ var ObjectKernelBase = class {
55
+ constructor(logger) {
56
+ this.plugins = /* @__PURE__ */ new Map();
57
+ this.services = /* @__PURE__ */ new Map();
58
+ this.hooks = /* @__PURE__ */ new Map();
59
+ this.state = "idle";
60
+ this.logger = logger;
61
+ }
62
+ /**
63
+ * Validate kernel state
64
+ * @param requiredState - Required state for the operation
65
+ * @throws Error if current state doesn't match
66
+ */
67
+ validateState(requiredState) {
68
+ if (this.state !== requiredState) {
69
+ throw new Error(
70
+ `[Kernel] Invalid state: expected '${requiredState}', got '${this.state}'`
71
+ );
72
+ }
73
+ }
74
+ /**
75
+ * Validate kernel is in idle state (for plugin registration)
76
+ */
77
+ validateIdle() {
78
+ if (this.state !== "idle") {
79
+ throw new Error("[Kernel] Cannot register plugins after bootstrap has started");
80
+ }
81
+ }
82
+ /**
83
+ * Create the plugin context
84
+ * Subclasses can override to customize context creation
85
+ */
86
+ createContext() {
87
+ return {
88
+ registerService: (name, service) => {
89
+ if (this.services instanceof Map) {
90
+ if (this.services.has(name)) {
91
+ throw new Error(`[Kernel] Service '${name}' already registered`);
92
+ }
93
+ this.services.set(name, service);
94
+ } else {
95
+ this.services.register(name, service);
96
+ }
97
+ this.logger.info(`Service '${name}' registered`, { service: name });
98
+ },
99
+ getService: (name) => {
100
+ if (this.services instanceof Map) {
101
+ const service = this.services.get(name);
102
+ if (!service) {
103
+ throw new Error(`[Kernel] Service '${name}' not found`);
104
+ }
105
+ return service;
106
+ } else {
107
+ return this.services.get(name);
108
+ }
109
+ },
110
+ hook: (name, handler) => {
111
+ if (!this.hooks.has(name)) {
112
+ this.hooks.set(name, []);
113
+ }
114
+ this.hooks.get(name).push(handler);
115
+ },
116
+ trigger: async (name, ...args) => {
117
+ const handlers = this.hooks.get(name) || [];
118
+ for (const handler of handlers) {
119
+ await handler(...args);
120
+ }
121
+ },
122
+ getServices: () => {
123
+ if (this.services instanceof Map) {
124
+ return new Map(this.services);
125
+ } else {
126
+ return /* @__PURE__ */ new Map();
127
+ }
128
+ },
129
+ logger: this.logger,
130
+ getKernel: () => this
131
+ };
132
+ }
133
+ /**
134
+ * Resolve plugin dependencies using topological sort
135
+ * @returns Ordered list of plugins (dependencies first)
136
+ */
137
+ resolveDependencies() {
138
+ const resolved = [];
139
+ const visited = /* @__PURE__ */ new Set();
140
+ const visiting = /* @__PURE__ */ new Set();
141
+ const visit = (pluginName) => {
142
+ if (visited.has(pluginName)) return;
143
+ if (visiting.has(pluginName)) {
144
+ throw new Error(`[Kernel] Circular dependency detected: ${pluginName}`);
145
+ }
146
+ const plugin = this.plugins.get(pluginName);
147
+ if (!plugin) {
148
+ throw new Error(`[Kernel] Plugin '${pluginName}' not found`);
149
+ }
150
+ visiting.add(pluginName);
151
+ const deps = plugin.dependencies || [];
152
+ for (const dep of deps) {
153
+ if (!this.plugins.has(dep)) {
154
+ throw new Error(
155
+ `[Kernel] Dependency '${dep}' not found for plugin '${pluginName}'`
156
+ );
157
+ }
158
+ visit(dep);
159
+ }
160
+ visiting.delete(pluginName);
161
+ visited.add(pluginName);
162
+ resolved.push(plugin);
163
+ };
164
+ for (const pluginName of this.plugins.keys()) {
165
+ visit(pluginName);
166
+ }
167
+ return resolved;
168
+ }
169
+ /**
170
+ * Run plugin init phase
171
+ * @param plugin - Plugin to initialize
172
+ */
173
+ async runPluginInit(plugin) {
174
+ const pluginName = plugin.name;
175
+ this.logger.info(`Initializing plugin: ${pluginName}`);
176
+ try {
177
+ await plugin.init(this.context);
178
+ this.logger.info(`Plugin initialized: ${pluginName}`);
179
+ } catch (error) {
180
+ this.logger.error(`Plugin init failed: ${pluginName}`, error);
181
+ throw error;
182
+ }
183
+ }
184
+ /**
185
+ * Run plugin start phase
186
+ * @param plugin - Plugin to start
187
+ */
188
+ async runPluginStart(plugin) {
189
+ if (!plugin.start) return;
190
+ const pluginName = plugin.name;
191
+ this.logger.info(`Starting plugin: ${pluginName}`);
192
+ try {
193
+ await plugin.start(this.context);
194
+ this.logger.info(`Plugin started: ${pluginName}`);
195
+ } catch (error) {
196
+ this.logger.error(`Plugin start failed: ${pluginName}`, error);
197
+ throw error;
198
+ }
199
+ }
200
+ /**
201
+ * Run plugin destroy phase
202
+ * @param plugin - Plugin to destroy
203
+ */
204
+ async runPluginDestroy(plugin) {
205
+ if (!plugin.destroy) return;
206
+ const pluginName = plugin.name;
207
+ this.logger.info(`Destroying plugin: ${pluginName}`);
208
+ try {
209
+ await plugin.destroy();
210
+ this.logger.info(`Plugin destroyed: ${pluginName}`);
211
+ } catch (error) {
212
+ this.logger.error(`Plugin destroy failed: ${pluginName}`, error);
213
+ throw error;
214
+ }
215
+ }
216
+ /**
217
+ * Trigger a hook with all registered handlers
218
+ * @param name - Hook name
219
+ * @param args - Arguments to pass to handlers
220
+ */
221
+ async triggerHook(name, ...args) {
222
+ const handlers = this.hooks.get(name) || [];
223
+ this.logger.debug(`Triggering hook: ${name}`, {
224
+ hook: name,
225
+ handlerCount: handlers.length
226
+ });
227
+ for (const handler of handlers) {
228
+ try {
229
+ await handler(...args);
230
+ } catch (error) {
231
+ this.logger.error(`Hook handler failed: ${name}`, error);
232
+ }
233
+ }
234
+ }
235
+ /**
236
+ * Get current kernel state
237
+ */
238
+ getState() {
239
+ return this.state;
240
+ }
241
+ /**
242
+ * Get all registered plugins
243
+ */
244
+ getPlugins() {
245
+ return new Map(this.plugins);
246
+ }
247
+ };
248
+
249
+ // src/utils/env.ts
250
+ var isNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
251
+ function getEnv(key, defaultValue) {
252
+ if (typeof process !== "undefined" && process.env) {
253
+ return process.env[key] || defaultValue;
254
+ }
255
+ try {
256
+ if (typeof globalThis !== "undefined" && globalThis.process?.env) {
257
+ return globalThis.process.env[key] || defaultValue;
258
+ }
259
+ } catch (e) {
260
+ }
261
+ return defaultValue;
262
+ }
263
+ function safeExit(code = 0) {
264
+ if (isNode) {
265
+ process.exit(code);
266
+ }
267
+ }
268
+ function getMemoryUsage() {
269
+ if (isNode) {
270
+ return process.memoryUsage();
271
+ }
272
+ return { heapUsed: 0, heapTotal: 0 };
273
+ }
274
+
275
+ // src/logger.ts
276
+ var import_meta = {};
277
+ var ObjectLogger = class _ObjectLogger {
278
+ // CommonJS require function for Node.js
279
+ constructor(config = {}) {
280
+ this.isNode = isNode;
281
+ this.config = {
282
+ name: config.name,
283
+ level: config.level ?? "info",
284
+ format: config.format ?? (this.isNode ? "json" : "pretty"),
285
+ redact: config.redact ?? ["password", "token", "secret", "key"],
286
+ sourceLocation: config.sourceLocation ?? false,
287
+ file: config.file,
288
+ rotation: config.rotation ?? {
289
+ maxSize: "10m",
290
+ maxFiles: 5
291
+ }
292
+ };
293
+ if (this.isNode) {
294
+ this.initPinoLogger();
295
+ }
296
+ }
297
+ /**
298
+ * Initialize Pino logger for Node.js
299
+ */
300
+ initPinoLogger() {
301
+ if (!this.isNode) return;
302
+ try {
303
+ const { createRequire } = eval('require("module")');
304
+ this.require = createRequire(import_meta.url);
305
+ const pino = this.require("pino");
306
+ const pinoOptions = {
307
+ level: this.config.level,
308
+ redact: {
309
+ paths: this.config.redact,
310
+ censor: "***REDACTED***"
311
+ }
312
+ };
313
+ if (this.config.name) {
314
+ pinoOptions.name = this.config.name;
315
+ }
316
+ const targets = [];
317
+ if (this.config.format === "pretty") {
318
+ let hasPretty = false;
319
+ try {
320
+ this.require.resolve("pino-pretty");
321
+ hasPretty = true;
322
+ } catch (e) {
323
+ }
324
+ if (hasPretty) {
325
+ targets.push({
326
+ target: "pino-pretty",
327
+ options: {
328
+ colorize: true,
329
+ translateTime: "SYS:standard",
330
+ ignore: "pid,hostname"
331
+ },
332
+ level: this.config.level
333
+ });
334
+ } else {
335
+ console.warn("[Logger] pino-pretty not found. Install it for pretty logging: pnpm add -D pino-pretty");
336
+ targets.push({
337
+ target: "pino/file",
338
+ options: { destination: 1 },
339
+ level: this.config.level
340
+ });
341
+ }
342
+ } else if (this.config.format === "json") {
343
+ targets.push({
344
+ target: "pino/file",
345
+ options: { destination: 1 },
346
+ // stdout
347
+ level: this.config.level
348
+ });
349
+ } else {
350
+ targets.push({
351
+ target: "pino/file",
352
+ options: { destination: 1 },
353
+ level: this.config.level
354
+ });
355
+ }
356
+ if (this.config.file) {
357
+ targets.push({
358
+ target: "pino/file",
359
+ options: {
360
+ destination: this.config.file,
361
+ mkdir: true
362
+ },
363
+ level: this.config.level
364
+ });
365
+ }
366
+ if (targets.length > 0) {
367
+ pinoOptions.transport = targets.length === 1 ? targets[0] : { targets };
368
+ }
369
+ this.pinoInstance = pino(pinoOptions);
370
+ this.pinoLogger = this.pinoInstance;
371
+ } catch (error) {
372
+ console.warn("[Logger] Pino not available, falling back to console:", error);
373
+ this.pinoLogger = null;
374
+ }
375
+ }
376
+ /**
377
+ * Redact sensitive keys from context object (for browser)
378
+ */
379
+ redactSensitive(obj) {
380
+ if (!obj || typeof obj !== "object") return obj;
381
+ const redacted = Array.isArray(obj) ? [...obj] : { ...obj };
382
+ for (const key in redacted) {
383
+ const lowerKey = key.toLowerCase();
384
+ const shouldRedact = this.config.redact.some(
385
+ (pattern) => lowerKey.includes(pattern.toLowerCase())
386
+ );
387
+ if (shouldRedact) {
388
+ redacted[key] = "***REDACTED***";
389
+ } else if (typeof redacted[key] === "object" && redacted[key] !== null) {
390
+ redacted[key] = this.redactSensitive(redacted[key]);
391
+ }
392
+ }
393
+ return redacted;
394
+ }
395
+ /**
396
+ * Format log entry for browser
397
+ */
398
+ formatBrowserLog(level, message, context) {
399
+ if (this.config.format === "json") {
400
+ return JSON.stringify({
401
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
402
+ level,
403
+ message,
404
+ ...context
405
+ });
406
+ }
407
+ if (this.config.format === "text") {
408
+ const parts = [(/* @__PURE__ */ new Date()).toISOString(), level.toUpperCase(), message];
409
+ if (context && Object.keys(context).length > 0) {
410
+ parts.push(JSON.stringify(context));
411
+ }
412
+ return parts.join(" | ");
413
+ }
414
+ const levelColors = {
415
+ debug: "\x1B[36m",
416
+ // Cyan
417
+ info: "\x1B[32m",
418
+ // Green
419
+ warn: "\x1B[33m",
420
+ // Yellow
421
+ error: "\x1B[31m",
422
+ // Red
423
+ fatal: "\x1B[35m",
424
+ // Magenta
425
+ silent: ""
426
+ };
427
+ const reset = "\x1B[0m";
428
+ const color = levelColors[level] || "";
429
+ let output = `${color}[${level.toUpperCase()}]${reset} ${message}`;
430
+ if (context && Object.keys(context).length > 0) {
431
+ output += ` ${JSON.stringify(context, null, 2)}`;
432
+ }
433
+ return output;
434
+ }
435
+ /**
436
+ * Log using browser console
437
+ */
438
+ logBrowser(level, message, context, error) {
439
+ const redactedContext = context ? this.redactSensitive(context) : void 0;
440
+ const mergedContext = error ? { ...redactedContext, error: { message: error.message, stack: error.stack } } : redactedContext;
441
+ const formatted = this.formatBrowserLog(level, message, mergedContext);
442
+ const consoleMethod = level === "debug" ? "debug" : level === "info" ? "log" : level === "warn" ? "warn" : level === "error" || level === "fatal" ? "error" : "log";
443
+ console[consoleMethod](formatted);
444
+ }
445
+ /**
446
+ * Public logging methods
447
+ */
448
+ debug(message, meta) {
449
+ if (this.isNode && this.pinoLogger) {
450
+ this.pinoLogger.debug(meta || {}, message);
451
+ } else {
452
+ this.logBrowser("debug", message, meta);
453
+ }
454
+ }
455
+ info(message, meta) {
456
+ if (this.isNode && this.pinoLogger) {
457
+ this.pinoLogger.info(meta || {}, message);
458
+ } else {
459
+ this.logBrowser("info", message, meta);
460
+ }
461
+ }
462
+ warn(message, meta) {
463
+ if (this.isNode && this.pinoLogger) {
464
+ this.pinoLogger.warn(meta || {}, message);
465
+ } else {
466
+ this.logBrowser("warn", message, meta);
467
+ }
468
+ }
469
+ error(message, errorOrMeta, meta) {
470
+ let error;
471
+ let context = {};
472
+ if (errorOrMeta instanceof Error) {
473
+ error = errorOrMeta;
474
+ context = meta || {};
475
+ } else {
476
+ context = errorOrMeta || {};
477
+ }
478
+ if (this.isNode && this.pinoLogger) {
479
+ const errorContext = error ? { err: error, ...context } : context;
480
+ this.pinoLogger.error(errorContext, message);
481
+ } else {
482
+ this.logBrowser("error", message, context, error);
483
+ }
484
+ }
485
+ fatal(message, errorOrMeta, meta) {
486
+ let error;
487
+ let context = {};
488
+ if (errorOrMeta instanceof Error) {
489
+ error = errorOrMeta;
490
+ context = meta || {};
491
+ } else {
492
+ context = errorOrMeta || {};
493
+ }
494
+ if (this.isNode && this.pinoLogger) {
495
+ const errorContext = error ? { err: error, ...context } : context;
496
+ this.pinoLogger.fatal(errorContext, message);
497
+ } else {
498
+ this.logBrowser("fatal", message, context, error);
499
+ }
500
+ }
501
+ /**
502
+ * Create a child logger with additional context
503
+ * Note: Child loggers share the parent's Pino instance
504
+ */
505
+ child(context) {
506
+ const childLogger = new _ObjectLogger(this.config);
507
+ if (this.isNode && this.pinoInstance) {
508
+ childLogger.pinoLogger = this.pinoInstance.child(context);
509
+ childLogger.pinoInstance = this.pinoInstance;
510
+ }
511
+ return childLogger;
512
+ }
513
+ /**
514
+ * Set trace context for distributed tracing
515
+ */
516
+ withTrace(traceId, spanId) {
517
+ return this.child({ traceId, spanId });
518
+ }
519
+ /**
520
+ * Cleanup resources
521
+ */
522
+ async destroy() {
523
+ if (this.pinoLogger && this.pinoLogger.flush) {
524
+ await new Promise((resolve) => {
525
+ this.pinoLogger.flush(() => resolve());
526
+ });
527
+ }
528
+ }
529
+ /**
530
+ * Compatibility method for console.log usage
531
+ */
532
+ log(message, ...args) {
533
+ this.info(message, args.length > 0 ? { args } : void 0);
534
+ }
535
+ };
536
+ function createLogger(config) {
537
+ return new ObjectLogger(config);
538
+ }
539
+
540
+ // src/kernel.ts
541
+ var import_system = require("@objectstack/spec/system");
542
+
543
+ // src/security/plugin-config-validator.ts
544
+ var import_zod = require("zod");
545
+ var PluginConfigValidator = class {
546
+ constructor(logger) {
547
+ this.logger = logger;
548
+ }
549
+ /**
550
+ * Validate plugin configuration against its Zod schema
551
+ *
552
+ * @param plugin - Plugin metadata with configSchema
553
+ * @param config - User-provided configuration
554
+ * @returns Validated and typed configuration
555
+ * @throws Error with detailed validation errors
556
+ */
557
+ validatePluginConfig(plugin, config) {
558
+ if (!plugin.configSchema) {
559
+ this.logger.debug(`Plugin ${plugin.name} has no config schema - skipping validation`);
560
+ return config;
561
+ }
562
+ try {
563
+ const validatedConfig = plugin.configSchema.parse(config);
564
+ this.logger.debug(`\u2705 Plugin config validated: ${plugin.name}`, {
565
+ plugin: plugin.name,
566
+ configKeys: Object.keys(config || {}).length
567
+ });
568
+ return validatedConfig;
569
+ } catch (error) {
570
+ if (error instanceof import_zod.z.ZodError) {
571
+ const formattedErrors = this.formatZodErrors(error);
572
+ const errorMessage = [
573
+ `Plugin ${plugin.name} configuration validation failed:`,
574
+ ...formattedErrors.map((e) => ` - ${e.path}: ${e.message}`)
575
+ ].join("\n");
576
+ this.logger.error(errorMessage, void 0, {
577
+ plugin: plugin.name,
578
+ errors: formattedErrors
579
+ });
580
+ throw new Error(errorMessage);
581
+ }
582
+ throw error;
583
+ }
584
+ }
585
+ /**
586
+ * Validate partial configuration (for incremental updates)
587
+ *
588
+ * @param plugin - Plugin metadata
589
+ * @param partialConfig - Partial configuration to validate
590
+ * @returns Validated partial configuration
591
+ */
592
+ validatePartialConfig(plugin, partialConfig) {
593
+ if (!plugin.configSchema) {
594
+ return partialConfig;
595
+ }
596
+ try {
597
+ const partialSchema = plugin.configSchema.partial();
598
+ const validatedConfig = partialSchema.parse(partialConfig);
599
+ this.logger.debug(`\u2705 Partial config validated: ${plugin.name}`);
600
+ return validatedConfig;
601
+ } catch (error) {
602
+ if (error instanceof import_zod.z.ZodError) {
603
+ const formattedErrors = this.formatZodErrors(error);
604
+ const errorMessage = [
605
+ `Plugin ${plugin.name} partial configuration validation failed:`,
606
+ ...formattedErrors.map((e) => ` - ${e.path}: ${e.message}`)
607
+ ].join("\n");
608
+ throw new Error(errorMessage);
609
+ }
610
+ throw error;
611
+ }
612
+ }
613
+ /**
614
+ * Get default configuration from schema
615
+ *
616
+ * @param plugin - Plugin metadata
617
+ * @returns Default configuration object
618
+ */
619
+ getDefaultConfig(plugin) {
620
+ if (!plugin.configSchema) {
621
+ return void 0;
622
+ }
623
+ try {
624
+ const defaults = plugin.configSchema.parse({});
625
+ this.logger.debug(`Default config extracted: ${plugin.name}`);
626
+ return defaults;
627
+ } catch (error) {
628
+ this.logger.debug(`No default config available: ${plugin.name}`);
629
+ return void 0;
630
+ }
631
+ }
632
+ /**
633
+ * Check if configuration is valid without throwing
634
+ *
635
+ * @param plugin - Plugin metadata
636
+ * @param config - Configuration to check
637
+ * @returns True if valid, false otherwise
638
+ */
639
+ isConfigValid(plugin, config) {
640
+ if (!plugin.configSchema) {
641
+ return true;
642
+ }
643
+ const result = plugin.configSchema.safeParse(config);
644
+ return result.success;
645
+ }
646
+ /**
647
+ * Get configuration errors without throwing
648
+ *
649
+ * @param plugin - Plugin metadata
650
+ * @param config - Configuration to check
651
+ * @returns Array of validation errors, or empty array if valid
652
+ */
653
+ getConfigErrors(plugin, config) {
654
+ if (!plugin.configSchema) {
655
+ return [];
656
+ }
657
+ const result = plugin.configSchema.safeParse(config);
658
+ if (result.success) {
659
+ return [];
660
+ }
661
+ return this.formatZodErrors(result.error);
662
+ }
663
+ // Private methods
664
+ formatZodErrors(error) {
665
+ return error.issues.map((e) => ({
666
+ path: e.path.join(".") || "root",
667
+ message: e.message
668
+ }));
669
+ }
670
+ };
671
+ function createPluginConfigValidator(logger) {
672
+ return new PluginConfigValidator(logger);
673
+ }
674
+
675
+ // src/plugin-loader.ts
676
+ var ServiceLifecycle = /* @__PURE__ */ ((ServiceLifecycle2) => {
677
+ ServiceLifecycle2["SINGLETON"] = "singleton";
678
+ ServiceLifecycle2["TRANSIENT"] = "transient";
679
+ ServiceLifecycle2["SCOPED"] = "scoped";
680
+ return ServiceLifecycle2;
681
+ })(ServiceLifecycle || {});
682
+ var PluginLoader = class {
683
+ constructor(logger) {
684
+ this.loadedPlugins = /* @__PURE__ */ new Map();
685
+ this.serviceFactories = /* @__PURE__ */ new Map();
686
+ this.serviceInstances = /* @__PURE__ */ new Map();
687
+ this.scopedServices = /* @__PURE__ */ new Map();
688
+ this.creating = /* @__PURE__ */ new Set();
689
+ this.logger = logger;
690
+ this.configValidator = new PluginConfigValidator(logger);
691
+ }
692
+ /**
693
+ * Set the plugin context for service factories
694
+ */
695
+ setContext(context) {
696
+ this.context = context;
697
+ }
698
+ /**
699
+ * Get a synchronous service instance if it exists (Sync Helper)
700
+ */
701
+ getServiceInstance(name) {
702
+ return this.serviceInstances.get(name);
703
+ }
704
+ /**
705
+ * Load a plugin asynchronously with validation
706
+ */
707
+ async loadPlugin(plugin) {
708
+ const startTime = Date.now();
709
+ try {
710
+ this.logger.info(`Loading plugin: ${plugin.name}`);
711
+ const metadata = this.toPluginMetadata(plugin);
712
+ this.validatePluginStructure(metadata);
713
+ const versionCheck = this.checkVersionCompatibility(metadata);
714
+ if (!versionCheck.compatible) {
715
+ throw new Error(`Version incompatible: ${versionCheck.message}`);
716
+ }
717
+ if (metadata.configSchema) {
718
+ this.validatePluginConfig(metadata);
719
+ }
720
+ if (metadata.signature) {
721
+ await this.verifyPluginSignature(metadata);
722
+ }
723
+ this.loadedPlugins.set(metadata.name, metadata);
724
+ const loadTime = Date.now() - startTime;
725
+ this.logger.info(`Plugin loaded: ${plugin.name} (${loadTime}ms)`);
726
+ return {
727
+ success: true,
728
+ plugin: metadata,
729
+ loadTime
730
+ };
731
+ } catch (error) {
732
+ this.logger.error(`Failed to load plugin: ${plugin.name}`, error);
733
+ return {
734
+ success: false,
735
+ error,
736
+ loadTime: Date.now() - startTime
737
+ };
738
+ }
739
+ }
740
+ /**
741
+ * Register a service with factory function
742
+ */
743
+ registerServiceFactory(registration) {
744
+ if (this.serviceFactories.has(registration.name)) {
745
+ throw new Error(`Service factory '${registration.name}' already registered`);
746
+ }
747
+ this.serviceFactories.set(registration.name, registration);
748
+ this.logger.debug(`Service factory registered: ${registration.name} (${registration.lifecycle})`);
749
+ }
750
+ /**
751
+ * Get or create a service instance based on lifecycle type
752
+ */
753
+ async getService(name, scopeId) {
754
+ const registration = this.serviceFactories.get(name);
755
+ if (!registration) {
756
+ const instance = this.serviceInstances.get(name);
757
+ if (!instance) {
758
+ throw new Error(`Service '${name}' not found`);
759
+ }
760
+ return instance;
761
+ }
762
+ switch (registration.lifecycle) {
763
+ case "singleton" /* SINGLETON */:
764
+ return await this.getSingletonService(registration);
765
+ case "transient" /* TRANSIENT */:
766
+ return await this.createTransientService(registration);
767
+ case "scoped" /* SCOPED */:
768
+ if (!scopeId) {
769
+ throw new Error(`Scope ID required for scoped service '${name}'`);
770
+ }
771
+ return await this.getScopedService(registration, scopeId);
772
+ default:
773
+ throw new Error(`Unknown service lifecycle: ${registration.lifecycle}`);
774
+ }
775
+ }
776
+ /**
777
+ * Register a static service instance (legacy support)
778
+ */
779
+ registerService(name, service) {
780
+ if (this.serviceInstances.has(name)) {
781
+ throw new Error(`Service '${name}' already registered`);
782
+ }
783
+ this.serviceInstances.set(name, service);
784
+ }
785
+ /**
786
+ * Check if a service is registered (either as instance or factory)
787
+ */
788
+ hasService(name) {
789
+ return this.serviceInstances.has(name) || this.serviceFactories.has(name);
790
+ }
791
+ /**
792
+ * Detect circular dependencies in service factories
793
+ * Note: This only detects cycles in service dependencies, not plugin dependencies.
794
+ * Plugin dependency cycles are detected in the kernel's resolveDependencies method.
795
+ */
796
+ detectCircularDependencies() {
797
+ const cycles = [];
798
+ const visited = /* @__PURE__ */ new Set();
799
+ const visiting = /* @__PURE__ */ new Set();
800
+ const visit = (serviceName, path = []) => {
801
+ if (visiting.has(serviceName)) {
802
+ const cycle = [...path, serviceName].join(" -> ");
803
+ cycles.push(cycle);
804
+ return;
805
+ }
806
+ if (visited.has(serviceName)) {
807
+ return;
808
+ }
809
+ visiting.add(serviceName);
810
+ const registration = this.serviceFactories.get(serviceName);
811
+ if (registration?.dependencies) {
812
+ for (const dep of registration.dependencies) {
813
+ visit(dep, [...path, serviceName]);
814
+ }
815
+ }
816
+ visiting.delete(serviceName);
817
+ visited.add(serviceName);
818
+ };
819
+ for (const serviceName of this.serviceFactories.keys()) {
820
+ visit(serviceName);
821
+ }
822
+ return cycles;
823
+ }
824
+ /**
825
+ * Check plugin health
826
+ */
827
+ async checkPluginHealth(pluginName) {
828
+ const plugin = this.loadedPlugins.get(pluginName);
829
+ if (!plugin) {
830
+ return {
831
+ healthy: false,
832
+ message: "Plugin not found",
833
+ lastCheck: /* @__PURE__ */ new Date()
834
+ };
835
+ }
836
+ if (!plugin.healthCheck) {
837
+ return {
838
+ healthy: true,
839
+ message: "No health check defined",
840
+ lastCheck: /* @__PURE__ */ new Date()
841
+ };
842
+ }
843
+ try {
844
+ const status = await plugin.healthCheck();
845
+ return {
846
+ ...status,
847
+ lastCheck: /* @__PURE__ */ new Date()
848
+ };
849
+ } catch (error) {
850
+ return {
851
+ healthy: false,
852
+ message: `Health check failed: ${error.message}`,
853
+ lastCheck: /* @__PURE__ */ new Date()
854
+ };
855
+ }
856
+ }
857
+ /**
858
+ * Clear scoped services for a scope
859
+ */
860
+ clearScope(scopeId) {
861
+ this.scopedServices.delete(scopeId);
862
+ this.logger.debug(`Cleared scope: ${scopeId}`);
863
+ }
864
+ /**
865
+ * Get all loaded plugins
866
+ */
867
+ getLoadedPlugins() {
868
+ return new Map(this.loadedPlugins);
869
+ }
870
+ // Private helper methods
871
+ toPluginMetadata(plugin) {
872
+ return {
873
+ ...plugin,
874
+ version: plugin.version || "0.0.0"
875
+ };
876
+ }
877
+ validatePluginStructure(plugin) {
878
+ if (!plugin.name) {
879
+ throw new Error("Plugin name is required");
880
+ }
881
+ if (!plugin.init) {
882
+ throw new Error("Plugin init function is required");
883
+ }
884
+ if (!this.isValidSemanticVersion(plugin.version)) {
885
+ throw new Error(`Invalid semantic version: ${plugin.version}`);
886
+ }
887
+ }
888
+ checkVersionCompatibility(plugin) {
889
+ const version = plugin.version;
890
+ if (!this.isValidSemanticVersion(version)) {
891
+ return {
892
+ compatible: false,
893
+ pluginVersion: version,
894
+ message: "Invalid semantic version format"
895
+ };
896
+ }
897
+ return {
898
+ compatible: true,
899
+ pluginVersion: version
900
+ };
901
+ }
902
+ isValidSemanticVersion(version) {
903
+ const semverRegex = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/;
904
+ return semverRegex.test(version);
905
+ }
906
+ validatePluginConfig(plugin, config) {
907
+ if (!plugin.configSchema) {
908
+ return;
909
+ }
910
+ if (config === void 0) {
911
+ this.logger.debug(`Plugin ${plugin.name} has configuration schema (config validation postponed)`);
912
+ return;
913
+ }
914
+ this.configValidator.validatePluginConfig(plugin, config);
915
+ }
916
+ async verifyPluginSignature(plugin) {
917
+ if (!plugin.signature) {
918
+ return;
919
+ }
920
+ this.logger.debug(`Plugin ${plugin.name} has signature (use PluginSignatureVerifier for verification)`);
921
+ }
922
+ async getSingletonService(registration) {
923
+ let instance = this.serviceInstances.get(registration.name);
924
+ if (!instance) {
925
+ instance = await this.createServiceInstance(registration);
926
+ this.serviceInstances.set(registration.name, instance);
927
+ this.logger.debug(`Singleton service created: ${registration.name}`);
928
+ }
929
+ return instance;
930
+ }
931
+ async createTransientService(registration) {
932
+ const instance = await this.createServiceInstance(registration);
933
+ this.logger.debug(`Transient service created: ${registration.name}`);
934
+ return instance;
935
+ }
936
+ async getScopedService(registration, scopeId) {
937
+ if (!this.scopedServices.has(scopeId)) {
938
+ this.scopedServices.set(scopeId, /* @__PURE__ */ new Map());
939
+ }
940
+ const scope = this.scopedServices.get(scopeId);
941
+ let instance = scope.get(registration.name);
942
+ if (!instance) {
943
+ instance = await this.createServiceInstance(registration);
944
+ scope.set(registration.name, instance);
945
+ this.logger.debug(`Scoped service created: ${registration.name} (scope: ${scopeId})`);
946
+ }
947
+ return instance;
948
+ }
949
+ async createServiceInstance(registration) {
950
+ if (!this.context) {
951
+ throw new Error(`[PluginLoader] Context not set - cannot create service '${registration.name}'`);
952
+ }
953
+ if (this.creating.has(registration.name)) {
954
+ throw new Error(`Circular dependency detected: ${Array.from(this.creating).join(" -> ")} -> ${registration.name}`);
955
+ }
956
+ this.creating.add(registration.name);
957
+ try {
958
+ return await registration.factory(this.context);
959
+ } finally {
960
+ this.creating.delete(registration.name);
961
+ }
962
+ }
963
+ };
964
+
965
+ // src/kernel.ts
966
+ var ObjectKernel = class {
967
+ constructor(config = {}) {
968
+ this.plugins = /* @__PURE__ */ new Map();
969
+ this.services = /* @__PURE__ */ new Map();
970
+ this.hooks = /* @__PURE__ */ new Map();
971
+ this.state = "idle";
972
+ this.startedPlugins = /* @__PURE__ */ new Set();
973
+ this.pluginStartTimes = /* @__PURE__ */ new Map();
974
+ this.shutdownHandlers = [];
975
+ this.config = {
976
+ defaultStartupTimeout: 3e4,
977
+ // 30 seconds
978
+ gracefulShutdown: true,
979
+ shutdownTimeout: 6e4,
980
+ // 60 seconds
981
+ rollbackOnFailure: true,
982
+ ...config
983
+ };
984
+ this.logger = createLogger(config.logger);
985
+ this.pluginLoader = new PluginLoader(this.logger);
986
+ this.context = {
987
+ registerService: (name, service) => {
988
+ this.registerService(name, service);
989
+ },
990
+ getService: (name) => {
991
+ const service = this.services.get(name);
992
+ if (service) {
993
+ return service;
994
+ }
995
+ const loaderService = this.pluginLoader.getServiceInstance(name);
996
+ if (loaderService) {
997
+ this.services.set(name, loaderService);
998
+ return loaderService;
999
+ }
1000
+ try {
1001
+ const service2 = this.pluginLoader.getService(name);
1002
+ if (service2 instanceof Promise) {
1003
+ throw new Error(`Service '${name}' is async - use await`);
1004
+ }
1005
+ return service2;
1006
+ } catch (error) {
1007
+ if (error.message?.includes("is async")) {
1008
+ throw error;
1009
+ }
1010
+ const isNotFoundError = error.message === `Service '${name}' not found`;
1011
+ if (!isNotFoundError) {
1012
+ throw error;
1013
+ }
1014
+ throw new Error(`[Kernel] Service '${name}' not found`);
1015
+ }
1016
+ },
1017
+ hook: (name, handler) => {
1018
+ if (!this.hooks.has(name)) {
1019
+ this.hooks.set(name, []);
1020
+ }
1021
+ this.hooks.get(name).push(handler);
1022
+ },
1023
+ trigger: async (name, ...args) => {
1024
+ const handlers = this.hooks.get(name) || [];
1025
+ for (const handler of handlers) {
1026
+ await handler(...args);
1027
+ }
1028
+ },
1029
+ getServices: () => {
1030
+ return new Map(this.services);
1031
+ },
1032
+ logger: this.logger,
1033
+ getKernel: () => this
1034
+ // Type compatibility
1035
+ };
1036
+ this.pluginLoader.setContext(this.context);
1037
+ if (this.config.gracefulShutdown) {
1038
+ this.registerShutdownSignals();
1039
+ }
1040
+ }
1041
+ /**
1042
+ * Register a plugin with enhanced validation
1043
+ */
1044
+ async use(plugin) {
1045
+ if (this.state !== "idle") {
1046
+ throw new Error("[Kernel] Cannot register plugins after bootstrap has started");
1047
+ }
1048
+ const result = await this.pluginLoader.loadPlugin(plugin);
1049
+ if (!result.success || !result.plugin) {
1050
+ throw new Error(`Failed to load plugin: ${plugin.name} - ${result.error?.message}`);
1051
+ }
1052
+ const pluginMeta = result.plugin;
1053
+ this.plugins.set(pluginMeta.name, pluginMeta);
1054
+ this.logger.info(`Plugin registered: ${pluginMeta.name}@${pluginMeta.version}`, {
1055
+ plugin: pluginMeta.name,
1056
+ version: pluginMeta.version
1057
+ });
1058
+ return this;
1059
+ }
1060
+ /**
1061
+ * Register a service instance directly
1062
+ */
1063
+ registerService(name, service) {
1064
+ if (this.services.has(name)) {
1065
+ throw new Error(`[Kernel] Service '${name}' already registered`);
1066
+ }
1067
+ this.services.set(name, service);
1068
+ this.pluginLoader.registerService(name, service);
1069
+ this.logger.info(`Service '${name}' registered`, { service: name });
1070
+ return this;
1071
+ }
1072
+ /**
1073
+ * Register a service factory with lifecycle management
1074
+ */
1075
+ registerServiceFactory(name, factory, lifecycle = "singleton" /* SINGLETON */, dependencies) {
1076
+ this.pluginLoader.registerServiceFactory({
1077
+ name,
1078
+ factory,
1079
+ lifecycle,
1080
+ dependencies
1081
+ });
1082
+ return this;
1083
+ }
1084
+ /**
1085
+ * Validate Critical System Requirements
1086
+ */
1087
+ validateSystemRequirements() {
1088
+ if (this.config.skipSystemValidation) {
1089
+ this.logger.debug("System requirement validation skipped");
1090
+ return;
1091
+ }
1092
+ this.logger.debug("Validating system service requirements...");
1093
+ const missingServices = [];
1094
+ const missingCoreServices = [];
1095
+ for (const [serviceName, criticality] of Object.entries(import_system.ServiceRequirementDef)) {
1096
+ const hasService = this.services.has(serviceName) || this.pluginLoader.hasService(serviceName);
1097
+ if (!hasService) {
1098
+ if (criticality === "required") {
1099
+ this.logger.error(`CRITICAL: Required service missing: ${serviceName}`);
1100
+ missingServices.push(serviceName);
1101
+ } else if (criticality === "core") {
1102
+ this.logger.warn(`CORE: Core service missing, functionality may be degraded: ${serviceName}`);
1103
+ missingCoreServices.push(serviceName);
1104
+ } else {
1105
+ this.logger.info(`Info: Optional service not present: ${serviceName}`);
1106
+ }
1107
+ }
1108
+ }
1109
+ if (missingServices.length > 0) {
1110
+ const errorMsg = `System failed to start. Missing critical services: ${missingServices.join(", ")}`;
1111
+ this.logger.error(errorMsg);
1112
+ throw new Error(errorMsg);
1113
+ }
1114
+ if (missingCoreServices.length > 0) {
1115
+ this.logger.warn(`System started with degraded capabilities. Missing core services: ${missingCoreServices.join(", ")}`);
1116
+ }
1117
+ this.logger.info("System requirement check passed");
1118
+ }
1119
+ /**
1120
+ * Bootstrap the kernel with enhanced features
1121
+ */
1122
+ async bootstrap() {
1123
+ if (this.state !== "idle") {
1124
+ throw new Error("[Kernel] Kernel already bootstrapped");
1125
+ }
1126
+ this.state = "initializing";
1127
+ this.logger.info("Bootstrap started");
1128
+ try {
1129
+ const cycles = this.pluginLoader.detectCircularDependencies();
1130
+ if (cycles.length > 0) {
1131
+ this.logger.warn("Circular service dependencies detected:", { cycles });
1132
+ }
1133
+ const orderedPlugins = this.resolveDependencies();
1134
+ this.logger.info("Phase 1: Init plugins");
1135
+ for (const plugin of orderedPlugins) {
1136
+ await this.initPluginWithTimeout(plugin);
1137
+ }
1138
+ this.logger.info("Phase 2: Start plugins");
1139
+ this.state = "running";
1140
+ for (const plugin of orderedPlugins) {
1141
+ const result = await this.startPluginWithTimeout(plugin);
1142
+ if (!result.success) {
1143
+ this.logger.error(`Plugin startup failed: ${plugin.name}`, result.error);
1144
+ if (this.config.rollbackOnFailure) {
1145
+ this.logger.warn("Rolling back started plugins...");
1146
+ await this.rollbackStartedPlugins();
1147
+ throw new Error(`Plugin ${plugin.name} failed to start - rollback complete`);
1148
+ }
1149
+ }
1150
+ }
1151
+ this.validateSystemRequirements();
1152
+ this.logger.debug("Triggering kernel:ready hook");
1153
+ await this.context.trigger("kernel:ready");
1154
+ this.logger.info("\u2705 Bootstrap complete");
1155
+ } catch (error) {
1156
+ this.state = "stopped";
1157
+ throw error;
1158
+ }
1159
+ }
1160
+ /**
1161
+ * Graceful shutdown with timeout
1162
+ */
1163
+ async shutdown() {
1164
+ if (this.state === "stopped" || this.state === "stopping") {
1165
+ this.logger.warn("Kernel already stopped or stopping");
1166
+ return;
1167
+ }
1168
+ if (this.state !== "running") {
1169
+ throw new Error("[Kernel] Kernel not running");
1170
+ }
1171
+ this.state = "stopping";
1172
+ this.logger.info("Graceful shutdown started");
1173
+ try {
1174
+ const shutdownPromise = this.performShutdown();
1175
+ const timeoutPromise = new Promise((_, reject) => {
1176
+ setTimeout(() => {
1177
+ reject(new Error("Shutdown timeout exceeded"));
1178
+ }, this.config.shutdownTimeout);
1179
+ });
1180
+ await Promise.race([shutdownPromise, timeoutPromise]);
1181
+ this.state = "stopped";
1182
+ this.logger.info("\u2705 Graceful shutdown complete");
1183
+ } catch (error) {
1184
+ this.logger.error("Shutdown error - forcing stop", error);
1185
+ this.state = "stopped";
1186
+ throw error;
1187
+ } finally {
1188
+ await this.logger.destroy();
1189
+ }
1190
+ }
1191
+ /**
1192
+ * Check health of a specific plugin
1193
+ */
1194
+ async checkPluginHealth(pluginName) {
1195
+ return await this.pluginLoader.checkPluginHealth(pluginName);
1196
+ }
1197
+ /**
1198
+ * Check health of all plugins
1199
+ */
1200
+ async checkAllPluginsHealth() {
1201
+ const results = /* @__PURE__ */ new Map();
1202
+ for (const pluginName of this.plugins.keys()) {
1203
+ const health = await this.checkPluginHealth(pluginName);
1204
+ results.set(pluginName, health);
1205
+ }
1206
+ return results;
1207
+ }
1208
+ /**
1209
+ * Get plugin startup metrics
1210
+ */
1211
+ getPluginMetrics() {
1212
+ return new Map(this.pluginStartTimes);
1213
+ }
1214
+ /**
1215
+ * Get a service (sync helper)
1216
+ */
1217
+ getService(name) {
1218
+ return this.context.getService(name);
1219
+ }
1220
+ /**
1221
+ * Get a service asynchronously (supports factories)
1222
+ */
1223
+ async getServiceAsync(name, scopeId) {
1224
+ return await this.pluginLoader.getService(name, scopeId);
1225
+ }
1226
+ /**
1227
+ * Check if kernel is running
1228
+ */
1229
+ isRunning() {
1230
+ return this.state === "running";
1231
+ }
1232
+ /**
1233
+ * Get kernel state
1234
+ */
1235
+ getState() {
1236
+ return this.state;
1237
+ }
1238
+ // Private methods
1239
+ async initPluginWithTimeout(plugin) {
1240
+ const timeout = plugin.startupTimeout || this.config.defaultStartupTimeout;
1241
+ this.logger.debug(`Init: ${plugin.name}`, { plugin: plugin.name });
1242
+ const initPromise = plugin.init(this.context);
1243
+ const timeoutPromise = new Promise((_, reject) => {
1244
+ setTimeout(() => {
1245
+ reject(new Error(`Plugin ${plugin.name} init timeout after ${timeout}ms`));
1246
+ }, timeout);
1247
+ });
1248
+ await Promise.race([initPromise, timeoutPromise]);
1249
+ }
1250
+ async startPluginWithTimeout(plugin) {
1251
+ if (!plugin.start) {
1252
+ return { success: true, pluginName: plugin.name };
1253
+ }
1254
+ const timeout = plugin.startupTimeout || this.config.defaultStartupTimeout;
1255
+ const startTime = Date.now();
1256
+ this.logger.debug(`Start: ${plugin.name}`, { plugin: plugin.name });
1257
+ try {
1258
+ const startPromise = plugin.start(this.context);
1259
+ const timeoutPromise = new Promise((_, reject) => {
1260
+ setTimeout(() => {
1261
+ reject(new Error(`Plugin ${plugin.name} start timeout after ${timeout}ms`));
1262
+ }, timeout);
1263
+ });
1264
+ await Promise.race([startPromise, timeoutPromise]);
1265
+ const duration = Date.now() - startTime;
1266
+ this.startedPlugins.add(plugin.name);
1267
+ this.pluginStartTimes.set(plugin.name, duration);
1268
+ this.logger.debug(`Plugin started: ${plugin.name} (${duration}ms)`);
1269
+ return {
1270
+ success: true,
1271
+ pluginName: plugin.name,
1272
+ startTime: duration
1273
+ };
1274
+ } catch (error) {
1275
+ const duration = Date.now() - startTime;
1276
+ const isTimeout = error.message.includes("timeout");
1277
+ return {
1278
+ success: false,
1279
+ pluginName: plugin.name,
1280
+ error,
1281
+ startTime: duration,
1282
+ timedOut: isTimeout
1283
+ };
1284
+ }
1285
+ }
1286
+ async rollbackStartedPlugins() {
1287
+ const pluginsToRollback = Array.from(this.startedPlugins).reverse();
1288
+ for (const pluginName of pluginsToRollback) {
1289
+ const plugin = this.plugins.get(pluginName);
1290
+ if (plugin?.destroy) {
1291
+ try {
1292
+ this.logger.debug(`Rollback: ${pluginName}`);
1293
+ await plugin.destroy();
1294
+ } catch (error) {
1295
+ this.logger.error(`Rollback failed for ${pluginName}`, error);
1296
+ }
1297
+ }
1298
+ }
1299
+ this.startedPlugins.clear();
1300
+ }
1301
+ async performShutdown() {
1302
+ await this.context.trigger("kernel:shutdown");
1303
+ const orderedPlugins = Array.from(this.plugins.values()).reverse();
1304
+ for (const plugin of orderedPlugins) {
1305
+ if (plugin.destroy) {
1306
+ this.logger.debug(`Destroy: ${plugin.name}`, { plugin: plugin.name });
1307
+ try {
1308
+ await plugin.destroy();
1309
+ } catch (error) {
1310
+ this.logger.error(`Error destroying plugin ${plugin.name}`, error);
1311
+ }
1312
+ }
1313
+ }
1314
+ for (const handler of this.shutdownHandlers) {
1315
+ try {
1316
+ await handler();
1317
+ } catch (error) {
1318
+ this.logger.error("Shutdown handler error", error);
1319
+ }
1320
+ }
1321
+ }
1322
+ resolveDependencies() {
1323
+ const resolved = [];
1324
+ const visited = /* @__PURE__ */ new Set();
1325
+ const visiting = /* @__PURE__ */ new Set();
1326
+ const visit = (pluginName) => {
1327
+ if (visited.has(pluginName)) return;
1328
+ if (visiting.has(pluginName)) {
1329
+ throw new Error(`[Kernel] Circular dependency detected: ${pluginName}`);
1330
+ }
1331
+ const plugin = this.plugins.get(pluginName);
1332
+ if (!plugin) {
1333
+ throw new Error(`[Kernel] Plugin '${pluginName}' not found`);
1334
+ }
1335
+ visiting.add(pluginName);
1336
+ const deps = plugin.dependencies || [];
1337
+ for (const dep of deps) {
1338
+ if (!this.plugins.has(dep)) {
1339
+ throw new Error(`[Kernel] Dependency '${dep}' not found for plugin '${pluginName}'`);
1340
+ }
1341
+ visit(dep);
1342
+ }
1343
+ visiting.delete(pluginName);
1344
+ visited.add(pluginName);
1345
+ resolved.push(plugin);
1346
+ };
1347
+ for (const pluginName of this.plugins.keys()) {
1348
+ visit(pluginName);
1349
+ }
1350
+ return resolved;
1351
+ }
1352
+ registerShutdownSignals() {
1353
+ const signals = ["SIGINT", "SIGTERM", "SIGQUIT"];
1354
+ let shutdownInProgress = false;
1355
+ const handleShutdown = async (signal) => {
1356
+ if (shutdownInProgress) {
1357
+ this.logger.warn(`Shutdown already in progress, ignoring ${signal}`);
1358
+ return;
1359
+ }
1360
+ shutdownInProgress = true;
1361
+ this.logger.info(`Received ${signal} - initiating graceful shutdown`);
1362
+ try {
1363
+ await this.shutdown();
1364
+ safeExit(0);
1365
+ } catch (error) {
1366
+ this.logger.error("Shutdown failed", error);
1367
+ safeExit(1);
1368
+ }
1369
+ };
1370
+ if (isNode) {
1371
+ for (const signal of signals) {
1372
+ process.on(signal, () => handleShutdown(signal));
1373
+ }
1374
+ }
1375
+ }
1376
+ /**
1377
+ * Register a custom shutdown handler
1378
+ */
1379
+ onShutdown(handler) {
1380
+ this.shutdownHandlers.push(handler);
1381
+ }
1382
+ };
1383
+
1384
+ // src/lite-kernel.ts
1385
+ var LiteKernel = class extends ObjectKernelBase {
1386
+ constructor(config) {
1387
+ const logger = createLogger(config?.logger);
1388
+ super(logger);
1389
+ this.context = this.createContext();
1390
+ }
1391
+ /**
1392
+ * Register a plugin
1393
+ * @param plugin - Plugin instance
1394
+ */
1395
+ use(plugin) {
1396
+ this.validateIdle();
1397
+ const pluginName = plugin.name;
1398
+ if (this.plugins.has(pluginName)) {
1399
+ throw new Error(`[Kernel] Plugin '${pluginName}' already registered`);
1400
+ }
1401
+ this.plugins.set(pluginName, plugin);
1402
+ return this;
1403
+ }
1404
+ /**
1405
+ * Bootstrap the kernel
1406
+ * 1. Resolve dependencies (topological sort)
1407
+ * 2. Init phase - plugins register services
1408
+ * 3. Start phase - plugins execute business logic
1409
+ * 4. Trigger 'kernel:ready' hook
1410
+ */
1411
+ async bootstrap() {
1412
+ this.validateState("idle");
1413
+ this.state = "initializing";
1414
+ this.logger.info("Bootstrap started");
1415
+ const orderedPlugins = this.resolveDependencies();
1416
+ this.logger.info("Phase 1: Init plugins");
1417
+ for (const plugin of orderedPlugins) {
1418
+ await this.runPluginInit(plugin);
1419
+ }
1420
+ this.logger.info("Phase 2: Start plugins");
1421
+ this.state = "running";
1422
+ for (const plugin of orderedPlugins) {
1423
+ await this.runPluginStart(plugin);
1424
+ }
1425
+ await this.triggerHook("kernel:ready");
1426
+ this.logger.info("\u2705 Bootstrap complete", {
1427
+ pluginCount: this.plugins.size
1428
+ });
1429
+ }
1430
+ /**
1431
+ * Shutdown the kernel
1432
+ * Calls destroy on all plugins in reverse order
1433
+ */
1434
+ async shutdown() {
1435
+ await this.destroy();
1436
+ }
1437
+ /**
1438
+ * Graceful shutdown - destroy all plugins in reverse order
1439
+ */
1440
+ async destroy() {
1441
+ if (this.state === "stopped") {
1442
+ this.logger.warn("Kernel already stopped");
1443
+ return;
1444
+ }
1445
+ this.state = "stopping";
1446
+ this.logger.info("Shutdown started");
1447
+ await this.triggerHook("kernel:shutdown");
1448
+ const orderedPlugins = this.resolveDependencies();
1449
+ for (const plugin of orderedPlugins.reverse()) {
1450
+ await this.runPluginDestroy(plugin);
1451
+ }
1452
+ this.state = "stopped";
1453
+ this.logger.info("\u2705 Shutdown complete");
1454
+ if (this.logger && typeof this.logger.destroy === "function") {
1455
+ await this.logger.destroy();
1456
+ }
1457
+ }
1458
+ /**
1459
+ * Get a service from the registry
1460
+ * Convenience method for external access
1461
+ */
1462
+ getService(name) {
1463
+ return this.context.getService(name);
1464
+ }
1465
+ /**
1466
+ * Check if kernel is running
1467
+ */
1468
+ isRunning() {
1469
+ return this.state === "running";
1470
+ }
1471
+ };
1472
+
1473
+ // src/api-registry.ts
1474
+ var import_api = require("@objectstack/spec/api");
1475
+ var ApiRegistry = class {
1476
+ constructor(logger, conflictResolution = "error", version = "1.0.0") {
1477
+ this.apis = /* @__PURE__ */ new Map();
1478
+ this.endpoints = /* @__PURE__ */ new Map();
1479
+ this.routes = /* @__PURE__ */ new Map();
1480
+ // Performance optimization: Auxiliary indices for O(1) lookups
1481
+ this.apisByType = /* @__PURE__ */ new Map();
1482
+ this.apisByTag = /* @__PURE__ */ new Map();
1483
+ this.apisByStatus = /* @__PURE__ */ new Map();
1484
+ this.logger = logger;
1485
+ this.conflictResolution = conflictResolution;
1486
+ this.version = version;
1487
+ this.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1488
+ }
1489
+ /**
1490
+ * Register an API with its endpoints
1491
+ *
1492
+ * @param api - API registry entry
1493
+ * @throws Error if API already registered or route conflicts detected
1494
+ */
1495
+ registerApi(api) {
1496
+ if (this.apis.has(api.id)) {
1497
+ throw new Error(`[ApiRegistry] API '${api.id}' already registered`);
1498
+ }
1499
+ const fullApi = import_api.ApiRegistryEntrySchema.parse(api);
1500
+ for (const endpoint of fullApi.endpoints) {
1501
+ this.validateEndpoint(endpoint, fullApi.id);
1502
+ }
1503
+ this.apis.set(fullApi.id, fullApi);
1504
+ for (const endpoint of fullApi.endpoints) {
1505
+ this.registerEndpoint(fullApi.id, endpoint);
1506
+ }
1507
+ this.updateIndices(fullApi);
1508
+ this.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1509
+ this.logger.info(`API registered: ${fullApi.id}`, {
1510
+ api: fullApi.id,
1511
+ type: fullApi.type,
1512
+ endpointCount: fullApi.endpoints.length
1513
+ });
1514
+ }
1515
+ /**
1516
+ * Unregister an API and all its endpoints
1517
+ *
1518
+ * @param apiId - API identifier
1519
+ */
1520
+ unregisterApi(apiId) {
1521
+ const api = this.apis.get(apiId);
1522
+ if (!api) {
1523
+ throw new Error(`[ApiRegistry] API '${apiId}' not found`);
1524
+ }
1525
+ for (const endpoint of api.endpoints) {
1526
+ this.unregisterEndpoint(apiId, endpoint.id);
1527
+ }
1528
+ this.removeFromIndices(api);
1529
+ this.apis.delete(apiId);
1530
+ this.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1531
+ this.logger.info(`API unregistered: ${apiId}`);
1532
+ }
1533
+ /**
1534
+ * Register a single endpoint
1535
+ *
1536
+ * @param apiId - API identifier
1537
+ * @param endpoint - Endpoint registration
1538
+ * @throws Error if route conflict detected
1539
+ */
1540
+ registerEndpoint(apiId, endpoint) {
1541
+ const endpointKey = `${apiId}:${endpoint.id}`;
1542
+ if (this.endpoints.has(endpointKey)) {
1543
+ throw new Error(`[ApiRegistry] Endpoint '${endpoint.id}' already registered for API '${apiId}'`);
1544
+ }
1545
+ this.endpoints.set(endpointKey, { api: apiId, endpoint });
1546
+ if (endpoint.path) {
1547
+ this.registerRoute(apiId, endpoint);
1548
+ }
1549
+ }
1550
+ /**
1551
+ * Unregister a single endpoint
1552
+ *
1553
+ * @param apiId - API identifier
1554
+ * @param endpointId - Endpoint identifier
1555
+ */
1556
+ unregisterEndpoint(apiId, endpointId) {
1557
+ const endpointKey = `${apiId}:${endpointId}`;
1558
+ const entry = this.endpoints.get(endpointKey);
1559
+ if (!entry) {
1560
+ return;
1561
+ }
1562
+ if (entry.endpoint.path) {
1563
+ const routeKey = this.getRouteKey(entry.endpoint);
1564
+ this.routes.delete(routeKey);
1565
+ }
1566
+ this.endpoints.delete(endpointKey);
1567
+ }
1568
+ /**
1569
+ * Register a route with conflict detection
1570
+ *
1571
+ * @param apiId - API identifier
1572
+ * @param endpoint - Endpoint registration
1573
+ * @throws Error if route conflict detected (based on strategy)
1574
+ */
1575
+ registerRoute(apiId, endpoint) {
1576
+ const routeKey = this.getRouteKey(endpoint);
1577
+ const priority = endpoint.priority ?? 100;
1578
+ const existingRoute = this.routes.get(routeKey);
1579
+ if (existingRoute) {
1580
+ this.handleRouteConflict(routeKey, apiId, endpoint, existingRoute, priority);
1581
+ return;
1582
+ }
1583
+ this.routes.set(routeKey, {
1584
+ api: apiId,
1585
+ endpointId: endpoint.id,
1586
+ priority
1587
+ });
1588
+ }
1589
+ /**
1590
+ * Handle route conflict based on resolution strategy
1591
+ *
1592
+ * @param routeKey - Route key
1593
+ * @param apiId - New API identifier
1594
+ * @param endpoint - New endpoint
1595
+ * @param existingRoute - Existing route registration
1596
+ * @param newPriority - New endpoint priority
1597
+ * @throws Error if strategy is 'error'
1598
+ */
1599
+ handleRouteConflict(routeKey, apiId, endpoint, existingRoute, newPriority) {
1600
+ const strategy = this.conflictResolution;
1601
+ switch (strategy) {
1602
+ case "error":
1603
+ throw new Error(
1604
+ `[ApiRegistry] Route conflict detected: '${routeKey}' is already registered by API '${existingRoute.api}' endpoint '${existingRoute.endpointId}'`
1605
+ );
1606
+ case "priority":
1607
+ if (newPriority > existingRoute.priority) {
1608
+ this.logger.warn(
1609
+ `Route conflict: replacing '${routeKey}' (priority ${existingRoute.priority} -> ${newPriority})`,
1610
+ {
1611
+ oldApi: existingRoute.api,
1612
+ oldEndpoint: existingRoute.endpointId,
1613
+ newApi: apiId,
1614
+ newEndpoint: endpoint.id
1615
+ }
1616
+ );
1617
+ this.routes.set(routeKey, {
1618
+ api: apiId,
1619
+ endpointId: endpoint.id,
1620
+ priority: newPriority
1621
+ });
1622
+ } else {
1623
+ this.logger.warn(
1624
+ `Route conflict: keeping existing '${routeKey}' (priority ${existingRoute.priority} >= ${newPriority})`,
1625
+ {
1626
+ existingApi: existingRoute.api,
1627
+ existingEndpoint: existingRoute.endpointId,
1628
+ newApi: apiId,
1629
+ newEndpoint: endpoint.id
1630
+ }
1631
+ );
1632
+ }
1633
+ break;
1634
+ case "first-wins":
1635
+ this.logger.warn(
1636
+ `Route conflict: keeping first registered '${routeKey}'`,
1637
+ {
1638
+ existingApi: existingRoute.api,
1639
+ newApi: apiId
1640
+ }
1641
+ );
1642
+ break;
1643
+ case "last-wins":
1644
+ this.logger.warn(
1645
+ `Route conflict: replacing with last registered '${routeKey}'`,
1646
+ {
1647
+ oldApi: existingRoute.api,
1648
+ newApi: apiId
1649
+ }
1650
+ );
1651
+ this.routes.set(routeKey, {
1652
+ api: apiId,
1653
+ endpointId: endpoint.id,
1654
+ priority: newPriority
1655
+ });
1656
+ break;
1657
+ default:
1658
+ throw new Error(`[ApiRegistry] Unknown conflict resolution strategy: ${strategy}`);
1659
+ }
1660
+ }
1661
+ /**
1662
+ * Generate a unique route key for conflict detection
1663
+ *
1664
+ * NOTE: This implementation uses exact string matching for route conflict detection.
1665
+ * It works well for static paths but has limitations with parameterized routes.
1666
+ * For example, `/api/users/:id` and `/api/users/:userId` will NOT be detected as conflicts
1667
+ * even though they are semantically identical parameterized patterns. Similarly,
1668
+ * `/api/:resource/list` and `/api/:entity/list` would also not be detected as conflicting.
1669
+ *
1670
+ * For more advanced conflict detection (e.g., path-to-regexp pattern matching),
1671
+ * consider integrating with your routing library's conflict detection mechanism.
1672
+ *
1673
+ * @param endpoint - Endpoint registration
1674
+ * @returns Route key (e.g., "GET:/api/v1/customers/:id")
1675
+ */
1676
+ getRouteKey(endpoint) {
1677
+ const method = endpoint.method || "ANY";
1678
+ return `${method}:${endpoint.path}`;
1679
+ }
1680
+ /**
1681
+ * Validate endpoint registration
1682
+ *
1683
+ * @param endpoint - Endpoint to validate
1684
+ * @param apiId - API identifier (for error messages)
1685
+ * @throws Error if endpoint is invalid
1686
+ */
1687
+ validateEndpoint(endpoint, apiId) {
1688
+ if (!endpoint.id) {
1689
+ throw new Error(`[ApiRegistry] Endpoint in API '${apiId}' missing 'id' field`);
1690
+ }
1691
+ if (!endpoint.path) {
1692
+ throw new Error(`[ApiRegistry] Endpoint '${endpoint.id}' in API '${apiId}' missing 'path' field`);
1693
+ }
1694
+ }
1695
+ /**
1696
+ * Get an API by ID
1697
+ *
1698
+ * @param apiId - API identifier
1699
+ * @returns API registry entry or undefined
1700
+ */
1701
+ getApi(apiId) {
1702
+ return this.apis.get(apiId);
1703
+ }
1704
+ /**
1705
+ * Get all registered APIs
1706
+ *
1707
+ * @returns Array of all APIs
1708
+ */
1709
+ getAllApis() {
1710
+ return Array.from(this.apis.values());
1711
+ }
1712
+ /**
1713
+ * Find APIs matching query criteria
1714
+ *
1715
+ * Performance optimized with auxiliary indices for O(1) lookups on type, tags, and status.
1716
+ *
1717
+ * @param query - Discovery query parameters
1718
+ * @returns Matching APIs
1719
+ */
1720
+ findApis(query) {
1721
+ let resultIds;
1722
+ if (query.type) {
1723
+ const typeIds = this.apisByType.get(query.type);
1724
+ if (!typeIds || typeIds.size === 0) {
1725
+ return { apis: [], total: 0, filters: query };
1726
+ }
1727
+ resultIds = new Set(typeIds);
1728
+ }
1729
+ if (query.status) {
1730
+ const statusIds = this.apisByStatus.get(query.status);
1731
+ if (!statusIds || statusIds.size === 0) {
1732
+ return { apis: [], total: 0, filters: query };
1733
+ }
1734
+ if (resultIds) {
1735
+ resultIds = new Set([...resultIds].filter((id) => statusIds.has(id)));
1736
+ } else {
1737
+ resultIds = new Set(statusIds);
1738
+ }
1739
+ if (resultIds.size === 0) {
1740
+ return { apis: [], total: 0, filters: query };
1741
+ }
1742
+ }
1743
+ if (query.tags && query.tags.length > 0) {
1744
+ const tagMatches = /* @__PURE__ */ new Set();
1745
+ for (const tag of query.tags) {
1746
+ const tagIds = this.apisByTag.get(tag);
1747
+ if (tagIds) {
1748
+ tagIds.forEach((id) => tagMatches.add(id));
1749
+ }
1750
+ }
1751
+ if (tagMatches.size === 0) {
1752
+ return { apis: [], total: 0, filters: query };
1753
+ }
1754
+ if (resultIds) {
1755
+ resultIds = new Set([...resultIds].filter((id) => tagMatches.has(id)));
1756
+ } else {
1757
+ resultIds = tagMatches;
1758
+ }
1759
+ if (resultIds.size === 0) {
1760
+ return { apis: [], total: 0, filters: query };
1761
+ }
1762
+ }
1763
+ let results;
1764
+ if (resultIds) {
1765
+ results = Array.from(resultIds).map((id) => this.apis.get(id)).filter((api) => api !== void 0);
1766
+ } else {
1767
+ results = Array.from(this.apis.values());
1768
+ }
1769
+ if (query.pluginSource) {
1770
+ results = results.filter(
1771
+ (api) => api.metadata?.pluginSource === query.pluginSource
1772
+ );
1773
+ }
1774
+ if (query.version) {
1775
+ results = results.filter((api) => api.version === query.version);
1776
+ }
1777
+ if (query.search) {
1778
+ const searchLower = query.search.toLowerCase();
1779
+ results = results.filter(
1780
+ (api) => api.name.toLowerCase().includes(searchLower) || api.description && api.description.toLowerCase().includes(searchLower)
1781
+ );
1782
+ }
1783
+ return {
1784
+ apis: results,
1785
+ total: results.length,
1786
+ filters: query
1787
+ };
1788
+ }
1789
+ /**
1790
+ * Get endpoint by API ID and endpoint ID
1791
+ *
1792
+ * @param apiId - API identifier
1793
+ * @param endpointId - Endpoint identifier
1794
+ * @returns Endpoint registration or undefined
1795
+ */
1796
+ getEndpoint(apiId, endpointId) {
1797
+ const key = `${apiId}:${endpointId}`;
1798
+ return this.endpoints.get(key)?.endpoint;
1799
+ }
1800
+ /**
1801
+ * Find endpoint by route (method + path)
1802
+ *
1803
+ * @param method - HTTP method
1804
+ * @param path - URL path
1805
+ * @returns Endpoint registration or undefined
1806
+ */
1807
+ findEndpointByRoute(method, path) {
1808
+ const routeKey = `${method}:${path}`;
1809
+ const route = this.routes.get(routeKey);
1810
+ if (!route) {
1811
+ return void 0;
1812
+ }
1813
+ const api = this.apis.get(route.api);
1814
+ const endpoint = this.getEndpoint(route.api, route.endpointId);
1815
+ if (!api || !endpoint) {
1816
+ return void 0;
1817
+ }
1818
+ return { api, endpoint };
1819
+ }
1820
+ /**
1821
+ * Get complete registry snapshot
1822
+ *
1823
+ * @returns Current registry state
1824
+ */
1825
+ getRegistry() {
1826
+ const apis = Array.from(this.apis.values());
1827
+ const byType = {};
1828
+ for (const api of apis) {
1829
+ if (!byType[api.type]) {
1830
+ byType[api.type] = [];
1831
+ }
1832
+ byType[api.type].push(api);
1833
+ }
1834
+ const byStatus = {};
1835
+ for (const api of apis) {
1836
+ const status = api.metadata?.status || "active";
1837
+ if (!byStatus[status]) {
1838
+ byStatus[status] = [];
1839
+ }
1840
+ byStatus[status].push(api);
1841
+ }
1842
+ const totalEndpoints = apis.reduce(
1843
+ (sum, api) => sum + api.endpoints.length,
1844
+ 0
1845
+ );
1846
+ return {
1847
+ version: this.version,
1848
+ conflictResolution: this.conflictResolution,
1849
+ apis,
1850
+ totalApis: apis.length,
1851
+ totalEndpoints,
1852
+ byType,
1853
+ byStatus,
1854
+ updatedAt: this.updatedAt
1855
+ };
1856
+ }
1857
+ /**
1858
+ * Clear all registered APIs
1859
+ *
1860
+ * **⚠️ SAFETY WARNING:**
1861
+ * This method clears all registered APIs and should be used with caution.
1862
+ *
1863
+ * **Usage Restrictions:**
1864
+ * - In production environments (NODE_ENV=production), a `force: true` parameter is required
1865
+ * - Primarily intended for testing and development hot-reload scenarios
1866
+ *
1867
+ * @param options - Clear options
1868
+ * @param options.force - Force clear in production environment (default: false)
1869
+ * @throws Error if called in production without force flag
1870
+ *
1871
+ * @example Safe usage in tests
1872
+ * ```typescript
1873
+ * beforeEach(() => {
1874
+ * registry.clear(); // OK in test environment
1875
+ * });
1876
+ * ```
1877
+ *
1878
+ * @example Usage in production (requires explicit force)
1879
+ * ```typescript
1880
+ * // In production, explicit force is required
1881
+ * registry.clear({ force: true });
1882
+ * ```
1883
+ */
1884
+ clear(options = {}) {
1885
+ const isProduction = this.isProductionEnvironment();
1886
+ if (isProduction && !options.force) {
1887
+ throw new Error(
1888
+ "[ApiRegistry] Cannot clear registry in production environment without force flag. Use clear({ force: true }) if you really want to clear the registry."
1889
+ );
1890
+ }
1891
+ this.apis.clear();
1892
+ this.endpoints.clear();
1893
+ this.routes.clear();
1894
+ this.apisByType.clear();
1895
+ this.apisByTag.clear();
1896
+ this.apisByStatus.clear();
1897
+ this.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1898
+ if (isProduction) {
1899
+ this.logger.warn("API registry forcefully cleared in production", { force: options.force });
1900
+ } else {
1901
+ this.logger.info("API registry cleared");
1902
+ }
1903
+ }
1904
+ /**
1905
+ * Get registry statistics
1906
+ *
1907
+ * @returns Registry statistics
1908
+ */
1909
+ getStats() {
1910
+ const apis = Array.from(this.apis.values());
1911
+ const apisByType = {};
1912
+ for (const api of apis) {
1913
+ apisByType[api.type] = (apisByType[api.type] || 0) + 1;
1914
+ }
1915
+ const endpointsByApi = {};
1916
+ for (const api of apis) {
1917
+ endpointsByApi[api.id] = api.endpoints.length;
1918
+ }
1919
+ return {
1920
+ totalApis: this.apis.size,
1921
+ totalEndpoints: this.endpoints.size,
1922
+ totalRoutes: this.routes.size,
1923
+ apisByType,
1924
+ endpointsByApi
1925
+ };
1926
+ }
1927
+ /**
1928
+ * Update auxiliary indices when an API is registered
1929
+ *
1930
+ * @param api - API entry to index
1931
+ * @private
1932
+ * @internal
1933
+ */
1934
+ updateIndices(api) {
1935
+ this.ensureIndexSet(this.apisByType, api.type).add(api.id);
1936
+ const status = api.metadata?.status || "active";
1937
+ this.ensureIndexSet(this.apisByStatus, status).add(api.id);
1938
+ const tags = api.metadata?.tags || [];
1939
+ for (const tag of tags) {
1940
+ this.ensureIndexSet(this.apisByTag, tag).add(api.id);
1941
+ }
1942
+ }
1943
+ /**
1944
+ * Remove API from auxiliary indices when unregistered
1945
+ *
1946
+ * @param api - API entry to remove from indices
1947
+ * @private
1948
+ * @internal
1949
+ */
1950
+ removeFromIndices(api) {
1951
+ this.removeFromIndexSet(this.apisByType, api.type, api.id);
1952
+ const status = api.metadata?.status || "active";
1953
+ this.removeFromIndexSet(this.apisByStatus, status, api.id);
1954
+ const tags = api.metadata?.tags || [];
1955
+ for (const tag of tags) {
1956
+ this.removeFromIndexSet(this.apisByTag, tag, api.id);
1957
+ }
1958
+ }
1959
+ /**
1960
+ * Helper to ensure an index set exists and return it
1961
+ *
1962
+ * @param map - Index map
1963
+ * @param key - Index key
1964
+ * @returns The Set for this key (created if needed)
1965
+ * @private
1966
+ * @internal
1967
+ */
1968
+ ensureIndexSet(map, key) {
1969
+ let set = map.get(key);
1970
+ if (!set) {
1971
+ set = /* @__PURE__ */ new Set();
1972
+ map.set(key, set);
1973
+ }
1974
+ return set;
1975
+ }
1976
+ /**
1977
+ * Helper to remove an ID from an index set and clean up empty sets
1978
+ *
1979
+ * @param map - Index map
1980
+ * @param key - Index key
1981
+ * @param id - API ID to remove
1982
+ * @private
1983
+ * @internal
1984
+ */
1985
+ removeFromIndexSet(map, key, id) {
1986
+ const set = map.get(key);
1987
+ if (set) {
1988
+ set.delete(id);
1989
+ if (set.size === 0) {
1990
+ map.delete(key);
1991
+ }
1992
+ }
1993
+ }
1994
+ /**
1995
+ * Check if running in production environment
1996
+ *
1997
+ * @returns true if NODE_ENV is 'production'
1998
+ * @private
1999
+ * @internal
2000
+ */
2001
+ isProductionEnvironment() {
2002
+ return getEnv("NODE_ENV") === "production";
2003
+ }
2004
+ };
2005
+
2006
+ // src/api-registry-plugin.ts
2007
+ function createApiRegistryPlugin(config = {}) {
2008
+ const {
2009
+ conflictResolution = "error",
2010
+ version = "1.0.0"
2011
+ } = config;
2012
+ return {
2013
+ name: "com.objectstack.core.api-registry",
2014
+ version: "1.0.0",
2015
+ init: async (ctx) => {
2016
+ const registry = new ApiRegistry(
2017
+ ctx.logger,
2018
+ conflictResolution,
2019
+ version
2020
+ );
2021
+ ctx.registerService("api-registry", registry);
2022
+ ctx.logger.info("API Registry plugin initialized", {
2023
+ conflictResolution,
2024
+ version
2025
+ });
2026
+ }
2027
+ };
2028
+ }
2029
+
2030
+ // src/qa/index.ts
2031
+ var qa_exports = {};
2032
+ __export(qa_exports, {
2033
+ HttpTestAdapter: () => HttpTestAdapter,
2034
+ TestRunner: () => TestRunner
2035
+ });
2036
+
2037
+ // src/qa/runner.ts
2038
+ var TestRunner = class {
2039
+ constructor(adapter) {
2040
+ this.adapter = adapter;
2041
+ }
2042
+ async runSuite(suite) {
2043
+ const results = [];
2044
+ for (const scenario of suite.scenarios) {
2045
+ results.push(await this.runScenario(scenario));
2046
+ }
2047
+ return results;
2048
+ }
2049
+ async runScenario(scenario) {
2050
+ const startTime = Date.now();
2051
+ const context = {};
2052
+ if (scenario.setup) {
2053
+ for (const step of scenario.setup) {
2054
+ try {
2055
+ await this.runStep(step, context);
2056
+ } catch (e) {
2057
+ return {
2058
+ scenarioId: scenario.id,
2059
+ passed: false,
2060
+ steps: [],
2061
+ error: `Setup failed: ${e instanceof Error ? e.message : String(e)}`,
2062
+ duration: Date.now() - startTime
2063
+ };
2064
+ }
2065
+ }
2066
+ }
2067
+ const stepResults = [];
2068
+ let scenarioPassed = true;
2069
+ let scenarioError = void 0;
2070
+ for (const step of scenario.steps) {
2071
+ const stepStartTime = Date.now();
2072
+ try {
2073
+ const output = await this.runStep(step, context);
2074
+ stepResults.push({
2075
+ stepName: step.name,
2076
+ passed: true,
2077
+ output,
2078
+ duration: Date.now() - stepStartTime
2079
+ });
2080
+ } catch (e) {
2081
+ scenarioPassed = false;
2082
+ scenarioError = e;
2083
+ stepResults.push({
2084
+ stepName: step.name,
2085
+ passed: false,
2086
+ error: e,
2087
+ duration: Date.now() - stepStartTime
2088
+ });
2089
+ break;
2090
+ }
2091
+ }
2092
+ if (scenario.teardown) {
2093
+ for (const step of scenario.teardown) {
2094
+ try {
2095
+ await this.runStep(step, context);
2096
+ } catch (e) {
2097
+ if (scenarioPassed) {
2098
+ scenarioPassed = false;
2099
+ scenarioError = `Teardown failed: ${e instanceof Error ? e.message : String(e)}`;
2100
+ }
2101
+ }
2102
+ }
2103
+ }
2104
+ return {
2105
+ scenarioId: scenario.id,
2106
+ passed: scenarioPassed,
2107
+ steps: stepResults,
2108
+ error: scenarioError,
2109
+ duration: Date.now() - startTime
2110
+ };
2111
+ }
2112
+ async runStep(step, context) {
2113
+ const resolvedAction = this.resolveVariables(step.action, context);
2114
+ const result = await this.adapter.execute(resolvedAction, context);
2115
+ if (step.capture) {
2116
+ for (const [varName, path] of Object.entries(step.capture)) {
2117
+ context[varName] = this.getValueByPath(result, path);
2118
+ }
2119
+ }
2120
+ if (step.assertions) {
2121
+ for (const assertion of step.assertions) {
2122
+ this.assert(result, assertion, context);
2123
+ }
2124
+ }
2125
+ return result;
2126
+ }
2127
+ resolveVariables(action, _context) {
2128
+ return action;
2129
+ }
2130
+ getValueByPath(obj, path) {
2131
+ if (!path) return obj;
2132
+ const parts = path.split(".");
2133
+ let current = obj;
2134
+ for (const part of parts) {
2135
+ if (current === null || current === void 0) return void 0;
2136
+ current = current[part];
2137
+ }
2138
+ return current;
2139
+ }
2140
+ assert(result, assertion, _context) {
2141
+ const actual = this.getValueByPath(result, assertion.field);
2142
+ const expected = assertion.expectedValue;
2143
+ switch (assertion.operator) {
2144
+ case "equals":
2145
+ if (actual !== expected) throw new Error(`Assertion failed: ${assertion.field} expected ${expected}, got ${actual}`);
2146
+ break;
2147
+ case "not_equals":
2148
+ if (actual === expected) throw new Error(`Assertion failed: ${assertion.field} expected not ${expected}, got ${actual}`);
2149
+ break;
2150
+ case "contains":
2151
+ if (Array.isArray(actual)) {
2152
+ if (!actual.includes(expected)) throw new Error(`Assertion failed: ${assertion.field} array does not contain ${expected}`);
2153
+ } else if (typeof actual === "string") {
2154
+ if (!actual.includes(String(expected))) throw new Error(`Assertion failed: ${assertion.field} string does not contain ${expected}`);
2155
+ }
2156
+ break;
2157
+ case "not_null":
2158
+ if (actual === null || actual === void 0) throw new Error(`Assertion failed: ${assertion.field} is null`);
2159
+ break;
2160
+ case "is_null":
2161
+ if (actual !== null && actual !== void 0) throw new Error(`Assertion failed: ${assertion.field} is not null`);
2162
+ break;
2163
+ // ... Add other operators
2164
+ default:
2165
+ throw new Error(`Unknown assertion operator: ${assertion.operator}`);
2166
+ }
2167
+ }
2168
+ };
2169
+
2170
+ // src/qa/http-adapter.ts
2171
+ var HttpTestAdapter = class {
2172
+ constructor(baseUrl, authToken) {
2173
+ this.baseUrl = baseUrl;
2174
+ this.authToken = authToken;
2175
+ }
2176
+ async execute(action, _context) {
2177
+ const headers = {
2178
+ "Content-Type": "application/json"
2179
+ };
2180
+ if (this.authToken) {
2181
+ headers["Authorization"] = `Bearer ${this.authToken}`;
2182
+ }
2183
+ if (action.user) {
2184
+ headers["X-Run-As"] = action.user;
2185
+ }
2186
+ switch (action.type) {
2187
+ case "create_record":
2188
+ return this.createRecord(action.target, action.payload || {}, headers);
2189
+ case "update_record":
2190
+ return this.updateRecord(action.target, action.payload || {}, headers);
2191
+ case "delete_record":
2192
+ return this.deleteRecord(action.target, action.payload || {}, headers);
2193
+ case "read_record":
2194
+ return this.readRecord(action.target, action.payload || {}, headers);
2195
+ case "query_records":
2196
+ return this.queryRecords(action.target, action.payload || {}, headers);
2197
+ case "api_call":
2198
+ return this.rawApiCall(action.target, action.payload || {}, headers);
2199
+ case "wait":
2200
+ const ms = Number(action.payload?.duration || 1e3);
2201
+ return new Promise((resolve) => setTimeout(() => resolve({ waited: ms }), ms));
2202
+ default:
2203
+ throw new Error(`Unsupported action type in HttpAdapter: ${action.type}`);
2204
+ }
2205
+ }
2206
+ async createRecord(objectName, data, headers) {
2207
+ const response = await fetch(`${this.baseUrl}/api/data/${objectName}`, {
2208
+ method: "POST",
2209
+ headers,
2210
+ body: JSON.stringify(data)
2211
+ });
2212
+ return this.handleResponse(response);
2213
+ }
2214
+ async updateRecord(objectName, data, headers) {
2215
+ const id = data._id || data.id;
2216
+ if (!id) throw new Error("Update record requires _id or id in payload");
2217
+ const response = await fetch(`${this.baseUrl}/api/data/${objectName}/${id}`, {
2218
+ method: "PUT",
2219
+ headers,
2220
+ body: JSON.stringify(data)
2221
+ });
2222
+ return this.handleResponse(response);
2223
+ }
2224
+ async deleteRecord(objectName, data, headers) {
2225
+ const id = data._id || data.id;
2226
+ if (!id) throw new Error("Delete record requires _id or id in payload");
2227
+ const response = await fetch(`${this.baseUrl}/api/data/${objectName}/${id}`, {
2228
+ method: "DELETE",
2229
+ headers
2230
+ });
2231
+ return this.handleResponse(response);
2232
+ }
2233
+ async readRecord(objectName, data, headers) {
2234
+ const id = data._id || data.id;
2235
+ if (!id) throw new Error("Read record requires _id or id in payload");
2236
+ const response = await fetch(`${this.baseUrl}/api/data/${objectName}/${id}`, {
2237
+ method: "GET",
2238
+ headers
2239
+ });
2240
+ return this.handleResponse(response);
2241
+ }
2242
+ async queryRecords(objectName, data, headers) {
2243
+ const response = await fetch(`${this.baseUrl}/api/data/${objectName}/query`, {
2244
+ method: "POST",
2245
+ headers,
2246
+ body: JSON.stringify(data)
2247
+ });
2248
+ return this.handleResponse(response);
2249
+ }
2250
+ async rawApiCall(endpoint, data, headers) {
2251
+ const method = data.method || "GET";
2252
+ const body = data.body ? JSON.stringify(data.body) : void 0;
2253
+ const url = endpoint.startsWith("http") ? endpoint : `${this.baseUrl}${endpoint}`;
2254
+ const response = await fetch(url, {
2255
+ method,
2256
+ headers,
2257
+ body
2258
+ });
2259
+ return this.handleResponse(response);
2260
+ }
2261
+ async handleResponse(response) {
2262
+ if (!response.ok) {
2263
+ const text = await response.text();
2264
+ throw new Error(`HTTP Error ${response.status}: ${text}`);
2265
+ }
2266
+ const contentType = response.headers.get("content-type");
2267
+ if (contentType && contentType.includes("application/json")) {
2268
+ return response.json();
2269
+ }
2270
+ return response.text();
2271
+ }
2272
+ };
2273
+
2274
+ // src/security/plugin-signature-verifier.ts
2275
+ var cryptoModule = null;
2276
+ if (typeof globalThis.window === "undefined") {
2277
+ try {
2278
+ cryptoModule = eval('require("crypto")');
2279
+ } catch {
2280
+ }
2281
+ }
2282
+ var PluginSignatureVerifier = class {
2283
+ constructor(config, logger) {
2284
+ this.config = config;
2285
+ this.logger = logger;
2286
+ this.validateConfig();
2287
+ }
2288
+ /**
2289
+ * Verify plugin signature
2290
+ *
2291
+ * @param plugin - Plugin metadata with signature
2292
+ * @returns Verification result
2293
+ * @throws Error if verification fails in strict mode
2294
+ */
2295
+ async verifyPluginSignature(plugin) {
2296
+ if (!plugin.signature) {
2297
+ return this.handleUnsignedPlugin(plugin);
2298
+ }
2299
+ try {
2300
+ const publisherId = this.extractPublisherId(plugin.name);
2301
+ const publicKey = this.config.trustedPublicKeys.get(publisherId);
2302
+ if (!publicKey) {
2303
+ const error = `No trusted public key for publisher: ${publisherId}`;
2304
+ this.logger.warn(error, { plugin: plugin.name, publisherId });
2305
+ if (this.config.strictMode && !this.config.allowSelfSigned) {
2306
+ throw new Error(error);
2307
+ }
2308
+ return {
2309
+ verified: false,
2310
+ error,
2311
+ publisherId
2312
+ };
2313
+ }
2314
+ const pluginHash = this.computePluginHash(plugin);
2315
+ const isValid = await this.verifyCryptoSignature(
2316
+ pluginHash,
2317
+ plugin.signature,
2318
+ publicKey
2319
+ );
2320
+ if (!isValid) {
2321
+ const error = `Signature verification failed for plugin: ${plugin.name}`;
2322
+ this.logger.error(error, void 0, { plugin: plugin.name, publisherId });
2323
+ throw new Error(error);
2324
+ }
2325
+ this.logger.info(`\u2705 Plugin signature verified: ${plugin.name}`, {
2326
+ plugin: plugin.name,
2327
+ publisherId,
2328
+ algorithm: this.config.algorithm
2329
+ });
2330
+ return {
2331
+ verified: true,
2332
+ publisherId,
2333
+ algorithm: this.config.algorithm
2334
+ };
2335
+ } catch (error) {
2336
+ this.logger.error(`Signature verification error: ${plugin.name}`, error);
2337
+ if (this.config.strictMode) {
2338
+ throw error;
2339
+ }
2340
+ return {
2341
+ verified: false,
2342
+ error: error.message
2343
+ };
2344
+ }
2345
+ }
2346
+ /**
2347
+ * Register a trusted public key for a publisher
2348
+ */
2349
+ registerPublicKey(publisherId, publicKey) {
2350
+ this.config.trustedPublicKeys.set(publisherId, publicKey);
2351
+ this.logger.info(`Trusted public key registered for: ${publisherId}`);
2352
+ }
2353
+ /**
2354
+ * Remove a trusted public key
2355
+ */
2356
+ revokePublicKey(publisherId) {
2357
+ this.config.trustedPublicKeys.delete(publisherId);
2358
+ this.logger.warn(`Public key revoked for: ${publisherId}`);
2359
+ }
2360
+ /**
2361
+ * Get list of trusted publishers
2362
+ */
2363
+ getTrustedPublishers() {
2364
+ return Array.from(this.config.trustedPublicKeys.keys());
2365
+ }
2366
+ // Private methods
2367
+ handleUnsignedPlugin(plugin) {
2368
+ if (this.config.strictMode) {
2369
+ const error = `Plugin missing signature (strict mode): ${plugin.name}`;
2370
+ this.logger.error(error, void 0, { plugin: plugin.name });
2371
+ throw new Error(error);
2372
+ }
2373
+ this.logger.warn(`\u26A0\uFE0F Plugin not signed: ${plugin.name}`, {
2374
+ plugin: plugin.name,
2375
+ recommendation: "Consider signing plugins for production environments"
2376
+ });
2377
+ return {
2378
+ verified: false,
2379
+ error: "Plugin not signed"
2380
+ };
2381
+ }
2382
+ extractPublisherId(pluginName) {
2383
+ const parts = pluginName.split(".");
2384
+ if (parts.length < 2) {
2385
+ throw new Error(`Invalid plugin name format: ${pluginName} (expected reverse domain notation)`);
2386
+ }
2387
+ return `${parts[0]}.${parts[1]}`;
2388
+ }
2389
+ computePluginHash(plugin) {
2390
+ if (typeof globalThis.window !== "undefined") {
2391
+ return this.computePluginHashBrowser(plugin);
2392
+ }
2393
+ return this.computePluginHashNode(plugin);
2394
+ }
2395
+ computePluginHashNode(plugin) {
2396
+ if (!cryptoModule) {
2397
+ this.logger.warn("crypto module not available, using fallback hash");
2398
+ return this.computePluginHashFallback(plugin);
2399
+ }
2400
+ const pluginCode = this.serializePluginCode(plugin);
2401
+ return cryptoModule.createHash("sha256").update(pluginCode).digest("hex");
2402
+ }
2403
+ computePluginHashBrowser(plugin) {
2404
+ this.logger.debug("Using browser hash (SubtleCrypto integration pending)");
2405
+ return this.computePluginHashFallback(plugin);
2406
+ }
2407
+ computePluginHashFallback(plugin) {
2408
+ const pluginCode = this.serializePluginCode(plugin);
2409
+ let hash = 0;
2410
+ for (let i = 0; i < pluginCode.length; i++) {
2411
+ const char = pluginCode.charCodeAt(i);
2412
+ hash = (hash << 5) - hash + char;
2413
+ hash = hash & hash;
2414
+ }
2415
+ return hash.toString(16);
2416
+ }
2417
+ serializePluginCode(plugin) {
2418
+ const parts = [
2419
+ plugin.name,
2420
+ plugin.version,
2421
+ plugin.init.toString()
2422
+ ];
2423
+ if (plugin.start) {
2424
+ parts.push(plugin.start.toString());
2425
+ }
2426
+ if (plugin.destroy) {
2427
+ parts.push(plugin.destroy.toString());
2428
+ }
2429
+ return parts.join("|");
2430
+ }
2431
+ async verifyCryptoSignature(data, signature, publicKey) {
2432
+ if (typeof globalThis.window !== "undefined") {
2433
+ return this.verifyCryptoSignatureBrowser(data, signature, publicKey);
2434
+ }
2435
+ return this.verifyCryptoSignatureNode(data, signature, publicKey);
2436
+ }
2437
+ verifyCryptoSignatureNode(data, signature, publicKey) {
2438
+ if (!cryptoModule) {
2439
+ this.logger.error("Crypto module not available for signature verification");
2440
+ return false;
2441
+ }
2442
+ try {
2443
+ if (this.config.algorithm === "ES256") {
2444
+ const verify = cryptoModule.createVerify("sha256");
2445
+ verify.update(data);
2446
+ return verify.verify(
2447
+ {
2448
+ key: publicKey,
2449
+ format: "pem",
2450
+ type: "spki"
2451
+ },
2452
+ signature,
2453
+ "base64"
2454
+ );
2455
+ } else {
2456
+ const verify = cryptoModule.createVerify("RSA-SHA256");
2457
+ verify.update(data);
2458
+ return verify.verify(publicKey, signature, "base64");
2459
+ }
2460
+ } catch (error) {
2461
+ this.logger.error("Signature verification failed", error);
2462
+ return false;
2463
+ }
2464
+ }
2465
+ async verifyCryptoSignatureBrowser(_data, _signature, _publicKey) {
2466
+ this.logger.warn("Browser signature verification not yet implemented");
2467
+ return false;
2468
+ }
2469
+ validateConfig() {
2470
+ if (!this.config.trustedPublicKeys || this.config.trustedPublicKeys.size === 0) {
2471
+ this.logger.warn("No trusted public keys configured - all signatures will fail");
2472
+ }
2473
+ if (!this.config.algorithm) {
2474
+ throw new Error("Signature algorithm must be specified");
2475
+ }
2476
+ if (!["RS256", "ES256"].includes(this.config.algorithm)) {
2477
+ throw new Error(`Unsupported algorithm: ${this.config.algorithm}`);
2478
+ }
2479
+ }
2480
+ };
2481
+
2482
+ // src/security/plugin-permission-enforcer.ts
2483
+ var PluginPermissionEnforcer = class {
2484
+ constructor(logger) {
2485
+ this.permissionRegistry = /* @__PURE__ */ new Map();
2486
+ this.capabilityRegistry = /* @__PURE__ */ new Map();
2487
+ this.logger = logger;
2488
+ }
2489
+ /**
2490
+ * Register plugin capabilities and build permission set
2491
+ *
2492
+ * @param pluginName - Plugin identifier
2493
+ * @param capabilities - Array of capability declarations
2494
+ */
2495
+ registerPluginPermissions(pluginName, capabilities) {
2496
+ this.capabilityRegistry.set(pluginName, capabilities);
2497
+ const permissions = {
2498
+ canAccessService: (service) => this.checkServiceAccess(capabilities, service),
2499
+ canTriggerHook: (hook) => this.checkHookAccess(capabilities, hook),
2500
+ canReadFile: (path) => this.checkFileRead(capabilities, path),
2501
+ canWriteFile: (path) => this.checkFileWrite(capabilities, path),
2502
+ canNetworkRequest: (url) => this.checkNetworkAccess(capabilities, url)
2503
+ };
2504
+ this.permissionRegistry.set(pluginName, permissions);
2505
+ this.logger.info(`Permissions registered for plugin: ${pluginName}`, {
2506
+ plugin: pluginName,
2507
+ capabilityCount: capabilities.length
2508
+ });
2509
+ }
2510
+ /**
2511
+ * Enforce service access permission
2512
+ *
2513
+ * @param pluginName - Plugin requesting access
2514
+ * @param serviceName - Service to access
2515
+ * @throws Error if permission denied
2516
+ */
2517
+ enforceServiceAccess(pluginName, serviceName) {
2518
+ const result = this.checkPermission(pluginName, (perms) => perms.canAccessService(serviceName));
2519
+ if (!result.allowed) {
2520
+ const error = `Permission denied: Plugin ${pluginName} cannot access service ${serviceName}`;
2521
+ this.logger.warn(error, {
2522
+ plugin: pluginName,
2523
+ service: serviceName,
2524
+ reason: result.reason
2525
+ });
2526
+ throw new Error(error);
2527
+ }
2528
+ this.logger.debug(`Service access granted: ${pluginName} -> ${serviceName}`);
2529
+ }
2530
+ /**
2531
+ * Enforce hook trigger permission
2532
+ *
2533
+ * @param pluginName - Plugin requesting access
2534
+ * @param hookName - Hook to trigger
2535
+ * @throws Error if permission denied
2536
+ */
2537
+ enforceHookTrigger(pluginName, hookName) {
2538
+ const result = this.checkPermission(pluginName, (perms) => perms.canTriggerHook(hookName));
2539
+ if (!result.allowed) {
2540
+ const error = `Permission denied: Plugin ${pluginName} cannot trigger hook ${hookName}`;
2541
+ this.logger.warn(error, {
2542
+ plugin: pluginName,
2543
+ hook: hookName,
2544
+ reason: result.reason
2545
+ });
2546
+ throw new Error(error);
2547
+ }
2548
+ this.logger.debug(`Hook trigger granted: ${pluginName} -> ${hookName}`);
2549
+ }
2550
+ /**
2551
+ * Enforce file read permission
2552
+ *
2553
+ * @param pluginName - Plugin requesting access
2554
+ * @param path - File path to read
2555
+ * @throws Error if permission denied
2556
+ */
2557
+ enforceFileRead(pluginName, path) {
2558
+ const result = this.checkPermission(pluginName, (perms) => perms.canReadFile(path));
2559
+ if (!result.allowed) {
2560
+ const error = `Permission denied: Plugin ${pluginName} cannot read file ${path}`;
2561
+ this.logger.warn(error, {
2562
+ plugin: pluginName,
2563
+ path,
2564
+ reason: result.reason
2565
+ });
2566
+ throw new Error(error);
2567
+ }
2568
+ this.logger.debug(`File read granted: ${pluginName} -> ${path}`);
2569
+ }
2570
+ /**
2571
+ * Enforce file write permission
2572
+ *
2573
+ * @param pluginName - Plugin requesting access
2574
+ * @param path - File path to write
2575
+ * @throws Error if permission denied
2576
+ */
2577
+ enforceFileWrite(pluginName, path) {
2578
+ const result = this.checkPermission(pluginName, (perms) => perms.canWriteFile(path));
2579
+ if (!result.allowed) {
2580
+ const error = `Permission denied: Plugin ${pluginName} cannot write file ${path}`;
2581
+ this.logger.warn(error, {
2582
+ plugin: pluginName,
2583
+ path,
2584
+ reason: result.reason
2585
+ });
2586
+ throw new Error(error);
2587
+ }
2588
+ this.logger.debug(`File write granted: ${pluginName} -> ${path}`);
2589
+ }
2590
+ /**
2591
+ * Enforce network request permission
2592
+ *
2593
+ * @param pluginName - Plugin requesting access
2594
+ * @param url - URL to access
2595
+ * @throws Error if permission denied
2596
+ */
2597
+ enforceNetworkRequest(pluginName, url) {
2598
+ const result = this.checkPermission(pluginName, (perms) => perms.canNetworkRequest(url));
2599
+ if (!result.allowed) {
2600
+ const error = `Permission denied: Plugin ${pluginName} cannot access URL ${url}`;
2601
+ this.logger.warn(error, {
2602
+ plugin: pluginName,
2603
+ url,
2604
+ reason: result.reason
2605
+ });
2606
+ throw new Error(error);
2607
+ }
2608
+ this.logger.debug(`Network request granted: ${pluginName} -> ${url}`);
2609
+ }
2610
+ /**
2611
+ * Get plugin capabilities
2612
+ *
2613
+ * @param pluginName - Plugin identifier
2614
+ * @returns Array of capabilities or undefined
2615
+ */
2616
+ getPluginCapabilities(pluginName) {
2617
+ return this.capabilityRegistry.get(pluginName);
2618
+ }
2619
+ /**
2620
+ * Get plugin permissions
2621
+ *
2622
+ * @param pluginName - Plugin identifier
2623
+ * @returns Permissions object or undefined
2624
+ */
2625
+ getPluginPermissions(pluginName) {
2626
+ return this.permissionRegistry.get(pluginName);
2627
+ }
2628
+ /**
2629
+ * Revoke all permissions for a plugin
2630
+ *
2631
+ * @param pluginName - Plugin identifier
2632
+ */
2633
+ revokePermissions(pluginName) {
2634
+ this.permissionRegistry.delete(pluginName);
2635
+ this.capabilityRegistry.delete(pluginName);
2636
+ this.logger.warn(`Permissions revoked for plugin: ${pluginName}`);
2637
+ }
2638
+ // Private methods
2639
+ checkPermission(pluginName, check) {
2640
+ const permissions = this.permissionRegistry.get(pluginName);
2641
+ if (!permissions) {
2642
+ return {
2643
+ allowed: false,
2644
+ reason: "Plugin permissions not registered"
2645
+ };
2646
+ }
2647
+ const allowed = check(permissions);
2648
+ return {
2649
+ allowed,
2650
+ reason: allowed ? void 0 : "No matching capability found"
2651
+ };
2652
+ }
2653
+ checkServiceAccess(capabilities, serviceName) {
2654
+ return capabilities.some((cap) => {
2655
+ const protocolId = cap.protocol.id;
2656
+ if (protocolId.includes("protocol.service.all")) {
2657
+ return true;
2658
+ }
2659
+ if (protocolId.includes(`protocol.service.${serviceName}`)) {
2660
+ return true;
2661
+ }
2662
+ const serviceCategory = serviceName.split(".")[0];
2663
+ if (protocolId.includes(`protocol.service.${serviceCategory}`)) {
2664
+ return true;
2665
+ }
2666
+ return false;
2667
+ });
2668
+ }
2669
+ checkHookAccess(capabilities, hookName) {
2670
+ return capabilities.some((cap) => {
2671
+ const protocolId = cap.protocol.id;
2672
+ if (protocolId.includes("protocol.hook.all")) {
2673
+ return true;
2674
+ }
2675
+ if (protocolId.includes(`protocol.hook.${hookName}`)) {
2676
+ return true;
2677
+ }
2678
+ const hookCategory = hookName.split(":")[0];
2679
+ if (protocolId.includes(`protocol.hook.${hookCategory}`)) {
2680
+ return true;
2681
+ }
2682
+ return false;
2683
+ });
2684
+ }
2685
+ checkFileRead(capabilities, _path) {
2686
+ return capabilities.some((cap) => {
2687
+ const protocolId = cap.protocol.id;
2688
+ if (protocolId.includes("protocol.filesystem.read")) {
2689
+ return true;
2690
+ }
2691
+ return false;
2692
+ });
2693
+ }
2694
+ checkFileWrite(capabilities, _path) {
2695
+ return capabilities.some((cap) => {
2696
+ const protocolId = cap.protocol.id;
2697
+ if (protocolId.includes("protocol.filesystem.write")) {
2698
+ return true;
2699
+ }
2700
+ return false;
2701
+ });
2702
+ }
2703
+ checkNetworkAccess(capabilities, _url) {
2704
+ return capabilities.some((cap) => {
2705
+ const protocolId = cap.protocol.id;
2706
+ if (protocolId.includes("protocol.network")) {
2707
+ return true;
2708
+ }
2709
+ return false;
2710
+ });
2711
+ }
2712
+ };
2713
+ var SecurePluginContext = class {
2714
+ constructor(pluginName, permissionEnforcer, baseContext) {
2715
+ this.pluginName = pluginName;
2716
+ this.permissionEnforcer = permissionEnforcer;
2717
+ this.baseContext = baseContext;
2718
+ }
2719
+ registerService(name, service) {
2720
+ this.baseContext.registerService(name, service);
2721
+ }
2722
+ getService(name) {
2723
+ this.permissionEnforcer.enforceServiceAccess(this.pluginName, name);
2724
+ return this.baseContext.getService(name);
2725
+ }
2726
+ getServices() {
2727
+ return this.baseContext.getServices();
2728
+ }
2729
+ hook(name, handler) {
2730
+ this.baseContext.hook(name, handler);
2731
+ }
2732
+ async trigger(name, ...args) {
2733
+ this.permissionEnforcer.enforceHookTrigger(this.pluginName, name);
2734
+ await this.baseContext.trigger(name, ...args);
2735
+ }
2736
+ get logger() {
2737
+ return this.baseContext.logger;
2738
+ }
2739
+ getKernel() {
2740
+ return this.baseContext.getKernel();
2741
+ }
2742
+ };
2743
+ function createPluginPermissionEnforcer(logger) {
2744
+ return new PluginPermissionEnforcer(logger);
2745
+ }
2746
+
2747
+ // src/security/permission-manager.ts
2748
+ var PluginPermissionManager = class {
2749
+ constructor(logger) {
2750
+ // Plugin permission definitions
2751
+ this.permissionSets = /* @__PURE__ */ new Map();
2752
+ // Granted permissions (pluginId -> Set of permission IDs)
2753
+ this.grants = /* @__PURE__ */ new Map();
2754
+ // Permission grant details
2755
+ this.grantDetails = /* @__PURE__ */ new Map();
2756
+ this.logger = logger.child({ component: "PermissionManager" });
2757
+ }
2758
+ /**
2759
+ * Register permission requirements for a plugin
2760
+ */
2761
+ registerPermissions(pluginId, permissionSet) {
2762
+ this.permissionSets.set(pluginId, permissionSet);
2763
+ this.logger.info("Permissions registered for plugin", {
2764
+ pluginId,
2765
+ permissionCount: permissionSet.permissions.length
2766
+ });
2767
+ }
2768
+ /**
2769
+ * Grant a permission to a plugin
2770
+ */
2771
+ grantPermission(pluginId, permissionId, grantedBy, expiresAt) {
2772
+ const permissionSet = this.permissionSets.get(pluginId);
2773
+ if (!permissionSet) {
2774
+ throw new Error(`No permissions registered for plugin: ${pluginId}`);
2775
+ }
2776
+ const permission = permissionSet.permissions.find((p) => p.id === permissionId);
2777
+ if (!permission) {
2778
+ throw new Error(`Permission ${permissionId} not declared by plugin ${pluginId}`);
2779
+ }
2780
+ if (!this.grants.has(pluginId)) {
2781
+ this.grants.set(pluginId, /* @__PURE__ */ new Set());
2782
+ }
2783
+ this.grants.get(pluginId).add(permissionId);
2784
+ const grantKey = `${pluginId}:${permissionId}`;
2785
+ this.grantDetails.set(grantKey, {
2786
+ permissionId,
2787
+ pluginId,
2788
+ grantedAt: /* @__PURE__ */ new Date(),
2789
+ grantedBy,
2790
+ expiresAt
2791
+ });
2792
+ this.logger.info("Permission granted", {
2793
+ pluginId,
2794
+ permissionId,
2795
+ grantedBy
2796
+ });
2797
+ }
2798
+ /**
2799
+ * Revoke a permission from a plugin
2800
+ */
2801
+ revokePermission(pluginId, permissionId) {
2802
+ const grants = this.grants.get(pluginId);
2803
+ if (grants) {
2804
+ grants.delete(permissionId);
2805
+ const grantKey = `${pluginId}:${permissionId}`;
2806
+ this.grantDetails.delete(grantKey);
2807
+ this.logger.info("Permission revoked", { pluginId, permissionId });
2808
+ }
2809
+ }
2810
+ /**
2811
+ * Grant all permissions for a plugin
2812
+ */
2813
+ grantAllPermissions(pluginId, grantedBy) {
2814
+ const permissionSet = this.permissionSets.get(pluginId);
2815
+ if (!permissionSet) {
2816
+ throw new Error(`No permissions registered for plugin: ${pluginId}`);
2817
+ }
2818
+ for (const permission of permissionSet.permissions) {
2819
+ this.grantPermission(pluginId, permission.id, grantedBy);
2820
+ }
2821
+ this.logger.info("All permissions granted", { pluginId, grantedBy });
2822
+ }
2823
+ /**
2824
+ * Check if a plugin has a specific permission
2825
+ */
2826
+ hasPermission(pluginId, permissionId) {
2827
+ const grants = this.grants.get(pluginId);
2828
+ if (!grants) {
2829
+ return false;
2830
+ }
2831
+ if (!grants.has(permissionId)) {
2832
+ return false;
2833
+ }
2834
+ const grantKey = `${pluginId}:${permissionId}`;
2835
+ const grantDetails = this.grantDetails.get(grantKey);
2836
+ if (grantDetails?.expiresAt && grantDetails.expiresAt < /* @__PURE__ */ new Date()) {
2837
+ this.revokePermission(pluginId, permissionId);
2838
+ return false;
2839
+ }
2840
+ return true;
2841
+ }
2842
+ /**
2843
+ * Check if plugin can perform an action on a resource
2844
+ */
2845
+ checkAccess(pluginId, resource, action, resourceId) {
2846
+ const permissionSet = this.permissionSets.get(pluginId);
2847
+ if (!permissionSet) {
2848
+ return {
2849
+ allowed: false,
2850
+ reason: "No permissions registered for plugin"
2851
+ };
2852
+ }
2853
+ const matchingPermissions = permissionSet.permissions.filter((p) => {
2854
+ if (p.resource !== resource) {
2855
+ return false;
2856
+ }
2857
+ if (!p.actions.includes(action)) {
2858
+ return false;
2859
+ }
2860
+ if (resourceId && p.filter?.resourceIds) {
2861
+ if (!p.filter.resourceIds.includes(resourceId)) {
2862
+ return false;
2863
+ }
2864
+ }
2865
+ return true;
2866
+ });
2867
+ if (matchingPermissions.length === 0) {
2868
+ return {
2869
+ allowed: false,
2870
+ reason: `No permission found for ${action} on ${resource}`
2871
+ };
2872
+ }
2873
+ const grantedPermissions = matchingPermissions.filter(
2874
+ (p) => this.hasPermission(pluginId, p.id)
2875
+ );
2876
+ if (grantedPermissions.length === 0) {
2877
+ return {
2878
+ allowed: false,
2879
+ reason: "Required permissions not granted",
2880
+ requiredPermission: matchingPermissions[0].id
2881
+ };
2882
+ }
2883
+ return {
2884
+ allowed: true,
2885
+ grantedPermissions: grantedPermissions.map((p) => p.id)
2886
+ };
2887
+ }
2888
+ /**
2889
+ * Get all permissions for a plugin
2890
+ */
2891
+ getPluginPermissions(pluginId) {
2892
+ const permissionSet = this.permissionSets.get(pluginId);
2893
+ return permissionSet?.permissions || [];
2894
+ }
2895
+ /**
2896
+ * Get granted permissions for a plugin
2897
+ */
2898
+ getGrantedPermissions(pluginId) {
2899
+ const grants = this.grants.get(pluginId);
2900
+ return grants ? Array.from(grants) : [];
2901
+ }
2902
+ /**
2903
+ * Get required but not granted permissions
2904
+ */
2905
+ getMissingPermissions(pluginId) {
2906
+ const permissionSet = this.permissionSets.get(pluginId);
2907
+ if (!permissionSet) {
2908
+ return [];
2909
+ }
2910
+ const granted = this.grants.get(pluginId) || /* @__PURE__ */ new Set();
2911
+ return permissionSet.permissions.filter(
2912
+ (p) => p.required && !granted.has(p.id)
2913
+ );
2914
+ }
2915
+ /**
2916
+ * Check if all required permissions are granted
2917
+ */
2918
+ hasAllRequiredPermissions(pluginId) {
2919
+ return this.getMissingPermissions(pluginId).length === 0;
2920
+ }
2921
+ /**
2922
+ * Get permission grant details
2923
+ */
2924
+ getGrantDetails(pluginId, permissionId) {
2925
+ const grantKey = `${pluginId}:${permissionId}`;
2926
+ return this.grantDetails.get(grantKey);
2927
+ }
2928
+ /**
2929
+ * Validate permission against scope constraints
2930
+ */
2931
+ validatePermissionScope(permission, context) {
2932
+ switch (permission.scope) {
2933
+ case "global":
2934
+ return true;
2935
+ case "tenant":
2936
+ return !!context.tenantId;
2937
+ case "user":
2938
+ return !!context.userId;
2939
+ case "resource":
2940
+ return !!context.resourceId;
2941
+ case "plugin":
2942
+ return true;
2943
+ default:
2944
+ return false;
2945
+ }
2946
+ }
2947
+ /**
2948
+ * Clear all permissions for a plugin
2949
+ */
2950
+ clearPluginPermissions(pluginId) {
2951
+ this.permissionSets.delete(pluginId);
2952
+ const grants = this.grants.get(pluginId);
2953
+ if (grants) {
2954
+ for (const permissionId of grants) {
2955
+ const grantKey = `${pluginId}:${permissionId}`;
2956
+ this.grantDetails.delete(grantKey);
2957
+ }
2958
+ this.grants.delete(pluginId);
2959
+ }
2960
+ this.logger.info("All permissions cleared", { pluginId });
2961
+ }
2962
+ /**
2963
+ * Shutdown permission manager
2964
+ */
2965
+ shutdown() {
2966
+ this.permissionSets.clear();
2967
+ this.grants.clear();
2968
+ this.grantDetails.clear();
2969
+ this.logger.info("Permission manager shutdown complete");
2970
+ }
2971
+ };
2972
+
2973
+ // src/security/sandbox-runtime.ts
2974
+ var PluginSandboxRuntime = class {
2975
+ constructor(logger) {
2976
+ // Active sandboxes (pluginId -> context)
2977
+ this.sandboxes = /* @__PURE__ */ new Map();
2978
+ // Resource monitoring intervals
2979
+ this.monitoringIntervals = /* @__PURE__ */ new Map();
2980
+ this.logger = logger.child({ component: "SandboxRuntime" });
2981
+ }
2982
+ /**
2983
+ * Create a sandbox for a plugin
2984
+ */
2985
+ createSandbox(pluginId, config) {
2986
+ if (this.sandboxes.has(pluginId)) {
2987
+ throw new Error(`Sandbox already exists for plugin: ${pluginId}`);
2988
+ }
2989
+ const context = {
2990
+ pluginId,
2991
+ config,
2992
+ startTime: /* @__PURE__ */ new Date(),
2993
+ resourceUsage: {
2994
+ memory: { current: 0, peak: 0, limit: config.memory?.maxHeap },
2995
+ cpu: { current: 0, average: 0, limit: config.cpu?.maxCpuPercent },
2996
+ connections: { current: 0, limit: config.network?.maxConnections }
2997
+ }
2998
+ };
2999
+ this.sandboxes.set(pluginId, context);
3000
+ this.startResourceMonitoring(pluginId);
3001
+ this.logger.info("Sandbox created", {
3002
+ pluginId,
3003
+ level: config.level,
3004
+ memoryLimit: config.memory?.maxHeap,
3005
+ cpuLimit: config.cpu?.maxCpuPercent
3006
+ });
3007
+ return context;
3008
+ }
3009
+ /**
3010
+ * Destroy a sandbox
3011
+ */
3012
+ destroySandbox(pluginId) {
3013
+ const context = this.sandboxes.get(pluginId);
3014
+ if (!context) {
3015
+ return;
3016
+ }
3017
+ this.stopResourceMonitoring(pluginId);
3018
+ this.sandboxes.delete(pluginId);
3019
+ this.logger.info("Sandbox destroyed", { pluginId });
3020
+ }
3021
+ /**
3022
+ * Check if resource access is allowed
3023
+ */
3024
+ checkResourceAccess(pluginId, resourceType, resourcePath) {
3025
+ const context = this.sandboxes.get(pluginId);
3026
+ if (!context) {
3027
+ return { allowed: false, reason: "Sandbox not found" };
3028
+ }
3029
+ const { config } = context;
3030
+ switch (resourceType) {
3031
+ case "file":
3032
+ return this.checkFileAccess(config, resourcePath);
3033
+ case "network":
3034
+ return this.checkNetworkAccess(config, resourcePath);
3035
+ case "process":
3036
+ return this.checkProcessAccess(config);
3037
+ case "env":
3038
+ return this.checkEnvAccess(config, resourcePath);
3039
+ default:
3040
+ return { allowed: false, reason: "Unknown resource type" };
3041
+ }
3042
+ }
3043
+ /**
3044
+ * Check file system access
3045
+ * WARNING: Uses simple prefix matching. For production, use proper path
3046
+ * resolution with path.resolve() and path.normalize() to prevent traversal.
3047
+ */
3048
+ checkFileAccess(config, path) {
3049
+ if (config.level === "none") {
3050
+ return { allowed: true };
3051
+ }
3052
+ if (!config.filesystem) {
3053
+ return { allowed: false, reason: "File system access not configured" };
3054
+ }
3055
+ if (!path) {
3056
+ return { allowed: config.filesystem.mode !== "none" };
3057
+ }
3058
+ const allowedPaths = config.filesystem.allowedPaths || [];
3059
+ const isAllowed = allowedPaths.some((allowed) => {
3060
+ return path.startsWith(allowed);
3061
+ });
3062
+ if (allowedPaths.length > 0 && !isAllowed) {
3063
+ return {
3064
+ allowed: false,
3065
+ reason: `Path not in allowed list: ${path}`
3066
+ };
3067
+ }
3068
+ const deniedPaths = config.filesystem.deniedPaths || [];
3069
+ const isDenied = deniedPaths.some((denied) => {
3070
+ return path.startsWith(denied);
3071
+ });
3072
+ if (isDenied) {
3073
+ return {
3074
+ allowed: false,
3075
+ reason: `Path is explicitly denied: ${path}`
3076
+ };
3077
+ }
3078
+ return { allowed: true };
3079
+ }
3080
+ /**
3081
+ * Check network access
3082
+ * WARNING: Uses simple string matching. For production, use proper URL
3083
+ * parsing with new URL() and check hostname property.
3084
+ */
3085
+ checkNetworkAccess(config, url) {
3086
+ if (config.level === "none") {
3087
+ return { allowed: true };
3088
+ }
3089
+ if (!config.network) {
3090
+ return { allowed: false, reason: "Network access not configured" };
3091
+ }
3092
+ if (config.network.mode === "none") {
3093
+ return { allowed: false, reason: "Network access disabled" };
3094
+ }
3095
+ if (!url) {
3096
+ return { allowed: config.network.mode !== "none" };
3097
+ }
3098
+ const allowedHosts = config.network.allowedHosts || [];
3099
+ if (allowedHosts.length > 0) {
3100
+ const isAllowed = allowedHosts.some((host) => {
3101
+ return url.includes(host);
3102
+ });
3103
+ if (!isAllowed) {
3104
+ return {
3105
+ allowed: false,
3106
+ reason: `Host not in allowed list: ${url}`
3107
+ };
3108
+ }
3109
+ }
3110
+ const deniedHosts = config.network.deniedHosts || [];
3111
+ const isDenied = deniedHosts.some((host) => {
3112
+ return url.includes(host);
3113
+ });
3114
+ if (isDenied) {
3115
+ return {
3116
+ allowed: false,
3117
+ reason: `Host is blocked: ${url}`
3118
+ };
3119
+ }
3120
+ return { allowed: true };
3121
+ }
3122
+ /**
3123
+ * Check process spawning access
3124
+ */
3125
+ checkProcessAccess(config) {
3126
+ if (config.level === "none") {
3127
+ return { allowed: true };
3128
+ }
3129
+ if (!config.process) {
3130
+ return { allowed: false, reason: "Process access not configured" };
3131
+ }
3132
+ if (!config.process.allowSpawn) {
3133
+ return { allowed: false, reason: "Process spawning not allowed" };
3134
+ }
3135
+ return { allowed: true };
3136
+ }
3137
+ /**
3138
+ * Check environment variable access
3139
+ */
3140
+ checkEnvAccess(config, varName) {
3141
+ if (config.level === "none") {
3142
+ return { allowed: true };
3143
+ }
3144
+ if (!config.process) {
3145
+ return { allowed: false, reason: "Environment access not configured" };
3146
+ }
3147
+ if (!varName) {
3148
+ return { allowed: true };
3149
+ }
3150
+ return { allowed: true };
3151
+ }
3152
+ /**
3153
+ * Check resource limits
3154
+ */
3155
+ checkResourceLimits(pluginId) {
3156
+ const context = this.sandboxes.get(pluginId);
3157
+ if (!context) {
3158
+ return { withinLimits: true, violations: [] };
3159
+ }
3160
+ const violations = [];
3161
+ const { resourceUsage, config } = context;
3162
+ if (config.memory?.maxHeap && resourceUsage.memory.current > config.memory.maxHeap) {
3163
+ violations.push(`Memory limit exceeded: ${resourceUsage.memory.current} > ${config.memory.maxHeap}`);
3164
+ }
3165
+ if (config.runtime?.resourceLimits?.maxCpu && resourceUsage.cpu.current > config.runtime.resourceLimits.maxCpu) {
3166
+ violations.push(`CPU limit exceeded: ${resourceUsage.cpu.current}% > ${config.runtime.resourceLimits.maxCpu}%`);
3167
+ }
3168
+ if (config.network?.maxConnections && resourceUsage.connections.current > config.network.maxConnections) {
3169
+ violations.push(`Connection limit exceeded: ${resourceUsage.connections.current} > ${config.network.maxConnections}`);
3170
+ }
3171
+ return {
3172
+ withinLimits: violations.length === 0,
3173
+ violations
3174
+ };
3175
+ }
3176
+ /**
3177
+ * Get resource usage for a plugin
3178
+ */
3179
+ getResourceUsage(pluginId) {
3180
+ const context = this.sandboxes.get(pluginId);
3181
+ return context?.resourceUsage;
3182
+ }
3183
+ /**
3184
+ * Start monitoring resource usage
3185
+ */
3186
+ startResourceMonitoring(pluginId) {
3187
+ const interval = setInterval(() => {
3188
+ this.updateResourceUsage(pluginId);
3189
+ }, 5e3);
3190
+ this.monitoringIntervals.set(pluginId, interval);
3191
+ }
3192
+ /**
3193
+ * Stop monitoring resource usage
3194
+ */
3195
+ stopResourceMonitoring(pluginId) {
3196
+ const interval = this.monitoringIntervals.get(pluginId);
3197
+ if (interval) {
3198
+ clearInterval(interval);
3199
+ this.monitoringIntervals.delete(pluginId);
3200
+ }
3201
+ }
3202
+ /**
3203
+ * Update resource usage statistics
3204
+ *
3205
+ * NOTE: Currently uses global process.memoryUsage() which tracks the entire
3206
+ * Node.js process, not individual plugins. For production, implement proper
3207
+ * per-plugin tracking using V8 heap snapshots or allocation tracking at
3208
+ * plugin boundaries.
3209
+ */
3210
+ updateResourceUsage(pluginId) {
3211
+ const context = this.sandboxes.get(pluginId);
3212
+ if (!context) {
3213
+ return;
3214
+ }
3215
+ const memoryUsage = getMemoryUsage();
3216
+ context.resourceUsage.memory.current = memoryUsage.heapUsed;
3217
+ context.resourceUsage.memory.peak = Math.max(
3218
+ context.resourceUsage.memory.peak,
3219
+ memoryUsage.heapUsed
3220
+ );
3221
+ context.resourceUsage.cpu.current = 0;
3222
+ const { withinLimits, violations } = this.checkResourceLimits(pluginId);
3223
+ if (!withinLimits) {
3224
+ this.logger.warn("Resource limit violations detected", {
3225
+ pluginId,
3226
+ violations
3227
+ });
3228
+ }
3229
+ }
3230
+ /**
3231
+ * Get all active sandboxes
3232
+ */
3233
+ getAllSandboxes() {
3234
+ return new Map(this.sandboxes);
3235
+ }
3236
+ /**
3237
+ * Shutdown sandbox runtime
3238
+ */
3239
+ shutdown() {
3240
+ for (const pluginId of this.monitoringIntervals.keys()) {
3241
+ this.stopResourceMonitoring(pluginId);
3242
+ }
3243
+ this.sandboxes.clear();
3244
+ this.logger.info("Sandbox runtime shutdown complete");
3245
+ }
3246
+ };
3247
+
3248
+ // src/security/security-scanner.ts
3249
+ var PluginSecurityScanner = class {
3250
+ constructor(logger, config) {
3251
+ // Known vulnerabilities database (CVE cache)
3252
+ this.vulnerabilityDb = /* @__PURE__ */ new Map();
3253
+ // Scan results cache
3254
+ this.scanResults = /* @__PURE__ */ new Map();
3255
+ this.passThreshold = 70;
3256
+ this.logger = logger.child({ component: "SecurityScanner" });
3257
+ if (config?.passThreshold !== void 0) {
3258
+ this.passThreshold = config.passThreshold;
3259
+ }
3260
+ }
3261
+ /**
3262
+ * Perform a comprehensive security scan on a plugin
3263
+ */
3264
+ async scan(target) {
3265
+ this.logger.info("Starting security scan", {
3266
+ pluginId: target.pluginId,
3267
+ version: target.version
3268
+ });
3269
+ const issues = [];
3270
+ try {
3271
+ const codeIssues = await this.scanCode(target);
3272
+ issues.push(...codeIssues);
3273
+ const depIssues = await this.scanDependencies(target);
3274
+ issues.push(...depIssues);
3275
+ const malwareIssues = await this.scanMalware(target);
3276
+ issues.push(...malwareIssues);
3277
+ const licenseIssues = await this.scanLicenses(target);
3278
+ issues.push(...licenseIssues);
3279
+ const configIssues = await this.scanConfiguration(target);
3280
+ issues.push(...configIssues);
3281
+ const score = this.calculateSecurityScore(issues);
3282
+ const result = {
3283
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3284
+ scanner: { name: "ObjectStack Security Scanner", version: "1.0.0" },
3285
+ status: score >= this.passThreshold ? "passed" : "failed",
3286
+ vulnerabilities: issues.map((issue) => ({
3287
+ id: issue.id,
3288
+ severity: issue.severity,
3289
+ category: issue.category,
3290
+ title: issue.title,
3291
+ description: issue.description,
3292
+ location: issue.location ? `${issue.location.file}:${issue.location.line}` : void 0,
3293
+ remediation: issue.remediation,
3294
+ affectedVersions: [],
3295
+ exploitAvailable: false,
3296
+ patchAvailable: false
3297
+ })),
3298
+ summary: {
3299
+ totalVulnerabilities: issues.length,
3300
+ criticalCount: issues.filter((i) => i.severity === "critical").length,
3301
+ highCount: issues.filter((i) => i.severity === "high").length,
3302
+ mediumCount: issues.filter((i) => i.severity === "medium").length,
3303
+ lowCount: issues.filter((i) => i.severity === "low").length,
3304
+ infoCount: issues.filter((i) => i.severity === "info").length
3305
+ }
3306
+ };
3307
+ this.scanResults.set(`${target.pluginId}:${target.version}`, result);
3308
+ this.logger.info("Security scan complete", {
3309
+ pluginId: target.pluginId,
3310
+ score,
3311
+ status: result.status,
3312
+ summary: result.summary
3313
+ });
3314
+ return result;
3315
+ } catch (error) {
3316
+ this.logger.error("Security scan failed", {
3317
+ pluginId: target.pluginId,
3318
+ error
3319
+ });
3320
+ throw error;
3321
+ }
3322
+ }
3323
+ /**
3324
+ * Scan code for vulnerabilities
3325
+ */
3326
+ async scanCode(target) {
3327
+ const issues = [];
3328
+ this.logger.debug("Code scan complete", {
3329
+ pluginId: target.pluginId,
3330
+ issuesFound: issues.length
3331
+ });
3332
+ return issues;
3333
+ }
3334
+ /**
3335
+ * Scan dependencies for known vulnerabilities
3336
+ */
3337
+ async scanDependencies(target) {
3338
+ const issues = [];
3339
+ if (!target.dependencies) {
3340
+ return issues;
3341
+ }
3342
+ for (const [depName, version] of Object.entries(target.dependencies)) {
3343
+ const vulnKey = `${depName}@${version}`;
3344
+ const vulnerability = this.vulnerabilityDb.get(vulnKey);
3345
+ if (vulnerability) {
3346
+ issues.push({
3347
+ id: `vuln-${vulnerability.cve || depName}`,
3348
+ severity: vulnerability.severity,
3349
+ category: "vulnerability",
3350
+ title: `Vulnerable dependency: ${depName}`,
3351
+ description: `${depName}@${version} has known security vulnerabilities`,
3352
+ remediation: vulnerability.fixedIn ? `Upgrade to ${vulnerability.fixedIn.join(" or ")}` : "No fix available",
3353
+ cve: vulnerability.cve
3354
+ });
3355
+ }
3356
+ }
3357
+ this.logger.debug("Dependency scan complete", {
3358
+ pluginId: target.pluginId,
3359
+ dependencies: Object.keys(target.dependencies).length,
3360
+ vulnerabilities: issues.length
3361
+ });
3362
+ return issues;
3363
+ }
3364
+ /**
3365
+ * Scan for malware patterns
3366
+ */
3367
+ async scanMalware(target) {
3368
+ const issues = [];
3369
+ this.logger.debug("Malware scan complete", {
3370
+ pluginId: target.pluginId,
3371
+ issuesFound: issues.length
3372
+ });
3373
+ return issues;
3374
+ }
3375
+ /**
3376
+ * Check license compliance
3377
+ */
3378
+ async scanLicenses(target) {
3379
+ const issues = [];
3380
+ if (!target.dependencies) {
3381
+ return issues;
3382
+ }
3383
+ this.logger.debug("License scan complete", {
3384
+ pluginId: target.pluginId,
3385
+ issuesFound: issues.length
3386
+ });
3387
+ return issues;
3388
+ }
3389
+ /**
3390
+ * Check configuration security
3391
+ */
3392
+ async scanConfiguration(target) {
3393
+ const issues = [];
3394
+ this.logger.debug("Configuration scan complete", {
3395
+ pluginId: target.pluginId,
3396
+ issuesFound: issues.length
3397
+ });
3398
+ return issues;
3399
+ }
3400
+ /**
3401
+ * Calculate security score based on issues
3402
+ */
3403
+ calculateSecurityScore(issues) {
3404
+ let score = 100;
3405
+ for (const issue of issues) {
3406
+ switch (issue.severity) {
3407
+ case "critical":
3408
+ score -= 20;
3409
+ break;
3410
+ case "high":
3411
+ score -= 10;
3412
+ break;
3413
+ case "medium":
3414
+ score -= 5;
3415
+ break;
3416
+ case "low":
3417
+ score -= 2;
3418
+ break;
3419
+ case "info":
3420
+ score -= 0;
3421
+ break;
3422
+ }
3423
+ }
3424
+ return Math.max(0, score);
3425
+ }
3426
+ /**
3427
+ * Add a vulnerability to the database
3428
+ */
3429
+ addVulnerability(packageName, version, vulnerability) {
3430
+ const key = `${packageName}@${version}`;
3431
+ this.vulnerabilityDb.set(key, vulnerability);
3432
+ this.logger.debug("Vulnerability added to database", {
3433
+ package: packageName,
3434
+ version,
3435
+ cve: vulnerability.cve
3436
+ });
3437
+ }
3438
+ /**
3439
+ * Get scan result from cache
3440
+ */
3441
+ getScanResult(pluginId, version) {
3442
+ return this.scanResults.get(`${pluginId}:${version}`);
3443
+ }
3444
+ /**
3445
+ * Clear scan results cache
3446
+ */
3447
+ clearCache() {
3448
+ this.scanResults.clear();
3449
+ this.logger.debug("Scan results cache cleared");
3450
+ }
3451
+ /**
3452
+ * Update vulnerability database from external source
3453
+ */
3454
+ async updateVulnerabilityDatabase() {
3455
+ this.logger.info("Updating vulnerability database");
3456
+ this.logger.info("Vulnerability database updated", {
3457
+ entries: this.vulnerabilityDb.size
3458
+ });
3459
+ }
3460
+ /**
3461
+ * Shutdown security scanner
3462
+ */
3463
+ shutdown() {
3464
+ this.vulnerabilityDb.clear();
3465
+ this.scanResults.clear();
3466
+ this.logger.info("Security scanner shutdown complete");
3467
+ }
3468
+ };
3469
+
3470
+ // src/health-monitor.ts
3471
+ var PluginHealthMonitor = class {
3472
+ constructor(logger) {
3473
+ this.healthChecks = /* @__PURE__ */ new Map();
3474
+ this.healthStatus = /* @__PURE__ */ new Map();
3475
+ this.healthReports = /* @__PURE__ */ new Map();
3476
+ this.checkIntervals = /* @__PURE__ */ new Map();
3477
+ this.failureCounters = /* @__PURE__ */ new Map();
3478
+ this.successCounters = /* @__PURE__ */ new Map();
3479
+ this.restartAttempts = /* @__PURE__ */ new Map();
3480
+ this.logger = logger.child({ component: "HealthMonitor" });
3481
+ }
3482
+ /**
3483
+ * Register a plugin for health monitoring
3484
+ */
3485
+ registerPlugin(pluginName, config) {
3486
+ this.healthChecks.set(pluginName, config);
3487
+ this.healthStatus.set(pluginName, "unknown");
3488
+ this.failureCounters.set(pluginName, 0);
3489
+ this.successCounters.set(pluginName, 0);
3490
+ this.restartAttempts.set(pluginName, 0);
3491
+ this.logger.info("Plugin registered for health monitoring", {
3492
+ plugin: pluginName,
3493
+ interval: config.interval
3494
+ });
3495
+ }
3496
+ /**
3497
+ * Start monitoring a plugin
3498
+ */
3499
+ startMonitoring(pluginName, plugin) {
3500
+ const config = this.healthChecks.get(pluginName);
3501
+ if (!config) {
3502
+ this.logger.warn("Cannot start monitoring - plugin not registered", { plugin: pluginName });
3503
+ return;
3504
+ }
3505
+ this.stopMonitoring(pluginName);
3506
+ const interval = setInterval(() => {
3507
+ this.performHealthCheck(pluginName, plugin, config).catch((error) => {
3508
+ this.logger.error("Health check failed with error", {
3509
+ plugin: pluginName,
3510
+ error
3511
+ });
3512
+ });
3513
+ }, config.interval);
3514
+ this.checkIntervals.set(pluginName, interval);
3515
+ this.logger.info("Health monitoring started", { plugin: pluginName });
3516
+ this.performHealthCheck(pluginName, plugin, config).catch((error) => {
3517
+ this.logger.error("Initial health check failed", {
3518
+ plugin: pluginName,
3519
+ error
3520
+ });
3521
+ });
3522
+ }
3523
+ /**
3524
+ * Stop monitoring a plugin
3525
+ */
3526
+ stopMonitoring(pluginName) {
3527
+ const interval = this.checkIntervals.get(pluginName);
3528
+ if (interval) {
3529
+ clearInterval(interval);
3530
+ this.checkIntervals.delete(pluginName);
3531
+ this.logger.info("Health monitoring stopped", { plugin: pluginName });
3532
+ }
3533
+ }
3534
+ /**
3535
+ * Perform a health check on a plugin
3536
+ */
3537
+ async performHealthCheck(pluginName, plugin, config) {
3538
+ const startTime = Date.now();
3539
+ let status = "healthy";
3540
+ let message;
3541
+ const checks = [];
3542
+ try {
3543
+ if (config.checkMethod && typeof plugin[config.checkMethod] === "function") {
3544
+ const checkResult = await Promise.race([
3545
+ plugin[config.checkMethod](),
3546
+ this.timeout(config.timeout, `Health check timeout after ${config.timeout}ms`)
3547
+ ]);
3548
+ if (checkResult === false || checkResult && checkResult.status === "unhealthy") {
3549
+ status = "unhealthy";
3550
+ message = checkResult?.message || "Custom health check failed";
3551
+ checks.push({ name: config.checkMethod, status: "failed", message });
3552
+ } else {
3553
+ checks.push({ name: config.checkMethod, status: "passed" });
3554
+ }
3555
+ } else {
3556
+ checks.push({ name: "plugin-loaded", status: "passed" });
3557
+ }
3558
+ if (status === "healthy") {
3559
+ this.successCounters.set(pluginName, (this.successCounters.get(pluginName) || 0) + 1);
3560
+ this.failureCounters.set(pluginName, 0);
3561
+ const currentStatus = this.healthStatus.get(pluginName);
3562
+ if (currentStatus === "unhealthy" || currentStatus === "degraded") {
3563
+ const successCount = this.successCounters.get(pluginName) || 0;
3564
+ if (successCount >= config.successThreshold) {
3565
+ this.healthStatus.set(pluginName, "healthy");
3566
+ this.logger.info("Plugin recovered to healthy state", { plugin: pluginName });
3567
+ } else {
3568
+ this.healthStatus.set(pluginName, "recovering");
3569
+ }
3570
+ } else {
3571
+ this.healthStatus.set(pluginName, "healthy");
3572
+ }
3573
+ } else {
3574
+ this.failureCounters.set(pluginName, (this.failureCounters.get(pluginName) || 0) + 1);
3575
+ this.successCounters.set(pluginName, 0);
3576
+ const failureCount = this.failureCounters.get(pluginName) || 0;
3577
+ if (failureCount >= config.failureThreshold) {
3578
+ this.healthStatus.set(pluginName, "unhealthy");
3579
+ this.logger.warn("Plugin marked as unhealthy", {
3580
+ plugin: pluginName,
3581
+ failures: failureCount
3582
+ });
3583
+ if (config.autoRestart) {
3584
+ await this.attemptRestart(pluginName, plugin, config);
3585
+ }
3586
+ } else {
3587
+ this.healthStatus.set(pluginName, "degraded");
3588
+ }
3589
+ }
3590
+ } catch (error) {
3591
+ status = "failed";
3592
+ message = error instanceof Error ? error.message : "Unknown error";
3593
+ this.failureCounters.set(pluginName, (this.failureCounters.get(pluginName) || 0) + 1);
3594
+ this.healthStatus.set(pluginName, "failed");
3595
+ checks.push({
3596
+ name: "health-check",
3597
+ status: "failed",
3598
+ message
3599
+ });
3600
+ this.logger.error("Health check exception", {
3601
+ plugin: pluginName,
3602
+ error
3603
+ });
3604
+ }
3605
+ const report = {
3606
+ status: this.healthStatus.get(pluginName) || "unknown",
3607
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3608
+ message,
3609
+ metrics: {
3610
+ uptime: Date.now() - startTime
3611
+ },
3612
+ checks: checks.length > 0 ? checks : void 0
3613
+ };
3614
+ this.healthReports.set(pluginName, report);
3615
+ }
3616
+ /**
3617
+ * Attempt to restart a plugin
3618
+ */
3619
+ async attemptRestart(pluginName, plugin, config) {
3620
+ const attempts = this.restartAttempts.get(pluginName) || 0;
3621
+ if (attempts >= config.maxRestartAttempts) {
3622
+ this.logger.error("Max restart attempts reached, giving up", {
3623
+ plugin: pluginName,
3624
+ attempts
3625
+ });
3626
+ this.healthStatus.set(pluginName, "failed");
3627
+ return;
3628
+ }
3629
+ this.restartAttempts.set(pluginName, attempts + 1);
3630
+ const delay = this.calculateBackoff(attempts, config.restartBackoff);
3631
+ this.logger.info("Scheduling plugin restart", {
3632
+ plugin: pluginName,
3633
+ attempt: attempts + 1,
3634
+ delay
3635
+ });
3636
+ await new Promise((resolve) => setTimeout(resolve, delay));
3637
+ try {
3638
+ if (plugin.destroy) {
3639
+ await plugin.destroy();
3640
+ }
3641
+ this.logger.info("Plugin restarted", { plugin: pluginName });
3642
+ this.failureCounters.set(pluginName, 0);
3643
+ this.successCounters.set(pluginName, 0);
3644
+ this.healthStatus.set(pluginName, "recovering");
3645
+ } catch (error) {
3646
+ this.logger.error("Plugin restart failed", {
3647
+ plugin: pluginName,
3648
+ error
3649
+ });
3650
+ this.healthStatus.set(pluginName, "failed");
3651
+ }
3652
+ }
3653
+ /**
3654
+ * Calculate backoff delay for restarts
3655
+ */
3656
+ calculateBackoff(attempt, strategy) {
3657
+ const baseDelay = 1e3;
3658
+ switch (strategy) {
3659
+ case "fixed":
3660
+ return baseDelay;
3661
+ case "linear":
3662
+ return baseDelay * (attempt + 1);
3663
+ case "exponential":
3664
+ return baseDelay * Math.pow(2, attempt);
3665
+ default:
3666
+ return baseDelay;
3667
+ }
3668
+ }
3669
+ /**
3670
+ * Get current health status of a plugin
3671
+ */
3672
+ getHealthStatus(pluginName) {
3673
+ return this.healthStatus.get(pluginName);
3674
+ }
3675
+ /**
3676
+ * Get latest health report for a plugin
3677
+ */
3678
+ getHealthReport(pluginName) {
3679
+ return this.healthReports.get(pluginName);
3680
+ }
3681
+ /**
3682
+ * Get all health statuses
3683
+ */
3684
+ getAllHealthStatuses() {
3685
+ return new Map(this.healthStatus);
3686
+ }
3687
+ /**
3688
+ * Shutdown health monitor
3689
+ */
3690
+ shutdown() {
3691
+ for (const pluginName of this.checkIntervals.keys()) {
3692
+ this.stopMonitoring(pluginName);
3693
+ }
3694
+ this.healthChecks.clear();
3695
+ this.healthStatus.clear();
3696
+ this.healthReports.clear();
3697
+ this.failureCounters.clear();
3698
+ this.successCounters.clear();
3699
+ this.restartAttempts.clear();
3700
+ this.logger.info("Health monitor shutdown complete");
3701
+ }
3702
+ /**
3703
+ * Timeout helper
3704
+ */
3705
+ timeout(ms, message) {
3706
+ return new Promise((_, reject) => {
3707
+ setTimeout(() => reject(new Error(message)), ms);
3708
+ });
3709
+ }
3710
+ };
3711
+
3712
+ // src/hot-reload.ts
3713
+ var generateUUID = () => {
3714
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
3715
+ return crypto.randomUUID();
3716
+ }
3717
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
3718
+ const r = Math.random() * 16 | 0;
3719
+ const v = c === "x" ? r : r & 3 | 8;
3720
+ return v.toString(16);
3721
+ });
3722
+ };
3723
+ var PluginStateManager = class {
3724
+ constructor(logger) {
3725
+ this.stateSnapshots = /* @__PURE__ */ new Map();
3726
+ this.memoryStore = /* @__PURE__ */ new Map();
3727
+ this.logger = logger.child({ component: "StateManager" });
3728
+ }
3729
+ /**
3730
+ * Save plugin state before reload
3731
+ */
3732
+ async saveState(pluginId, version, state, config) {
3733
+ const snapshot = {
3734
+ pluginId,
3735
+ version,
3736
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3737
+ state,
3738
+ metadata: {
3739
+ checksum: this.calculateChecksum(state),
3740
+ compressed: false
3741
+ }
3742
+ };
3743
+ const snapshotId = generateUUID();
3744
+ switch (config.stateStrategy) {
3745
+ case "memory":
3746
+ this.memoryStore.set(snapshotId, snapshot);
3747
+ this.logger.debug("State saved to memory", { pluginId, snapshotId });
3748
+ break;
3749
+ case "disk":
3750
+ this.memoryStore.set(snapshotId, snapshot);
3751
+ this.logger.debug("State saved to disk (memory fallback)", { pluginId, snapshotId });
3752
+ break;
3753
+ case "distributed":
3754
+ this.memoryStore.set(snapshotId, snapshot);
3755
+ this.logger.debug("State saved to distributed store (memory fallback)", {
3756
+ pluginId,
3757
+ snapshotId
3758
+ });
3759
+ break;
3760
+ case "none":
3761
+ this.logger.debug("State persistence disabled", { pluginId });
3762
+ break;
3763
+ }
3764
+ this.stateSnapshots.set(pluginId, snapshot);
3765
+ return snapshotId;
3766
+ }
3767
+ /**
3768
+ * Restore plugin state after reload
3769
+ */
3770
+ async restoreState(pluginId, snapshotId) {
3771
+ let snapshot;
3772
+ if (snapshotId) {
3773
+ snapshot = this.memoryStore.get(snapshotId);
3774
+ } else {
3775
+ snapshot = this.stateSnapshots.get(pluginId);
3776
+ }
3777
+ if (!snapshot) {
3778
+ this.logger.warn("No state snapshot found", { pluginId, snapshotId });
3779
+ return void 0;
3780
+ }
3781
+ if (snapshot.metadata?.checksum) {
3782
+ const currentChecksum = this.calculateChecksum(snapshot.state);
3783
+ if (currentChecksum !== snapshot.metadata.checksum) {
3784
+ this.logger.error("State checksum mismatch - data may be corrupted", {
3785
+ pluginId,
3786
+ expected: snapshot.metadata.checksum,
3787
+ actual: currentChecksum
3788
+ });
3789
+ return void 0;
3790
+ }
3791
+ }
3792
+ this.logger.debug("State restored", { pluginId, version: snapshot.version });
3793
+ return snapshot.state;
3794
+ }
3795
+ /**
3796
+ * Clear state for a plugin
3797
+ */
3798
+ clearState(pluginId) {
3799
+ this.stateSnapshots.delete(pluginId);
3800
+ this.logger.debug("State cleared", { pluginId });
3801
+ }
3802
+ /**
3803
+ * Calculate simple checksum for state verification
3804
+ * WARNING: This is a simple hash for demo purposes.
3805
+ * In production, use a cryptographic hash like SHA-256.
3806
+ */
3807
+ calculateChecksum(state) {
3808
+ const stateStr = JSON.stringify(state);
3809
+ let hash = 0;
3810
+ for (let i = 0; i < stateStr.length; i++) {
3811
+ const char = stateStr.charCodeAt(i);
3812
+ hash = (hash << 5) - hash + char;
3813
+ hash = hash & hash;
3814
+ }
3815
+ return hash.toString(16);
3816
+ }
3817
+ /**
3818
+ * Shutdown state manager
3819
+ */
3820
+ shutdown() {
3821
+ this.stateSnapshots.clear();
3822
+ this.memoryStore.clear();
3823
+ this.logger.info("State manager shutdown complete");
3824
+ }
3825
+ };
3826
+ var HotReloadManager = class {
3827
+ constructor(logger) {
3828
+ this.reloadConfigs = /* @__PURE__ */ new Map();
3829
+ this.watchHandles = /* @__PURE__ */ new Map();
3830
+ this.reloadTimers = /* @__PURE__ */ new Map();
3831
+ this.logger = logger.child({ component: "HotReload" });
3832
+ this.stateManager = new PluginStateManager(logger);
3833
+ }
3834
+ /**
3835
+ * Register a plugin for hot reload
3836
+ */
3837
+ registerPlugin(pluginName, config) {
3838
+ if (!config.enabled) {
3839
+ this.logger.debug("Hot reload disabled for plugin", { plugin: pluginName });
3840
+ return;
3841
+ }
3842
+ this.reloadConfigs.set(pluginName, config);
3843
+ this.logger.info("Plugin registered for hot reload", {
3844
+ plugin: pluginName,
3845
+ watchPatterns: config.watchPatterns,
3846
+ stateStrategy: config.stateStrategy
3847
+ });
3848
+ }
3849
+ /**
3850
+ * Start watching for changes (requires file system integration)
3851
+ */
3852
+ startWatching(pluginName) {
3853
+ const config = this.reloadConfigs.get(pluginName);
3854
+ if (!config || !config.enabled) {
3855
+ return;
3856
+ }
3857
+ this.logger.info("File watching started", {
3858
+ plugin: pluginName,
3859
+ patterns: config.watchPatterns
3860
+ });
3861
+ }
3862
+ /**
3863
+ * Stop watching for changes
3864
+ */
3865
+ stopWatching(pluginName) {
3866
+ const handle = this.watchHandles.get(pluginName);
3867
+ if (handle) {
3868
+ this.watchHandles.delete(pluginName);
3869
+ this.logger.info("File watching stopped", { plugin: pluginName });
3870
+ }
3871
+ const timer = this.reloadTimers.get(pluginName);
3872
+ if (timer) {
3873
+ clearTimeout(timer);
3874
+ this.reloadTimers.delete(pluginName);
3875
+ }
3876
+ }
3877
+ /**
3878
+ * Trigger hot reload for a plugin
3879
+ */
3880
+ async reloadPlugin(pluginName, plugin, version, getPluginState, restorePluginState) {
3881
+ const config = this.reloadConfigs.get(pluginName);
3882
+ if (!config) {
3883
+ this.logger.warn("Cannot reload - plugin not registered", { plugin: pluginName });
3884
+ return false;
3885
+ }
3886
+ this.logger.info("Starting hot reload", { plugin: pluginName });
3887
+ try {
3888
+ if (config.beforeReload) {
3889
+ this.logger.debug("Executing before reload hooks", {
3890
+ plugin: pluginName,
3891
+ hooks: config.beforeReload
3892
+ });
3893
+ }
3894
+ let snapshotId;
3895
+ if (config.preserveState && config.stateStrategy !== "none") {
3896
+ const state = getPluginState();
3897
+ snapshotId = await this.stateManager.saveState(
3898
+ pluginName,
3899
+ version,
3900
+ state,
3901
+ config
3902
+ );
3903
+ this.logger.debug("Plugin state saved", { plugin: pluginName, snapshotId });
3904
+ }
3905
+ if (plugin.destroy) {
3906
+ this.logger.debug("Destroying plugin", { plugin: pluginName });
3907
+ const shutdownPromise = plugin.destroy();
3908
+ const timeoutPromise = new Promise((_, reject) => {
3909
+ setTimeout(() => reject(new Error("Shutdown timeout")), config.shutdownTimeout);
3910
+ });
3911
+ await Promise.race([shutdownPromise, timeoutPromise]);
3912
+ this.logger.debug("Plugin destroyed successfully", { plugin: pluginName });
3913
+ }
3914
+ this.logger.debug("Plugin module would be reloaded here", { plugin: pluginName });
3915
+ if (snapshotId && config.preserveState) {
3916
+ const restoredState = await this.stateManager.restoreState(pluginName, snapshotId);
3917
+ if (restoredState) {
3918
+ restorePluginState(restoredState);
3919
+ this.logger.debug("Plugin state restored", { plugin: pluginName });
3920
+ }
3921
+ }
3922
+ if (config.afterReload) {
3923
+ this.logger.debug("Executing after reload hooks", {
3924
+ plugin: pluginName,
3925
+ hooks: config.afterReload
3926
+ });
3927
+ }
3928
+ this.logger.info("Hot reload completed successfully", { plugin: pluginName });
3929
+ return true;
3930
+ } catch (error) {
3931
+ this.logger.error("Hot reload failed", {
3932
+ plugin: pluginName,
3933
+ error
3934
+ });
3935
+ return false;
3936
+ }
3937
+ }
3938
+ /**
3939
+ * Schedule a reload with debouncing
3940
+ */
3941
+ scheduleReload(pluginName, reloadFn) {
3942
+ const config = this.reloadConfigs.get(pluginName);
3943
+ if (!config) {
3944
+ return;
3945
+ }
3946
+ const existingTimer = this.reloadTimers.get(pluginName);
3947
+ if (existingTimer) {
3948
+ clearTimeout(existingTimer);
3949
+ }
3950
+ const timer = setTimeout(() => {
3951
+ this.logger.debug("Debounce period elapsed, executing reload", {
3952
+ plugin: pluginName
3953
+ });
3954
+ reloadFn().catch((error) => {
3955
+ this.logger.error("Scheduled reload failed", {
3956
+ plugin: pluginName,
3957
+ error
3958
+ });
3959
+ });
3960
+ this.reloadTimers.delete(pluginName);
3961
+ }, config.debounceDelay);
3962
+ this.reloadTimers.set(pluginName, timer);
3963
+ this.logger.debug("Reload scheduled with debounce", {
3964
+ plugin: pluginName,
3965
+ delay: config.debounceDelay
3966
+ });
3967
+ }
3968
+ /**
3969
+ * Get state manager for direct access
3970
+ */
3971
+ getStateManager() {
3972
+ return this.stateManager;
3973
+ }
3974
+ /**
3975
+ * Shutdown hot reload manager
3976
+ */
3977
+ shutdown() {
3978
+ for (const pluginName of this.watchHandles.keys()) {
3979
+ this.stopWatching(pluginName);
3980
+ }
3981
+ for (const timer of this.reloadTimers.values()) {
3982
+ clearTimeout(timer);
3983
+ }
3984
+ this.reloadConfigs.clear();
3985
+ this.watchHandles.clear();
3986
+ this.reloadTimers.clear();
3987
+ this.stateManager.shutdown();
3988
+ this.logger.info("Hot reload manager shutdown complete");
3989
+ }
3990
+ };
3991
+
3992
+ // src/dependency-resolver.ts
3993
+ var SemanticVersionManager = class {
3994
+ /**
3995
+ * Parse a version string into semantic version components
3996
+ */
3997
+ static parse(versionStr) {
3998
+ const cleanVersion = versionStr.replace(/^v/, "");
3999
+ const match = cleanVersion.match(
4000
+ /^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9.-]+))?(?:\+([a-zA-Z0-9.-]+))?$/
4001
+ );
4002
+ if (!match) {
4003
+ throw new Error(`Invalid semantic version: ${versionStr}`);
4004
+ }
4005
+ return {
4006
+ major: parseInt(match[1], 10),
4007
+ minor: parseInt(match[2], 10),
4008
+ patch: parseInt(match[3], 10),
4009
+ preRelease: match[4],
4010
+ build: match[5]
4011
+ };
4012
+ }
4013
+ /**
4014
+ * Convert semantic version back to string
4015
+ */
4016
+ static toString(version) {
4017
+ let str = `${version.major}.${version.minor}.${version.patch}`;
4018
+ if (version.preRelease) {
4019
+ str += `-${version.preRelease}`;
4020
+ }
4021
+ if (version.build) {
4022
+ str += `+${version.build}`;
4023
+ }
4024
+ return str;
4025
+ }
4026
+ /**
4027
+ * Compare two semantic versions
4028
+ * Returns: -1 if a < b, 0 if a === b, 1 if a > b
4029
+ */
4030
+ static compare(a, b) {
4031
+ if (a.major !== b.major) return a.major - b.major;
4032
+ if (a.minor !== b.minor) return a.minor - b.minor;
4033
+ if (a.patch !== b.patch) return a.patch - b.patch;
4034
+ if (a.preRelease && !b.preRelease) return -1;
4035
+ if (!a.preRelease && b.preRelease) return 1;
4036
+ if (a.preRelease && b.preRelease) {
4037
+ return a.preRelease.localeCompare(b.preRelease);
4038
+ }
4039
+ return 0;
4040
+ }
4041
+ /**
4042
+ * Check if version satisfies constraint
4043
+ */
4044
+ static satisfies(version, constraint) {
4045
+ const constraintStr = constraint;
4046
+ if (constraintStr === "*" || constraintStr === "latest") {
4047
+ return true;
4048
+ }
4049
+ if (/^[\d.]+$/.test(constraintStr)) {
4050
+ const exact = this.parse(constraintStr);
4051
+ return this.compare(version, exact) === 0;
4052
+ }
4053
+ if (constraintStr.startsWith("^")) {
4054
+ const base = this.parse(constraintStr.slice(1));
4055
+ return version.major === base.major && this.compare(version, base) >= 0;
4056
+ }
4057
+ if (constraintStr.startsWith("~")) {
4058
+ const base = this.parse(constraintStr.slice(1));
4059
+ return version.major === base.major && version.minor === base.minor && this.compare(version, base) >= 0;
4060
+ }
4061
+ if (constraintStr.startsWith(">=")) {
4062
+ const base = this.parse(constraintStr.slice(2));
4063
+ return this.compare(version, base) >= 0;
4064
+ }
4065
+ if (constraintStr.startsWith(">")) {
4066
+ const base = this.parse(constraintStr.slice(1));
4067
+ return this.compare(version, base) > 0;
4068
+ }
4069
+ if (constraintStr.startsWith("<=")) {
4070
+ const base = this.parse(constraintStr.slice(2));
4071
+ return this.compare(version, base) <= 0;
4072
+ }
4073
+ if (constraintStr.startsWith("<")) {
4074
+ const base = this.parse(constraintStr.slice(1));
4075
+ return this.compare(version, base) < 0;
4076
+ }
4077
+ const rangeMatch = constraintStr.match(/^([\d.]+)\s*-\s*([\d.]+)$/);
4078
+ if (rangeMatch) {
4079
+ const min = this.parse(rangeMatch[1]);
4080
+ const max = this.parse(rangeMatch[2]);
4081
+ return this.compare(version, min) >= 0 && this.compare(version, max) <= 0;
4082
+ }
4083
+ return false;
4084
+ }
4085
+ /**
4086
+ * Determine compatibility level between two versions
4087
+ */
4088
+ static getCompatibilityLevel(from, to) {
4089
+ const cmp = this.compare(from, to);
4090
+ if (cmp === 0) {
4091
+ return "fully-compatible";
4092
+ }
4093
+ if (from.major !== to.major) {
4094
+ return "breaking-changes";
4095
+ }
4096
+ if (from.minor < to.minor) {
4097
+ return "backward-compatible";
4098
+ }
4099
+ if (from.patch < to.patch) {
4100
+ return "fully-compatible";
4101
+ }
4102
+ return "incompatible";
4103
+ }
4104
+ };
4105
+ var DependencyResolver = class {
4106
+ constructor(logger) {
4107
+ this.logger = logger.child({ component: "DependencyResolver" });
4108
+ }
4109
+ /**
4110
+ * Resolve dependencies using topological sort
4111
+ */
4112
+ resolve(plugins) {
4113
+ const graph = /* @__PURE__ */ new Map();
4114
+ const inDegree = /* @__PURE__ */ new Map();
4115
+ for (const [pluginName, pluginInfo] of plugins) {
4116
+ if (!graph.has(pluginName)) {
4117
+ graph.set(pluginName, []);
4118
+ inDegree.set(pluginName, 0);
4119
+ }
4120
+ const deps = pluginInfo.dependencies || [];
4121
+ for (const dep of deps) {
4122
+ if (!plugins.has(dep)) {
4123
+ throw new Error(`Missing dependency: ${pluginName} requires ${dep}`);
4124
+ }
4125
+ if (!graph.has(dep)) {
4126
+ graph.set(dep, []);
4127
+ inDegree.set(dep, 0);
4128
+ }
4129
+ graph.get(dep).push(pluginName);
4130
+ inDegree.set(pluginName, (inDegree.get(pluginName) || 0) + 1);
4131
+ }
4132
+ }
4133
+ const queue = [];
4134
+ const result = [];
4135
+ for (const [node, degree] of inDegree) {
4136
+ if (degree === 0) {
4137
+ queue.push(node);
4138
+ }
4139
+ }
4140
+ while (queue.length > 0) {
4141
+ const node = queue.shift();
4142
+ result.push(node);
4143
+ const dependents = graph.get(node) || [];
4144
+ for (const dependent of dependents) {
4145
+ const newDegree = (inDegree.get(dependent) || 0) - 1;
4146
+ inDegree.set(dependent, newDegree);
4147
+ if (newDegree === 0) {
4148
+ queue.push(dependent);
4149
+ }
4150
+ }
4151
+ }
4152
+ if (result.length !== plugins.size) {
4153
+ const remaining = Array.from(plugins.keys()).filter((p) => !result.includes(p));
4154
+ this.logger.error("Circular dependency detected", { remaining });
4155
+ throw new Error(`Circular dependency detected among: ${remaining.join(", ")}`);
4156
+ }
4157
+ this.logger.debug("Dependencies resolved", { order: result });
4158
+ return result;
4159
+ }
4160
+ /**
4161
+ * Detect dependency conflicts
4162
+ */
4163
+ detectConflicts(plugins) {
4164
+ const conflicts = [];
4165
+ const versionRequirements = /* @__PURE__ */ new Map();
4166
+ for (const [pluginName, pluginInfo] of plugins) {
4167
+ if (!pluginInfo.dependencies) continue;
4168
+ for (const [depName, constraint] of Object.entries(pluginInfo.dependencies)) {
4169
+ if (!versionRequirements.has(depName)) {
4170
+ versionRequirements.set(depName, /* @__PURE__ */ new Map());
4171
+ }
4172
+ versionRequirements.get(depName).set(pluginName, constraint);
4173
+ }
4174
+ }
4175
+ for (const [depName, requirements] of versionRequirements) {
4176
+ const depInfo = plugins.get(depName);
4177
+ if (!depInfo) continue;
4178
+ const depVersion = SemanticVersionManager.parse(depInfo.version);
4179
+ const unsatisfied = [];
4180
+ for (const [requiringPlugin, constraint] of requirements) {
4181
+ if (!SemanticVersionManager.satisfies(depVersion, constraint)) {
4182
+ unsatisfied.push({
4183
+ pluginId: requiringPlugin,
4184
+ version: constraint
4185
+ });
4186
+ }
4187
+ }
4188
+ if (unsatisfied.length > 0) {
4189
+ conflicts.push({
4190
+ type: "version-mismatch",
4191
+ severity: "error",
4192
+ description: `Version mismatch for ${depName}: detected ${unsatisfied.length} unsatisfied requirements`,
4193
+ plugins: [
4194
+ { pluginId: depName, version: depInfo.version },
4195
+ ...unsatisfied
4196
+ ],
4197
+ resolutions: [{
4198
+ strategy: "upgrade",
4199
+ description: `Upgrade ${depName} to satisfy all constraints`,
4200
+ targetPlugins: [depName],
4201
+ automatic: false
4202
+ }]
4203
+ });
4204
+ }
4205
+ }
4206
+ try {
4207
+ this.resolve(new Map(
4208
+ Array.from(plugins.entries()).map(([name, info]) => [
4209
+ name,
4210
+ { version: info.version, dependencies: info.dependencies ? Object.keys(info.dependencies) : [] }
4211
+ ])
4212
+ ));
4213
+ } catch (error) {
4214
+ if (error instanceof Error && error.message.includes("Circular dependency")) {
4215
+ conflicts.push({
4216
+ type: "circular-dependency",
4217
+ severity: "critical",
4218
+ description: error.message,
4219
+ plugins: [],
4220
+ // Would need to extract from error
4221
+ resolutions: [{
4222
+ strategy: "manual",
4223
+ description: "Remove circular dependency by restructuring plugins",
4224
+ automatic: false
4225
+ }]
4226
+ });
4227
+ }
4228
+ }
4229
+ return conflicts;
4230
+ }
4231
+ /**
4232
+ * Find best version that satisfies all constraints
4233
+ */
4234
+ findBestVersion(availableVersions, constraints) {
4235
+ const versions = availableVersions.map((v) => ({ str: v, parsed: SemanticVersionManager.parse(v) })).sort((a, b) => -SemanticVersionManager.compare(a.parsed, b.parsed));
4236
+ for (const version of versions) {
4237
+ const satisfiesAll = constraints.every(
4238
+ (constraint) => SemanticVersionManager.satisfies(version.parsed, constraint)
4239
+ );
4240
+ if (satisfiesAll) {
4241
+ return version.str;
4242
+ }
4243
+ }
4244
+ return void 0;
4245
+ }
4246
+ /**
4247
+ * Check if dependencies form a valid DAG (no cycles)
4248
+ */
4249
+ isAcyclic(dependencies) {
4250
+ try {
4251
+ const plugins = new Map(
4252
+ Array.from(dependencies.entries()).map(([name, deps]) => [
4253
+ name,
4254
+ { dependencies: deps }
4255
+ ])
4256
+ );
4257
+ this.resolve(plugins);
4258
+ return true;
4259
+ } catch {
4260
+ return false;
4261
+ }
4262
+ }
4263
+ };
4264
+ // Annotate the CommonJS export names for ESM import in node:
4265
+ 0 && (module.exports = {
4266
+ ApiRegistry,
4267
+ DependencyResolver,
4268
+ HotReloadManager,
4269
+ LiteKernel,
4270
+ ObjectKernel,
4271
+ ObjectKernelBase,
4272
+ ObjectLogger,
4273
+ PluginConfigValidator,
4274
+ PluginHealthMonitor,
4275
+ PluginLoader,
4276
+ PluginPermissionEnforcer,
4277
+ PluginPermissionManager,
4278
+ PluginSandboxRuntime,
4279
+ PluginSecurityScanner,
4280
+ PluginSignatureVerifier,
4281
+ QA,
4282
+ SecurePluginContext,
4283
+ SemanticVersionManager,
4284
+ ServiceLifecycle,
4285
+ createApiRegistryPlugin,
4286
+ createLogger,
4287
+ createPluginConfigValidator,
4288
+ createPluginPermissionEnforcer,
4289
+ getEnv,
4290
+ getMemoryUsage,
4291
+ isNode,
4292
+ safeExit
4293
+ });
4294
+ //# sourceMappingURL=index.cjs.map