koatty 4.4.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,8 +1,11 @@
1
+ import { createRequire } from 'node:module';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { dirname } from 'node:path';
1
4
  import { LoadConfigs } from 'koatty_config';
2
5
  export { Config } from 'koatty_config';
3
- import { IOC, TAGGED_CLS } from 'koatty_container';
6
+ import { IOC, TAGGED_CLS, Container } from 'koatty_container';
4
7
  export * from 'koatty_container';
5
- import { AppEvent, AppEventArr, asyncEvent, ComponentManager, implementsMiddlewareInterface, MIDDLEWARE_OPTIONS, protocolMiddleware, implementsControllerInterface, implementsServiceInterface, implementsAspectInterface, Koatty } from 'koatty_core';
8
+ import { AppEvent, AppEventArr, asyncEvent, ComponentManager, implementsMiddlewareInterface, MIDDLEWARE_OPTIONS, protocolMiddleware, implementsServiceInterface, implementsAspectInterface, Koatty } from 'koatty_core';
6
9
  export * from 'koatty_core';
7
10
  export * from 'koatty_exception';
8
11
  import { Helper } from 'koatty_lib';
@@ -10,14 +13,18 @@ export { Helper } from 'koatty_lib';
10
13
  export * from 'koatty_router';
11
14
  export * from 'koatty_serve';
12
15
  import { DefaultLogger } from 'koatty_logger';
13
- export { Log, LogLevelType, DefaultLogger as Logger } from 'koatty_logger';
16
+ export { Log, DefaultLogger as Logger } from 'koatty_logger';
14
17
  import { Load } from 'koatty_loader';
15
18
  import { Trace } from 'koatty_trace';
16
19
  import * as path from 'path';
20
+ import * as fs from 'fs';
17
21
 
22
+ createRequire(import.meta.url);
23
+ const __filename$1 = fileURLToPath(import.meta.url);
24
+ const __dirname$1 = dirname(__filename$1);
18
25
  /*!
19
26
  * @Author: richen
20
- * @Date: 2026-09-28 04:29:21
27
+ * @Date: 2026-09-30 05:09:10
21
28
  * @License: BSD (3-Clause)
22
29
  * @Copyright (c) - <richenlin(at)gmail.com>
23
30
  * @HomePage: https://koatty.org/
@@ -34,7 +41,7 @@ var require_package = __commonJS({
34
41
  "package.json"(exports$1, module) {
35
42
  module.exports = {
36
43
  name: "koatty",
37
- version: "4.4.0",
44
+ version: "5.0.0",
38
45
  description: "Koa + Typescript = koatty. Use Typescript's decorator implement auto injection.",
39
46
  scripts: {
40
47
  build: "pnpm run build:js && pnpm run build:dts && pnpm run build:doc && pnpm run build:cp",
@@ -45,7 +52,8 @@ var require_package = __commonJS({
45
52
  clean: "rimraf dist temp docs/api",
46
53
  lint: "eslint --ext .ts,.js ./src",
47
54
  test: "pnpm run lint && jest --passWithNoTests",
48
- "test:cov": "jest --collectCoverage --detectOpenHandles"
55
+ "test:cov": "jest --collectCoverage --detectOpenHandles",
56
+ "test:example": "jest --config examples/mcp-order-service/jest.config.js --coverage=false"
49
57
  },
50
58
  main: "./dist/index.js",
51
59
  exports: {
@@ -142,7 +150,13 @@ var require_package = __commonJS({
142
150
  koatty_trace: "workspace:*",
143
151
  "ts-morph": "^27.0.2"
144
152
  },
145
- types: "./dist/index.d.ts"
153
+ types: "./dist/index.d.ts",
154
+ files: [
155
+ "dist",
156
+ "README.md",
157
+ "CHANGELOG.md",
158
+ "LICENSE"
159
+ ]
146
160
  };
147
161
  }
148
162
  });
@@ -189,7 +203,7 @@ function checkRuntime() {
189
203
  __name(checkRuntime, "checkRuntime");
190
204
  var checkUTRuntime = /* @__PURE__ */ __name(() => {
191
205
  let isUTRuntime = false;
192
- const argv = JSON.stringify(process.argv[1]);
206
+ const argv = String(process.argv[1] ?? "");
193
207
  if (argv.indexOf("jest") > -1) {
194
208
  isUTRuntime = true;
195
209
  }
@@ -207,11 +221,264 @@ var LOGO = `
207
221
  -------------------------------------------
208
222
  https://github.com/koatty
209
223
  `;
224
+
225
+ // src/config/config.ts
226
+ var config_default = {
227
+ /*app config*/
228
+ logsLevel: "debug"
229
+ };
230
+
231
+ // src/config/middleware.ts
232
+ var middleware_default = {
233
+ // List of loaded middleware(except for the middleware loaded by default),
234
+ // executed in the order of elements
235
+ list: [],
236
+ // middleware configuration
237
+ config: {
238
+ // ============================================
239
+ // Trace Middleware - 请求链路追踪中间件
240
+ // ============================================
241
+ // Koatty-Trace 用于追踪请求链路、记录日志、收集指标
242
+ // 它会自动作为第一个中间件加载,无需添加到 list 中
243
+ trace: {
244
+ // response timeout in milliseconds
245
+ timeout: 1e4,
246
+ // request id header name
247
+ requestIdHeaderName: "X-Request-Id",
248
+ // request id name
249
+ requestIdName: "requestId",
250
+ // id generator function
251
+ // idFactory: Function,
252
+ // encoding
253
+ encoding: "utf-8",
254
+ // Whether to enable trace (default: false)
255
+ enableTrace: false,
256
+ // asynchooks enabled
257
+ asyncHooks: false,
258
+ /**
259
+ * Metrics configuration
260
+ */
261
+ metricsConf: {
262
+ /**
263
+ * Metrics reporter function
264
+ */
265
+ // reporter: (metrics: {
266
+ // duration: number,
267
+ // status: number,
268
+ // path: string,
269
+ // attributes: Record<string, any>,
270
+ // }) => void,
271
+ /**
272
+ * Default attributes for metrics
273
+ */
274
+ defaultAttributes: {},
275
+ /**
276
+ * Prometheus metrics endpoint (production only)
277
+ */
278
+ metricsEndpoint: "/metrics",
279
+ /**
280
+ * Metrics report interval in milliseconds (default: 5000)
281
+ */
282
+ reportInterval: 5e3,
283
+ /**
284
+ * Prometheus metrics port (default: 9464)
285
+ */
286
+ metricsPort: 9464
287
+ },
288
+ /**
289
+ * OpenTelemetry configuration
290
+ */
291
+ opentelemetryConf: {
292
+ /**
293
+ * OTLP endpoint URL
294
+ */
295
+ endpoint: "http://localhost:4318/v1/traces",
296
+ /**
297
+ * Whether to enable topology analysis (default: same as enableTrace)
298
+ *
299
+ */
300
+ enableTopology: false,
301
+ /**
302
+ * OTLP headers
303
+ */
304
+ headers: {},
305
+ /**
306
+ * Resource attributes
307
+ */
308
+ resourceAttributes: {},
309
+ /**
310
+ * Instrumentations to enable
311
+ */
312
+ instrumentations: [],
313
+ /**
314
+ * Exporter timeout in milliseconds
315
+ */
316
+ timeout: 1e4,
317
+ /**
318
+ * Maximum lifetime for a span in milliseconds
319
+ */
320
+ spanTimeout: 3e4,
321
+ /**
322
+ * Maximum number of active spans in memory (default: 1000)
323
+ */
324
+ maxActiveSpans: 1e3,
325
+ /**
326
+ * Request attributes to be added to the span
327
+ */
328
+ // spanAttributes: (ctx: KoattyContext) => Record<string, any>,
329
+ /**
330
+ * Sampling rate (0.0 - 1.0)
331
+ */
332
+ samplingRate: 1,
333
+ /**
334
+ * Maximum number of spans in batch queue
335
+ */
336
+ batchMaxQueueSize: 2048,
337
+ /**
338
+ * Maximum number of spans to export in one batch
339
+ */
340
+ batchMaxExportSize: 512,
341
+ /**
342
+ * Delay between batch exports in milliseconds
343
+ */
344
+ batchDelayMillis: 5e3,
345
+ /**
346
+ * Timeout for batch export in milliseconds
347
+ */
348
+ batchExportTimeout: 3e4
349
+ },
350
+ /**
351
+ * Retry configuration
352
+ */
353
+ retryConf: {
354
+ /**
355
+ * Whether to enable retry mechanism (default: false)
356
+ */
357
+ enabled: false,
358
+ /**
359
+ * Max retry count when error occurs (default: 3)
360
+ */
361
+ count: 3,
362
+ /**
363
+ * Retry interval in milliseconds (default: 1000)
364
+ */
365
+ interval: 1e3
366
+ }
367
+ }
368
+ }
369
+ };
370
+
371
+ // src/config/plugin.ts
372
+ var plugin_default = {
373
+ // ============================================================
374
+ // User plugin loading list (executed in order)
375
+ // Note: Core components (@Component) are loaded automatically
376
+ // ============================================================
377
+ list: [],
378
+ // ============================================================
379
+ // Component/Plugin configuration
380
+ // ============================================================
381
+ config: {
382
+ // --- Core components (@Component) ---
383
+ // Framework built-in components, usually do not need to modify
384
+ // Can be disabled by setting enabled: false
385
+ RouterComponent: {
386
+ enabled: true
387
+ },
388
+ ServeComponent: {
389
+ enabled: true
390
+ }
391
+ }
392
+ };
393
+
394
+ // src/config/router.ts
395
+ var router_default = {
396
+ /**
397
+ * prefix of the router
398
+ */
399
+ // prefix: string,
400
+ /**
401
+ * Methods which should be supported by the router.
402
+ */
403
+ // methods?: string[];
404
+ // routerPath?: string;
405
+ /**
406
+ * Whether or not routing should be case-sensitive.
407
+ */
408
+ // sensitive?: boolean;
409
+ /**
410
+ * Whether or not routes should matched strictly.
411
+ *
412
+ * If strict matching is enabled, the trailing slash is taken into
413
+ * account when matching routes.
414
+ */
415
+ // strict?: boolean;
416
+ /**
417
+ * payload options
418
+ *
419
+ */
420
+ payload: {
421
+ extTypes: {
422
+ json: [
423
+ "application/json"
424
+ ],
425
+ form: [
426
+ "application/x-www-form-urlencoded"
427
+ ],
428
+ text: [
429
+ "text/plain"
430
+ ],
431
+ multipart: [
432
+ "multipart/form-data"
433
+ ],
434
+ xml: [
435
+ "text/xml"
436
+ ],
437
+ grpc: [
438
+ "application/grpc"
439
+ ],
440
+ graphql: [
441
+ "application/graphql+json"
442
+ ],
443
+ websocket: [
444
+ "application/websocket"
445
+ ]
446
+ },
447
+ // body-size limit intentionally unset (ADR-102): the security profile
448
+ // decides (1mb in production, 20mb in development) unless the project
449
+ // config/router.ts sets payload.limit explicitly
450
+ encoding: "utf-8",
451
+ multiples: true
452
+ }
453
+ };
454
+
455
+ // src/config/server.ts
456
+ var server_default = {
457
+ hostname: process.env.IP || "127.0.0.1",
458
+ // server port: single value (e.g. 3000) or array (e.g. [3000, 3001])
459
+ // For multi-protocol: if port is array, each port maps to corresponding protocol
460
+ // If port is single value, first protocol uses it, others auto-increment (3000, 3001, 3002...)
461
+ port: process.env.PORT || process.env.APP_PORT || 3e3,
462
+ // Server protocol: 'http' | 'https' | 'http2' | 'grpc' | 'ws' | 'wss' | 'graphql'
463
+ // For multi-protocol: use array like ["http", "grpc"] and configure ext below
464
+ protocol: "http",
465
+ trace: false,
466
+ ssl: {
467
+ mode: "auto",
468
+ key: "",
469
+ cert: "",
470
+ ca: ""
471
+ // ca file path
472
+ }
473
+ };
474
+
475
+ // src/core/Loader.ts
210
476
  var Loader = class _Loader {
211
477
  static {
212
478
  __name(this, "Loader");
213
479
  }
214
480
  app;
481
+ static loggingApps = /* @__PURE__ */ new Set();
215
482
  /**
216
483
  * Creates an instance of Loader.
217
484
  * @param {KoattyApplication} app
@@ -239,7 +506,7 @@ var Loader = class _Loader {
239
506
  }
240
507
  const rootPath = app.rootPath || process.cwd();
241
508
  const appPath = app.appPath || path.resolve(rootPath, app.appDebug ? "src" : "dist");
242
- const koattyPath = path.resolve(__dirname, "..");
509
+ const koattyPath = path.resolve(__dirname$1, "..");
243
510
  Helper.define(app, "rootPath", rootPath);
244
511
  Helper.define(app, "appPath", appPath);
245
512
  Helper.define(app, "koattyPath", koattyPath);
@@ -250,6 +517,11 @@ var Loader = class _Loader {
250
517
  app.version = app.version || pkg2.version;
251
518
  }
252
519
  }
520
+ Helper.define(app, "paths", {
521
+ rootPath,
522
+ appPath,
523
+ koattyPath
524
+ });
253
525
  process.env.ROOT_PATH = rootPath;
254
526
  process.env.APP_PATH = appPath;
255
527
  process.env.KOATTY_PATH = koattyPath;
@@ -314,16 +586,31 @@ var Loader = class _Loader {
314
586
  const componentMetas = _Loader.GetComponentMeta(app, target);
315
587
  const configurationMetas = _Loader.GetConfigurationMeta(app, target);
316
588
  const exSet = /* @__PURE__ */ new Set();
317
- Load(componentMetas, "", (fileName, xpath, xTarget) => {
589
+ const manifestFile = path.join(app.rootPath, ".koatty", "manifest.json");
590
+ Load(componentMetas, app.rootPath, (fileName, xpath, xTarget) => {
318
591
  checkClass(fileName, xpath, xTarget, exSet);
592
+ const exports$1 = typeof xTarget === "function" ? [
593
+ xTarget
594
+ ] : Object.values(xTarget ?? {});
595
+ for (const component of exports$1) {
596
+ if (Helper.isClass(component)) app.container.saveClass(IOC.getType(component), component, IOC.getIdentifier(component));
597
+ }
319
598
  }, [
320
599
  "**/**.js",
321
600
  "**/**.ts",
322
601
  "!**/**.d.ts"
323
602
  ], [
324
603
  ...configurationMetas,
325
- `${target.name || ".no"}.ts`
326
- ]);
604
+ `${target.name || ".no"}.ts`,
605
+ `${target.name || ".no"}.js`
606
+ ], {
607
+ manifestFile: app.env === "production" && fs.existsSync(manifestFile) ? manifestFile : void 0
608
+ });
609
+ for (const item of IOC.listClass("COMPONENT")) {
610
+ const id = IOC.getIdentifier(item.target);
611
+ const options = IOC.getPropertyData("COMPONENT_OPTIONS", item.target, id);
612
+ if (options?.scope === "core") app.container.saveClass("COMPONENT", item.target, id);
613
+ }
327
614
  exSet.clear();
328
615
  }
329
616
  /**
@@ -338,6 +625,7 @@ var Loader = class _Loader {
338
625
  const data = app.getMetaData("_configs") || [];
339
626
  const configs = data[0] || {};
340
627
  if (configs.config) {
628
+ _Loader.loggingApps.add(app);
341
629
  const opt = configs.config;
342
630
  let logLevel = "debug", logFilePath = "", sensFields = [];
343
631
  if (app.env === "production") {
@@ -372,7 +660,8 @@ var Loader = class _Loader {
372
660
  }
373
661
  app.once(AppEvent.appStop, async () => {
374
662
  await DefaultLogger.flushBatch();
375
- await DefaultLogger.destroy();
663
+ _Loader.loggingApps.delete(app);
664
+ if (_Loader.loggingApps.size === 0) DefaultLogger.enableBatch(false);
376
665
  });
377
666
  }
378
667
  }
@@ -393,8 +682,8 @@ var Loader = class _Loader {
393
682
  */
394
683
  static async LoadAllComponents(app, target) {
395
684
  try {
396
- if (Helper.isFunction(IOC.preloadMetadata)) {
397
- IOC.preloadMetadata();
685
+ if (Helper.isFunction(app.container.preloadMetadata)) {
686
+ app.container.preloadMetadata();
398
687
  }
399
688
  } catch (error) {
400
689
  DefaultLogger.Error("[Loader] preloadMetadata failed:", error);
@@ -450,12 +739,15 @@ var Loader = class _Loader {
450
739
  await asyncEvent(app, event);
451
740
  break;
452
741
  case AppEvent.loadServe:
742
+ await app.container?.ready?.();
453
743
  DefaultLogger.Log("Koatty", "", "Emit loadServe ...");
454
744
  await asyncEvent(app, event);
455
745
  break;
456
746
  case AppEvent.appReady:
457
747
  DefaultLogger.Log("Koatty", "", "Emit appReady ...");
458
748
  await asyncEvent(app, event);
749
+ await app.container?.ready?.();
750
+ app.container?.seal?.();
459
751
  break;
460
752
  default:
461
753
  await asyncEvent(app, event);
@@ -473,18 +765,23 @@ var Loader = class _Loader {
473
765
  * @param {string[]} [loadPath] - Optional array of paths to load application configs from
474
766
  */
475
767
  LoadConfigs(loadPath) {
476
- const frameConfig = {};
477
- Load([
478
- "./config"
479
- ], this.app.koattyPath, function(name, path2, exp) {
480
- frameConfig[name] = exp;
481
- });
768
+ const frameConfig = Helper.clone({
769
+ config: config_default,
770
+ middleware: middleware_default,
771
+ plugin: plugin_default,
772
+ router: router_default,
773
+ server: server_default
774
+ }, true);
482
775
  if (Helper.isArray(loadPath)) {
483
776
  loadPath = loadPath.length > 0 ? loadPath : [
484
777
  "./config"
485
778
  ];
486
779
  }
487
- let appConfig = LoadConfigs(loadPath, this.app.appPath);
780
+ const manifest = path.join(this.app.rootPath, ".koatty/manifest.json");
781
+ let appConfig = LoadConfigs(loadPath, this.app.appPath, void 0, void 0, void 0, this.app.env === "production" && fs.existsSync(manifest) ? {
782
+ manifestFile: manifest,
783
+ baseDir: this.app.rootPath
784
+ } : void 0);
488
785
  appConfig = Helper.extend(frameConfig, appConfig, true);
489
786
  this.app.setMetaData("_configs", appConfig);
490
787
  }
@@ -516,16 +813,16 @@ var Loader = class _Loader {
516
813
  } catch (error) {
517
814
  DefaultLogger.Warn(`Trace middleware failed to load: ${error.message}`);
518
815
  }
519
- const appMiddleware = IOC.listClass("MIDDLEWARE") ?? [];
816
+ const appMiddleware = this.app.container.listClass("MIDDLEWARE") ?? [];
520
817
  appMiddleware.forEach((item) => {
521
818
  item.id = (item.id ?? "").replace("MIDDLEWARE:", "");
522
819
  if (item.id && Helper.isClass(item.target)) {
523
- IOC.reg(item.id, item.target, {
820
+ this.app.container.reg(item.id, item.target, {
524
821
  scope: "Prototype",
525
822
  type: "MIDDLEWARE",
526
823
  args: []
527
824
  });
528
- const ctl = IOC.getInsByClass(item.target);
825
+ const ctl = this.app.container.getInsByClass(item.target);
529
826
  if (!implementsMiddlewareInterface(ctl)) {
530
827
  throw Error(`The middleware ${item.id} must implements interface 'IMiddleware'.`);
531
828
  }
@@ -538,7 +835,7 @@ var Loader = class _Loader {
538
835
  });
539
836
  const middlewareConfig = middlewareConf.config || {};
540
837
  for (const key of Array.from(appMList)) {
541
- const handle = IOC.get(key, "MIDDLEWARE");
838
+ const handle = this.app.container.get(key, "MIDDLEWARE");
542
839
  if (!handle) {
543
840
  throw Error(`Middleware ${key} load error.`);
544
841
  }
@@ -551,7 +848,7 @@ var Loader = class _Loader {
551
848
  let decoratorOptions = {};
552
849
  if (middlewareClass) {
553
850
  try {
554
- decoratorOptions = IOC.getPropertyData(MIDDLEWARE_OPTIONS, middlewareClass, key) || {};
851
+ decoratorOptions = this.app.container.getPropertyData(MIDDLEWARE_OPTIONS, middlewareClass, key) || {};
555
852
  } catch {
556
853
  decoratorOptions = {};
557
854
  }
@@ -591,21 +888,17 @@ var Loader = class _Loader {
591
888
  * @throws {Error} If a controller does not implement the IController interface.
592
889
  */
593
890
  async LoadControllers() {
594
- const controllerList = IOC.listClass("CONTROLLER");
891
+ const controllerList = this.app.container.listClass("CONTROLLER");
595
892
  const controllers = [];
596
893
  controllerList.forEach((item) => {
597
894
  item.id = (item.id ?? "").replace("CONTROLLER:", "");
598
895
  if (item.id && Helper.isClass(item.target)) {
599
896
  DefaultLogger.Debug(`Load controller: ${item.id}`);
600
- IOC.reg(item.id, item.target, {
897
+ this.app.container.reg(item.id, item.target, {
601
898
  scope: "Prototype",
602
899
  type: "CONTROLLER",
603
900
  args: []
604
901
  });
605
- const ctl = IOC.getInsByClass(item.target);
606
- if (!implementsControllerInterface(ctl)) {
607
- throw Error(`The controller ${item.id} must implements interface 'IController'.`);
608
- }
609
902
  controllers.push(item.id);
610
903
  }
611
904
  });
@@ -622,17 +915,20 @@ var Loader = class _Loader {
622
915
  * @throws {Error} When service does not implement IService interface
623
916
  */
624
917
  async LoadServices() {
625
- const serviceList = IOC.listClass("SERVICE");
918
+ const serviceList = this.app.container.listClass("SERVICE");
626
919
  for (const item of serviceList) {
627
920
  item.id = (item.id ?? "").replace("SERVICE:", "");
628
921
  if (item.id && Helper.isClass(item.target)) {
629
922
  DefaultLogger.Debug(`Load service: ${item.id}`);
630
- IOC.reg(item.id, item.target, {
923
+ const options = this.app.container.getPropertyData("SERVICE_OPTIONS", item.target, item.id) ?? {};
924
+ this.app.container.reg(item.id, item.target, {
631
925
  scope: "Singleton",
926
+ ...options,
632
927
  type: "SERVICE",
633
- args: []
928
+ args: options.args ?? []
634
929
  });
635
- const ctl = IOC.getInsByClass(item.target);
930
+ if (options.scope === "Request") continue;
931
+ const ctl = this.app.container.getInsByClass(item.target);
636
932
  if (!implementsServiceInterface(ctl)) {
637
933
  throw Error(`The service ${item.id} must implements interface 'IService'.`);
638
934
  }
@@ -651,17 +947,17 @@ var Loader = class _Loader {
651
947
  * @throws {Error} When plugin loading fails
652
948
  */
653
949
  async LoadComponents(componentManager) {
654
- const componentList = IOC.listClass("COMPONENT");
950
+ const componentList = this.app.container.listClass("COMPONENT");
655
951
  componentList.forEach((item) => {
656
952
  item.id = (item.id ?? "").replace("COMPONENT:", "");
657
953
  if (Helper.isClass(item.target)) {
658
- IOC.reg(item.id, item.target, {
954
+ this.app.container.reg(item.id, item.target, {
659
955
  scope: "Singleton",
660
956
  type: "COMPONENT",
661
957
  args: []
662
958
  });
663
959
  if (item.id && item.id.endsWith("Aspect")) {
664
- const ctl = IOC.getInsByClass(item.target);
960
+ const ctl = this.app.container.getInsByClass(item.target);
665
961
  if (!implementsAspectInterface(ctl)) {
666
962
  throw Error(`The aspect ${item.id} must implements interface 'IAspect'.`);
667
963
  }
@@ -681,7 +977,7 @@ var Loader = class _Loader {
681
977
  }
682
978
  const pluginConfList = pluginsConf.list ?? [];
683
979
  for (const key of pluginConfList) {
684
- const handle = IOC.get(key, "COMPONENT");
980
+ const handle = this.app.container.get(key, "COMPONENT");
685
981
  if (!handle) {
686
982
  throw Error(`Plugin ${key} load error.`);
687
983
  }
@@ -730,7 +1026,8 @@ var bootstrapApplication = /* @__PURE__ */ __name(async function(target, bootFun
730
1026
  DefaultLogger.Log("Koatty", "", "Execute bootFunc ...");
731
1027
  await bootFunc(app);
732
1028
  }
733
- IOC.setApp(app);
1029
+ if (!app.container || app.container === IOC) app.container = new Container();
1030
+ app.container.setApp(app);
734
1031
  DefaultLogger.Log("Koatty", "", "ComponentScan ...");
735
1032
  Loader.CheckAllComponents(app, target);
736
1033
  await Loader.LoadAllComponents(app, target);
@@ -772,7 +1069,6 @@ var listenCallback = /* @__PURE__ */ __name((app) => {
772
1069
  DefaultLogger.Log("Koatty", "", `App Environment: ${app.env}`);
773
1070
  if (app.appDebug) DefaultLogger.Warn(`Running in debug mode.`);
774
1071
  DefaultLogger.Log("Koatty", "", "====================================");
775
- asyncEvent(app, AppEvent.appStart);
776
1072
  }, "listenCallback");
777
1073
  function Bootstrap(bootFunc) {
778
1074
  return IOC.createDecorator((target, _context) => {
@@ -780,6 +1076,7 @@ function Bootstrap(bootFunc) {
780
1076
  throw new Error(`class does not inherit from Koatty`);
781
1077
  }
782
1078
  IOC.saveClass("COMPONENT", target, "KOATTY_APP");
1079
+ if (checkUTRuntime()) return;
783
1080
  ExecBootStrap(bootFunc)(target).catch((err) => {
784
1081
  DefaultLogger.Fatal(err);
785
1082
  process.exitCode = 1;