@noxfly/noxus 3.0.0-dev.1 → 3.0.0-dev.11

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 (57) hide show
  1. package/README.md +121 -8
  2. package/dist/child.d.mts +8 -2
  3. package/dist/child.d.ts +8 -2
  4. package/dist/child.js +405 -862
  5. package/dist/child.js.map +1 -0
  6. package/dist/child.mjs +396 -854
  7. package/dist/child.mjs.map +1 -0
  8. package/dist/main.d.mts +180 -132
  9. package/dist/main.d.ts +180 -132
  10. package/dist/main.js +1087 -926
  11. package/dist/main.js.map +1 -0
  12. package/dist/main.mjs +1038 -877
  13. package/dist/main.mjs.map +1 -0
  14. package/dist/preload.js.map +1 -0
  15. package/dist/preload.mjs.map +1 -0
  16. package/dist/renderer.d.mts +30 -4
  17. package/dist/renderer.d.ts +30 -4
  18. package/dist/renderer.js +191 -128
  19. package/dist/renderer.js.map +1 -0
  20. package/dist/renderer.mjs +180 -116
  21. package/dist/renderer.mjs.map +1 -0
  22. package/package.json +18 -9
  23. package/.editorconfig +0 -16
  24. package/.github/copilot-instructions.md +0 -32
  25. package/.vscode/settings.json +0 -3
  26. package/eslint.config.js +0 -109
  27. package/scripts/postbuild.js +0 -31
  28. package/src/DI/app-injector.ts +0 -151
  29. package/src/DI/injector-explorer.ts +0 -143
  30. package/src/DI/token.ts +0 -53
  31. package/src/decorators/controller.decorator.ts +0 -58
  32. package/src/decorators/guards.decorator.ts +0 -15
  33. package/src/decorators/injectable.decorator.ts +0 -81
  34. package/src/decorators/method.decorator.ts +0 -66
  35. package/src/decorators/middleware.decorator.ts +0 -15
  36. package/src/index.ts +0 -10
  37. package/src/internal/app.ts +0 -217
  38. package/src/internal/bootstrap.ts +0 -108
  39. package/src/internal/exceptions.ts +0 -57
  40. package/src/internal/preload-bridge.ts +0 -75
  41. package/src/internal/renderer-client.ts +0 -338
  42. package/src/internal/renderer-events.ts +0 -110
  43. package/src/internal/request.ts +0 -97
  44. package/src/internal/router.ts +0 -353
  45. package/src/internal/routes.ts +0 -78
  46. package/src/internal/socket.ts +0 -73
  47. package/src/main.ts +0 -26
  48. package/src/non-electron-process.ts +0 -22
  49. package/src/preload.ts +0 -10
  50. package/src/renderer.ts +0 -13
  51. package/src/utils/forward-ref.ts +0 -31
  52. package/src/utils/logger.ts +0 -430
  53. package/src/utils/radix-tree.ts +0 -210
  54. package/src/utils/types.ts +0 -21
  55. package/src/window/window-manager.ts +0 -255
  56. package/tsconfig.json +0 -29
  57. package/tsup.config.ts +0 -50
package/dist/main.js CHANGED
@@ -10,6 +10,11 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
10
10
  var __getOwnPropNames = Object.getOwnPropertyNames;
11
11
  var __getProtoOf = Object.getPrototypeOf;
12
12
  var __hasOwnProp = Object.prototype.hasOwnProperty;
13
+ var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
14
+ var __typeError = (msg) => {
15
+ throw TypeError(msg);
16
+ };
17
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
13
18
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
14
19
  var __esm = (fn, res) => function __init() {
15
20
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
@@ -35,14 +40,45 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
35
40
  mod
36
41
  ));
37
42
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
38
- var __decorateClass = (decorators, target, key, kind) => {
39
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
40
- for (var i = decorators.length - 1, decorator; i >= 0; i--)
41
- if (decorator = decorators[i])
42
- result = (kind ? decorator(target, key, result) : decorator(result)) || result;
43
- if (kind && result) __defProp(target, key, result);
44
- return result;
43
+ var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
44
+ var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
45
+ var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
46
+ var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
47
+ var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
48
+ var __runInitializers = (array, flags, self, value) => {
49
+ for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
50
+ return value;
51
+ };
52
+ var __decorateElement = (array, flags, name, decorators, target, extra) => {
53
+ var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
54
+ var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
55
+ var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
56
+ var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() {
57
+ return __privateGet(this, extra);
58
+ }, set [name](x) {
59
+ return __privateSet(this, extra, x);
60
+ } }, name));
61
+ k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
62
+ for (var i = decorators.length - 1; i >= 0; i--) {
63
+ ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
64
+ if (k) {
65
+ ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
66
+ if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
67
+ if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
68
+ }
69
+ it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
70
+ if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
71
+ else if (typeof it !== "object" || it === null) __typeError("Object expected");
72
+ else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
73
+ }
74
+ return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
45
75
  };
76
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
77
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
78
+ var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
79
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
80
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
81
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
46
82
 
47
83
  // src/utils/forward-ref.ts
48
84
  function forwardRef(fn) {
@@ -74,6 +110,7 @@ var init_token = __esm({
74
110
  _Token = class _Token {
75
111
  constructor(target) {
76
112
  this.target = target;
113
+ __publicField(this, "description");
77
114
  this.description = typeof target === "string" ? target : target.name;
78
115
  }
79
116
  toString() {
@@ -86,113 +123,6 @@ var init_token = __esm({
86
123
  }
87
124
  });
88
125
 
89
- // src/DI/app-injector.ts
90
- function keyOf(k) {
91
- return k;
92
- }
93
- function inject(t) {
94
- return RootInjector.resolve(t);
95
- }
96
- var _AppInjector, AppInjector, RootInjector;
97
- var init_app_injector = __esm({
98
- "src/DI/app-injector.ts"() {
99
- "use strict";
100
- init_forward_ref();
101
- init_token();
102
- __name(keyOf, "keyOf");
103
- _AppInjector = class _AppInjector {
104
- constructor(name = null) {
105
- this.name = name;
106
- this.bindings = /* @__PURE__ */ new Map();
107
- this.singletons = /* @__PURE__ */ new Map();
108
- this.scoped = /* @__PURE__ */ new Map();
109
- }
110
- /**
111
- * Creates a child scope for per-request lifetime resolution.
112
- */
113
- createScope() {
114
- const scope = new _AppInjector();
115
- scope.bindings = this.bindings;
116
- scope.singletons = this.singletons;
117
- return scope;
118
- }
119
- /**
120
- * Registers a binding explicitly.
121
- */
122
- register(key, implementation, lifetime, deps = []) {
123
- const k = keyOf(key);
124
- if (!this.bindings.has(k)) {
125
- this.bindings.set(k, { lifetime, implementation, deps });
126
- }
127
- }
128
- /**
129
- * Resolves a dependency by token or class reference.
130
- */
131
- resolve(target) {
132
- if (target instanceof ForwardReference) {
133
- return this._resolveForwardRef(target);
134
- }
135
- const k = keyOf(target);
136
- const binding = this.bindings.get(k);
137
- if (!binding) {
138
- const name = target instanceof Token ? target.description : target.name ?? "unknown";
139
- throw new Error(
140
- `[Noxus DI] No binding found for "${name}".
141
- Did you forget to declare it in @Injectable({ deps }) or in bootstrapApplication({ singletons })?`
142
- );
143
- }
144
- switch (binding.lifetime) {
145
- case "transient":
146
- return this._instantiate(binding);
147
- case "scope": {
148
- if (this.scoped.has(k)) return this.scoped.get(k);
149
- const inst = this._instantiate(binding);
150
- this.scoped.set(k, inst);
151
- return inst;
152
- }
153
- case "singleton": {
154
- if (this.singletons.has(k)) return this.singletons.get(k);
155
- const inst = this._instantiate(binding);
156
- this.singletons.set(k, inst);
157
- if (binding.instance === void 0) {
158
- binding.instance = inst;
159
- }
160
- return inst;
161
- }
162
- }
163
- }
164
- // -------------------------------------------------------------------------
165
- _resolveForwardRef(ref) {
166
- return new Proxy({}, {
167
- get: /* @__PURE__ */ __name((_obj, prop, receiver) => {
168
- const realType = ref.forwardRefFn();
169
- const instance = this.resolve(realType);
170
- const value = Reflect.get(instance, prop, receiver);
171
- return typeof value === "function" ? value.bind(instance) : value;
172
- }, "get"),
173
- set: /* @__PURE__ */ __name((_obj, prop, value, receiver) => {
174
- const realType = ref.forwardRefFn();
175
- const instance = this.resolve(realType);
176
- return Reflect.set(instance, prop, value, receiver);
177
- }, "set"),
178
- getPrototypeOf: /* @__PURE__ */ __name(() => {
179
- const realType = ref.forwardRefFn();
180
- return realType.prototype;
181
- }, "getPrototypeOf")
182
- });
183
- }
184
- _instantiate(binding) {
185
- const resolvedDeps = binding.deps.map((dep) => this.resolve(dep));
186
- return new binding.implementation(...resolvedDeps);
187
- }
188
- };
189
- __name(_AppInjector, "AppInjector");
190
- AppInjector = _AppInjector;
191
- RootInjector = new AppInjector("root");
192
- __name(inject, "inject");
193
- }
194
- });
195
-
196
126
  // src/utils/logger.ts
197
127
  function getPrettyTimestamp() {
198
128
  const now = /* @__PURE__ */ new Date();
@@ -441,6 +371,10 @@ var init_logger = __esm({
441
371
  });
442
372
 
443
373
  // src/DI/injector-explorer.ts
374
+ var injector_explorer_exports = {};
375
+ __export(injector_explorer_exports, {
376
+ InjectorExplorer: () => InjectorExplorer
377
+ });
444
378
  var _InjectorExplorer, InjectorExplorer;
445
379
  var init_injector_explorer = __esm({
446
380
  "src/DI/injector-explorer.ts"() {
@@ -451,6 +385,13 @@ var init_injector_explorer = __esm({
451
385
  // -------------------------------------------------------------------------
452
386
  // Public API
453
387
  // -------------------------------------------------------------------------
388
+ /**
389
+ * Sets the callback used to register controllers.
390
+ * Must be called once before processPending (typically by bootstrapApplication).
391
+ */
392
+ static setControllerRegistrar(registrar) {
393
+ _InjectorExplorer.controllerRegistrar = registrar;
394
+ }
454
395
  static enqueue(reg) {
455
396
  if (_InjectorExplorer.processed && !_InjectorExplorer.accumulating) {
456
397
  _InjectorExplorer._registerImmediate(reg);
@@ -476,16 +417,37 @@ var init_injector_explorer = __esm({
476
417
  /**
477
418
  * Exits accumulation mode and flushes queued registrations
478
419
  * with the same two-phase guarantee as processPending.
420
+ * Serialised through a lock to prevent concurrent lazy loads from corrupting the queue.
479
421
  */
480
422
  static flushAccumulated(routeGuards = [], routeMiddlewares = [], pathPrefix = "") {
481
- _InjectorExplorer.accumulating = false;
482
- const queue = [..._InjectorExplorer.pending];
423
+ _InjectorExplorer.loadingLock = _InjectorExplorer.loadingLock.then(() => {
424
+ _InjectorExplorer.accumulating = false;
425
+ const queue = [..._InjectorExplorer.pending];
426
+ _InjectorExplorer.pending.length = 0;
427
+ _InjectorExplorer._phaseOne(queue);
428
+ for (const reg of queue) {
429
+ if (reg.isController) reg.pathPrefix = pathPrefix;
430
+ }
431
+ _InjectorExplorer._phaseTwo(queue, void 0, routeGuards, routeMiddlewares);
432
+ });
433
+ return _InjectorExplorer.loadingLock;
434
+ }
435
+ /**
436
+ * Returns a Promise that resolves once all pending flushAccumulated calls
437
+ * have completed. Useful for awaiting lazy-load serialisation.
438
+ */
439
+ static waitForFlush() {
440
+ return _InjectorExplorer.loadingLock;
441
+ }
442
+ /**
443
+ * Resets the explorer state. Intended for tests only.
444
+ */
445
+ static reset() {
483
446
  _InjectorExplorer.pending.length = 0;
484
- _InjectorExplorer._phaseOne(queue);
485
- for (const reg of queue) {
486
- if (reg.isController) reg.pathPrefix = pathPrefix;
487
- }
488
- _InjectorExplorer._phaseTwo(queue, void 0, routeGuards, routeMiddlewares);
447
+ _InjectorExplorer.processed = false;
448
+ _InjectorExplorer.accumulating = false;
449
+ _InjectorExplorer.loadingLock = Promise.resolve();
450
+ _InjectorExplorer.controllerRegistrar = null;
489
451
  }
490
452
  // -------------------------------------------------------------------------
491
453
  // Private helpers
@@ -496,8 +458,15 @@ var init_injector_explorer = __esm({
496
458
  RootInjector.register(reg.key, reg.implementation, reg.lifetime, reg.deps);
497
459
  }
498
460
  }
499
- /** Phase 2: resolve singletons and register controllers in the router. */
461
+ /** Phase 2: validate deps, resolve singletons and register controllers via the registrar callback. */
500
462
  static _phaseTwo(queue, overrides, routeGuards = [], routeMiddlewares = []) {
463
+ for (const reg of queue) {
464
+ for (const dep of reg.deps) {
465
+ if (!RootInjector.bindings.has(dep) && !RootInjector.singletons.has(dep)) {
466
+ Logger.warn(`[Noxus DI] "${reg.implementation.name}" declares dep "${dep.name ?? dep}" which has no binding`);
467
+ }
468
+ }
469
+ }
501
470
  for (const reg of queue) {
502
471
  if (overrides?.has(reg.key)) {
503
472
  const override = overrides.get(reg.key);
@@ -509,9 +478,15 @@ var init_injector_explorer = __esm({
509
478
  RootInjector.resolve(reg.key);
510
479
  }
511
480
  if (reg.isController) {
512
- const { Router: Router2 } = (init_router(), __toCommonJS(router_exports));
513
- const router = RootInjector.resolve(Router2);
514
- router.registerController(reg.implementation, reg.pathPrefix ?? "", routeGuards, routeMiddlewares);
481
+ if (!_InjectorExplorer.controllerRegistrar) {
482
+ throw new Error("[Noxus DI] No controller registrar set. Call InjectorExplorer.setControllerRegistrar() before processing.");
483
+ }
484
+ _InjectorExplorer.controllerRegistrar(
485
+ reg.implementation,
486
+ reg.pathPrefix ?? "",
487
+ routeGuards,
488
+ routeMiddlewares
489
+ );
515
490
  } else if (reg.lifetime !== "singleton") {
516
491
  Logger.log(`Registered ${reg.implementation.name} as ${reg.lifetime}`);
517
492
  }
@@ -522,782 +497,135 @@ var init_injector_explorer = __esm({
522
497
  if (reg.lifetime === "singleton") {
523
498
  RootInjector.resolve(reg.key);
524
499
  }
525
- if (reg.isController) {
526
- const { Router: Router2 } = (init_router(), __toCommonJS(router_exports));
527
- const router = RootInjector.resolve(Router2);
528
- router.registerController(reg.implementation);
500
+ if (reg.isController && _InjectorExplorer.controllerRegistrar) {
501
+ _InjectorExplorer.controllerRegistrar(reg.implementation, "", [], []);
529
502
  }
530
503
  }
531
504
  };
532
505
  __name(_InjectorExplorer, "InjectorExplorer");
533
- _InjectorExplorer.pending = [];
534
- _InjectorExplorer.processed = false;
535
- _InjectorExplorer.accumulating = false;
506
+ __publicField(_InjectorExplorer, "pending", []);
507
+ __publicField(_InjectorExplorer, "processed", false);
508
+ __publicField(_InjectorExplorer, "accumulating", false);
509
+ __publicField(_InjectorExplorer, "loadingLock", Promise.resolve());
510
+ __publicField(_InjectorExplorer, "controllerRegistrar", null);
536
511
  InjectorExplorer = _InjectorExplorer;
537
512
  }
538
513
  });
539
514
 
540
- // src/decorators/controller.decorator.ts
541
- function Controller(options = {}) {
542
- return (target) => {
543
- const meta = {
544
- deps: options.deps ?? []
545
- };
546
- controllerMetaMap.set(target, meta);
547
- InjectorExplorer.enqueue({
548
- key: target,
549
- implementation: target,
550
- lifetime: "scope",
551
- deps: options.deps ?? [],
552
- isController: true
553
- });
554
- };
515
+ // src/DI/app-injector.ts
516
+ function keyOf(k) {
517
+ return k;
555
518
  }
556
- function getControllerMetadata(target) {
557
- return controllerMetaMap.get(target);
519
+ function resetRootInjector() {
520
+ RootInjector.bindings.clear();
521
+ RootInjector.singletons.clear();
522
+ RootInjector.scoped.clear();
523
+ const { InjectorExplorer: InjectorExplorer2 } = (init_injector_explorer(), __toCommonJS(injector_explorer_exports));
524
+ InjectorExplorer2.reset();
558
525
  }
559
- var controllerMetaMap;
560
- var init_controller_decorator = __esm({
561
- "src/decorators/controller.decorator.ts"() {
562
- "use strict";
563
- init_injector_explorer();
564
- controllerMetaMap = /* @__PURE__ */ new WeakMap();
565
- __name(Controller, "Controller");
566
- __name(getControllerMetadata, "getControllerMetadata");
567
- }
568
- });
569
-
570
- // src/decorators/injectable.decorator.ts
571
- function Injectable(options = {}) {
572
- const { lifetime = "scope", deps = [] } = options;
573
- return (target) => {
574
- if (typeof target !== "function" || !target.prototype) {
575
- throw new Error(`@Injectable can only be applied to classes, not ${typeof target}`);
576
- }
577
- const key = target;
578
- InjectorExplorer.enqueue({
579
- key,
580
- implementation: key,
581
- lifetime,
582
- deps,
583
- isController: false
584
- });
585
- };
526
+ function inject(t) {
527
+ return RootInjector.resolve(t);
586
528
  }
587
- var init_injectable_decorator = __esm({
588
- "src/decorators/injectable.decorator.ts"() {
529
+ var _AppInjector, AppInjector, RootInjector;
530
+ var init_app_injector = __esm({
531
+ "src/DI/app-injector.ts"() {
589
532
  "use strict";
590
- init_injector_explorer();
533
+ init_forward_ref();
591
534
  init_token();
592
- __name(Injectable, "Injectable");
593
- }
594
- });
595
-
596
- // src/decorators/method.decorator.ts
597
- function isAtomicHttpMethod(m) {
598
- return typeof m === "string" && ATOMIC_METHODS.has(m);
599
- }
600
- function createRouteDecorator(verb) {
601
- return (path2, options = {}) => {
602
- return (target, propertyKey) => {
603
- const ctor = target.constructor;
604
- const existing = routeMetaMap.get(ctor) ?? [];
605
- existing.push({
606
- method: verb,
607
- path: (path2 ?? "").trim().replace(/^\/|\/$/g, ""),
608
- handler: propertyKey,
609
- guards: options.guards ?? [],
610
- middlewares: options.middlewares ?? []
611
- });
612
- routeMetaMap.set(ctor, existing);
613
- };
614
- };
615
- }
616
- function getRouteMetadata(target) {
617
- return routeMetaMap.get(target) ?? [];
618
- }
619
- var ATOMIC_METHODS, routeMetaMap, Get, Post, Put, Patch, Delete;
620
- var init_method_decorator = __esm({
621
- "src/decorators/method.decorator.ts"() {
622
- "use strict";
623
- ATOMIC_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
624
- __name(isAtomicHttpMethod, "isAtomicHttpMethod");
625
- routeMetaMap = /* @__PURE__ */ new WeakMap();
626
- __name(createRouteDecorator, "createRouteDecorator");
627
- __name(getRouteMetadata, "getRouteMetadata");
628
- Get = createRouteDecorator("GET");
629
- Post = createRouteDecorator("POST");
630
- Put = createRouteDecorator("PUT");
631
- Patch = createRouteDecorator("PATCH");
632
- Delete = createRouteDecorator("DELETE");
633
- }
634
- });
635
-
636
- // src/utils/radix-tree.ts
637
- var _RadixNode, RadixNode, _RadixTree, RadixTree;
638
- var init_radix_tree = __esm({
639
- "src/utils/radix-tree.ts"() {
640
- "use strict";
641
- _RadixNode = class _RadixNode {
642
- /**
643
- * Creates a new RadixNode.
644
- * @param segment - The segment of the path this node represents.
645
- */
646
- constructor(segment) {
647
- this.children = [];
648
- this.segment = segment;
649
- this.isParam = segment.startsWith(":");
650
- if (this.isParam) {
651
- this.paramName = segment.slice(1);
652
- }
653
- }
654
- /**
655
- * Matches a child node against a given segment.
656
- * This method checks if the segment matches any of the children nodes.
657
- * @param segment - The segment to match against the children of this node.
658
- * @returns A child node that matches the segment, or undefined if no match is found.
659
- */
660
- matchChild(segment) {
661
- for (const child of this.children) {
662
- if (child.isParam || segment.startsWith(child.segment))
663
- return child;
664
- }
665
- return void 0;
666
- }
667
- /**
668
- * Finds a child node that matches the segment exactly.
669
- * This method checks if there is a child node that matches the segment exactly.
670
- * @param segment - The segment to find an exact match for among the children of this node.
671
- * @returns A child node that matches the segment exactly, or undefined if no match is found.
672
- */
673
- findExactChild(segment) {
674
- return this.children.find((c) => c.segment === segment);
535
+ __name(keyOf, "keyOf");
536
+ _AppInjector = class _AppInjector {
537
+ constructor(name = null) {
538
+ this.name = name;
539
+ __publicField(this, "bindings", /* @__PURE__ */ new Map());
540
+ __publicField(this, "singletons", /* @__PURE__ */ new Map());
541
+ __publicField(this, "scoped", /* @__PURE__ */ new Map());
675
542
  }
676
543
  /**
677
- * Adds a child node to this node's children.
678
- * This method adds a new child node to the list of children for this node.
679
- * @param node - The child node to add to this node's children.
544
+ * Creates a child scope for per-request lifetime resolution.
680
545
  */
681
- addChild(node) {
682
- this.children.push(node);
683
- }
684
- };
685
- __name(_RadixNode, "RadixNode");
686
- RadixNode = _RadixNode;
687
- _RadixTree = class _RadixTree {
688
- constructor() {
689
- this.root = new RadixNode("");
546
+ createScope() {
547
+ const scope = new _AppInjector();
548
+ scope.bindings = this.bindings;
549
+ scope.singletons = this.singletons;
550
+ return scope;
690
551
  }
691
552
  /**
692
- * Inserts a path and its associated value into the Radix Tree.
693
- * This method normalizes the path and inserts it into the tree, associating it with
694
- * @param path - The path to insert into the tree.
695
- * @param value - The value to associate with the path.
553
+ * Registers a binding explicitly.
696
554
  */
697
- insert(path2, value) {
698
- const segments = this.normalize(path2);
699
- this.insertRecursive(this.root, segments, value);
555
+ register(key, implementation, lifetime, deps = []) {
556
+ const k = keyOf(key);
557
+ if (!this.bindings.has(k)) {
558
+ this.bindings.set(k, { lifetime, implementation, deps });
559
+ }
700
560
  }
701
561
  /**
702
- * Recursively inserts a path into the Radix Tree.
703
- * This method traverses the tree and inserts the segments of the path, creating new nodes
704
- * @param node - The node to start inserting from.
705
- * @param segments - The segments of the path to insert.
706
- * @param value - The value to associate with the path.
562
+ * Resolves a dependency by token or class reference.
707
563
  */
708
- insertRecursive(node, segments, value) {
709
- if (segments.length === 0) {
710
- node.value = value;
711
- return;
564
+ resolve(target) {
565
+ if (target instanceof ForwardReference) {
566
+ return this._resolveForwardRef(target);
567
+ }
568
+ const k = keyOf(target);
569
+ if (this.singletons.has(k)) {
570
+ return this.singletons.get(k);
712
571
  }
713
- const segment = segments[0] ?? "";
714
- let child = node.children.find(
715
- (c) => c.isParam === segment.startsWith(":") && (c.isParam || c.segment === segment)
716
- );
717
- if (!child) {
718
- child = new RadixNode(segment);
719
- node.addChild(child);
572
+ const binding = this.bindings.get(k);
573
+ if (!binding) {
574
+ const name = target instanceof Token ? target.description : target.name ?? "unknown";
575
+ throw new Error(
576
+ `[Noxus DI] No binding found for "${name}".
577
+ Did you forget to declare it in @Injectable({ deps }) or in bootstrapApplication({ singletons })?`
578
+ );
720
579
  }
721
- this.insertRecursive(child, segments.slice(1), value);
722
- }
723
- /**
724
- * Searches for a path in the Radix Tree.
725
- * This method normalizes the path and searches for it in the tree, returning the node
726
- * @param path - The path to search for in the Radix Tree.
727
- * @returns An ISearchResult containing the node and parameters if a match is found, otherwise undefined.
728
- */
729
- search(path2) {
730
- const segments = this.normalize(path2);
731
- return this.searchRecursive(this.root, segments, {});
732
- }
733
- /**
734
- * Recursively searches for a path in the Radix Tree.
735
- * This method traverses the tree and searches for the segments of the path, collecting parameters
736
- * @param node - The node to start searching from.
737
- * @param segments - The segments of the path to search for.
738
- * @param params - The parameters collected during the search.
739
- * @returns An ISearchResult containing the node and parameters if a match is found, otherwise undefined.
740
- */
741
- searchRecursive(node, segments, params) {
742
- if (segments.length === 0) {
743
- if (node.value !== void 0) {
744
- return {
745
- node,
746
- params
747
- };
580
+ switch (binding.lifetime) {
581
+ case "transient":
582
+ return this._instantiate(binding);
583
+ case "scope": {
584
+ if (this.scoped.has(k)) return this.scoped.get(k);
585
+ const inst = this._instantiate(binding);
586
+ this.scoped.set(k, inst);
587
+ return inst;
748
588
  }
749
- return void 0;
750
- }
751
- const [segment, ...rest] = segments;
752
- for (const child of node.children) {
753
- if (child.isParam) {
754
- const paramName = child.paramName;
755
- const childParams = {
756
- ...params,
757
- [paramName]: segment ?? ""
758
- };
759
- if (rest.length === 0) {
760
- return {
761
- node: child,
762
- params: childParams
763
- };
764
- }
765
- const result = this.searchRecursive(child, rest, childParams);
766
- if (result)
767
- return result;
768
- } else if (segment === child.segment) {
769
- if (rest.length === 0) {
770
- return {
771
- node: child,
772
- params
773
- };
589
+ case "singleton": {
590
+ if (this.singletons.has(k)) return this.singletons.get(k);
591
+ const inst = this._instantiate(binding);
592
+ this.singletons.set(k, inst);
593
+ if (binding.instance === void 0) {
594
+ binding.instance = inst;
774
595
  }
775
- const result = this.searchRecursive(child, rest, params);
776
- if (result)
777
- return result;
596
+ return inst;
778
597
  }
779
598
  }
780
- return void 0;
781
- }
782
- /**
783
- * Normalizes a path into an array of segments.
784
- * This method removes leading and trailing slashes, splits the path by slashes, and
785
- * @param path - The path to normalize.
786
- * @returns An array of normalized path segments.
787
- */
788
- normalize(path2) {
789
- const segments = path2.replace(/^\/+|\/+$/g, "").split("/").filter(Boolean);
790
- return ["", ...segments];
791
- }
792
- };
793
- __name(_RadixTree, "RadixTree");
794
- RadixTree = _RadixTree;
795
- }
796
- });
797
-
798
- // src/internal/exceptions.ts
799
- var _ResponseException, ResponseException, _BadRequestException, BadRequestException, _UnauthorizedException, UnauthorizedException, _PaymentRequiredException, PaymentRequiredException, _ForbiddenException, ForbiddenException, _NotFoundException, NotFoundException, _MethodNotAllowedException, MethodNotAllowedException, _NotAcceptableException, NotAcceptableException, _RequestTimeoutException, RequestTimeoutException, _ConflictException, ConflictException, _UpgradeRequiredException, UpgradeRequiredException, _TooManyRequestsException, TooManyRequestsException, _InternalServerException, InternalServerException, _NotImplementedException, NotImplementedException, _BadGatewayException, BadGatewayException, _ServiceUnavailableException, ServiceUnavailableException, _GatewayTimeoutException, GatewayTimeoutException, _HttpVersionNotSupportedException, HttpVersionNotSupportedException, _VariantAlsoNegotiatesException, VariantAlsoNegotiatesException, _InsufficientStorageException, InsufficientStorageException, _LoopDetectedException, LoopDetectedException, _NotExtendedException, NotExtendedException, _NetworkAuthenticationRequiredException, NetworkAuthenticationRequiredException, _NetworkConnectTimeoutException, NetworkConnectTimeoutException;
800
- var init_exceptions = __esm({
801
- "src/internal/exceptions.ts"() {
802
- "use strict";
803
- _ResponseException = class _ResponseException extends Error {
804
- constructor(statusOrMessage, message) {
805
- let statusCode;
806
- if (typeof statusOrMessage === "number") {
807
- statusCode = statusOrMessage;
808
- } else if (typeof statusOrMessage === "string") {
809
- message = statusOrMessage;
810
- }
811
- super(message ?? "");
812
- this.status = 0;
813
- if (statusCode !== void 0) {
814
- this.status = statusCode;
815
- }
816
- this.name = this.constructor.name.replace(/([A-Z])/g, " $1");
817
- }
818
- };
819
- __name(_ResponseException, "ResponseException");
820
- ResponseException = _ResponseException;
821
- _BadRequestException = class _BadRequestException extends ResponseException {
822
- constructor() {
823
- super(...arguments);
824
- this.status = 400;
825
- }
826
- };
827
- __name(_BadRequestException, "BadRequestException");
828
- BadRequestException = _BadRequestException;
829
- _UnauthorizedException = class _UnauthorizedException extends ResponseException {
830
- constructor() {
831
- super(...arguments);
832
- this.status = 401;
833
- }
834
- };
835
- __name(_UnauthorizedException, "UnauthorizedException");
836
- UnauthorizedException = _UnauthorizedException;
837
- _PaymentRequiredException = class _PaymentRequiredException extends ResponseException {
838
- constructor() {
839
- super(...arguments);
840
- this.status = 402;
841
- }
842
- };
843
- __name(_PaymentRequiredException, "PaymentRequiredException");
844
- PaymentRequiredException = _PaymentRequiredException;
845
- _ForbiddenException = class _ForbiddenException extends ResponseException {
846
- constructor() {
847
- super(...arguments);
848
- this.status = 403;
849
- }
850
- };
851
- __name(_ForbiddenException, "ForbiddenException");
852
- ForbiddenException = _ForbiddenException;
853
- _NotFoundException = class _NotFoundException extends ResponseException {
854
- constructor() {
855
- super(...arguments);
856
- this.status = 404;
857
- }
858
- };
859
- __name(_NotFoundException, "NotFoundException");
860
- NotFoundException = _NotFoundException;
861
- _MethodNotAllowedException = class _MethodNotAllowedException extends ResponseException {
862
- constructor() {
863
- super(...arguments);
864
- this.status = 405;
865
- }
866
- };
867
- __name(_MethodNotAllowedException, "MethodNotAllowedException");
868
- MethodNotAllowedException = _MethodNotAllowedException;
869
- _NotAcceptableException = class _NotAcceptableException extends ResponseException {
870
- constructor() {
871
- super(...arguments);
872
- this.status = 406;
873
- }
874
- };
875
- __name(_NotAcceptableException, "NotAcceptableException");
876
- NotAcceptableException = _NotAcceptableException;
877
- _RequestTimeoutException = class _RequestTimeoutException extends ResponseException {
878
- constructor() {
879
- super(...arguments);
880
- this.status = 408;
881
- }
882
- };
883
- __name(_RequestTimeoutException, "RequestTimeoutException");
884
- RequestTimeoutException = _RequestTimeoutException;
885
- _ConflictException = class _ConflictException extends ResponseException {
886
- constructor() {
887
- super(...arguments);
888
- this.status = 409;
889
- }
890
- };
891
- __name(_ConflictException, "ConflictException");
892
- ConflictException = _ConflictException;
893
- _UpgradeRequiredException = class _UpgradeRequiredException extends ResponseException {
894
- constructor() {
895
- super(...arguments);
896
- this.status = 426;
897
- }
898
- };
899
- __name(_UpgradeRequiredException, "UpgradeRequiredException");
900
- UpgradeRequiredException = _UpgradeRequiredException;
901
- _TooManyRequestsException = class _TooManyRequestsException extends ResponseException {
902
- constructor() {
903
- super(...arguments);
904
- this.status = 429;
905
- }
906
- };
907
- __name(_TooManyRequestsException, "TooManyRequestsException");
908
- TooManyRequestsException = _TooManyRequestsException;
909
- _InternalServerException = class _InternalServerException extends ResponseException {
910
- constructor() {
911
- super(...arguments);
912
- this.status = 500;
913
- }
914
- };
915
- __name(_InternalServerException, "InternalServerException");
916
- InternalServerException = _InternalServerException;
917
- _NotImplementedException = class _NotImplementedException extends ResponseException {
918
- constructor() {
919
- super(...arguments);
920
- this.status = 501;
921
- }
922
- };
923
- __name(_NotImplementedException, "NotImplementedException");
924
- NotImplementedException = _NotImplementedException;
925
- _BadGatewayException = class _BadGatewayException extends ResponseException {
926
- constructor() {
927
- super(...arguments);
928
- this.status = 502;
929
- }
930
- };
931
- __name(_BadGatewayException, "BadGatewayException");
932
- BadGatewayException = _BadGatewayException;
933
- _ServiceUnavailableException = class _ServiceUnavailableException extends ResponseException {
934
- constructor() {
935
- super(...arguments);
936
- this.status = 503;
937
- }
938
- };
939
- __name(_ServiceUnavailableException, "ServiceUnavailableException");
940
- ServiceUnavailableException = _ServiceUnavailableException;
941
- _GatewayTimeoutException = class _GatewayTimeoutException extends ResponseException {
942
- constructor() {
943
- super(...arguments);
944
- this.status = 504;
945
- }
946
- };
947
- __name(_GatewayTimeoutException, "GatewayTimeoutException");
948
- GatewayTimeoutException = _GatewayTimeoutException;
949
- _HttpVersionNotSupportedException = class _HttpVersionNotSupportedException extends ResponseException {
950
- constructor() {
951
- super(...arguments);
952
- this.status = 505;
953
- }
954
- };
955
- __name(_HttpVersionNotSupportedException, "HttpVersionNotSupportedException");
956
- HttpVersionNotSupportedException = _HttpVersionNotSupportedException;
957
- _VariantAlsoNegotiatesException = class _VariantAlsoNegotiatesException extends ResponseException {
958
- constructor() {
959
- super(...arguments);
960
- this.status = 506;
961
- }
962
- };
963
- __name(_VariantAlsoNegotiatesException, "VariantAlsoNegotiatesException");
964
- VariantAlsoNegotiatesException = _VariantAlsoNegotiatesException;
965
- _InsufficientStorageException = class _InsufficientStorageException extends ResponseException {
966
- constructor() {
967
- super(...arguments);
968
- this.status = 507;
969
- }
970
- };
971
- __name(_InsufficientStorageException, "InsufficientStorageException");
972
- InsufficientStorageException = _InsufficientStorageException;
973
- _LoopDetectedException = class _LoopDetectedException extends ResponseException {
974
- constructor() {
975
- super(...arguments);
976
- this.status = 508;
977
- }
978
- };
979
- __name(_LoopDetectedException, "LoopDetectedException");
980
- LoopDetectedException = _LoopDetectedException;
981
- _NotExtendedException = class _NotExtendedException extends ResponseException {
982
- constructor() {
983
- super(...arguments);
984
- this.status = 510;
985
- }
986
- };
987
- __name(_NotExtendedException, "NotExtendedException");
988
- NotExtendedException = _NotExtendedException;
989
- _NetworkAuthenticationRequiredException = class _NetworkAuthenticationRequiredException extends ResponseException {
990
- constructor() {
991
- super(...arguments);
992
- this.status = 511;
993
- }
994
- };
995
- __name(_NetworkAuthenticationRequiredException, "NetworkAuthenticationRequiredException");
996
- NetworkAuthenticationRequiredException = _NetworkAuthenticationRequiredException;
997
- _NetworkConnectTimeoutException = class _NetworkConnectTimeoutException extends ResponseException {
998
- constructor() {
999
- super(...arguments);
1000
- this.status = 599;
1001
- }
1002
- };
1003
- __name(_NetworkConnectTimeoutException, "NetworkConnectTimeoutException");
1004
- NetworkConnectTimeoutException = _NetworkConnectTimeoutException;
1005
- }
1006
- });
1007
-
1008
- // src/internal/request.ts
1009
- function createRendererEventMessage(event, payload) {
1010
- return {
1011
- type: RENDERER_EVENT_TYPE,
1012
- event,
1013
- payload
1014
- };
1015
- }
1016
- function isRendererEventMessage(value) {
1017
- if (value === null || typeof value !== "object") {
1018
- return false;
1019
- }
1020
- const possibleMessage = value;
1021
- return possibleMessage.type === RENDERER_EVENT_TYPE && typeof possibleMessage.event === "string";
1022
- }
1023
- var _Request, Request, RENDERER_EVENT_TYPE;
1024
- var init_request = __esm({
1025
- "src/internal/request.ts"() {
1026
- "use strict";
1027
- init_app_injector();
1028
- _Request = class _Request {
1029
- constructor(event, senderId, id, method, path2, body) {
1030
- this.event = event;
1031
- this.senderId = senderId;
1032
- this.id = id;
1033
- this.method = method;
1034
- this.path = path2;
1035
- this.body = body;
1036
- this.context = RootInjector.createScope();
1037
- this.params = {};
1038
- this.path = path2.replace(/^\/|\/$/g, "");
1039
- }
1040
- };
1041
- __name(_Request, "Request");
1042
- Request = _Request;
1043
- RENDERER_EVENT_TYPE = "noxus:event";
1044
- __name(createRendererEventMessage, "createRendererEventMessage");
1045
- __name(isRendererEventMessage, "isRendererEventMessage");
1046
- }
1047
- });
1048
-
1049
- // src/internal/router.ts
1050
- var router_exports = {};
1051
- __export(router_exports, {
1052
- Router: () => Router
1053
- });
1054
- var Router;
1055
- var init_router = __esm({
1056
- "src/internal/router.ts"() {
1057
- "use strict";
1058
- init_controller_decorator();
1059
- init_injectable_decorator();
1060
- init_method_decorator();
1061
- init_injector_explorer();
1062
- init_logger();
1063
- init_radix_tree();
1064
- init_exceptions();
1065
- init_request();
1066
- Router = class {
1067
- constructor() {
1068
- this.routes = new RadixTree();
1069
- this.rootMiddlewares = [];
1070
- this.lazyRoutes = /* @__PURE__ */ new Map();
1071
- }
1072
- // -------------------------------------------------------------------------
1073
- // Registration
1074
- // -------------------------------------------------------------------------
1075
- registerController(controllerClass, pathPrefix, routeGuards = [], routeMiddlewares = []) {
1076
- const meta = getControllerMetadata(controllerClass);
1077
- if (!meta) {
1078
- throw new Error(`[Noxus] Missing @Controller decorator on ${controllerClass.name}`);
1079
- }
1080
- const routeMeta = getRouteMetadata(controllerClass);
1081
- for (const def of routeMeta) {
1082
- const fullPath = `${pathPrefix}/${def.path}`.replace(/\/+/g, "/").replace(/\/$/, "") || "/";
1083
- const guards = [.../* @__PURE__ */ new Set([...routeGuards, ...def.guards])];
1084
- const middlewares = [.../* @__PURE__ */ new Set([...routeMiddlewares, ...def.middlewares])];
1085
- const routeDef = {
1086
- method: def.method,
1087
- path: fullPath,
1088
- controller: controllerClass,
1089
- handler: def.handler,
1090
- guards,
1091
- middlewares
1092
- };
1093
- this.routes.insert(fullPath + "/" + def.method, routeDef);
1094
- const guardInfo = guards.length ? `<${guards.map((g) => g.name).join("|")}>` : "";
1095
- Logger.log(`Mapped {${def.method} /${fullPath}}${guardInfo} route`);
1096
- }
1097
- const ctrlGuardInfo = routeGuards.length ? `<${routeGuards.map((g) => g.name).join("|")}>` : "";
1098
- Logger.log(`Mapped ${controllerClass.name}${ctrlGuardInfo} controller's routes`);
1099
- return this;
1100
- }
1101
- registerLazyRoute(pathPrefix, load, guards = [], middlewares = []) {
1102
- const normalized = pathPrefix.replace(/^\/+|\/+$/g, "");
1103
- this.lazyRoutes.set(normalized, { load, guards, middlewares, loading: null, loaded: false });
1104
- Logger.log(`Registered lazy route prefix {${normalized}}`);
1105
- return this;
1106
- }
1107
- defineRootMiddleware(middleware) {
1108
- this.rootMiddlewares.push(middleware);
1109
- return this;
1110
- }
1111
- // -------------------------------------------------------------------------
1112
- // Request handling
1113
- // -------------------------------------------------------------------------
1114
- async handle(request) {
1115
- return request.method === "BATCH" ? this.handleBatch(request) : this.handleAtomic(request);
1116
- }
1117
- async handleAtomic(request) {
1118
- Logger.comment(`> ${request.method} /${request.path}`);
1119
- const t0 = performance.now();
1120
- const response = { requestId: request.id, status: 200, body: null };
1121
- let isCritical = false;
1122
- try {
1123
- const routeDef = await this.findRoute(request);
1124
- await this.resolveController(request, response, routeDef);
1125
- if (response.status >= 400) throw new ResponseException(response.status, response.error);
1126
- } catch (error) {
1127
- this.fillErrorResponse(response, error, (c) => {
1128
- isCritical = c;
1129
- });
1130
- } finally {
1131
- this.logResponse(request, response, performance.now() - t0, isCritical);
1132
- return response;
1133
- }
1134
- }
1135
- async handleBatch(request) {
1136
- Logger.comment(`> ${request.method} /${request.path}`);
1137
- const t0 = performance.now();
1138
- const response = {
1139
- requestId: request.id,
1140
- status: 200,
1141
- body: { responses: [] }
1142
- };
1143
- let isCritical = false;
1144
- try {
1145
- const payload = this.normalizeBatchPayload(request.body);
1146
- response.body.responses = await Promise.all(
1147
- payload.requests.map((item, i) => {
1148
- const id = item.requestId ?? `${request.id}:${i}`;
1149
- return this.handleAtomic(new Request(request.event, request.senderId, id, item.method, item.path, item.body));
1150
- })
1151
- );
1152
- } catch (error) {
1153
- this.fillErrorResponse(response, error, (c) => {
1154
- isCritical = c;
1155
- });
1156
- } finally {
1157
- this.logResponse(request, response, performance.now() - t0, isCritical);
1158
- return response;
1159
- }
1160
- }
1161
- // -------------------------------------------------------------------------
1162
- // Route resolution
1163
- // -------------------------------------------------------------------------
1164
- tryFindRoute(request) {
1165
- const matched = this.routes.search(request.path);
1166
- if (!matched?.node || matched.node.children.length === 0) return void 0;
1167
- return matched.node.findExactChild(request.method)?.value;
1168
- }
1169
- async findRoute(request) {
1170
- const direct = this.tryFindRoute(request);
1171
- if (direct) return direct;
1172
- await this.tryLoadLazyRoute(request.path);
1173
- const afterLazy = this.tryFindRoute(request);
1174
- if (afterLazy) return afterLazy;
1175
- throw new NotFoundException(`No route matches ${request.method} ${request.path}`);
1176
- }
1177
- async tryLoadLazyRoute(requestPath) {
1178
- const firstSegment = requestPath.replace(/^\/+/, "").split("/")[0] ?? "";
1179
- for (const [prefix, entry] of this.lazyRoutes) {
1180
- if (entry.loaded) continue;
1181
- const normalized = requestPath.replace(/^\/+/, "");
1182
- if (normalized === prefix || normalized.startsWith(prefix + "/") || firstSegment === prefix) {
1183
- if (!entry.loading) entry.loading = this.loadLazyModule(prefix, entry);
1184
- await entry.loading;
1185
- return;
1186
- }
1187
- }
1188
- }
1189
- async loadLazyModule(prefix, entry) {
1190
- const t0 = performance.now();
1191
- InjectorExplorer.beginAccumulate();
1192
- await entry.load?.();
1193
- entry.loading = null;
1194
- entry.load = null;
1195
- InjectorExplorer.flushAccumulated(entry.guards, entry.middlewares, prefix);
1196
- entry.loaded = true;
1197
- Logger.info(`Lazy-loaded module for prefix {${prefix}} in ${Math.round(performance.now() - t0)}ms`);
1198
- }
1199
- // -------------------------------------------------------------------------
1200
- // Pipeline
1201
- // -------------------------------------------------------------------------
1202
- async resolveController(request, response, routeDef) {
1203
- const instance = request.context.resolve(routeDef.controller);
1204
- Object.assign(request.params, this.extractParams(request.path, routeDef.path));
1205
- await this.runPipeline(request, response, routeDef, instance);
1206
- }
1207
- async runPipeline(request, response, routeDef, controllerInstance) {
1208
- const middlewares = [.../* @__PURE__ */ new Set([...this.rootMiddlewares, ...routeDef.middlewares])];
1209
- const mwMax = middlewares.length - 1;
1210
- const guardMax = mwMax + routeDef.guards.length;
1211
- let index = -1;
1212
- const dispatch = /* @__PURE__ */ __name(async (i) => {
1213
- if (i <= index) throw new Error("next() called multiple times");
1214
- index = i;
1215
- if (i <= mwMax) {
1216
- await this.runMiddleware(request, response, dispatch.bind(null, i + 1), middlewares[i]);
1217
- if (response.status >= 400) throw new ResponseException(response.status, response.error);
1218
- return;
1219
- }
1220
- if (i <= guardMax) {
1221
- await this.runGuard(request, routeDef.guards[i - middlewares.length]);
1222
- await dispatch(i + 1);
1223
- return;
1224
- }
1225
- const action = controllerInstance[routeDef.handler];
1226
- response.body = await action.call(controllerInstance, request, response);
1227
- if (response.body === void 0) response.body = {};
1228
- }, "dispatch");
1229
- await dispatch(0);
1230
- }
1231
- async runMiddleware(request, response, next, middleware) {
1232
- await middleware(request, response, next);
1233
- }
1234
- async runGuard(request, guard) {
1235
- if (!await guard(request)) {
1236
- throw new UnauthorizedException(`Unauthorized for ${request.method} ${request.path}`);
1237
- }
1238
599
  }
1239
600
  // -------------------------------------------------------------------------
1240
- // Utilities
1241
- // -------------------------------------------------------------------------
1242
- extractParams(actual, template) {
1243
- const aParts = actual.split("/");
1244
- const tParts = template.split("/");
1245
- const params = {};
1246
- tParts.forEach((part, i) => {
1247
- if (part.startsWith(":")) params[part.slice(1)] = aParts[i] ?? "";
601
+ _resolveForwardRef(ref) {
602
+ let resolved;
603
+ return new Proxy({}, {
604
+ get: /* @__PURE__ */ __name((_obj, prop, receiver) => {
605
+ resolved ?? (resolved = this.resolve(ref.forwardRefFn()));
606
+ const value = Reflect.get(resolved, prop, receiver);
607
+ return typeof value === "function" ? value.bind(resolved) : value;
608
+ }, "get"),
609
+ set: /* @__PURE__ */ __name((_obj, prop, value, receiver) => {
610
+ resolved ?? (resolved = this.resolve(ref.forwardRefFn()));
611
+ return Reflect.set(resolved, prop, value, receiver);
612
+ }, "set"),
613
+ getPrototypeOf: /* @__PURE__ */ __name(() => {
614
+ resolved ?? (resolved = this.resolve(ref.forwardRefFn()));
615
+ return Object.getPrototypeOf(resolved);
616
+ }, "getPrototypeOf")
1248
617
  });
1249
- return params;
1250
- }
1251
- normalizeBatchPayload(body) {
1252
- if (body === null || typeof body !== "object") {
1253
- throw new BadRequestException("Batch payload must be an object containing a requests array.");
1254
- }
1255
- const { requests } = body;
1256
- if (!Array.isArray(requests)) throw new BadRequestException("Batch payload must define a requests array.");
1257
- return { requests: requests.map((e, i) => this.normalizeBatchItem(e, i)) };
1258
- }
1259
- normalizeBatchItem(entry, index) {
1260
- if (entry === null || typeof entry !== "object") throw new BadRequestException(`Batch request at index ${index} must be an object.`);
1261
- const { requestId, path: path2, method, body } = entry;
1262
- if (requestId !== void 0 && typeof requestId !== "string") throw new BadRequestException(`Batch request at index ${index} has an invalid requestId.`);
1263
- if (typeof path2 !== "string" || !path2.length) throw new BadRequestException(`Batch request at index ${index} must define a non-empty path.`);
1264
- if (typeof method !== "string") throw new BadRequestException(`Batch request at index ${index} must define an HTTP method.`);
1265
- const normalized = method.toUpperCase();
1266
- if (!isAtomicHttpMethod(normalized)) throw new BadRequestException(`Batch request at index ${index} uses unsupported method ${method}.`);
1267
- return { requestId, path: path2, method: normalized, body };
1268
618
  }
1269
- fillErrorResponse(response, error, setCritical) {
1270
- response.body = void 0;
1271
- if (error instanceof ResponseException) {
1272
- response.status = error.status;
1273
- response.error = error.message;
1274
- response.stack = error.stack;
1275
- } else if (error instanceof Error) {
1276
- setCritical(true);
1277
- response.status = 500;
1278
- response.error = error.message || "Internal Server Error";
1279
- response.stack = error.stack;
1280
- } else {
1281
- setCritical(true);
1282
- response.status = 500;
1283
- response.error = "Unknown error occurred";
1284
- }
1285
- }
1286
- logResponse(request, response, ms, isCritical) {
1287
- const msg = `< ${response.status} ${request.method} /${request.path} ${Logger.colors.yellow}${Math.round(ms)}ms${Logger.colors.initial}`;
1288
- if (response.status < 400) Logger.log(msg);
1289
- else if (response.status < 500) Logger.warn(msg);
1290
- else isCritical ? Logger.critical(msg) : Logger.error(msg);
1291
- if (response.error) {
1292
- isCritical ? Logger.critical(response.error) : Logger.error(response.error);
1293
- if (response.stack) Logger.errorStack(response.stack);
1294
- }
619
+ _instantiate(binding) {
620
+ const resolvedDeps = binding.deps.map((dep) => this.resolve(dep));
621
+ return new binding.implementation(...resolvedDeps);
1295
622
  }
1296
623
  };
1297
- __name(Router, "Router");
1298
- Router = __decorateClass([
1299
- Injectable({ lifetime: "singleton" })
1300
- ], Router);
624
+ __name(_AppInjector, "AppInjector");
625
+ AppInjector = _AppInjector;
626
+ RootInjector = new AppInjector("root");
627
+ __name(resetRootInjector, "resetRootInjector");
628
+ __name(inject, "inject");
1301
629
  }
1302
630
  });
1303
631
 
@@ -1355,27 +683,789 @@ __export(main_exports, {
1355
683
  inject: () => inject,
1356
684
  isAtomicHttpMethod: () => isAtomicHttpMethod,
1357
685
  isRendererEventMessage: () => isRendererEventMessage,
686
+ resetRootInjector: () => resetRootInjector,
1358
687
  token: () => token
1359
688
  });
1360
689
  module.exports = __toCommonJS(main_exports);
1361
690
  init_app_injector();
1362
691
  init_token();
1363
- init_router();
692
+
693
+ // src/decorators/controller.decorator.ts
694
+ init_injector_explorer();
695
+ var controllerMetaMap = /* @__PURE__ */ new WeakMap();
696
+ function Controller(options = {}) {
697
+ return (target, _context) => {
698
+ const meta = {
699
+ deps: options.deps ?? []
700
+ };
701
+ controllerMetaMap.set(target, meta);
702
+ InjectorExplorer.enqueue({
703
+ key: target,
704
+ implementation: target,
705
+ lifetime: "scope",
706
+ deps: options.deps ?? [],
707
+ isController: true
708
+ });
709
+ };
710
+ }
711
+ __name(Controller, "Controller");
712
+ function getControllerMetadata(target) {
713
+ return controllerMetaMap.get(target);
714
+ }
715
+ __name(getControllerMetadata, "getControllerMetadata");
716
+
717
+ // src/decorators/injectable.decorator.ts
718
+ init_injector_explorer();
719
+ init_token();
720
+ function Injectable(options = {}) {
721
+ const { lifetime = "scope", deps = [] } = options;
722
+ return (target, _context) => {
723
+ const key = target;
724
+ InjectorExplorer.enqueue({
725
+ key,
726
+ implementation: key,
727
+ lifetime,
728
+ deps,
729
+ isController: false
730
+ });
731
+ };
732
+ }
733
+ __name(Injectable, "Injectable");
734
+
735
+ // src/decorators/method.decorator.ts
736
+ var ATOMIC_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
737
+ function isAtomicHttpMethod(m) {
738
+ return typeof m === "string" && ATOMIC_METHODS.has(m);
739
+ }
740
+ __name(isAtomicHttpMethod, "isAtomicHttpMethod");
741
+ var methodMeta = /* @__PURE__ */ new WeakMap();
742
+ function createRouteDecorator(verb) {
743
+ return (path2, options = {}) => {
744
+ return (value, context) => {
745
+ methodMeta.set(value, {
746
+ method: verb,
747
+ path: (path2 ?? "").trim().replace(/^\/|\/$/g, ""),
748
+ handler: context.name,
749
+ guards: options.guards ?? [],
750
+ middlewares: options.middlewares ?? []
751
+ });
752
+ };
753
+ };
754
+ }
755
+ __name(createRouteDecorator, "createRouteDecorator");
756
+ function getRouteMetadata(target) {
757
+ const routes = [];
758
+ const proto = target.prototype;
759
+ for (const key of Object.getOwnPropertyNames(proto)) {
760
+ const fn = proto[key];
761
+ if (typeof fn === "function" && methodMeta.has(fn)) {
762
+ const meta = methodMeta.get(fn);
763
+ if (meta) {
764
+ routes.push(meta);
765
+ }
766
+ }
767
+ }
768
+ return routes;
769
+ }
770
+ __name(getRouteMetadata, "getRouteMetadata");
771
+ var Get = createRouteDecorator("GET");
772
+ var Post = createRouteDecorator("POST");
773
+ var Put = createRouteDecorator("PUT");
774
+ var Patch = createRouteDecorator("PATCH");
775
+ var Delete = createRouteDecorator("DELETE");
776
+
777
+ // src/internal/router.ts
778
+ init_injector_explorer();
779
+ init_logger();
780
+
781
+ // src/utils/radix-tree.ts
782
+ var _RadixNode = class _RadixNode {
783
+ /**
784
+ * Creates a new RadixNode.
785
+ * @param segment - The segment of the path this node represents.
786
+ */
787
+ constructor(segment) {
788
+ __publicField(this, "segment");
789
+ __publicField(this, "children", []);
790
+ __publicField(this, "value");
791
+ __publicField(this, "isParam");
792
+ __publicField(this, "paramName");
793
+ this.segment = segment;
794
+ this.isParam = segment.startsWith(":");
795
+ if (this.isParam) {
796
+ this.paramName = segment.slice(1);
797
+ }
798
+ }
799
+ /**
800
+ * Matches a child node against a given segment.
801
+ * This method checks if the segment matches any of the children nodes.
802
+ * @param segment - The segment to match against the children of this node.
803
+ * @returns A child node that matches the segment, or undefined if no match is found.
804
+ */
805
+ matchChild(segment) {
806
+ for (const child of this.children) {
807
+ if (child.isParam || segment.startsWith(child.segment))
808
+ return child;
809
+ }
810
+ return void 0;
811
+ }
812
+ /**
813
+ * Finds a child node that matches the segment exactly.
814
+ * This method checks if there is a child node that matches the segment exactly.
815
+ * @param segment - The segment to find an exact match for among the children of this node.
816
+ * @returns A child node that matches the segment exactly, or undefined if no match is found.
817
+ */
818
+ findExactChild(segment) {
819
+ return this.children.find((c) => c.segment === segment);
820
+ }
821
+ /**
822
+ * Adds a child node to this node's children.
823
+ * This method adds a new child node to the list of children for this node.
824
+ * @param node - The child node to add to this node's children.
825
+ */
826
+ addChild(node) {
827
+ this.children.push(node);
828
+ }
829
+ };
830
+ __name(_RadixNode, "RadixNode");
831
+ var RadixNode = _RadixNode;
832
+ var _RadixTree = class _RadixTree {
833
+ constructor() {
834
+ __publicField(this, "root", new RadixNode(""));
835
+ }
836
+ /**
837
+ * Inserts a path and its associated value into the Radix Tree.
838
+ * This method normalizes the path and inserts it into the tree, associating it with
839
+ * @param path - The path to insert into the tree.
840
+ * @param value - The value to associate with the path.
841
+ */
842
+ insert(path2, value) {
843
+ const segments = this.normalize(path2);
844
+ this.insertRecursive(this.root, segments, value);
845
+ }
846
+ /**
847
+ * Recursively inserts a path into the Radix Tree.
848
+ * This method traverses the tree and inserts the segments of the path, creating new nodes
849
+ * @param node - The node to start inserting from.
850
+ * @param segments - The segments of the path to insert.
851
+ * @param value - The value to associate with the path.
852
+ */
853
+ insertRecursive(node, segments, value) {
854
+ if (segments.length === 0) {
855
+ node.value = value;
856
+ return;
857
+ }
858
+ const segment = segments[0] ?? "";
859
+ let child = node.children.find(
860
+ (c) => c.isParam === segment.startsWith(":") && (c.isParam || c.segment === segment)
861
+ );
862
+ if (!child) {
863
+ child = new RadixNode(segment);
864
+ node.addChild(child);
865
+ }
866
+ this.insertRecursive(child, segments.slice(1), value);
867
+ }
868
+ /**
869
+ * Searches for a path in the Radix Tree.
870
+ * This method normalizes the path and searches for it in the tree, returning the node
871
+ * @param path - The path to search for in the Radix Tree.
872
+ * @returns An ISearchResult containing the node and parameters if a match is found, otherwise undefined.
873
+ */
874
+ search(path2) {
875
+ const segments = this.normalize(path2);
876
+ return this.searchRecursive(this.root, segments, {});
877
+ }
878
+ collectValues(node, values = []) {
879
+ if (!node) {
880
+ node = this.root;
881
+ }
882
+ if (node.value !== void 0) {
883
+ values.push(node.value);
884
+ }
885
+ for (const child of node.children) {
886
+ this.collectValues(child, values);
887
+ }
888
+ return values;
889
+ }
890
+ /**
891
+ * Recursively searches for a path in the Radix Tree.
892
+ * This method traverses the tree and searches for the segments of the path, collecting parameters
893
+ * @param node - The node to start searching from.
894
+ * @param segments - The segments of the path to search for.
895
+ * @param params - The parameters collected during the search.
896
+ * @returns An ISearchResult containing the node and parameters if a match is found, otherwise undefined.
897
+ */
898
+ searchRecursive(node, segments, params) {
899
+ if (segments.length === 0) {
900
+ if (node.value !== void 0) {
901
+ return {
902
+ node,
903
+ params
904
+ };
905
+ }
906
+ return void 0;
907
+ }
908
+ const [segment, ...rest] = segments;
909
+ const staticChildren = [];
910
+ const paramChildren = [];
911
+ for (const child of node.children) {
912
+ if (child.isParam) {
913
+ paramChildren.push(child);
914
+ } else if (segment === child.segment) {
915
+ staticChildren.push(child);
916
+ }
917
+ }
918
+ for (const child of staticChildren) {
919
+ if (rest.length === 0) {
920
+ if (child.value !== void 0 || child.children.length > 0) {
921
+ return { node: child, params };
922
+ }
923
+ }
924
+ const result = this.searchRecursive(child, rest, params);
925
+ if (result) return result;
926
+ }
927
+ for (const child of paramChildren) {
928
+ const paramName = child.paramName;
929
+ const childParams = {
930
+ ...params,
931
+ [paramName]: segment ?? ""
932
+ };
933
+ if (rest.length === 0) {
934
+ if (child.value !== void 0 || child.children.length > 0) {
935
+ return { node: child, params: childParams };
936
+ }
937
+ }
938
+ const result = this.searchRecursive(child, rest, childParams);
939
+ if (result) return result;
940
+ }
941
+ return void 0;
942
+ }
943
+ /**
944
+ * Normalizes a path into an array of segments.
945
+ * This method removes leading and trailing slashes, splits the path by slashes, and
946
+ * @param path - The path to normalize.
947
+ * @returns An array of normalized path segments.
948
+ */
949
+ normalize(path2) {
950
+ const segments = path2.replace(/^\/+|\/+$/g, "").split("/").filter(Boolean);
951
+ return ["", ...segments];
952
+ }
953
+ };
954
+ __name(_RadixTree, "RadixTree");
955
+ var RadixTree = _RadixTree;
956
+
957
+ // src/internal/exceptions.ts
958
+ var _ResponseException = class _ResponseException extends Error {
959
+ constructor(statusOrMessage, message) {
960
+ let statusCode;
961
+ if (typeof statusOrMessage === "number") {
962
+ statusCode = statusOrMessage;
963
+ } else if (typeof statusOrMessage === "string") {
964
+ message = statusOrMessage;
965
+ }
966
+ super(message ?? "");
967
+ __publicField(this, "status", 0);
968
+ if (statusCode !== void 0) {
969
+ this.status = statusCode;
970
+ }
971
+ this.name = this.constructor.name.replace(/([A-Z])/g, " $1");
972
+ }
973
+ };
974
+ __name(_ResponseException, "ResponseException");
975
+ var ResponseException = _ResponseException;
976
+ var _BadRequestException = class _BadRequestException extends ResponseException {
977
+ constructor() {
978
+ super(...arguments);
979
+ __publicField(this, "status", 400);
980
+ }
981
+ };
982
+ __name(_BadRequestException, "BadRequestException");
983
+ var BadRequestException = _BadRequestException;
984
+ var _UnauthorizedException = class _UnauthorizedException extends ResponseException {
985
+ constructor() {
986
+ super(...arguments);
987
+ __publicField(this, "status", 401);
988
+ }
989
+ };
990
+ __name(_UnauthorizedException, "UnauthorizedException");
991
+ var UnauthorizedException = _UnauthorizedException;
992
+ var _PaymentRequiredException = class _PaymentRequiredException extends ResponseException {
993
+ constructor() {
994
+ super(...arguments);
995
+ __publicField(this, "status", 402);
996
+ }
997
+ };
998
+ __name(_PaymentRequiredException, "PaymentRequiredException");
999
+ var PaymentRequiredException = _PaymentRequiredException;
1000
+ var _ForbiddenException = class _ForbiddenException extends ResponseException {
1001
+ constructor() {
1002
+ super(...arguments);
1003
+ __publicField(this, "status", 403);
1004
+ }
1005
+ };
1006
+ __name(_ForbiddenException, "ForbiddenException");
1007
+ var ForbiddenException = _ForbiddenException;
1008
+ var _NotFoundException = class _NotFoundException extends ResponseException {
1009
+ constructor() {
1010
+ super(...arguments);
1011
+ __publicField(this, "status", 404);
1012
+ }
1013
+ };
1014
+ __name(_NotFoundException, "NotFoundException");
1015
+ var NotFoundException = _NotFoundException;
1016
+ var _MethodNotAllowedException = class _MethodNotAllowedException extends ResponseException {
1017
+ constructor() {
1018
+ super(...arguments);
1019
+ __publicField(this, "status", 405);
1020
+ }
1021
+ };
1022
+ __name(_MethodNotAllowedException, "MethodNotAllowedException");
1023
+ var MethodNotAllowedException = _MethodNotAllowedException;
1024
+ var _NotAcceptableException = class _NotAcceptableException extends ResponseException {
1025
+ constructor() {
1026
+ super(...arguments);
1027
+ __publicField(this, "status", 406);
1028
+ }
1029
+ };
1030
+ __name(_NotAcceptableException, "NotAcceptableException");
1031
+ var NotAcceptableException = _NotAcceptableException;
1032
+ var _RequestTimeoutException = class _RequestTimeoutException extends ResponseException {
1033
+ constructor() {
1034
+ super(...arguments);
1035
+ __publicField(this, "status", 408);
1036
+ }
1037
+ };
1038
+ __name(_RequestTimeoutException, "RequestTimeoutException");
1039
+ var RequestTimeoutException = _RequestTimeoutException;
1040
+ var _ConflictException = class _ConflictException extends ResponseException {
1041
+ constructor() {
1042
+ super(...arguments);
1043
+ __publicField(this, "status", 409);
1044
+ }
1045
+ };
1046
+ __name(_ConflictException, "ConflictException");
1047
+ var ConflictException = _ConflictException;
1048
+ var _UpgradeRequiredException = class _UpgradeRequiredException extends ResponseException {
1049
+ constructor() {
1050
+ super(...arguments);
1051
+ __publicField(this, "status", 426);
1052
+ }
1053
+ };
1054
+ __name(_UpgradeRequiredException, "UpgradeRequiredException");
1055
+ var UpgradeRequiredException = _UpgradeRequiredException;
1056
+ var _TooManyRequestsException = class _TooManyRequestsException extends ResponseException {
1057
+ constructor() {
1058
+ super(...arguments);
1059
+ __publicField(this, "status", 429);
1060
+ }
1061
+ };
1062
+ __name(_TooManyRequestsException, "TooManyRequestsException");
1063
+ var TooManyRequestsException = _TooManyRequestsException;
1064
+ var _InternalServerException = class _InternalServerException extends ResponseException {
1065
+ constructor() {
1066
+ super(...arguments);
1067
+ __publicField(this, "status", 500);
1068
+ }
1069
+ };
1070
+ __name(_InternalServerException, "InternalServerException");
1071
+ var InternalServerException = _InternalServerException;
1072
+ var _NotImplementedException = class _NotImplementedException extends ResponseException {
1073
+ constructor() {
1074
+ super(...arguments);
1075
+ __publicField(this, "status", 501);
1076
+ }
1077
+ };
1078
+ __name(_NotImplementedException, "NotImplementedException");
1079
+ var NotImplementedException = _NotImplementedException;
1080
+ var _BadGatewayException = class _BadGatewayException extends ResponseException {
1081
+ constructor() {
1082
+ super(...arguments);
1083
+ __publicField(this, "status", 502);
1084
+ }
1085
+ };
1086
+ __name(_BadGatewayException, "BadGatewayException");
1087
+ var BadGatewayException = _BadGatewayException;
1088
+ var _ServiceUnavailableException = class _ServiceUnavailableException extends ResponseException {
1089
+ constructor() {
1090
+ super(...arguments);
1091
+ __publicField(this, "status", 503);
1092
+ }
1093
+ };
1094
+ __name(_ServiceUnavailableException, "ServiceUnavailableException");
1095
+ var ServiceUnavailableException = _ServiceUnavailableException;
1096
+ var _GatewayTimeoutException = class _GatewayTimeoutException extends ResponseException {
1097
+ constructor() {
1098
+ super(...arguments);
1099
+ __publicField(this, "status", 504);
1100
+ }
1101
+ };
1102
+ __name(_GatewayTimeoutException, "GatewayTimeoutException");
1103
+ var GatewayTimeoutException = _GatewayTimeoutException;
1104
+ var _HttpVersionNotSupportedException = class _HttpVersionNotSupportedException extends ResponseException {
1105
+ constructor() {
1106
+ super(...arguments);
1107
+ __publicField(this, "status", 505);
1108
+ }
1109
+ };
1110
+ __name(_HttpVersionNotSupportedException, "HttpVersionNotSupportedException");
1111
+ var HttpVersionNotSupportedException = _HttpVersionNotSupportedException;
1112
+ var _VariantAlsoNegotiatesException = class _VariantAlsoNegotiatesException extends ResponseException {
1113
+ constructor() {
1114
+ super(...arguments);
1115
+ __publicField(this, "status", 506);
1116
+ }
1117
+ };
1118
+ __name(_VariantAlsoNegotiatesException, "VariantAlsoNegotiatesException");
1119
+ var VariantAlsoNegotiatesException = _VariantAlsoNegotiatesException;
1120
+ var _InsufficientStorageException = class _InsufficientStorageException extends ResponseException {
1121
+ constructor() {
1122
+ super(...arguments);
1123
+ __publicField(this, "status", 507);
1124
+ }
1125
+ };
1126
+ __name(_InsufficientStorageException, "InsufficientStorageException");
1127
+ var InsufficientStorageException = _InsufficientStorageException;
1128
+ var _LoopDetectedException = class _LoopDetectedException extends ResponseException {
1129
+ constructor() {
1130
+ super(...arguments);
1131
+ __publicField(this, "status", 508);
1132
+ }
1133
+ };
1134
+ __name(_LoopDetectedException, "LoopDetectedException");
1135
+ var LoopDetectedException = _LoopDetectedException;
1136
+ var _NotExtendedException = class _NotExtendedException extends ResponseException {
1137
+ constructor() {
1138
+ super(...arguments);
1139
+ __publicField(this, "status", 510);
1140
+ }
1141
+ };
1142
+ __name(_NotExtendedException, "NotExtendedException");
1143
+ var NotExtendedException = _NotExtendedException;
1144
+ var _NetworkAuthenticationRequiredException = class _NetworkAuthenticationRequiredException extends ResponseException {
1145
+ constructor() {
1146
+ super(...arguments);
1147
+ __publicField(this, "status", 511);
1148
+ }
1149
+ };
1150
+ __name(_NetworkAuthenticationRequiredException, "NetworkAuthenticationRequiredException");
1151
+ var NetworkAuthenticationRequiredException = _NetworkAuthenticationRequiredException;
1152
+ var _NetworkConnectTimeoutException = class _NetworkConnectTimeoutException extends ResponseException {
1153
+ constructor() {
1154
+ super(...arguments);
1155
+ __publicField(this, "status", 599);
1156
+ }
1157
+ };
1158
+ __name(_NetworkConnectTimeoutException, "NetworkConnectTimeoutException");
1159
+ var NetworkConnectTimeoutException = _NetworkConnectTimeoutException;
1160
+
1161
+ // src/internal/request.ts
1162
+ init_app_injector();
1163
+ var _Request = class _Request {
1164
+ constructor(event, senderId, id, method, path2, body, query) {
1165
+ this.event = event;
1166
+ this.senderId = senderId;
1167
+ this.id = id;
1168
+ this.method = method;
1169
+ this.path = path2;
1170
+ this.body = body;
1171
+ __publicField(this, "context", RootInjector.createScope());
1172
+ __publicField(this, "params", {});
1173
+ __publicField(this, "query");
1174
+ this.path = path2.replace(/^\/|\/$/g, "");
1175
+ this.query = query ?? {};
1176
+ }
1177
+ };
1178
+ __name(_Request, "Request");
1179
+ var Request = _Request;
1180
+ var RENDERER_EVENT_TYPE = "noxus:event";
1181
+ function createRendererEventMessage(event, payload) {
1182
+ return {
1183
+ type: RENDERER_EVENT_TYPE,
1184
+ event,
1185
+ payload
1186
+ };
1187
+ }
1188
+ __name(createRendererEventMessage, "createRendererEventMessage");
1189
+ function isRendererEventMessage(value) {
1190
+ if (value === null || typeof value !== "object") {
1191
+ return false;
1192
+ }
1193
+ const possibleMessage = value;
1194
+ return possibleMessage.type === RENDERER_EVENT_TYPE && typeof possibleMessage.event === "string";
1195
+ }
1196
+ __name(isRendererEventMessage, "isRendererEventMessage");
1197
+
1198
+ // src/internal/router.ts
1199
+ var _Router_decorators, _init;
1200
+ _Router_decorators = [Injectable({ lifetime: "singleton" })];
1201
+ var _Router = class _Router {
1202
+ constructor() {
1203
+ __publicField(this, "routes", new RadixTree());
1204
+ __publicField(this, "rootMiddlewares", []);
1205
+ __publicField(this, "lazyRoutes", /* @__PURE__ */ new Map());
1206
+ __publicField(this, "lazyLoadLock", Promise.resolve());
1207
+ }
1208
+ // -------------------------------------------------------------------------
1209
+ // Registration
1210
+ // -------------------------------------------------------------------------
1211
+ registerController(controllerClass, pathPrefix, routeGuards = [], routeMiddlewares = []) {
1212
+ const meta = getControllerMetadata(controllerClass);
1213
+ if (!meta) {
1214
+ throw new Error(`[Noxus] Missing @Controller decorator on ${controllerClass.name}`);
1215
+ }
1216
+ const routeMeta = getRouteMetadata(controllerClass);
1217
+ for (const def of routeMeta) {
1218
+ const fullPath = `${pathPrefix}/${def.path}`.replace(/\/+/g, "/").replace(/\/$/, "") || "/";
1219
+ const guards = [.../* @__PURE__ */ new Set([...routeGuards, ...def.guards])];
1220
+ const middlewares = [.../* @__PURE__ */ new Set([...routeMiddlewares, ...def.middlewares])];
1221
+ const routeDef = {
1222
+ method: def.method,
1223
+ path: fullPath,
1224
+ controller: controllerClass,
1225
+ handler: def.handler,
1226
+ guards,
1227
+ middlewares
1228
+ };
1229
+ this.routes.insert(fullPath + "/" + def.method, routeDef);
1230
+ const guardInfo = guards.length ? `<${guards.map((g) => g.name).join("|")}>` : "";
1231
+ Logger.log(`Mapped {${def.method} /${fullPath}}${guardInfo} route`);
1232
+ }
1233
+ const ctrlGuardInfo = routeGuards.length ? `<${routeGuards.map((g) => g.name).join("|")}>` : "";
1234
+ Logger.log(`Mapped ${controllerClass.name}${ctrlGuardInfo} controller's routes`);
1235
+ return this;
1236
+ }
1237
+ registerLazyRoute(pathPrefix, load, guards = [], middlewares = []) {
1238
+ const normalized = pathPrefix.replace(/^\/+|\/+$/g, "");
1239
+ this.lazyRoutes.set(normalized, { load, guards, middlewares, loading: null, loaded: false });
1240
+ Logger.log(`Registered lazy route prefix {${normalized}}`);
1241
+ return this;
1242
+ }
1243
+ defineRootMiddleware(middleware) {
1244
+ this.rootMiddlewares.push(middleware);
1245
+ return this;
1246
+ }
1247
+ getRegisteredRoutes() {
1248
+ const allRoutes = this.routes.collectValues();
1249
+ return allRoutes.map((r) => ({ method: r.method, path: r.path }));
1250
+ }
1251
+ getLazyRoutes() {
1252
+ return [...this.lazyRoutes.entries()].map(([prefix, entry]) => ({
1253
+ prefix,
1254
+ loaded: entry.loaded
1255
+ }));
1256
+ }
1257
+ // -------------------------------------------------------------------------
1258
+ // Request handling
1259
+ // -------------------------------------------------------------------------
1260
+ async handle(request) {
1261
+ return request.method === "BATCH" ? this.handleBatch(request) : this.handleAtomic(request);
1262
+ }
1263
+ async handleAtomic(request) {
1264
+ Logger.comment(`> ${request.method} /${request.path}`);
1265
+ const t0 = performance.now();
1266
+ const response = { requestId: request.id, status: 200, body: null };
1267
+ let isCritical = false;
1268
+ try {
1269
+ const routeDef = await this.findRoute(request);
1270
+ await this.resolveController(request, response, routeDef);
1271
+ if (response.status >= 400) throw new ResponseException(response.status, response.error);
1272
+ } catch (error) {
1273
+ this.fillErrorResponse(response, error, (c) => {
1274
+ isCritical = c;
1275
+ });
1276
+ } finally {
1277
+ this.logResponse(request, response, performance.now() - t0, isCritical);
1278
+ return response;
1279
+ }
1280
+ }
1281
+ async handleBatch(request) {
1282
+ Logger.comment(`> ${request.method} /${request.path}`);
1283
+ const t0 = performance.now();
1284
+ const response = {
1285
+ requestId: request.id,
1286
+ status: 200,
1287
+ body: { responses: [] }
1288
+ };
1289
+ let isCritical = false;
1290
+ try {
1291
+ const payload = this.normalizeBatchPayload(request.body);
1292
+ response.body.responses = await Promise.all(
1293
+ payload.requests.map((item, i) => {
1294
+ const id = item.requestId ?? `${request.id}:${i}`;
1295
+ return this.handleAtomic(new Request(request.event, request.senderId, id, item.method, item.path, item.body, item.query));
1296
+ })
1297
+ );
1298
+ } catch (error) {
1299
+ this.fillErrorResponse(response, error, (c) => {
1300
+ isCritical = c;
1301
+ });
1302
+ } finally {
1303
+ this.logResponse(request, response, performance.now() - t0, isCritical);
1304
+ return response;
1305
+ }
1306
+ }
1307
+ // -------------------------------------------------------------------------
1308
+ // Route resolution
1309
+ // -------------------------------------------------------------------------
1310
+ tryFindRoute(request) {
1311
+ const matched = this.routes.search(request.path);
1312
+ if (!matched?.node || matched.node.children.length === 0) return void 0;
1313
+ return matched.node.findExactChild(request.method)?.value;
1314
+ }
1315
+ async findRoute(request) {
1316
+ const direct = this.tryFindRoute(request);
1317
+ if (direct) return direct;
1318
+ await this.tryLoadLazyRoute(request.path);
1319
+ const afterLazy = this.tryFindRoute(request);
1320
+ if (afterLazy) return afterLazy;
1321
+ throw new NotFoundException(`No route matches ${request.method} ${request.path}`);
1322
+ }
1323
+ async tryLoadLazyRoute(requestPath) {
1324
+ const firstSegment = requestPath.replace(/^\/+/, "").split("/")[0] ?? "";
1325
+ for (const [prefix, entry] of this.lazyRoutes) {
1326
+ if (entry.loaded) continue;
1327
+ const normalized = requestPath.replace(/^\/+/, "");
1328
+ if (normalized === prefix || normalized.startsWith(prefix + "/") || firstSegment === prefix) {
1329
+ if (!entry.loading) entry.loading = this.loadLazyModule(prefix, entry);
1330
+ await entry.loading;
1331
+ return;
1332
+ }
1333
+ }
1334
+ }
1335
+ loadLazyModule(prefix, entry) {
1336
+ const task = this.lazyLoadLock.then(async () => {
1337
+ const t0 = performance.now();
1338
+ InjectorExplorer.beginAccumulate();
1339
+ await entry.load?.();
1340
+ entry.load = null;
1341
+ await InjectorExplorer.flushAccumulated(entry.guards, entry.middlewares, prefix);
1342
+ entry.loaded = true;
1343
+ entry.loading = null;
1344
+ Logger.info(`Lazy-loaded module for prefix {${prefix}} in ${Math.round(performance.now() - t0)}ms`);
1345
+ });
1346
+ this.lazyLoadLock = task;
1347
+ return task;
1348
+ }
1349
+ // -------------------------------------------------------------------------
1350
+ // Pipeline
1351
+ // -------------------------------------------------------------------------
1352
+ async resolveController(request, response, routeDef) {
1353
+ const instance = request.context.resolve(routeDef.controller);
1354
+ Object.assign(request.params, this.extractParams(request.path, routeDef.path));
1355
+ await this.runPipeline(request, response, routeDef, instance);
1356
+ }
1357
+ async runPipeline(request, response, routeDef, controllerInstance) {
1358
+ const middlewares = [.../* @__PURE__ */ new Set([...this.rootMiddlewares, ...routeDef.middlewares])];
1359
+ const mwMax = middlewares.length - 1;
1360
+ const guardMax = mwMax + routeDef.guards.length;
1361
+ let index = -1;
1362
+ const dispatch = /* @__PURE__ */ __name(async (i) => {
1363
+ if (i <= index) throw new Error("next() called multiple times");
1364
+ index = i;
1365
+ if (i <= mwMax) {
1366
+ await this.runMiddleware(request, response, dispatch.bind(null, i + 1), middlewares[i]);
1367
+ if (response.status >= 400) throw new ResponseException(response.status, response.error);
1368
+ return;
1369
+ }
1370
+ if (i <= guardMax) {
1371
+ await this.runGuard(request, routeDef.guards[i - middlewares.length]);
1372
+ await dispatch(i + 1);
1373
+ return;
1374
+ }
1375
+ const action = controllerInstance[routeDef.handler];
1376
+ response.body = await action.call(controllerInstance, request, response);
1377
+ if (response.body === void 0) response.body = {};
1378
+ }, "dispatch");
1379
+ await dispatch(0);
1380
+ }
1381
+ async runMiddleware(request, response, next, middleware) {
1382
+ await middleware(request, response, next);
1383
+ }
1384
+ async runGuard(request, guard) {
1385
+ if (!await guard(request)) {
1386
+ throw new UnauthorizedException(`Unauthorized for ${request.method} ${request.path}`);
1387
+ }
1388
+ }
1389
+ // -------------------------------------------------------------------------
1390
+ // Utilities
1391
+ // -------------------------------------------------------------------------
1392
+ extractParams(actual, template) {
1393
+ const aParts = actual.split("/");
1394
+ const tParts = template.split("/");
1395
+ const params = {};
1396
+ tParts.forEach((part, i) => {
1397
+ if (part.startsWith(":")) params[part.slice(1)] = aParts[i] ?? "";
1398
+ });
1399
+ return params;
1400
+ }
1401
+ normalizeBatchPayload(body) {
1402
+ if (body === null || typeof body !== "object") {
1403
+ throw new BadRequestException("Batch payload must be an object containing a requests array.");
1404
+ }
1405
+ const { requests } = body;
1406
+ if (!Array.isArray(requests)) throw new BadRequestException("Batch payload must define a requests array.");
1407
+ return { requests: requests.map((e, i) => this.normalizeBatchItem(e, i)) };
1408
+ }
1409
+ normalizeBatchItem(entry, index) {
1410
+ if (entry === null || typeof entry !== "object") throw new BadRequestException(`Batch request at index ${index} must be an object.`);
1411
+ const { requestId, path: path2, method, body, query } = entry;
1412
+ if (requestId !== void 0 && typeof requestId !== "string") throw new BadRequestException(`Batch request at index ${index} has an invalid requestId.`);
1413
+ if (typeof path2 !== "string" || !path2.length) throw new BadRequestException(`Batch request at index ${index} must define a non-empty path.`);
1414
+ if (typeof method !== "string") throw new BadRequestException(`Batch request at index ${index} must define an HTTP method.`);
1415
+ const normalized = method.toUpperCase();
1416
+ if (!isAtomicHttpMethod(normalized)) throw new BadRequestException(`Batch request at index ${index} uses unsupported method ${method}.`);
1417
+ return { requestId, path: path2, method: normalized, body, query };
1418
+ }
1419
+ fillErrorResponse(response, error, setCritical) {
1420
+ response.body = void 0;
1421
+ if (error instanceof ResponseException) {
1422
+ response.status = error.status;
1423
+ response.error = error.message;
1424
+ response.stack = error.stack;
1425
+ } else if (error instanceof Error) {
1426
+ setCritical(true);
1427
+ response.status = 500;
1428
+ response.error = error.message || "Internal Server Error";
1429
+ response.stack = error.stack;
1430
+ } else {
1431
+ setCritical(true);
1432
+ response.status = 500;
1433
+ response.error = "Unknown error occurred";
1434
+ }
1435
+ }
1436
+ logResponse(request, response, ms, isCritical) {
1437
+ const msg = `< ${response.status} ${request.method} /${request.path} ${Logger.colors.yellow}${Math.round(ms)}ms${Logger.colors.initial}`;
1438
+ if (response.status < 400) Logger.log(msg);
1439
+ else if (response.status < 500) Logger.warn(msg);
1440
+ else isCritical ? Logger.critical(msg) : Logger.error(msg);
1441
+ if (response.error) {
1442
+ isCritical ? Logger.critical(response.error) : Logger.error(response.error);
1443
+ if (response.stack) Logger.errorStack(response.stack);
1444
+ }
1445
+ }
1446
+ };
1447
+ _init = __decoratorStart(null);
1448
+ _Router = __decorateElement(_init, 0, "Router", _Router_decorators, _Router);
1449
+ __name(_Router, "Router");
1450
+ __runInitializers(_init, 1, _Router);
1451
+ var Router = _Router;
1364
1452
 
1365
1453
  // src/internal/app.ts
1366
1454
  var import_main2 = require("electron/main");
1367
- init_injectable_decorator();
1368
1455
  init_app_injector();
1369
1456
  init_injector_explorer();
1370
1457
  init_logger();
1371
1458
 
1372
1459
  // src/window/window-manager.ts
1373
1460
  var import_main = require("electron/main");
1374
- init_injectable_decorator();
1375
1461
  init_logger();
1376
- var WindowManager = class {
1462
+ var _WindowManager_decorators, _init2;
1463
+ _WindowManager_decorators = [Injectable({ lifetime: "singleton" })];
1464
+ var _WindowManager = class _WindowManager {
1377
1465
  constructor() {
1378
- this._windows = /* @__PURE__ */ new Map();
1466
+ __publicField(this, "_windows", /* @__PURE__ */ new Map());
1467
+ __publicField(this, "listeners", /* @__PURE__ */ new Map());
1468
+ __publicField(this, "_mainWindowId");
1379
1469
  }
1380
1470
  // -------------------------------------------------------------------------
1381
1471
  // Creation
@@ -1425,18 +1515,24 @@ var WindowManager = class {
1425
1515
  * win.loadFile('index.html');
1426
1516
  */
1427
1517
  async createSplash(options = {}) {
1428
- const { animationDuration = 600, ...bwOptions } = options;
1518
+ const {
1519
+ animationDuration = 10,
1520
+ expandToWorkArea = true,
1521
+ ...bwOptions
1522
+ } = options;
1429
1523
  const win = new import_main.BrowserWindow({
1430
1524
  width: 600,
1431
1525
  height: 600,
1432
1526
  center: true,
1433
- frame: false,
1434
1527
  show: true,
1435
1528
  ...bwOptions
1436
1529
  });
1437
1530
  this._register(win, true);
1438
1531
  Logger.log(`[WindowManager] Splash window #${win.id} created`);
1439
- await this._expandToWorkArea(win, animationDuration);
1532
+ if (expandToWorkArea) {
1533
+ await (() => new Promise((r) => setTimeout(r, 500)))();
1534
+ await this._expandToWorkArea(win, animationDuration);
1535
+ }
1440
1536
  return win;
1441
1537
  }
1442
1538
  // -------------------------------------------------------------------------
@@ -1495,10 +1591,24 @@ var WindowManager = class {
1495
1591
  */
1496
1592
  broadcast(channel, ...args) {
1497
1593
  for (const win of this._windows.values()) {
1498
- if (!win.isDestroyed()) win.webContents.send(channel, ...args);
1594
+ if (!win.isDestroyed()) {
1595
+ win.webContents.send(channel, ...args);
1596
+ }
1499
1597
  }
1500
1598
  }
1501
1599
  // -------------------------------------------------------------------------
1600
+ // Events
1601
+ // -------------------------------------------------------------------------
1602
+ on(event, handler) {
1603
+ const set = this.listeners.get(event) ?? /* @__PURE__ */ new Set();
1604
+ set.add(handler);
1605
+ this.listeners.set(event, set);
1606
+ return () => set.delete(handler);
1607
+ }
1608
+ _emit(event, win) {
1609
+ this.listeners.get(event)?.forEach((h) => h(win));
1610
+ }
1611
+ // -------------------------------------------------------------------------
1502
1612
  // Private
1503
1613
  // -------------------------------------------------------------------------
1504
1614
  _register(win, isMain) {
@@ -1506,10 +1616,16 @@ var WindowManager = class {
1506
1616
  if (isMain && this._mainWindowId === void 0) {
1507
1617
  this._mainWindowId = win.id;
1508
1618
  }
1619
+ this._emit("created", win);
1620
+ win.on("focus", () => this._emit("focused", win));
1621
+ win.on("blur", () => this._emit("blurred", win));
1509
1622
  win.once("closed", () => {
1510
1623
  this._windows.delete(win.id);
1511
- if (this._mainWindowId === win.id) this._mainWindowId = void 0;
1624
+ if (this._mainWindowId === win.id) {
1625
+ this._mainWindowId = void 0;
1626
+ }
1512
1627
  Logger.log(`[WindowManager] Window #${win.id} closed`);
1628
+ this._emit("closed", win);
1513
1629
  });
1514
1630
  }
1515
1631
  /**
@@ -1519,36 +1635,34 @@ var WindowManager = class {
1519
1635
  */
1520
1636
  _expandToWorkArea(win, animationDuration) {
1521
1637
  return new Promise((resolve) => {
1522
- const { x, y, width, height } = import_main.screen.getPrimaryDisplay().workArea;
1523
- win.setBounds({ x, y, width, height }, true);
1638
+ win.maximize();
1524
1639
  let resolved = false;
1525
1640
  const done = /* @__PURE__ */ __name(() => {
1526
- if (resolved) return;
1641
+ if (resolved) {
1642
+ return;
1643
+ }
1527
1644
  resolved = true;
1528
1645
  win.removeListener("resize", done);
1529
1646
  resolve();
1530
1647
  }, "done");
1531
1648
  win.once("resize", done);
1532
- setTimeout(done, animationDuration + 100);
1649
+ setTimeout(done, animationDuration);
1533
1650
  });
1534
1651
  }
1535
1652
  };
1536
- __name(WindowManager, "WindowManager");
1537
- WindowManager = __decorateClass([
1538
- Injectable({ lifetime: "singleton" })
1539
- ], WindowManager);
1540
-
1541
- // src/internal/app.ts
1542
- init_request();
1543
- init_router();
1653
+ _init2 = __decoratorStart(null);
1654
+ _WindowManager = __decorateElement(_init2, 0, "WindowManager", _WindowManager_decorators, _WindowManager);
1655
+ __name(_WindowManager, "WindowManager");
1656
+ __runInitializers(_init2, 1, _WindowManager);
1657
+ var WindowManager = _WindowManager;
1544
1658
 
1545
1659
  // src/internal/socket.ts
1546
- init_injectable_decorator();
1547
1660
  init_logger();
1548
- init_request();
1549
- var NoxSocket = class {
1661
+ var _NoxSocket_decorators, _init3;
1662
+ _NoxSocket_decorators = [Injectable({ lifetime: "singleton" })];
1663
+ var _NoxSocket = class _NoxSocket {
1550
1664
  constructor() {
1551
- this.channels = /* @__PURE__ */ new Map();
1665
+ __publicField(this, "channels", /* @__PURE__ */ new Map());
1552
1666
  }
1553
1667
  register(senderId, requestChannel, socketChannel) {
1554
1668
  this.channels.set(senderId, { request: requestChannel, socket: socketChannel });
@@ -1568,7 +1682,6 @@ var NoxSocket = class {
1568
1682
  throw new Error("Renderer event name must be a non-empty string.");
1569
1683
  }
1570
1684
  const recipients = targetSenderIds ?? this.getSenderIds();
1571
- let delivered = 0;
1572
1685
  for (const senderId of recipients) {
1573
1686
  const channel = this.channels.get(senderId);
1574
1687
  if (!channel) {
@@ -1577,40 +1690,46 @@ var NoxSocket = class {
1577
1690
  }
1578
1691
  try {
1579
1692
  channel.socket.port1.postMessage(createRendererEventMessage(normalizedEvent, payload));
1580
- delivered++;
1581
1693
  } catch (error) {
1582
1694
  Logger.error(`[Noxus] Failed to emit "${normalizedEvent}" to sender ${senderId}.`, error);
1583
1695
  }
1584
1696
  }
1585
- return delivered;
1586
1697
  }
1587
1698
  emitToRenderer(senderId, eventName, payload) {
1588
- return this.emit(eventName, payload, [senderId]) > 0;
1699
+ if (!this.channels.has(senderId)) {
1700
+ return false;
1701
+ }
1702
+ this.emit(eventName, payload, [senderId]);
1703
+ return true;
1589
1704
  }
1590
1705
  };
1591
- __name(NoxSocket, "NoxSocket");
1592
- NoxSocket = __decorateClass([
1593
- Injectable({ lifetime: "singleton" })
1594
- ], NoxSocket);
1706
+ _init3 = __decoratorStart(null);
1707
+ _NoxSocket = __decorateElement(_init3, 0, "NoxSocket", _NoxSocket_decorators, _NoxSocket);
1708
+ __name(_NoxSocket, "NoxSocket");
1709
+ __runInitializers(_init3, 1, _NoxSocket);
1710
+ var NoxSocket = _NoxSocket;
1595
1711
 
1596
1712
  // src/internal/app.ts
1597
- var NoxApp = class {
1598
- constructor() {
1599
- this.router = inject(Router);
1600
- this.socket = inject(NoxSocket);
1601
- this.windowManager = inject(WindowManager);
1713
+ var _NoxApp_decorators, _init4;
1714
+ _NoxApp_decorators = [Injectable({ lifetime: "singleton", deps: [Router, NoxSocket, WindowManager] })];
1715
+ var _NoxApp = class _NoxApp {
1716
+ constructor(router, socket, windowManager) {
1717
+ this.router = router;
1718
+ this.socket = socket;
1719
+ this.windowManager = windowManager;
1720
+ __publicField(this, "appService");
1602
1721
  // -------------------------------------------------------------------------
1603
1722
  // IPC
1604
1723
  // -------------------------------------------------------------------------
1605
- this.onRendererMessage = /* @__PURE__ */ __name(async (event) => {
1606
- const { senderId, requestId, path: path2, method, body } = event.data;
1724
+ __publicField(this, "onRendererMessage", /* @__PURE__ */ __name(async (event) => {
1725
+ const { senderId, requestId, path: path2, method, body, query } = event.data;
1607
1726
  const channels = this.socket.get(senderId);
1608
1727
  if (!channels) {
1609
1728
  Logger.error(`No message channel found for sender ID: ${senderId}`);
1610
1729
  return;
1611
1730
  }
1612
1731
  try {
1613
- const request = new Request(event, senderId, requestId, method, path2, body);
1732
+ const request = new Request(event, senderId, requestId, method, path2, body, query);
1614
1733
  const response = await this.router.handle(request);
1615
1734
  channels.request.port1.postMessage(response);
1616
1735
  } catch (err) {
@@ -1622,7 +1741,7 @@ var NoxApp = class {
1622
1741
  };
1623
1742
  channels.request.port1.postMessage(response);
1624
1743
  }
1625
- }, "onRendererMessage");
1744
+ }, "onRendererMessage"));
1626
1745
  }
1627
1746
  // -------------------------------------------------------------------------
1628
1747
  // Initialisation
@@ -1661,7 +1780,7 @@ var NoxApp = class {
1661
1780
  async load(importFns) {
1662
1781
  InjectorExplorer.beginAccumulate();
1663
1782
  await Promise.all(importFns.map((fn) => fn()));
1664
- InjectorExplorer.flushAccumulated();
1783
+ await InjectorExplorer.flushAccumulated();
1665
1784
  return this;
1666
1785
  }
1667
1786
  /**
@@ -1730,27 +1849,44 @@ var NoxApp = class {
1730
1849
  this.socket.unregister(channelSenderId);
1731
1850
  }
1732
1851
  };
1733
- __name(NoxApp, "NoxApp");
1734
- NoxApp = __decorateClass([
1735
- Injectable({ lifetime: "singleton", deps: [Router, NoxSocket, WindowManager] })
1736
- ], NoxApp);
1852
+ _init4 = __decoratorStart(null);
1853
+ _NoxApp = __decorateElement(_init4, 0, "NoxApp", _NoxApp_decorators, _NoxApp);
1854
+ __name(_NoxApp, "NoxApp");
1855
+ __runInitializers(_init4, 1, _NoxApp);
1856
+ var NoxApp = _NoxApp;
1737
1857
 
1738
1858
  // src/internal/bootstrap.ts
1739
1859
  var import_main3 = require("electron/main");
1740
1860
  init_app_injector();
1741
1861
  init_injector_explorer();
1862
+ init_logger();
1742
1863
  async function bootstrapApplication(config = {}) {
1743
1864
  await import_main3.app.whenReady();
1865
+ if (config.logLevel !== void 0) {
1866
+ if (config.logLevel === "none") {
1867
+ Logger.setLogLevel([]);
1868
+ } else if (Array.isArray(config.logLevel)) {
1869
+ Logger.setLogLevel(config.logLevel);
1870
+ } else {
1871
+ Logger.setLogLevel(config.logLevel);
1872
+ }
1873
+ }
1744
1874
  const overrides = /* @__PURE__ */ new Map();
1745
1875
  for (const { token: token2, useValue } of config.singletons ?? []) {
1746
1876
  overrides.set(token2, useValue);
1747
1877
  RootInjector.singletons.set(token2, useValue);
1748
1878
  }
1879
+ InjectorExplorer.setControllerRegistrar((controllerClass, pathPrefix, routeGuards, routeMiddlewares) => {
1880
+ const router = inject(Router);
1881
+ router.registerController(controllerClass, pathPrefix, routeGuards, routeMiddlewares);
1882
+ });
1749
1883
  InjectorExplorer.processPending(overrides);
1750
1884
  const noxApp = inject(NoxApp);
1751
1885
  if (config.routes?.length) {
1752
1886
  for (const route of config.routes) {
1753
- noxApp.lazy(route.path, route.load, route.guards, route.middlewares);
1887
+ if (route.load) {
1888
+ noxApp.lazy(route.path, route.load, route.guards, route.middlewares);
1889
+ }
1754
1890
  }
1755
1891
  }
1756
1892
  if (config.eagerLoad?.length) {
@@ -1762,29 +1898,53 @@ async function bootstrapApplication(config = {}) {
1762
1898
  __name(bootstrapApplication, "bootstrapApplication");
1763
1899
 
1764
1900
  // src/main.ts
1765
- init_exceptions();
1766
- init_controller_decorator();
1767
- init_injectable_decorator();
1768
- init_method_decorator();
1769
1901
  init_logger();
1770
1902
  init_forward_ref();
1771
- init_request();
1772
1903
 
1773
1904
  // src/internal/routes.ts
1774
1905
  function defineRoutes(routes) {
1775
- const paths = routes.map((r) => r.path.replace(/^\/+|\/+$/g, ""));
1906
+ const flat = flattenRoutes(routes);
1907
+ const paths = flat.map((r) => r.path);
1776
1908
  const duplicates = paths.filter((p, i) => paths.indexOf(p) !== i);
1777
1909
  if (duplicates.length > 0) {
1778
1910
  throw new Error(
1779
1911
  `[Noxus] Duplicate route prefixes detected: ${[...new Set(duplicates)].map((d) => `"${d}"`).join(", ")}`
1780
1912
  );
1781
1913
  }
1782
- return routes.map((r) => ({
1783
- ...r,
1784
- path: r.path.replace(/^\/+|\/+$/g, "")
1785
- }));
1914
+ const sorted = [...paths].sort();
1915
+ for (let i = 0; i < sorted.length - 1; i++) {
1916
+ const a = sorted[i];
1917
+ const b = sorted[i + 1];
1918
+ if (b.startsWith(a + "/")) {
1919
+ throw new Error(
1920
+ `[Noxus] Overlapping route prefixes detected: "${a}" and "${b}". Use nested children under "${a}" instead of declaring both as top-level routes.`
1921
+ );
1922
+ }
1923
+ }
1924
+ return flat;
1786
1925
  }
1787
1926
  __name(defineRoutes, "defineRoutes");
1927
+ function flattenRoutes(routes, parentPath = "", parentGuards = [], parentMiddlewares = []) {
1928
+ const result = [];
1929
+ for (const route of routes) {
1930
+ const path2 = [parentPath, route.path.replace(/^\/+|\/+$/g, "")].filter(Boolean).join("/");
1931
+ const guards = [.../* @__PURE__ */ new Set([...parentGuards, ...route.guards ?? []])];
1932
+ const middlewares = [.../* @__PURE__ */ new Set([...parentMiddlewares, ...route.middlewares ?? []])];
1933
+ if (route.load) {
1934
+ result.push({ ...route, path: path2, guards, middlewares });
1935
+ }
1936
+ if (route.children?.length) {
1937
+ result.push(...flattenRoutes(route.children, path2, guards, middlewares));
1938
+ }
1939
+ if (!route.load && !route.children?.length) {
1940
+ throw new Error(
1941
+ `[Noxus] Route "${path2}" has neither a load function nor children. It must have at least one of them.`
1942
+ );
1943
+ }
1944
+ }
1945
+ return result;
1946
+ }
1947
+ __name(flattenRoutes, "flattenRoutes");
1788
1948
  // Annotate the CommonJS export names for ESM import in node:
1789
1949
  0 && (module.exports = {
1790
1950
  AppInjector,
@@ -1838,6 +1998,7 @@ __name(defineRoutes, "defineRoutes");
1838
1998
  inject,
1839
1999
  isAtomicHttpMethod,
1840
2000
  isRendererEventMessage,
2001
+ resetRootInjector,
1841
2002
  token
1842
2003
  });
1843
2004
  /**