@objectstack/core 4.0.4 → 4.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 (75) hide show
  1. package/README.md +95 -10
  2. package/dist/index.cjs +169 -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 +175 -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.js CHANGED
@@ -1,4 +1,10 @@
1
1
  var __defProp = Object.defineProperty;
2
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
+ }) : x)(function(x) {
5
+ if (typeof require !== "undefined") return require.apply(this, arguments);
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
+ });
2
8
  var __export = (target, all) => {
3
9
  for (var name in all)
4
10
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -95,7 +101,13 @@ var ObjectKernelBase = class {
95
101
  }
96
102
  },
97
103
  logger: this.logger,
98
- getKernel: () => this
104
+ getKernel: () => this,
105
+ registerServiceFactory: (_name, _factory, _lifecycle, _dependencies) => {
106
+ throw new Error("[KernelBase] registerServiceFactory not supported \u2014 use ObjectKernel");
107
+ },
108
+ getServiceScoped: async (_name, _scopeId) => {
109
+ throw new Error("[KernelBase] getServiceScoped not supported \u2014 use ObjectKernel");
110
+ }
99
111
  };
100
112
  }
101
113
  /**
@@ -214,144 +226,58 @@ var ObjectKernelBase = class {
214
226
  }
215
227
  };
216
228
 
217
- // src/utils/env.ts
218
- var isNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
219
- function getEnv(key, defaultValue) {
220
- if (typeof process !== "undefined" && process.env) {
221
- return process.env[key] || defaultValue;
222
- }
223
- try {
224
- if (typeof globalThis !== "undefined" && globalThis.process?.env) {
225
- return globalThis.process.env[key] || defaultValue;
226
- }
227
- } catch (e) {
228
- }
229
- return defaultValue;
230
- }
231
- function safeExit(code = 0) {
232
- if (isNode) {
233
- process.exit(code);
234
- }
235
- }
236
- function getMemoryUsage() {
237
- if (isNode) {
238
- return process.memoryUsage();
239
- }
240
- return { heapUsed: 0, heapTotal: 0 };
241
- }
242
-
243
229
  // src/logger.ts
230
+ var LEVEL_ORDER = {
231
+ debug: 0,
232
+ info: 1,
233
+ warn: 2,
234
+ error: 3,
235
+ fatal: 4,
236
+ silent: 5
237
+ };
238
+ var LEVEL_COLORS = {
239
+ debug: "\x1B[36m",
240
+ info: "\x1B[32m",
241
+ warn: "\x1B[33m",
242
+ error: "\x1B[31m",
243
+ fatal: "\x1B[35m",
244
+ silent: ""
245
+ };
246
+ var RESET = "\x1B[0m";
244
247
  var ObjectLogger = class _ObjectLogger {
245
- // CommonJS require function for Node.js
246
- constructor(config = {}) {
247
- this.isNode = isNode;
248
+ constructor(config = {}, bindings = {}) {
248
249
  this.config = {
249
250
  name: config.name,
250
251
  level: config.level ?? "info",
251
- format: config.format ?? (this.isNode ? "json" : "pretty"),
252
+ format: config.format ?? "pretty",
252
253
  redact: config.redact ?? ["password", "token", "secret", "key"],
253
254
  sourceLocation: config.sourceLocation ?? false,
254
255
  file: config.file,
255
- rotation: config.rotation ?? {
256
- maxSize: "10m",
257
- maxFiles: 5
258
- }
256
+ rotation: config.rotation ?? { maxSize: "10m", maxFiles: 5 }
259
257
  };
260
- if (this.isNode) {
261
- this.initPinoLogger();
258
+ this.bindings = bindings;
259
+ if (this.config.file && typeof process !== "undefined") {
260
+ this.openFileStream(this.config.file);
262
261
  }
263
262
  }
264
- /**
265
- * Initialize Pino logger for Node.js
266
- */
267
- async initPinoLogger() {
268
- if (!this.isNode) return;
263
+ openFileStream(path) {
269
264
  try {
270
- const { createRequire } = await import("module");
271
- this.require = createRequire(import.meta.url);
272
- const pino = this.require("pino");
273
- const pinoOptions = {
274
- level: this.config.level,
275
- redact: {
276
- paths: this.config.redact,
277
- censor: "***REDACTED***"
278
- }
279
- };
280
- if (this.config.name) {
281
- pinoOptions.name = this.config.name;
282
- }
283
- const targets = [];
284
- if (this.config.format === "pretty") {
285
- let hasPretty = false;
286
- try {
287
- this.require.resolve("pino-pretty");
288
- hasPretty = true;
289
- } catch (e) {
290
- }
291
- if (hasPretty) {
292
- targets.push({
293
- target: "pino-pretty",
294
- options: {
295
- colorize: true,
296
- translateTime: "SYS:standard",
297
- ignore: "pid,hostname"
298
- },
299
- level: this.config.level
300
- });
301
- } else {
302
- console.warn("[Logger] pino-pretty not found. Install it for pretty logging: pnpm add -D pino-pretty");
303
- targets.push({
304
- target: "pino/file",
305
- options: { destination: 1 },
306
- level: this.config.level
307
- });
308
- }
309
- } else if (this.config.format === "json") {
310
- targets.push({
311
- target: "pino/file",
312
- options: { destination: 1 },
313
- // stdout
314
- level: this.config.level
315
- });
316
- } else {
317
- targets.push({
318
- target: "pino/file",
319
- options: { destination: 1 },
320
- level: this.config.level
321
- });
322
- }
323
- if (this.config.file) {
324
- targets.push({
325
- target: "pino/file",
326
- options: {
327
- destination: this.config.file,
328
- mkdir: true
329
- },
330
- level: this.config.level
331
- });
332
- }
333
- if (targets.length > 0) {
334
- pinoOptions.transport = targets.length === 1 ? targets[0] : { targets };
335
- }
336
- this.pinoInstance = pino(pinoOptions);
337
- this.pinoLogger = this.pinoInstance;
338
- } catch (error) {
339
- console.warn("[Logger] Pino not available, falling back to console:", error);
340
- this.pinoLogger = null;
265
+ const fs = __require("fs");
266
+ const dir = __require("path").dirname(path);
267
+ fs.mkdirSync(dir, { recursive: true });
268
+ this.fileStream = fs.createWriteStream(path, { flags: "a" });
269
+ } catch {
341
270
  }
342
271
  }
343
- /**
344
- * Redact sensitive keys from context object (for browser)
345
- */
272
+ isEnabled(level) {
273
+ return LEVEL_ORDER[level] >= LEVEL_ORDER[this.config.level];
274
+ }
346
275
  redactSensitive(obj) {
347
276
  if (!obj || typeof obj !== "object") return obj;
348
277
  const redacted = Array.isArray(obj) ? [...obj] : { ...obj };
349
278
  for (const key in redacted) {
350
- const lowerKey = key.toLowerCase();
351
- const shouldRedact = this.config.redact.some(
352
- (pattern) => lowerKey.includes(pattern.toLowerCase())
353
- );
354
- if (shouldRedact) {
279
+ const lower = key.toLowerCase();
280
+ if (this.config.redact.some((p) => lower.includes(p.toLowerCase()))) {
355
281
  redacted[key] = "***REDACTED***";
356
282
  } else if (typeof redacted[key] === "object" && redacted[key] !== null) {
357
283
  redacted[key] = this.redactSensitive(redacted[key]);
@@ -359,146 +285,89 @@ var ObjectLogger = class _ObjectLogger {
359
285
  }
360
286
  return redacted;
361
287
  }
362
- /**
363
- * Format log entry for browser
364
- */
365
- formatBrowserLog(level, message, context) {
288
+ write(level, message, meta, error) {
289
+ if (!this.isEnabled(level)) return;
290
+ const context = this.redactSensitive({
291
+ ...this.bindings,
292
+ ...meta,
293
+ ...error ? { error: { message: error.message, stack: error.stack } } : {}
294
+ });
295
+ const hasContext = Object.keys(context).length > 0;
296
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
297
+ let line;
366
298
  if (this.config.format === "json") {
367
- return JSON.stringify({
368
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
299
+ line = JSON.stringify({
300
+ time: ts,
369
301
  level,
370
- message,
302
+ ...this.config.name ? { name: this.config.name } : {},
303
+ msg: message,
371
304
  ...context
372
305
  });
373
- }
374
- if (this.config.format === "text") {
375
- const parts = [(/* @__PURE__ */ new Date()).toISOString(), level.toUpperCase(), message];
376
- if (context && Object.keys(context).length > 0) {
377
- parts.push(JSON.stringify(context));
306
+ } else if (this.config.format === "text") {
307
+ const parts = [ts, level.toUpperCase(), message];
308
+ if (hasContext) parts.push(JSON.stringify(context));
309
+ line = parts.join(" | ");
310
+ } else {
311
+ const color = LEVEL_COLORS[level] || "";
312
+ const label = this.config.name ? `[${this.config.name}] ` : "";
313
+ line = `${color}${ts} ${level.toUpperCase()}${RESET} ${label}${message}`;
314
+ if (hasContext) line += ` ${JSON.stringify(context)}`;
315
+ }
316
+ const out = line + "\n";
317
+ if (typeof process !== "undefined" && process.stderr) {
318
+ if (level === "error" || level === "fatal") {
319
+ process.stderr.write(out);
320
+ } else {
321
+ process.stdout?.write(out);
378
322
  }
379
- return parts.join(" | ");
380
- }
381
- const levelColors = {
382
- debug: "\x1B[36m",
383
- // Cyan
384
- info: "\x1B[32m",
385
- // Green
386
- warn: "\x1B[33m",
387
- // Yellow
388
- error: "\x1B[31m",
389
- // Red
390
- fatal: "\x1B[35m",
391
- // Magenta
392
- silent: ""
393
- };
394
- const reset = "\x1B[0m";
395
- const color = levelColors[level] || "";
396
- let output = `${color}[${level.toUpperCase()}]${reset} ${message}`;
397
- if (context && Object.keys(context).length > 0) {
398
- output += ` ${JSON.stringify(context, null, 2)}`;
323
+ } else if (typeof console !== "undefined") {
324
+ const fn = level === "error" || level === "fatal" ? console.error : level === "warn" ? console.warn : level === "debug" ? console.debug : console.log;
325
+ fn(line);
326
+ }
327
+ if (this.fileStream) {
328
+ this.fileStream.write(out);
399
329
  }
400
- return output;
401
- }
402
- /**
403
- * Log using browser console
404
- */
405
- logBrowser(level, message, context, error) {
406
- const redactedContext = context ? this.redactSensitive(context) : void 0;
407
- const mergedContext = error ? { ...redactedContext, error: { message: error.message, stack: error.stack } } : redactedContext;
408
- const formatted = this.formatBrowserLog(level, message, mergedContext);
409
- const consoleMethod = level === "debug" ? "debug" : level === "info" ? "log" : level === "warn" ? "warn" : level === "error" || level === "fatal" ? "error" : "log";
410
- console[consoleMethod](formatted);
411
330
  }
412
- /**
413
- * Public logging methods
414
- */
415
331
  debug(message, meta) {
416
- if (this.isNode && this.pinoLogger) {
417
- this.pinoLogger.debug(meta || {}, message);
418
- } else {
419
- this.logBrowser("debug", message, meta);
420
- }
332
+ this.write("debug", message, meta);
421
333
  }
422
334
  info(message, meta) {
423
- if (this.isNode && this.pinoLogger) {
424
- this.pinoLogger.info(meta || {}, message);
425
- } else {
426
- this.logBrowser("info", message, meta);
427
- }
335
+ this.write("info", message, meta);
428
336
  }
429
337
  warn(message, meta) {
430
- if (this.isNode && this.pinoLogger) {
431
- this.pinoLogger.warn(meta || {}, message);
432
- } else {
433
- this.logBrowser("warn", message, meta);
434
- }
338
+ this.write("warn", message, meta);
435
339
  }
436
340
  error(message, errorOrMeta, meta) {
437
- let error;
438
- let context = {};
439
341
  if (errorOrMeta instanceof Error) {
440
- error = errorOrMeta;
441
- context = meta || {};
442
- } else {
443
- context = errorOrMeta || {};
444
- }
445
- if (this.isNode && this.pinoLogger) {
446
- const errorContext = error ? { err: error, ...context } : context;
447
- this.pinoLogger.error(errorContext, message);
342
+ this.write("error", message, meta, errorOrMeta);
448
343
  } else {
449
- this.logBrowser("error", message, context, error);
344
+ this.write("error", message, errorOrMeta);
450
345
  }
451
346
  }
452
347
  fatal(message, errorOrMeta, meta) {
453
- let error;
454
- let context = {};
455
348
  if (errorOrMeta instanceof Error) {
456
- error = errorOrMeta;
457
- context = meta || {};
458
- } else {
459
- context = errorOrMeta || {};
460
- }
461
- if (this.isNode && this.pinoLogger) {
462
- const errorContext = error ? { err: error, ...context } : context;
463
- this.pinoLogger.fatal(errorContext, message);
349
+ this.write("fatal", message, meta, errorOrMeta);
464
350
  } else {
465
- this.logBrowser("fatal", message, context, error);
351
+ this.write("fatal", message, errorOrMeta);
466
352
  }
467
353
  }
468
- /**
469
- * Create a child logger with additional context
470
- * Note: Child loggers share the parent's Pino instance
471
- */
354
+ log(message, ...args) {
355
+ this.info(message, args.length > 0 ? { args } : void 0);
356
+ }
472
357
  child(context) {
473
- const childLogger = new _ObjectLogger(this.config);
474
- if (this.isNode && this.pinoInstance) {
475
- childLogger.pinoLogger = this.pinoInstance.child(context);
476
- childLogger.pinoInstance = this.pinoInstance;
477
- }
478
- return childLogger;
358
+ const child = new _ObjectLogger(this.config, { ...this.bindings, ...context });
359
+ child.fileStream = this.fileStream;
360
+ return child;
479
361
  }
480
- /**
481
- * Set trace context for distributed tracing
482
- */
483
362
  withTrace(traceId, spanId) {
484
363
  return this.child({ traceId, spanId });
485
364
  }
486
- /**
487
- * Cleanup resources
488
- */
489
365
  async destroy() {
490
- if (this.pinoLogger && this.pinoLogger.flush) {
491
- await new Promise((resolve) => {
492
- this.pinoLogger.flush(() => resolve());
493
- });
366
+ if (this.fileStream) {
367
+ await new Promise((resolve) => this.fileStream.end(resolve));
368
+ this.fileStream = void 0;
494
369
  }
495
370
  }
496
- /**
497
- * Compatibility method for console.log usage
498
- */
499
- log(message, ...args) {
500
- this.info(message, args.length > 0 ? { args } : void 0);
501
- }
502
371
  };
503
372
  function createLogger(config) {
504
373
  return new ObjectLogger(config);
@@ -919,13 +788,13 @@ var PluginLoader = class {
919
788
  const scope = this.scopedServices.get(scopeId);
920
789
  let instance = scope.get(registration.name);
921
790
  if (!instance) {
922
- instance = await this.createServiceInstance(registration);
791
+ instance = await this.createServiceInstance(registration, scopeId);
923
792
  scope.set(registration.name, instance);
924
793
  this.logger.debug(`Scoped service created: ${registration.name} (scope: ${scopeId})`);
925
794
  }
926
795
  return instance;
927
796
  }
928
- async createServiceInstance(registration) {
797
+ async createServiceInstance(registration, scopeId) {
929
798
  if (!this.context) {
930
799
  throw new Error(`[PluginLoader] Context not set - cannot create service '${registration.name}'`);
931
800
  }
@@ -934,13 +803,39 @@ var PluginLoader = class {
934
803
  }
935
804
  this.creating.add(registration.name);
936
805
  try {
937
- return await registration.factory(this.context);
806
+ return await registration.factory(this.context, scopeId);
938
807
  } finally {
939
808
  this.creating.delete(registration.name);
940
809
  }
941
810
  }
942
811
  };
943
812
 
813
+ // src/utils/env.ts
814
+ var isNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
815
+ function getEnv(key, defaultValue) {
816
+ if (typeof process !== "undefined" && process.env) {
817
+ return process.env[key] || defaultValue;
818
+ }
819
+ try {
820
+ if (typeof globalThis !== "undefined" && globalThis.process?.env) {
821
+ return globalThis.process.env[key] || defaultValue;
822
+ }
823
+ } catch (e) {
824
+ }
825
+ return defaultValue;
826
+ }
827
+ function safeExit(code = 0) {
828
+ if (isNode) {
829
+ process.exit(code);
830
+ }
831
+ }
832
+ function getMemoryUsage() {
833
+ if (isNode) {
834
+ return process.memoryUsage();
835
+ }
836
+ return { heapUsed: 0, heapTotal: 0 };
837
+ }
838
+
944
839
  // src/fallbacks/memory-cache.ts
945
840
  function createMemoryCache() {
946
841
  const store = /* @__PURE__ */ new Map();
@@ -1031,6 +926,22 @@ function createMemoryJob() {
1031
926
  }
1032
927
 
1033
928
  // src/fallbacks/memory-i18n.ts
929
+ function deepMerge(target, source) {
930
+ const result = { ...target };
931
+ for (const key of Object.keys(source)) {
932
+ const tVal = target[key];
933
+ const sVal = source[key];
934
+ if (tVal && sVal && typeof tVal === "object" && !Array.isArray(tVal) && typeof sVal === "object" && !Array.isArray(sVal)) {
935
+ result[key] = deepMerge(
936
+ tVal,
937
+ sVal
938
+ );
939
+ } else {
940
+ result[key] = sVal;
941
+ }
942
+ }
943
+ return result;
944
+ }
1034
945
  function resolveLocale(requestedLocale, availableLocales) {
1035
946
  if (availableLocales.length === 0) return void 0;
1036
947
  if (availableLocales.includes(requestedLocale)) return requestedLocale;
@@ -1076,8 +987,12 @@ function createMemoryI18n() {
1076
987
  return resolveTranslations(locale) ?? {};
1077
988
  },
1078
989
  loadTranslations(locale, data) {
1079
- const existing = translations.get(locale) ?? {};
1080
- translations.set(locale, { ...existing, ...data });
990
+ const existing = translations.get(locale);
991
+ if (existing) {
992
+ translations.set(locale, deepMerge(existing, data));
993
+ } else {
994
+ translations.set(locale, { ...data });
995
+ }
1081
996
  },
1082
997
  getLocales() {
1083
998
  return [...translations.keys()];
@@ -1166,6 +1081,9 @@ var ObjectKernel = class {
1166
1081
  registerService: (name, service) => {
1167
1082
  this.registerService(name, service);
1168
1083
  },
1084
+ registerServiceFactory: (name, factory, lifecycle, dependencies) => {
1085
+ this.registerServiceFactory(name, factory, lifecycle, dependencies);
1086
+ },
1169
1087
  getService: (name) => {
1170
1088
  const service = this.services.get(name);
1171
1089
  if (service) {
@@ -1219,6 +1137,9 @@ var ObjectKernel = class {
1219
1137
  getServices: () => {
1220
1138
  return new Map(this.services);
1221
1139
  },
1140
+ getServiceScoped: (name, scopeId) => {
1141
+ return this.pluginLoader.getService(name, scopeId);
1142
+ },
1222
1143
  logger: this.logger,
1223
1144
  getKernel: () => this
1224
1145
  // Type compatibility
@@ -1360,6 +1281,7 @@ var ObjectKernel = class {
1360
1281
  const result = await this.startPluginWithTimeout(plugin);
1361
1282
  if (!result.success) {
1362
1283
  this.logger.error(`Plugin startup failed: ${plugin.name}`, result.error);
1284
+ console.error(`[Kernel] Plugin startup failed: ${plugin.name}`, result.error instanceof Error ? result.error.message : result.error, result.error instanceof Error ? result.error.stack : "");
1363
1285
  if (this.config.rollbackOnFailure) {
1364
1286
  this.logger.warn("Rolling back started plugins...");
1365
1287
  await this.rollbackStartedPlugins();
@@ -1392,17 +1314,19 @@ var ObjectKernel = class {
1392
1314
  try {
1393
1315
  const shutdownPromise = this.performShutdown();
1394
1316
  const timeoutPromise = new Promise((_, reject) => {
1395
- setTimeout(() => {
1317
+ const t = setTimeout(() => {
1396
1318
  reject(new Error("Shutdown timeout exceeded"));
1397
1319
  }, this.config.shutdownTimeout);
1320
+ if (t.unref) t.unref();
1398
1321
  });
1399
1322
  await Promise.race([shutdownPromise, timeoutPromise]);
1400
1323
  this.state = "stopped";
1401
1324
  this.logger.info("\u2705 Graceful shutdown complete");
1402
1325
  } catch (error) {
1403
- this.logger.error("Shutdown error - forcing stop", error);
1326
+ this.logger.error("Shutdown timed out \u2014 forcing exit", error);
1404
1327
  this.state = "stopped";
1405
- throw error;
1328
+ await this.logger.destroy();
1329
+ process.exit(1);
1406
1330
  } finally {
1407
1331
  await this.logger.destroy();
1408
1332
  }
@@ -1442,6 +1366,13 @@ var ObjectKernel = class {
1442
1366
  async getServiceAsync(name, scopeId) {
1443
1367
  return await this.pluginLoader.getService(name, scopeId);
1444
1368
  }
1369
+ /**
1370
+ * Clear all scoped service instances for a given scope (e.g., projectId).
1371
+ * Releases driver connections and metadata caches for idle projects.
1372
+ */
1373
+ clearScope(scopeId) {
1374
+ this.pluginLoader.clearScope(scopeId);
1375
+ }
1445
1376
  /**
1446
1377
  * Check if kernel is running
1447
1378
  */
@@ -3021,6 +2952,12 @@ var SecurePluginContext = class {
3021
2952
  getKernel() {
3022
2953
  return this.baseContext.getKernel();
3023
2954
  }
2955
+ registerServiceFactory(name, factory, lifecycle, dependencies) {
2956
+ this.baseContext.registerServiceFactory(name, factory, lifecycle, dependencies);
2957
+ }
2958
+ getServiceScoped(name, scopeId) {
2959
+ return this.baseContext.getServiceScoped(name, scopeId);
2960
+ }
3024
2961
  };
3025
2962
  function createPluginPermissionEnforcer(logger) {
3026
2963
  return new PluginPermissionEnforcer(logger);
@@ -4690,272 +4627,6 @@ var NamespaceResolver = class {
4690
4627
  return `${shortName}_${ns}`;
4691
4628
  }
4692
4629
  };
4693
-
4694
- // src/package-manager.ts
4695
- var PackageManager = class {
4696
- constructor(logger, options = {}) {
4697
- this.packages = /* @__PURE__ */ new Map();
4698
- this.snapshots = /* @__PURE__ */ new Map();
4699
- this.logger = logger.child({ component: "PackageManager" });
4700
- this.dependencyResolver = new DependencyResolver(logger);
4701
- this.namespaceResolver = new NamespaceResolver(logger);
4702
- this.platformVersion = options.platformVersion || "3.0.0";
4703
- }
4704
- /**
4705
- * Install a package with full dependency resolution and namespace checking.
4706
- */
4707
- async install(packageId, version, manifest) {
4708
- this.logger.info("Installing package", { packageId, version });
4709
- if (this.packages.has(packageId)) {
4710
- const existing = this.packages.get(packageId);
4711
- if (existing.status === "installed") {
4712
- return {
4713
- success: false,
4714
- packageId,
4715
- version,
4716
- installedDependencies: [],
4717
- namespaceConflicts: [],
4718
- errorMessage: `Package ${packageId}@${existing.version} is already installed. Use upgrade instead.`
4719
- };
4720
- }
4721
- }
4722
- const engine = manifest.engine?.objectstack;
4723
- if (engine) {
4724
- const platformSemver = SemanticVersionManager.parse(this.platformVersion);
4725
- if (!SemanticVersionManager.satisfies(platformSemver, engine)) {
4726
- return {
4727
- success: false,
4728
- packageId,
4729
- version,
4730
- installedDependencies: [],
4731
- namespaceConflicts: [],
4732
- errorMessage: `Package requires platform ${engine}, but current platform is v${this.platformVersion}`
4733
- };
4734
- }
4735
- }
4736
- const namespaces = this.namespaceResolver.extractNamespaces(manifest);
4737
- const nsCheck = this.namespaceResolver.checkAvailability(packageId, namespaces);
4738
- if (!nsCheck.available) {
4739
- return {
4740
- success: false,
4741
- packageId,
4742
- version,
4743
- installedDependencies: [],
4744
- namespaceConflicts: nsCheck.conflicts.map((c) => ({
4745
- namespace: c.namespace,
4746
- existingPackageId: c.existingPackageId
4747
- })),
4748
- errorMessage: `Namespace conflicts detected: ${nsCheck.conflicts.map((c) => c.namespace).join(", ")}`
4749
- };
4750
- }
4751
- const deps = manifest.dependencies;
4752
- const depNames = deps ? Object.keys(deps) : [];
4753
- const missingDeps = depNames.filter((d) => !this.packages.has(d));
4754
- if (missingDeps.length > 0) {
4755
- return {
4756
- success: false,
4757
- packageId,
4758
- version,
4759
- installedDependencies: [],
4760
- namespaceConflicts: [],
4761
- errorMessage: `Missing dependencies: ${missingDeps.join(", ")}`
4762
- };
4763
- }
4764
- this.packages.set(packageId, {
4765
- packageId,
4766
- version,
4767
- manifest,
4768
- installedAt: (/* @__PURE__ */ new Date()).toISOString(),
4769
- status: "installed",
4770
- namespaces,
4771
- dependencies: depNames
4772
- });
4773
- this.namespaceResolver.register(packageId, namespaces);
4774
- this.logger.info("Package installed", { packageId, version, namespaces: namespaces.length });
4775
- return {
4776
- success: true,
4777
- packageId,
4778
- version,
4779
- installedDependencies: depNames,
4780
- namespaceConflicts: []
4781
- };
4782
- }
4783
- /**
4784
- * Uninstall a package, checking for dependents first.
4785
- */
4786
- async uninstall(packageId) {
4787
- const pkg = this.packages.get(packageId);
4788
- if (!pkg) {
4789
- return { success: false, errorMessage: `Package ${packageId} is not installed` };
4790
- }
4791
- const dependents = [];
4792
- for (const [id, record] of this.packages) {
4793
- if (id !== packageId && record.dependencies.includes(packageId)) {
4794
- dependents.push(id);
4795
- }
4796
- }
4797
- if (dependents.length > 0) {
4798
- return {
4799
- success: false,
4800
- errorMessage: `Cannot uninstall ${packageId}: depended upon by ${dependents.join(", ")}`
4801
- };
4802
- }
4803
- this.namespaceResolver.unregister(packageId);
4804
- this.packages.delete(packageId);
4805
- this.snapshots.delete(packageId);
4806
- this.logger.info("Package uninstalled", { packageId });
4807
- return { success: true };
4808
- }
4809
- /**
4810
- * Upgrade a package: snapshot → update → register.
4811
- */
4812
- async upgrade(packageId, newVersion, newManifest) {
4813
- const existing = this.packages.get(packageId);
4814
- if (!existing) {
4815
- return {
4816
- success: false,
4817
- packageId,
4818
- fromVersion: "",
4819
- toVersion: newVersion,
4820
- snapshot: { packageId, previousVersion: "", previousManifest: {}, previousNamespaces: [], installedAt: "", createdAt: (/* @__PURE__ */ new Date()).toISOString() },
4821
- errorMessage: `Package ${packageId} is not installed`
4822
- };
4823
- }
4824
- const snapshot = {
4825
- packageId,
4826
- previousVersion: existing.version,
4827
- previousManifest: existing.manifest,
4828
- previousNamespaces: [...existing.namespaces],
4829
- installedAt: existing.installedAt,
4830
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
4831
- };
4832
- this.snapshots.set(packageId, snapshot);
4833
- const engine = newManifest.engine?.objectstack;
4834
- if (engine) {
4835
- const platformSemver = SemanticVersionManager.parse(this.platformVersion);
4836
- if (!SemanticVersionManager.satisfies(platformSemver, engine)) {
4837
- return {
4838
- success: false,
4839
- packageId,
4840
- fromVersion: existing.version,
4841
- toVersion: newVersion,
4842
- snapshot,
4843
- errorMessage: `New version requires platform ${engine}, current is v${this.platformVersion}`
4844
- };
4845
- }
4846
- }
4847
- const newNamespaces = this.namespaceResolver.extractNamespaces(newManifest);
4848
- this.namespaceResolver.unregister(packageId);
4849
- const nsCheck = this.namespaceResolver.checkAvailability(packageId, newNamespaces);
4850
- if (!nsCheck.available) {
4851
- this.namespaceResolver.register(packageId, existing.namespaces);
4852
- return {
4853
- success: false,
4854
- packageId,
4855
- fromVersion: existing.version,
4856
- toVersion: newVersion,
4857
- snapshot,
4858
- errorMessage: `Namespace conflicts in new version: ${nsCheck.conflicts.map((c) => c.namespace).join(", ")}`
4859
- };
4860
- }
4861
- this.namespaceResolver.register(packageId, newNamespaces);
4862
- const deps = newManifest.dependencies;
4863
- this.packages.set(packageId, {
4864
- packageId,
4865
- version: newVersion,
4866
- manifest: newManifest,
4867
- installedAt: existing.installedAt,
4868
- status: "installed",
4869
- namespaces: newNamespaces,
4870
- dependencies: deps ? Object.keys(deps) : []
4871
- });
4872
- this.logger.info("Package upgraded", { packageId, from: existing.version, to: newVersion });
4873
- return {
4874
- success: true,
4875
- packageId,
4876
- fromVersion: existing.version,
4877
- toVersion: newVersion,
4878
- snapshot
4879
- };
4880
- }
4881
- /**
4882
- * Rollback a package to its pre-upgrade snapshot.
4883
- */
4884
- async rollback(packageId) {
4885
- const snapshot = this.snapshots.get(packageId);
4886
- if (!snapshot) {
4887
- return {
4888
- success: false,
4889
- packageId,
4890
- restoredVersion: "",
4891
- errorMessage: `No upgrade snapshot found for ${packageId}`
4892
- };
4893
- }
4894
- this.namespaceResolver.unregister(packageId);
4895
- this.namespaceResolver.register(packageId, snapshot.previousNamespaces);
4896
- const deps = snapshot.previousManifest.dependencies;
4897
- this.packages.set(packageId, {
4898
- packageId,
4899
- version: snapshot.previousVersion,
4900
- manifest: snapshot.previousManifest,
4901
- installedAt: snapshot.installedAt,
4902
- status: "installed",
4903
- namespaces: snapshot.previousNamespaces,
4904
- dependencies: deps ? Object.keys(deps) : []
4905
- });
4906
- this.snapshots.delete(packageId);
4907
- this.logger.info("Package rolled back", { packageId, to: snapshot.previousVersion });
4908
- return {
4909
- success: true,
4910
- packageId,
4911
- restoredVersion: snapshot.previousVersion
4912
- };
4913
- }
4914
- /**
4915
- * Get an installed package record.
4916
- */
4917
- getPackage(packageId) {
4918
- return this.packages.get(packageId);
4919
- }
4920
- /**
4921
- * List all installed packages.
4922
- */
4923
- listPackages() {
4924
- return Array.from(this.packages.values());
4925
- }
4926
- /**
4927
- * Resolve dependencies for a set of packages.
4928
- */
4929
- resolveDependencies(packages) {
4930
- return this.dependencyResolver.resolve(packages);
4931
- }
4932
- /**
4933
- * Check namespace availability for a package's metadata.
4934
- */
4935
- checkNamespaces(packageId, config) {
4936
- const namespaces = this.namespaceResolver.extractNamespaces(config);
4937
- const result = this.namespaceResolver.checkAvailability(packageId, namespaces);
4938
- return {
4939
- available: result.available,
4940
- conflicts: result.conflicts.map((c) => ({
4941
- namespace: c.namespace,
4942
- existingPackageId: c.existingPackageId
4943
- }))
4944
- };
4945
- }
4946
- /**
4947
- * Get the namespace resolver instance.
4948
- */
4949
- getNamespaceResolver() {
4950
- return this.namespaceResolver;
4951
- }
4952
- /**
4953
- * Get a snapshot for a given package (if available).
4954
- */
4955
- getSnapshot(packageId) {
4956
- return this.snapshots.get(packageId);
4957
- }
4958
- };
4959
4630
  export {
4960
4631
  ApiRegistry,
4961
4632
  CORE_FALLBACK_FACTORIES,
@@ -4966,7 +4637,6 @@ export {
4966
4637
  ObjectKernel,
4967
4638
  ObjectKernelBase,
4968
4639
  ObjectLogger,
4969
- PackageManager,
4970
4640
  PluginConfigValidator,
4971
4641
  PluginHealthMonitor,
4972
4642
  PluginLoader,