@objectstack/core 4.0.4 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +95 -10
  2. package/dist/index.cjs +172 -507
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +24 -223
  5. package/dist/index.d.ts +24 -223
  6. package/dist/index.js +178 -505
  7. package/dist/index.js.map +1 -1
  8. package/dist/logger.cjs +177 -0
  9. package/dist/logger.cjs.map +1 -0
  10. package/dist/logger.d.cts +26 -0
  11. package/dist/logger.d.ts +26 -0
  12. package/dist/logger.js +158 -0
  13. package/dist/logger.js.map +1 -0
  14. package/package.json +36 -15
  15. package/.turbo/turbo-build.log +0 -22
  16. package/ADVANCED_FEATURES.md +0 -380
  17. package/API_REGISTRY.md +0 -392
  18. package/CHANGELOG.md +0 -472
  19. package/PHASE2_IMPLEMENTATION.md +0 -388
  20. package/REFACTORING_SUMMARY.md +0 -40
  21. package/examples/api-registry-example.ts +0 -559
  22. package/examples/kernel-features-example.ts +0 -311
  23. package/examples/phase2-integration.ts +0 -357
  24. package/src/api-registry-plugin.test.ts +0 -393
  25. package/src/api-registry-plugin.ts +0 -89
  26. package/src/api-registry.test.ts +0 -1089
  27. package/src/api-registry.ts +0 -739
  28. package/src/contracts/data-engine.ts +0 -57
  29. package/src/contracts/http-server.ts +0 -151
  30. package/src/contracts/logger.ts +0 -72
  31. package/src/dependency-resolver.test.ts +0 -287
  32. package/src/dependency-resolver.ts +0 -390
  33. package/src/fallbacks/fallbacks.test.ts +0 -281
  34. package/src/fallbacks/index.ts +0 -26
  35. package/src/fallbacks/memory-cache.ts +0 -34
  36. package/src/fallbacks/memory-i18n.ts +0 -112
  37. package/src/fallbacks/memory-job.ts +0 -23
  38. package/src/fallbacks/memory-metadata.ts +0 -50
  39. package/src/fallbacks/memory-queue.ts +0 -28
  40. package/src/health-monitor.test.ts +0 -81
  41. package/src/health-monitor.ts +0 -318
  42. package/src/hot-reload.ts +0 -382
  43. package/src/index.ts +0 -50
  44. package/src/kernel-base.ts +0 -273
  45. package/src/kernel.test.ts +0 -624
  46. package/src/kernel.ts +0 -631
  47. package/src/lite-kernel.test.ts +0 -248
  48. package/src/lite-kernel.ts +0 -137
  49. package/src/logger.test.ts +0 -116
  50. package/src/logger.ts +0 -355
  51. package/src/namespace-resolver.test.ts +0 -130
  52. package/src/namespace-resolver.ts +0 -188
  53. package/src/package-manager.test.ts +0 -225
  54. package/src/package-manager.ts +0 -428
  55. package/src/plugin-loader.test.ts +0 -421
  56. package/src/plugin-loader.ts +0 -484
  57. package/src/qa/adapter.ts +0 -16
  58. package/src/qa/http-adapter.ts +0 -116
  59. package/src/qa/index.ts +0 -5
  60. package/src/qa/runner.ts +0 -189
  61. package/src/security/index.ts +0 -50
  62. package/src/security/permission-manager.test.ts +0 -256
  63. package/src/security/permission-manager.ts +0 -338
  64. package/src/security/plugin-config-validator.test.ts +0 -276
  65. package/src/security/plugin-config-validator.ts +0 -193
  66. package/src/security/plugin-permission-enforcer.test.ts +0 -251
  67. package/src/security/plugin-permission-enforcer.ts +0 -436
  68. package/src/security/plugin-signature-verifier.ts +0 -403
  69. package/src/security/sandbox-runtime.ts +0 -462
  70. package/src/security/security-scanner.ts +0 -367
  71. package/src/types.ts +0 -120
  72. package/src/utils/env.test.ts +0 -62
  73. package/src/utils/env.ts +0 -53
  74. package/tsconfig.json +0 -10
  75. package/vitest.config.ts +0 -10
package/dist/index.cjs CHANGED
@@ -39,7 +39,6 @@ __export(index_exports, {
39
39
  ObjectKernel: () => ObjectKernel,
40
40
  ObjectKernelBase: () => ObjectKernelBase,
41
41
  ObjectLogger: () => ObjectLogger,
42
- PackageManager: () => PackageManager,
43
42
  PluginConfigValidator: () => PluginConfigValidator,
44
43
  PluginHealthMonitor: () => PluginHealthMonitor,
45
44
  PluginLoader: () => PluginLoader,
@@ -160,7 +159,13 @@ var ObjectKernelBase = class {
160
159
  }
161
160
  },
162
161
  logger: this.logger,
163
- getKernel: () => this
162
+ getKernel: () => this,
163
+ registerServiceFactory: (_name, _factory, _lifecycle, _dependencies) => {
164
+ throw new Error("[KernelBase] registerServiceFactory not supported \u2014 use ObjectKernel");
165
+ },
166
+ getServiceScoped: async (_name, _scopeId) => {
167
+ throw new Error("[KernelBase] getServiceScoped not supported \u2014 use ObjectKernel");
168
+ }
164
169
  };
165
170
  }
166
171
  /**
@@ -279,145 +284,58 @@ var ObjectKernelBase = class {
279
284
  }
280
285
  };
281
286
 
282
- // src/utils/env.ts
283
- var isNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
284
- function getEnv(key, defaultValue) {
285
- if (typeof process !== "undefined" && process.env) {
286
- return process.env[key] || defaultValue;
287
- }
288
- try {
289
- if (typeof globalThis !== "undefined" && globalThis.process?.env) {
290
- return globalThis.process.env[key] || defaultValue;
291
- }
292
- } catch (e) {
293
- }
294
- return defaultValue;
295
- }
296
- function safeExit(code = 0) {
297
- if (isNode) {
298
- process.exit(code);
299
- }
300
- }
301
- function getMemoryUsage() {
302
- if (isNode) {
303
- return process.memoryUsage();
304
- }
305
- return { heapUsed: 0, heapTotal: 0 };
306
- }
307
-
308
287
  // src/logger.ts
309
- var import_meta = {};
288
+ var LEVEL_ORDER = {
289
+ debug: 0,
290
+ info: 1,
291
+ warn: 2,
292
+ error: 3,
293
+ fatal: 4,
294
+ silent: 5
295
+ };
296
+ var LEVEL_COLORS = {
297
+ debug: "\x1B[36m",
298
+ info: "\x1B[32m",
299
+ warn: "\x1B[33m",
300
+ error: "\x1B[31m",
301
+ fatal: "\x1B[35m",
302
+ silent: ""
303
+ };
304
+ var RESET = "\x1B[0m";
310
305
  var ObjectLogger = class _ObjectLogger {
311
- // CommonJS require function for Node.js
312
- constructor(config = {}) {
313
- this.isNode = isNode;
306
+ constructor(config = {}, bindings = {}) {
314
307
  this.config = {
315
308
  name: config.name,
316
309
  level: config.level ?? "info",
317
- format: config.format ?? (this.isNode ? "json" : "pretty"),
310
+ format: config.format ?? "pretty",
318
311
  redact: config.redact ?? ["password", "token", "secret", "key"],
319
312
  sourceLocation: config.sourceLocation ?? false,
320
313
  file: config.file,
321
- rotation: config.rotation ?? {
322
- maxSize: "10m",
323
- maxFiles: 5
324
- }
314
+ rotation: config.rotation ?? { maxSize: "10m", maxFiles: 5 }
325
315
  };
326
- if (this.isNode) {
327
- this.initPinoLogger();
316
+ this.bindings = bindings;
317
+ if (this.config.file && typeof process !== "undefined") {
318
+ this.openFileStream(this.config.file);
328
319
  }
329
320
  }
330
- /**
331
- * Initialize Pino logger for Node.js
332
- */
333
- async initPinoLogger() {
334
- if (!this.isNode) return;
321
+ openFileStream(path) {
335
322
  try {
336
- const { createRequire } = await import("module");
337
- this.require = createRequire(import_meta.url);
338
- const pino = this.require("pino");
339
- const pinoOptions = {
340
- level: this.config.level,
341
- redact: {
342
- paths: this.config.redact,
343
- censor: "***REDACTED***"
344
- }
345
- };
346
- if (this.config.name) {
347
- pinoOptions.name = this.config.name;
348
- }
349
- const targets = [];
350
- if (this.config.format === "pretty") {
351
- let hasPretty = false;
352
- try {
353
- this.require.resolve("pino-pretty");
354
- hasPretty = true;
355
- } catch (e) {
356
- }
357
- if (hasPretty) {
358
- targets.push({
359
- target: "pino-pretty",
360
- options: {
361
- colorize: true,
362
- translateTime: "SYS:standard",
363
- ignore: "pid,hostname"
364
- },
365
- level: this.config.level
366
- });
367
- } else {
368
- console.warn("[Logger] pino-pretty not found. Install it for pretty logging: pnpm add -D pino-pretty");
369
- targets.push({
370
- target: "pino/file",
371
- options: { destination: 1 },
372
- level: this.config.level
373
- });
374
- }
375
- } else if (this.config.format === "json") {
376
- targets.push({
377
- target: "pino/file",
378
- options: { destination: 1 },
379
- // stdout
380
- level: this.config.level
381
- });
382
- } else {
383
- targets.push({
384
- target: "pino/file",
385
- options: { destination: 1 },
386
- level: this.config.level
387
- });
388
- }
389
- if (this.config.file) {
390
- targets.push({
391
- target: "pino/file",
392
- options: {
393
- destination: this.config.file,
394
- mkdir: true
395
- },
396
- level: this.config.level
397
- });
398
- }
399
- if (targets.length > 0) {
400
- pinoOptions.transport = targets.length === 1 ? targets[0] : { targets };
401
- }
402
- this.pinoInstance = pino(pinoOptions);
403
- this.pinoLogger = this.pinoInstance;
404
- } catch (error) {
405
- console.warn("[Logger] Pino not available, falling back to console:", error);
406
- this.pinoLogger = null;
323
+ const fs = require("fs");
324
+ const dir = require("path").dirname(path);
325
+ fs.mkdirSync(dir, { recursive: true });
326
+ this.fileStream = fs.createWriteStream(path, { flags: "a" });
327
+ } catch {
407
328
  }
408
329
  }
409
- /**
410
- * Redact sensitive keys from context object (for browser)
411
- */
330
+ isEnabled(level) {
331
+ return LEVEL_ORDER[level] >= LEVEL_ORDER[this.config.level];
332
+ }
412
333
  redactSensitive(obj) {
413
334
  if (!obj || typeof obj !== "object") return obj;
414
335
  const redacted = Array.isArray(obj) ? [...obj] : { ...obj };
415
336
  for (const key in redacted) {
416
- const lowerKey = key.toLowerCase();
417
- const shouldRedact = this.config.redact.some(
418
- (pattern) => lowerKey.includes(pattern.toLowerCase())
419
- );
420
- if (shouldRedact) {
337
+ const lower = key.toLowerCase();
338
+ if (this.config.redact.some((p) => lower.includes(p.toLowerCase()))) {
421
339
  redacted[key] = "***REDACTED***";
422
340
  } else if (typeof redacted[key] === "object" && redacted[key] !== null) {
423
341
  redacted[key] = this.redactSensitive(redacted[key]);
@@ -425,146 +343,89 @@ var ObjectLogger = class _ObjectLogger {
425
343
  }
426
344
  return redacted;
427
345
  }
428
- /**
429
- * Format log entry for browser
430
- */
431
- formatBrowserLog(level, message, context) {
346
+ write(level, message, meta, error) {
347
+ if (!this.isEnabled(level)) return;
348
+ const context = this.redactSensitive({
349
+ ...this.bindings,
350
+ ...meta,
351
+ ...error ? { error: { message: error.message, stack: error.stack } } : {}
352
+ });
353
+ const hasContext = Object.keys(context).length > 0;
354
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
355
+ let line;
432
356
  if (this.config.format === "json") {
433
- return JSON.stringify({
434
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
357
+ line = JSON.stringify({
358
+ time: ts,
435
359
  level,
436
- message,
360
+ ...this.config.name ? { name: this.config.name } : {},
361
+ msg: message,
437
362
  ...context
438
363
  });
439
- }
440
- if (this.config.format === "text") {
441
- const parts = [(/* @__PURE__ */ new Date()).toISOString(), level.toUpperCase(), message];
442
- if (context && Object.keys(context).length > 0) {
443
- parts.push(JSON.stringify(context));
364
+ } else if (this.config.format === "text") {
365
+ const parts = [ts, level.toUpperCase(), message];
366
+ if (hasContext) parts.push(JSON.stringify(context));
367
+ line = parts.join(" | ");
368
+ } else {
369
+ const color = LEVEL_COLORS[level] || "";
370
+ const label = this.config.name ? `[${this.config.name}] ` : "";
371
+ line = `${color}${ts} ${level.toUpperCase()}${RESET} ${label}${message}`;
372
+ if (hasContext) line += ` ${JSON.stringify(context)}`;
373
+ }
374
+ const out = line + "\n";
375
+ if (typeof process !== "undefined" && process.stderr) {
376
+ if (level === "error" || level === "fatal") {
377
+ process.stderr.write(out);
378
+ } else {
379
+ process.stdout?.write(out);
444
380
  }
445
- return parts.join(" | ");
446
- }
447
- const levelColors = {
448
- debug: "\x1B[36m",
449
- // Cyan
450
- info: "\x1B[32m",
451
- // Green
452
- warn: "\x1B[33m",
453
- // Yellow
454
- error: "\x1B[31m",
455
- // Red
456
- fatal: "\x1B[35m",
457
- // Magenta
458
- silent: ""
459
- };
460
- const reset = "\x1B[0m";
461
- const color = levelColors[level] || "";
462
- let output = `${color}[${level.toUpperCase()}]${reset} ${message}`;
463
- if (context && Object.keys(context).length > 0) {
464
- output += ` ${JSON.stringify(context, null, 2)}`;
381
+ } else if (typeof console !== "undefined") {
382
+ const fn = level === "error" || level === "fatal" ? console.error : level === "warn" ? console.warn : level === "debug" ? console.debug : console.log;
383
+ fn(line);
384
+ }
385
+ if (this.fileStream) {
386
+ this.fileStream.write(out);
465
387
  }
466
- return output;
467
- }
468
- /**
469
- * Log using browser console
470
- */
471
- logBrowser(level, message, context, error) {
472
- const redactedContext = context ? this.redactSensitive(context) : void 0;
473
- const mergedContext = error ? { ...redactedContext, error: { message: error.message, stack: error.stack } } : redactedContext;
474
- const formatted = this.formatBrowserLog(level, message, mergedContext);
475
- const consoleMethod = level === "debug" ? "debug" : level === "info" ? "log" : level === "warn" ? "warn" : level === "error" || level === "fatal" ? "error" : "log";
476
- console[consoleMethod](formatted);
477
388
  }
478
- /**
479
- * Public logging methods
480
- */
481
389
  debug(message, meta) {
482
- if (this.isNode && this.pinoLogger) {
483
- this.pinoLogger.debug(meta || {}, message);
484
- } else {
485
- this.logBrowser("debug", message, meta);
486
- }
390
+ this.write("debug", message, meta);
487
391
  }
488
392
  info(message, meta) {
489
- if (this.isNode && this.pinoLogger) {
490
- this.pinoLogger.info(meta || {}, message);
491
- } else {
492
- this.logBrowser("info", message, meta);
493
- }
393
+ this.write("info", message, meta);
494
394
  }
495
395
  warn(message, meta) {
496
- if (this.isNode && this.pinoLogger) {
497
- this.pinoLogger.warn(meta || {}, message);
498
- } else {
499
- this.logBrowser("warn", message, meta);
500
- }
396
+ this.write("warn", message, meta);
501
397
  }
502
398
  error(message, errorOrMeta, meta) {
503
- let error;
504
- let context = {};
505
399
  if (errorOrMeta instanceof Error) {
506
- error = errorOrMeta;
507
- context = meta || {};
508
- } else {
509
- context = errorOrMeta || {};
510
- }
511
- if (this.isNode && this.pinoLogger) {
512
- const errorContext = error ? { err: error, ...context } : context;
513
- this.pinoLogger.error(errorContext, message);
400
+ this.write("error", message, meta, errorOrMeta);
514
401
  } else {
515
- this.logBrowser("error", message, context, error);
402
+ this.write("error", message, errorOrMeta);
516
403
  }
517
404
  }
518
405
  fatal(message, errorOrMeta, meta) {
519
- let error;
520
- let context = {};
521
406
  if (errorOrMeta instanceof Error) {
522
- error = errorOrMeta;
523
- context = meta || {};
524
- } else {
525
- context = errorOrMeta || {};
526
- }
527
- if (this.isNode && this.pinoLogger) {
528
- const errorContext = error ? { err: error, ...context } : context;
529
- this.pinoLogger.fatal(errorContext, message);
407
+ this.write("fatal", message, meta, errorOrMeta);
530
408
  } else {
531
- this.logBrowser("fatal", message, context, error);
409
+ this.write("fatal", message, errorOrMeta);
532
410
  }
533
411
  }
534
- /**
535
- * Create a child logger with additional context
536
- * Note: Child loggers share the parent's Pino instance
537
- */
412
+ log(message, ...args) {
413
+ this.info(message, args.length > 0 ? { args } : void 0);
414
+ }
538
415
  child(context) {
539
- const childLogger = new _ObjectLogger(this.config);
540
- if (this.isNode && this.pinoInstance) {
541
- childLogger.pinoLogger = this.pinoInstance.child(context);
542
- childLogger.pinoInstance = this.pinoInstance;
543
- }
544
- return childLogger;
416
+ const child = new _ObjectLogger(this.config, { ...this.bindings, ...context });
417
+ child.fileStream = this.fileStream;
418
+ return child;
545
419
  }
546
- /**
547
- * Set trace context for distributed tracing
548
- */
549
420
  withTrace(traceId, spanId) {
550
421
  return this.child({ traceId, spanId });
551
422
  }
552
- /**
553
- * Cleanup resources
554
- */
555
423
  async destroy() {
556
- if (this.pinoLogger && this.pinoLogger.flush) {
557
- await new Promise((resolve) => {
558
- this.pinoLogger.flush(() => resolve());
559
- });
424
+ if (this.fileStream) {
425
+ await new Promise((resolve) => this.fileStream.end(resolve));
426
+ this.fileStream = void 0;
560
427
  }
561
428
  }
562
- /**
563
- * Compatibility method for console.log usage
564
- */
565
- log(message, ...args) {
566
- this.info(message, args.length > 0 ? { args } : void 0);
567
- }
568
429
  };
569
430
  function createLogger(config) {
570
431
  return new ObjectLogger(config);
@@ -985,13 +846,13 @@ var PluginLoader = class {
985
846
  const scope = this.scopedServices.get(scopeId);
986
847
  let instance = scope.get(registration.name);
987
848
  if (!instance) {
988
- instance = await this.createServiceInstance(registration);
849
+ instance = await this.createServiceInstance(registration, scopeId);
989
850
  scope.set(registration.name, instance);
990
851
  this.logger.debug(`Scoped service created: ${registration.name} (scope: ${scopeId})`);
991
852
  }
992
853
  return instance;
993
854
  }
994
- async createServiceInstance(registration) {
855
+ async createServiceInstance(registration, scopeId) {
995
856
  if (!this.context) {
996
857
  throw new Error(`[PluginLoader] Context not set - cannot create service '${registration.name}'`);
997
858
  }
@@ -1000,13 +861,39 @@ var PluginLoader = class {
1000
861
  }
1001
862
  this.creating.add(registration.name);
1002
863
  try {
1003
- return await registration.factory(this.context);
864
+ return await registration.factory(this.context, scopeId);
1004
865
  } finally {
1005
866
  this.creating.delete(registration.name);
1006
867
  }
1007
868
  }
1008
869
  };
1009
870
 
871
+ // src/utils/env.ts
872
+ var isNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
873
+ function getEnv(key, defaultValue) {
874
+ if (typeof process !== "undefined" && process.env) {
875
+ return process.env[key] || defaultValue;
876
+ }
877
+ try {
878
+ if (typeof globalThis !== "undefined" && globalThis.process?.env) {
879
+ return globalThis.process.env[key] || defaultValue;
880
+ }
881
+ } catch (e) {
882
+ }
883
+ return defaultValue;
884
+ }
885
+ function safeExit(code = 0) {
886
+ if (isNode) {
887
+ process.exit(code);
888
+ }
889
+ }
890
+ function getMemoryUsage() {
891
+ if (isNode) {
892
+ return process.memoryUsage();
893
+ }
894
+ return { heapUsed: 0, heapTotal: 0 };
895
+ }
896
+
1010
897
  // src/fallbacks/memory-cache.ts
1011
898
  function createMemoryCache() {
1012
899
  const store = /* @__PURE__ */ new Map();
@@ -1097,6 +984,22 @@ function createMemoryJob() {
1097
984
  }
1098
985
 
1099
986
  // src/fallbacks/memory-i18n.ts
987
+ function deepMerge(target, source) {
988
+ const result = { ...target };
989
+ for (const key of Object.keys(source)) {
990
+ const tVal = target[key];
991
+ const sVal = source[key];
992
+ if (tVal && sVal && typeof tVal === "object" && !Array.isArray(tVal) && typeof sVal === "object" && !Array.isArray(sVal)) {
993
+ result[key] = deepMerge(
994
+ tVal,
995
+ sVal
996
+ );
997
+ } else {
998
+ result[key] = sVal;
999
+ }
1000
+ }
1001
+ return result;
1002
+ }
1100
1003
  function resolveLocale(requestedLocale, availableLocales) {
1101
1004
  if (availableLocales.length === 0) return void 0;
1102
1005
  if (availableLocales.includes(requestedLocale)) return requestedLocale;
@@ -1142,8 +1045,12 @@ function createMemoryI18n() {
1142
1045
  return resolveTranslations(locale) ?? {};
1143
1046
  },
1144
1047
  loadTranslations(locale, data) {
1145
- const existing = translations.get(locale) ?? {};
1146
- translations.set(locale, { ...existing, ...data });
1048
+ const existing = translations.get(locale);
1049
+ if (existing) {
1050
+ translations.set(locale, deepMerge(existing, data));
1051
+ } else {
1052
+ translations.set(locale, { ...data });
1053
+ }
1147
1054
  },
1148
1055
  getLocales() {
1149
1056
  return [...translations.keys()];
@@ -1232,6 +1139,9 @@ var ObjectKernel = class {
1232
1139
  registerService: (name, service) => {
1233
1140
  this.registerService(name, service);
1234
1141
  },
1142
+ registerServiceFactory: (name, factory, lifecycle, dependencies) => {
1143
+ this.registerServiceFactory(name, factory, lifecycle, dependencies);
1144
+ },
1235
1145
  getService: (name) => {
1236
1146
  const service = this.services.get(name);
1237
1147
  if (service) {
@@ -1285,6 +1195,9 @@ var ObjectKernel = class {
1285
1195
  getServices: () => {
1286
1196
  return new Map(this.services);
1287
1197
  },
1198
+ getServiceScoped: (name, scopeId) => {
1199
+ return this.pluginLoader.getService(name, scopeId);
1200
+ },
1288
1201
  logger: this.logger,
1289
1202
  getKernel: () => this
1290
1203
  // Type compatibility
@@ -1426,6 +1339,7 @@ var ObjectKernel = class {
1426
1339
  const result = await this.startPluginWithTimeout(plugin);
1427
1340
  if (!result.success) {
1428
1341
  this.logger.error(`Plugin startup failed: ${plugin.name}`, result.error);
1342
+ console.error(`[Kernel] Plugin startup failed: ${plugin.name}`, result.error instanceof Error ? result.error.message : result.error, result.error instanceof Error ? result.error.stack : "");
1429
1343
  if (this.config.rollbackOnFailure) {
1430
1344
  this.logger.warn("Rolling back started plugins...");
1431
1345
  await this.rollbackStartedPlugins();
@@ -1436,6 +1350,8 @@ var ObjectKernel = class {
1436
1350
  this.validateSystemRequirements();
1437
1351
  this.logger.debug("Triggering kernel:ready hook");
1438
1352
  await this.context.trigger("kernel:ready");
1353
+ this.logger.debug("Triggering kernel:listening hook");
1354
+ await this.context.trigger("kernel:listening");
1439
1355
  this.logger.info("\u2705 Bootstrap complete");
1440
1356
  } catch (error) {
1441
1357
  this.state = "stopped";
@@ -1458,17 +1374,19 @@ var ObjectKernel = class {
1458
1374
  try {
1459
1375
  const shutdownPromise = this.performShutdown();
1460
1376
  const timeoutPromise = new Promise((_, reject) => {
1461
- setTimeout(() => {
1377
+ const t = setTimeout(() => {
1462
1378
  reject(new Error("Shutdown timeout exceeded"));
1463
1379
  }, this.config.shutdownTimeout);
1380
+ if (t.unref) t.unref();
1464
1381
  });
1465
1382
  await Promise.race([shutdownPromise, timeoutPromise]);
1466
1383
  this.state = "stopped";
1467
1384
  this.logger.info("\u2705 Graceful shutdown complete");
1468
1385
  } catch (error) {
1469
- this.logger.error("Shutdown error - forcing stop", error);
1386
+ this.logger.error("Shutdown timed out \u2014 forcing exit", error);
1470
1387
  this.state = "stopped";
1471
- throw error;
1388
+ await this.logger.destroy();
1389
+ process.exit(1);
1472
1390
  } finally {
1473
1391
  await this.logger.destroy();
1474
1392
  }
@@ -1508,6 +1426,13 @@ var ObjectKernel = class {
1508
1426
  async getServiceAsync(name, scopeId) {
1509
1427
  return await this.pluginLoader.getService(name, scopeId);
1510
1428
  }
1429
+ /**
1430
+ * Clear all scoped service instances for a given scope (e.g., projectId).
1431
+ * Releases driver connections and metadata caches for idle projects.
1432
+ */
1433
+ clearScope(scopeId) {
1434
+ this.pluginLoader.clearScope(scopeId);
1435
+ }
1511
1436
  /**
1512
1437
  * Check if kernel is running
1513
1438
  */
@@ -1708,6 +1633,7 @@ var LiteKernel = class extends ObjectKernelBase {
1708
1633
  await this.runPluginStart(plugin);
1709
1634
  }
1710
1635
  await this.triggerHook("kernel:ready");
1636
+ await this.triggerHook("kernel:listening");
1711
1637
  this.logger.info("\u2705 Bootstrap complete", {
1712
1638
  pluginCount: this.plugins.size
1713
1639
  });
@@ -3087,6 +3013,12 @@ var SecurePluginContext = class {
3087
3013
  getKernel() {
3088
3014
  return this.baseContext.getKernel();
3089
3015
  }
3016
+ registerServiceFactory(name, factory, lifecycle, dependencies) {
3017
+ this.baseContext.registerServiceFactory(name, factory, lifecycle, dependencies);
3018
+ }
3019
+ getServiceScoped(name, scopeId) {
3020
+ return this.baseContext.getServiceScoped(name, scopeId);
3021
+ }
3090
3022
  };
3091
3023
  function createPluginPermissionEnforcer(logger) {
3092
3024
  return new PluginPermissionEnforcer(logger);
@@ -4756,272 +4688,6 @@ var NamespaceResolver = class {
4756
4688
  return `${shortName}_${ns}`;
4757
4689
  }
4758
4690
  };
4759
-
4760
- // src/package-manager.ts
4761
- var PackageManager = class {
4762
- constructor(logger, options = {}) {
4763
- this.packages = /* @__PURE__ */ new Map();
4764
- this.snapshots = /* @__PURE__ */ new Map();
4765
- this.logger = logger.child({ component: "PackageManager" });
4766
- this.dependencyResolver = new DependencyResolver(logger);
4767
- this.namespaceResolver = new NamespaceResolver(logger);
4768
- this.platformVersion = options.platformVersion || "3.0.0";
4769
- }
4770
- /**
4771
- * Install a package with full dependency resolution and namespace checking.
4772
- */
4773
- async install(packageId, version, manifest) {
4774
- this.logger.info("Installing package", { packageId, version });
4775
- if (this.packages.has(packageId)) {
4776
- const existing = this.packages.get(packageId);
4777
- if (existing.status === "installed") {
4778
- return {
4779
- success: false,
4780
- packageId,
4781
- version,
4782
- installedDependencies: [],
4783
- namespaceConflicts: [],
4784
- errorMessage: `Package ${packageId}@${existing.version} is already installed. Use upgrade instead.`
4785
- };
4786
- }
4787
- }
4788
- const engine = manifest.engine?.objectstack;
4789
- if (engine) {
4790
- const platformSemver = SemanticVersionManager.parse(this.platformVersion);
4791
- if (!SemanticVersionManager.satisfies(platformSemver, engine)) {
4792
- return {
4793
- success: false,
4794
- packageId,
4795
- version,
4796
- installedDependencies: [],
4797
- namespaceConflicts: [],
4798
- errorMessage: `Package requires platform ${engine}, but current platform is v${this.platformVersion}`
4799
- };
4800
- }
4801
- }
4802
- const namespaces = this.namespaceResolver.extractNamespaces(manifest);
4803
- const nsCheck = this.namespaceResolver.checkAvailability(packageId, namespaces);
4804
- if (!nsCheck.available) {
4805
- return {
4806
- success: false,
4807
- packageId,
4808
- version,
4809
- installedDependencies: [],
4810
- namespaceConflicts: nsCheck.conflicts.map((c) => ({
4811
- namespace: c.namespace,
4812
- existingPackageId: c.existingPackageId
4813
- })),
4814
- errorMessage: `Namespace conflicts detected: ${nsCheck.conflicts.map((c) => c.namespace).join(", ")}`
4815
- };
4816
- }
4817
- const deps = manifest.dependencies;
4818
- const depNames = deps ? Object.keys(deps) : [];
4819
- const missingDeps = depNames.filter((d) => !this.packages.has(d));
4820
- if (missingDeps.length > 0) {
4821
- return {
4822
- success: false,
4823
- packageId,
4824
- version,
4825
- installedDependencies: [],
4826
- namespaceConflicts: [],
4827
- errorMessage: `Missing dependencies: ${missingDeps.join(", ")}`
4828
- };
4829
- }
4830
- this.packages.set(packageId, {
4831
- packageId,
4832
- version,
4833
- manifest,
4834
- installedAt: (/* @__PURE__ */ new Date()).toISOString(),
4835
- status: "installed",
4836
- namespaces,
4837
- dependencies: depNames
4838
- });
4839
- this.namespaceResolver.register(packageId, namespaces);
4840
- this.logger.info("Package installed", { packageId, version, namespaces: namespaces.length });
4841
- return {
4842
- success: true,
4843
- packageId,
4844
- version,
4845
- installedDependencies: depNames,
4846
- namespaceConflicts: []
4847
- };
4848
- }
4849
- /**
4850
- * Uninstall a package, checking for dependents first.
4851
- */
4852
- async uninstall(packageId) {
4853
- const pkg = this.packages.get(packageId);
4854
- if (!pkg) {
4855
- return { success: false, errorMessage: `Package ${packageId} is not installed` };
4856
- }
4857
- const dependents = [];
4858
- for (const [id, record] of this.packages) {
4859
- if (id !== packageId && record.dependencies.includes(packageId)) {
4860
- dependents.push(id);
4861
- }
4862
- }
4863
- if (dependents.length > 0) {
4864
- return {
4865
- success: false,
4866
- errorMessage: `Cannot uninstall ${packageId}: depended upon by ${dependents.join(", ")}`
4867
- };
4868
- }
4869
- this.namespaceResolver.unregister(packageId);
4870
- this.packages.delete(packageId);
4871
- this.snapshots.delete(packageId);
4872
- this.logger.info("Package uninstalled", { packageId });
4873
- return { success: true };
4874
- }
4875
- /**
4876
- * Upgrade a package: snapshot → update → register.
4877
- */
4878
- async upgrade(packageId, newVersion, newManifest) {
4879
- const existing = this.packages.get(packageId);
4880
- if (!existing) {
4881
- return {
4882
- success: false,
4883
- packageId,
4884
- fromVersion: "",
4885
- toVersion: newVersion,
4886
- snapshot: { packageId, previousVersion: "", previousManifest: {}, previousNamespaces: [], installedAt: "", createdAt: (/* @__PURE__ */ new Date()).toISOString() },
4887
- errorMessage: `Package ${packageId} is not installed`
4888
- };
4889
- }
4890
- const snapshot = {
4891
- packageId,
4892
- previousVersion: existing.version,
4893
- previousManifest: existing.manifest,
4894
- previousNamespaces: [...existing.namespaces],
4895
- installedAt: existing.installedAt,
4896
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
4897
- };
4898
- this.snapshots.set(packageId, snapshot);
4899
- const engine = newManifest.engine?.objectstack;
4900
- if (engine) {
4901
- const platformSemver = SemanticVersionManager.parse(this.platformVersion);
4902
- if (!SemanticVersionManager.satisfies(platformSemver, engine)) {
4903
- return {
4904
- success: false,
4905
- packageId,
4906
- fromVersion: existing.version,
4907
- toVersion: newVersion,
4908
- snapshot,
4909
- errorMessage: `New version requires platform ${engine}, current is v${this.platformVersion}`
4910
- };
4911
- }
4912
- }
4913
- const newNamespaces = this.namespaceResolver.extractNamespaces(newManifest);
4914
- this.namespaceResolver.unregister(packageId);
4915
- const nsCheck = this.namespaceResolver.checkAvailability(packageId, newNamespaces);
4916
- if (!nsCheck.available) {
4917
- this.namespaceResolver.register(packageId, existing.namespaces);
4918
- return {
4919
- success: false,
4920
- packageId,
4921
- fromVersion: existing.version,
4922
- toVersion: newVersion,
4923
- snapshot,
4924
- errorMessage: `Namespace conflicts in new version: ${nsCheck.conflicts.map((c) => c.namespace).join(", ")}`
4925
- };
4926
- }
4927
- this.namespaceResolver.register(packageId, newNamespaces);
4928
- const deps = newManifest.dependencies;
4929
- this.packages.set(packageId, {
4930
- packageId,
4931
- version: newVersion,
4932
- manifest: newManifest,
4933
- installedAt: existing.installedAt,
4934
- status: "installed",
4935
- namespaces: newNamespaces,
4936
- dependencies: deps ? Object.keys(deps) : []
4937
- });
4938
- this.logger.info("Package upgraded", { packageId, from: existing.version, to: newVersion });
4939
- return {
4940
- success: true,
4941
- packageId,
4942
- fromVersion: existing.version,
4943
- toVersion: newVersion,
4944
- snapshot
4945
- };
4946
- }
4947
- /**
4948
- * Rollback a package to its pre-upgrade snapshot.
4949
- */
4950
- async rollback(packageId) {
4951
- const snapshot = this.snapshots.get(packageId);
4952
- if (!snapshot) {
4953
- return {
4954
- success: false,
4955
- packageId,
4956
- restoredVersion: "",
4957
- errorMessage: `No upgrade snapshot found for ${packageId}`
4958
- };
4959
- }
4960
- this.namespaceResolver.unregister(packageId);
4961
- this.namespaceResolver.register(packageId, snapshot.previousNamespaces);
4962
- const deps = snapshot.previousManifest.dependencies;
4963
- this.packages.set(packageId, {
4964
- packageId,
4965
- version: snapshot.previousVersion,
4966
- manifest: snapshot.previousManifest,
4967
- installedAt: snapshot.installedAt,
4968
- status: "installed",
4969
- namespaces: snapshot.previousNamespaces,
4970
- dependencies: deps ? Object.keys(deps) : []
4971
- });
4972
- this.snapshots.delete(packageId);
4973
- this.logger.info("Package rolled back", { packageId, to: snapshot.previousVersion });
4974
- return {
4975
- success: true,
4976
- packageId,
4977
- restoredVersion: snapshot.previousVersion
4978
- };
4979
- }
4980
- /**
4981
- * Get an installed package record.
4982
- */
4983
- getPackage(packageId) {
4984
- return this.packages.get(packageId);
4985
- }
4986
- /**
4987
- * List all installed packages.
4988
- */
4989
- listPackages() {
4990
- return Array.from(this.packages.values());
4991
- }
4992
- /**
4993
- * Resolve dependencies for a set of packages.
4994
- */
4995
- resolveDependencies(packages) {
4996
- return this.dependencyResolver.resolve(packages);
4997
- }
4998
- /**
4999
- * Check namespace availability for a package's metadata.
5000
- */
5001
- checkNamespaces(packageId, config) {
5002
- const namespaces = this.namespaceResolver.extractNamespaces(config);
5003
- const result = this.namespaceResolver.checkAvailability(packageId, namespaces);
5004
- return {
5005
- available: result.available,
5006
- conflicts: result.conflicts.map((c) => ({
5007
- namespace: c.namespace,
5008
- existingPackageId: c.existingPackageId
5009
- }))
5010
- };
5011
- }
5012
- /**
5013
- * Get the namespace resolver instance.
5014
- */
5015
- getNamespaceResolver() {
5016
- return this.namespaceResolver;
5017
- }
5018
- /**
5019
- * Get a snapshot for a given package (if available).
5020
- */
5021
- getSnapshot(packageId) {
5022
- return this.snapshots.get(packageId);
5023
- }
5024
- };
5025
4691
  // Annotate the CommonJS export names for ESM import in node:
5026
4692
  0 && (module.exports = {
5027
4693
  ApiRegistry,
@@ -5033,7 +4699,6 @@ var PackageManager = class {
5033
4699
  ObjectKernel,
5034
4700
  ObjectKernelBase,
5035
4701
  ObjectLogger,
5036
- PackageManager,
5037
4702
  PluginConfigValidator,
5038
4703
  PluginHealthMonitor,
5039
4704
  PluginLoader,