@noxfly/noxus 3.0.0-dev.2 → 3.0.0-dev.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.mjs CHANGED
@@ -75,116 +75,6 @@ var init_token = __esm({
75
75
  }
76
76
  });
77
77
 
78
- // src/DI/app-injector.ts
79
- function keyOf(k) {
80
- return k;
81
- }
82
- function inject(t) {
83
- return RootInjector.resolve(t);
84
- }
85
- var _AppInjector, AppInjector, RootInjector;
86
- var init_app_injector = __esm({
87
- "src/DI/app-injector.ts"() {
88
- "use strict";
89
- init_forward_ref();
90
- init_token();
91
- __name(keyOf, "keyOf");
92
- _AppInjector = class _AppInjector {
93
- constructor(name = null) {
94
- this.name = name;
95
- this.bindings = /* @__PURE__ */ new Map();
96
- this.singletons = /* @__PURE__ */ new Map();
97
- this.scoped = /* @__PURE__ */ new Map();
98
- }
99
- /**
100
- * Creates a child scope for per-request lifetime resolution.
101
- */
102
- createScope() {
103
- const scope = new _AppInjector();
104
- scope.bindings = this.bindings;
105
- scope.singletons = this.singletons;
106
- return scope;
107
- }
108
- /**
109
- * Registers a binding explicitly.
110
- */
111
- register(key, implementation, lifetime, deps = []) {
112
- const k = keyOf(key);
113
- if (!this.bindings.has(k)) {
114
- this.bindings.set(k, { lifetime, implementation, deps });
115
- }
116
- }
117
- /**
118
- * Resolves a dependency by token or class reference.
119
- */
120
- resolve(target) {
121
- if (target instanceof ForwardReference) {
122
- return this._resolveForwardRef(target);
123
- }
124
- const k = keyOf(target);
125
- if (this.singletons.has(k)) {
126
- return this.singletons.get(k);
127
- }
128
- const binding = this.bindings.get(k);
129
- if (!binding) {
130
- const name = target instanceof Token ? target.description : target.name ?? "unknown";
131
- throw new Error(
132
- `[Noxus DI] No binding found for "${name}".
133
- Did you forget to declare it in @Injectable({ deps }) or in bootstrapApplication({ singletons })?`
134
- );
135
- }
136
- switch (binding.lifetime) {
137
- case "transient":
138
- return this._instantiate(binding);
139
- case "scope": {
140
- if (this.scoped.has(k)) return this.scoped.get(k);
141
- const inst = this._instantiate(binding);
142
- this.scoped.set(k, inst);
143
- return inst;
144
- }
145
- case "singleton": {
146
- if (this.singletons.has(k)) return this.singletons.get(k);
147
- const inst = this._instantiate(binding);
148
- this.singletons.set(k, inst);
149
- if (binding.instance === void 0) {
150
- binding.instance = inst;
151
- }
152
- return inst;
153
- }
154
- }
155
- }
156
- // -------------------------------------------------------------------------
157
- _resolveForwardRef(ref) {
158
- return new Proxy({}, {
159
- get: /* @__PURE__ */ __name((_obj, prop, receiver) => {
160
- const realType = ref.forwardRefFn();
161
- const instance = this.resolve(realType);
162
- const value = Reflect.get(instance, prop, receiver);
163
- return typeof value === "function" ? value.bind(instance) : value;
164
- }, "get"),
165
- set: /* @__PURE__ */ __name((_obj, prop, value, receiver) => {
166
- const realType = ref.forwardRefFn();
167
- const instance = this.resolve(realType);
168
- return Reflect.set(instance, prop, value, receiver);
169
- }, "set"),
170
- getPrototypeOf: /* @__PURE__ */ __name(() => {
171
- const realType = ref.forwardRefFn();
172
- return realType.prototype;
173
- }, "getPrototypeOf")
174
- });
175
- }
176
- _instantiate(binding) {
177
- const resolvedDeps = binding.deps.map((dep) => this.resolve(dep));
178
- return new binding.implementation(...resolvedDeps);
179
- }
180
- };
181
- __name(_AppInjector, "AppInjector");
182
- AppInjector = _AppInjector;
183
- RootInjector = new AppInjector("root");
184
- __name(inject, "inject");
185
- }
186
- });
187
-
188
78
  // src/utils/logger.ts
189
79
  import * as fs from "fs";
190
80
  import * as path from "path";
@@ -433,6 +323,10 @@ var init_logger = __esm({
433
323
  });
434
324
 
435
325
  // src/DI/injector-explorer.ts
326
+ var injector_explorer_exports = {};
327
+ __export(injector_explorer_exports, {
328
+ InjectorExplorer: () => InjectorExplorer
329
+ });
436
330
  var _InjectorExplorer, InjectorExplorer;
437
331
  var init_injector_explorer = __esm({
438
332
  "src/DI/injector-explorer.ts"() {
@@ -443,6 +337,13 @@ var init_injector_explorer = __esm({
443
337
  // -------------------------------------------------------------------------
444
338
  // Public API
445
339
  // -------------------------------------------------------------------------
340
+ /**
341
+ * Sets the callback used to register controllers.
342
+ * Must be called once before processPending (typically by bootstrapApplication).
343
+ */
344
+ static setControllerRegistrar(registrar) {
345
+ _InjectorExplorer.controllerRegistrar = registrar;
346
+ }
446
347
  static enqueue(reg) {
447
348
  if (_InjectorExplorer.processed && !_InjectorExplorer.accumulating) {
448
349
  _InjectorExplorer._registerImmediate(reg);
@@ -468,16 +369,37 @@ var init_injector_explorer = __esm({
468
369
  /**
469
370
  * Exits accumulation mode and flushes queued registrations
470
371
  * with the same two-phase guarantee as processPending.
372
+ * Serialised through a lock to prevent concurrent lazy loads from corrupting the queue.
471
373
  */
472
374
  static flushAccumulated(routeGuards = [], routeMiddlewares = [], pathPrefix = "") {
473
- _InjectorExplorer.accumulating = false;
474
- const queue = [..._InjectorExplorer.pending];
375
+ _InjectorExplorer.loadingLock = _InjectorExplorer.loadingLock.then(() => {
376
+ _InjectorExplorer.accumulating = false;
377
+ const queue = [..._InjectorExplorer.pending];
378
+ _InjectorExplorer.pending.length = 0;
379
+ _InjectorExplorer._phaseOne(queue);
380
+ for (const reg of queue) {
381
+ if (reg.isController) reg.pathPrefix = pathPrefix;
382
+ }
383
+ _InjectorExplorer._phaseTwo(queue, void 0, routeGuards, routeMiddlewares);
384
+ });
385
+ return _InjectorExplorer.loadingLock;
386
+ }
387
+ /**
388
+ * Returns a Promise that resolves once all pending flushAccumulated calls
389
+ * have completed. Useful for awaiting lazy-load serialisation.
390
+ */
391
+ static waitForFlush() {
392
+ return _InjectorExplorer.loadingLock;
393
+ }
394
+ /**
395
+ * Resets the explorer state. Intended for tests only.
396
+ */
397
+ static reset() {
475
398
  _InjectorExplorer.pending.length = 0;
476
- _InjectorExplorer._phaseOne(queue);
477
- for (const reg of queue) {
478
- if (reg.isController) reg.pathPrefix = pathPrefix;
479
- }
480
- _InjectorExplorer._phaseTwo(queue, void 0, routeGuards, routeMiddlewares);
399
+ _InjectorExplorer.processed = false;
400
+ _InjectorExplorer.accumulating = false;
401
+ _InjectorExplorer.loadingLock = Promise.resolve();
402
+ _InjectorExplorer.controllerRegistrar = null;
481
403
  }
482
404
  // -------------------------------------------------------------------------
483
405
  // Private helpers
@@ -488,8 +410,15 @@ var init_injector_explorer = __esm({
488
410
  RootInjector.register(reg.key, reg.implementation, reg.lifetime, reg.deps);
489
411
  }
490
412
  }
491
- /** Phase 2: resolve singletons and register controllers in the router. */
413
+ /** Phase 2: validate deps, resolve singletons and register controllers via the registrar callback. */
492
414
  static _phaseTwo(queue, overrides, routeGuards = [], routeMiddlewares = []) {
415
+ for (const reg of queue) {
416
+ for (const dep of reg.deps) {
417
+ if (!RootInjector.bindings.has(dep) && !RootInjector.singletons.has(dep)) {
418
+ Logger.warn(`[Noxus DI] "${reg.implementation.name}" declares dep "${dep.name ?? dep}" which has no binding`);
419
+ }
420
+ }
421
+ }
493
422
  for (const reg of queue) {
494
423
  if (overrides?.has(reg.key)) {
495
424
  const override = overrides.get(reg.key);
@@ -501,9 +430,15 @@ var init_injector_explorer = __esm({
501
430
  RootInjector.resolve(reg.key);
502
431
  }
503
432
  if (reg.isController) {
504
- const { Router: Router2 } = (init_router(), __toCommonJS(router_exports));
505
- const router = RootInjector.resolve(Router2);
506
- router.registerController(reg.implementation, reg.pathPrefix ?? "", routeGuards, routeMiddlewares);
433
+ if (!_InjectorExplorer.controllerRegistrar) {
434
+ throw new Error("[Noxus DI] No controller registrar set. Call InjectorExplorer.setControllerRegistrar() before processing.");
435
+ }
436
+ _InjectorExplorer.controllerRegistrar(
437
+ reg.implementation,
438
+ reg.pathPrefix ?? "",
439
+ routeGuards,
440
+ routeMiddlewares
441
+ );
507
442
  } else if (reg.lifetime !== "singleton") {
508
443
  Logger.log(`Registered ${reg.implementation.name} as ${reg.lifetime}`);
509
444
  }
@@ -514,10 +449,8 @@ var init_injector_explorer = __esm({
514
449
  if (reg.lifetime === "singleton") {
515
450
  RootInjector.resolve(reg.key);
516
451
  }
517
- if (reg.isController) {
518
- const { Router: Router2 } = (init_router(), __toCommonJS(router_exports));
519
- const router = RootInjector.resolve(Router2);
520
- router.registerController(reg.implementation);
452
+ if (reg.isController && _InjectorExplorer.controllerRegistrar) {
453
+ _InjectorExplorer.controllerRegistrar(reg.implementation, "", [], []);
521
454
  }
522
455
  }
523
456
  };
@@ -525,11 +458,136 @@ var init_injector_explorer = __esm({
525
458
  _InjectorExplorer.pending = [];
526
459
  _InjectorExplorer.processed = false;
527
460
  _InjectorExplorer.accumulating = false;
461
+ _InjectorExplorer.loadingLock = Promise.resolve();
462
+ _InjectorExplorer.controllerRegistrar = null;
528
463
  InjectorExplorer = _InjectorExplorer;
529
464
  }
530
465
  });
531
466
 
467
+ // src/DI/app-injector.ts
468
+ function keyOf(k) {
469
+ return k;
470
+ }
471
+ function resetRootInjector() {
472
+ RootInjector.bindings.clear();
473
+ RootInjector.singletons.clear();
474
+ RootInjector.scoped.clear();
475
+ const { InjectorExplorer: InjectorExplorer2 } = (init_injector_explorer(), __toCommonJS(injector_explorer_exports));
476
+ InjectorExplorer2.reset();
477
+ }
478
+ function inject(t) {
479
+ return RootInjector.resolve(t);
480
+ }
481
+ var _AppInjector, AppInjector, RootInjector;
482
+ var init_app_injector = __esm({
483
+ "src/DI/app-injector.ts"() {
484
+ "use strict";
485
+ init_forward_ref();
486
+ init_token();
487
+ __name(keyOf, "keyOf");
488
+ _AppInjector = class _AppInjector {
489
+ constructor(name = null) {
490
+ this.name = name;
491
+ this.bindings = /* @__PURE__ */ new Map();
492
+ this.singletons = /* @__PURE__ */ new Map();
493
+ this.scoped = /* @__PURE__ */ new Map();
494
+ }
495
+ /**
496
+ * Creates a child scope for per-request lifetime resolution.
497
+ */
498
+ createScope() {
499
+ const scope = new _AppInjector();
500
+ scope.bindings = this.bindings;
501
+ scope.singletons = this.singletons;
502
+ return scope;
503
+ }
504
+ /**
505
+ * Registers a binding explicitly.
506
+ */
507
+ register(key, implementation, lifetime, deps = []) {
508
+ const k = keyOf(key);
509
+ if (!this.bindings.has(k)) {
510
+ this.bindings.set(k, { lifetime, implementation, deps });
511
+ }
512
+ }
513
+ /**
514
+ * Resolves a dependency by token or class reference.
515
+ */
516
+ resolve(target) {
517
+ if (target instanceof ForwardReference) {
518
+ return this._resolveForwardRef(target);
519
+ }
520
+ const k = keyOf(target);
521
+ if (this.singletons.has(k)) {
522
+ return this.singletons.get(k);
523
+ }
524
+ const binding = this.bindings.get(k);
525
+ if (!binding) {
526
+ const name = target instanceof Token ? target.description : target.name ?? "unknown";
527
+ throw new Error(
528
+ `[Noxus DI] No binding found for "${name}".
529
+ Did you forget to declare it in @Injectable({ deps }) or in bootstrapApplication({ singletons })?`
530
+ );
531
+ }
532
+ switch (binding.lifetime) {
533
+ case "transient":
534
+ return this._instantiate(binding);
535
+ case "scope": {
536
+ if (this.scoped.has(k)) return this.scoped.get(k);
537
+ const inst = this._instantiate(binding);
538
+ this.scoped.set(k, inst);
539
+ return inst;
540
+ }
541
+ case "singleton": {
542
+ if (this.singletons.has(k)) return this.singletons.get(k);
543
+ const inst = this._instantiate(binding);
544
+ this.singletons.set(k, inst);
545
+ if (binding.instance === void 0) {
546
+ binding.instance = inst;
547
+ }
548
+ return inst;
549
+ }
550
+ }
551
+ }
552
+ // -------------------------------------------------------------------------
553
+ _resolveForwardRef(ref) {
554
+ let resolved;
555
+ return new Proxy({}, {
556
+ get: /* @__PURE__ */ __name((_obj, prop, receiver) => {
557
+ resolved ?? (resolved = this.resolve(ref.forwardRefFn()));
558
+ const value = Reflect.get(resolved, prop, receiver);
559
+ return typeof value === "function" ? value.bind(resolved) : value;
560
+ }, "get"),
561
+ set: /* @__PURE__ */ __name((_obj, prop, value, receiver) => {
562
+ resolved ?? (resolved = this.resolve(ref.forwardRefFn()));
563
+ return Reflect.set(resolved, prop, value, receiver);
564
+ }, "set"),
565
+ getPrototypeOf: /* @__PURE__ */ __name(() => {
566
+ resolved ?? (resolved = this.resolve(ref.forwardRefFn()));
567
+ return Object.getPrototypeOf(resolved);
568
+ }, "getPrototypeOf")
569
+ });
570
+ }
571
+ _instantiate(binding) {
572
+ const resolvedDeps = binding.deps.map((dep) => this.resolve(dep));
573
+ return new binding.implementation(...resolvedDeps);
574
+ }
575
+ };
576
+ __name(_AppInjector, "AppInjector");
577
+ AppInjector = _AppInjector;
578
+ RootInjector = new AppInjector("root");
579
+ __name(resetRootInjector, "resetRootInjector");
580
+ __name(inject, "inject");
581
+ }
582
+ });
583
+
584
+ // src/main.ts
585
+ init_app_injector();
586
+ init_token();
587
+
532
588
  // src/decorators/controller.decorator.ts
589
+ init_injector_explorer();
590
+ var controllerMetaMap = /* @__PURE__ */ new WeakMap();
533
591
  function Controller(options = {}) {
534
592
  return (target) => {
535
593
  const meta = {
@@ -545,21 +603,15 @@ function Controller(options = {}) {
545
603
  });
546
604
  };
547
605
  }
606
+ __name(Controller, "Controller");
548
607
  function getControllerMetadata(target) {
549
608
  return controllerMetaMap.get(target);
550
609
  }
551
- var controllerMetaMap;
552
- var init_controller_decorator = __esm({
553
- "src/decorators/controller.decorator.ts"() {
554
- "use strict";
555
- init_injector_explorer();
556
- controllerMetaMap = /* @__PURE__ */ new WeakMap();
557
- __name(Controller, "Controller");
558
- __name(getControllerMetadata, "getControllerMetadata");
559
- }
560
- });
610
+ __name(getControllerMetadata, "getControllerMetadata");
561
611
 
562
612
  // src/decorators/injectable.decorator.ts
613
+ init_injector_explorer();
614
+ init_token();
563
615
  function Injectable(options = {}) {
564
616
  const { lifetime = "scope", deps = [] } = options;
565
617
  return (target) => {
@@ -576,19 +628,15 @@ function Injectable(options = {}) {
576
628
  });
577
629
  };
578
630
  }
579
- var init_injectable_decorator = __esm({
580
- "src/decorators/injectable.decorator.ts"() {
581
- "use strict";
582
- init_injector_explorer();
583
- init_token();
584
- __name(Injectable, "Injectable");
585
- }
586
- });
631
+ __name(Injectable, "Injectable");
587
632
 
588
633
  // src/decorators/method.decorator.ts
634
+ var ATOMIC_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
589
635
  function isAtomicHttpMethod(m) {
590
636
  return typeof m === "string" && ATOMIC_METHODS.has(m);
591
637
  }
638
+ __name(isAtomicHttpMethod, "isAtomicHttpMethod");
639
+ var routeMetaMap = /* @__PURE__ */ new WeakMap();
592
640
  function createRouteDecorator(verb) {
593
641
  return (path2, options = {}) => {
594
642
  return (target, propertyKey) => {
@@ -605,399 +653,416 @@ function createRouteDecorator(verb) {
605
653
  };
606
654
  };
607
655
  }
656
+ __name(createRouteDecorator, "createRouteDecorator");
608
657
  function getRouteMetadata(target) {
609
658
  return routeMetaMap.get(target) ?? [];
610
659
  }
611
- var ATOMIC_METHODS, routeMetaMap, Get, Post, Put, Patch, Delete;
612
- var init_method_decorator = __esm({
613
- "src/decorators/method.decorator.ts"() {
614
- "use strict";
615
- ATOMIC_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
616
- __name(isAtomicHttpMethod, "isAtomicHttpMethod");
617
- routeMetaMap = /* @__PURE__ */ new WeakMap();
618
- __name(createRouteDecorator, "createRouteDecorator");
619
- __name(getRouteMetadata, "getRouteMetadata");
620
- Get = createRouteDecorator("GET");
621
- Post = createRouteDecorator("POST");
622
- Put = createRouteDecorator("PUT");
623
- Patch = createRouteDecorator("PATCH");
624
- Delete = createRouteDecorator("DELETE");
625
- }
626
- });
660
+ __name(getRouteMetadata, "getRouteMetadata");
661
+ var Get = createRouteDecorator("GET");
662
+ var Post = createRouteDecorator("POST");
663
+ var Put = createRouteDecorator("PUT");
664
+ var Patch = createRouteDecorator("PATCH");
665
+ var Delete = createRouteDecorator("DELETE");
666
+
667
+ // src/internal/router.ts
668
+ init_injector_explorer();
669
+ init_logger();
627
670
 
628
671
  // src/utils/radix-tree.ts
629
- var _RadixNode, RadixNode, _RadixTree, RadixTree;
630
- var init_radix_tree = __esm({
631
- "src/utils/radix-tree.ts"() {
632
- "use strict";
633
- _RadixNode = class _RadixNode {
634
- /**
635
- * Creates a new RadixNode.
636
- * @param segment - The segment of the path this node represents.
637
- */
638
- constructor(segment) {
639
- this.children = [];
640
- this.segment = segment;
641
- this.isParam = segment.startsWith(":");
642
- if (this.isParam) {
643
- this.paramName = segment.slice(1);
644
- }
645
- }
646
- /**
647
- * Matches a child node against a given segment.
648
- * This method checks if the segment matches any of the children nodes.
649
- * @param segment - The segment to match against the children of this node.
650
- * @returns A child node that matches the segment, or undefined if no match is found.
651
- */
652
- matchChild(segment) {
653
- for (const child of this.children) {
654
- if (child.isParam || segment.startsWith(child.segment))
655
- return child;
656
- }
657
- return void 0;
658
- }
659
- /**
660
- * Finds a child node that matches the segment exactly.
661
- * This method checks if there is a child node that matches the segment exactly.
662
- * @param segment - The segment to find an exact match for among the children of this node.
663
- * @returns A child node that matches the segment exactly, or undefined if no match is found.
664
- */
665
- findExactChild(segment) {
666
- return this.children.find((c) => c.segment === segment);
667
- }
668
- /**
669
- * Adds a child node to this node's children.
670
- * This method adds a new child node to the list of children for this node.
671
- * @param node - The child node to add to this node's children.
672
- */
673
- addChild(node) {
674
- this.children.push(node);
675
- }
676
- };
677
- __name(_RadixNode, "RadixNode");
678
- RadixNode = _RadixNode;
679
- _RadixTree = class _RadixTree {
680
- constructor() {
681
- this.root = new RadixNode("");
682
- }
683
- /**
684
- * Inserts a path and its associated value into the Radix Tree.
685
- * This method normalizes the path and inserts it into the tree, associating it with
686
- * @param path - The path to insert into the tree.
687
- * @param value - The value to associate with the path.
688
- */
689
- insert(path2, value) {
690
- const segments = this.normalize(path2);
691
- this.insertRecursive(this.root, segments, value);
692
- }
693
- /**
694
- * Recursively inserts a path into the Radix Tree.
695
- * This method traverses the tree and inserts the segments of the path, creating new nodes
696
- * @param node - The node to start inserting from.
697
- * @param segments - The segments of the path to insert.
698
- * @param value - The value to associate with the path.
699
- */
700
- insertRecursive(node, segments, value) {
701
- if (segments.length === 0) {
702
- node.value = value;
703
- return;
704
- }
705
- const segment = segments[0] ?? "";
706
- let child = node.children.find(
707
- (c) => c.isParam === segment.startsWith(":") && (c.isParam || c.segment === segment)
708
- );
709
- if (!child) {
710
- child = new RadixNode(segment);
711
- node.addChild(child);
712
- }
713
- this.insertRecursive(child, segments.slice(1), value);
714
- }
715
- /**
716
- * Searches for a path in the Radix Tree.
717
- * This method normalizes the path and searches for it in the tree, returning the node
718
- * @param path - The path to search for in the Radix Tree.
719
- * @returns An ISearchResult containing the node and parameters if a match is found, otherwise undefined.
720
- */
721
- search(path2) {
722
- const segments = this.normalize(path2);
723
- return this.searchRecursive(this.root, segments, {});
724
- }
725
- /**
726
- * Recursively searches for a path in the Radix Tree.
727
- * This method traverses the tree and searches for the segments of the path, collecting parameters
728
- * @param node - The node to start searching from.
729
- * @param segments - The segments of the path to search for.
730
- * @param params - The parameters collected during the search.
731
- * @returns An ISearchResult containing the node and parameters if a match is found, otherwise undefined.
732
- */
733
- searchRecursive(node, segments, params) {
734
- if (segments.length === 0) {
735
- if (node.value !== void 0) {
736
- return {
737
- node,
738
- params
739
- };
740
- }
741
- return void 0;
742
- }
743
- const [segment, ...rest] = segments;
744
- for (const child of node.children) {
745
- if (child.isParam) {
746
- const paramName = child.paramName;
747
- const childParams = {
748
- ...params,
749
- [paramName]: segment ?? ""
750
- };
751
- if (rest.length === 0) {
752
- return {
753
- node: child,
754
- params: childParams
755
- };
756
- }
757
- const result = this.searchRecursive(child, rest, childParams);
758
- if (result)
759
- return result;
760
- } else if (segment === child.segment) {
761
- if (rest.length === 0) {
762
- return {
763
- node: child,
764
- params
765
- };
766
- }
767
- const result = this.searchRecursive(child, rest, params);
768
- if (result)
769
- return result;
770
- }
771
- }
772
- return void 0;
773
- }
774
- /**
775
- * Normalizes a path into an array of segments.
776
- * This method removes leading and trailing slashes, splits the path by slashes, and
777
- * @param path - The path to normalize.
778
- * @returns An array of normalized path segments.
779
- */
780
- normalize(path2) {
781
- const segments = path2.replace(/^\/+|\/+$/g, "").split("/").filter(Boolean);
782
- return ["", ...segments];
783
- }
784
- };
785
- __name(_RadixTree, "RadixTree");
786
- RadixTree = _RadixTree;
672
+ var _RadixNode = class _RadixNode {
673
+ /**
674
+ * Creates a new RadixNode.
675
+ * @param segment - The segment of the path this node represents.
676
+ */
677
+ constructor(segment) {
678
+ this.children = [];
679
+ this.segment = segment;
680
+ this.isParam = segment.startsWith(":");
681
+ if (this.isParam) {
682
+ this.paramName = segment.slice(1);
683
+ }
787
684
  }
788
- });
789
-
790
- // src/internal/exceptions.ts
791
- 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;
792
- var init_exceptions = __esm({
793
- "src/internal/exceptions.ts"() {
794
- "use strict";
795
- _ResponseException = class _ResponseException extends Error {
796
- constructor(statusOrMessage, message) {
797
- let statusCode;
798
- if (typeof statusOrMessage === "number") {
799
- statusCode = statusOrMessage;
800
- } else if (typeof statusOrMessage === "string") {
801
- message = statusOrMessage;
802
- }
803
- super(message ?? "");
804
- this.status = 0;
805
- if (statusCode !== void 0) {
806
- this.status = statusCode;
807
- }
808
- this.name = this.constructor.name.replace(/([A-Z])/g, " $1");
809
- }
810
- };
811
- __name(_ResponseException, "ResponseException");
812
- ResponseException = _ResponseException;
813
- _BadRequestException = class _BadRequestException extends ResponseException {
814
- constructor() {
815
- super(...arguments);
816
- this.status = 400;
817
- }
818
- };
819
- __name(_BadRequestException, "BadRequestException");
820
- BadRequestException = _BadRequestException;
821
- _UnauthorizedException = class _UnauthorizedException extends ResponseException {
822
- constructor() {
823
- super(...arguments);
824
- this.status = 401;
825
- }
826
- };
827
- __name(_UnauthorizedException, "UnauthorizedException");
828
- UnauthorizedException = _UnauthorizedException;
829
- _PaymentRequiredException = class _PaymentRequiredException extends ResponseException {
830
- constructor() {
831
- super(...arguments);
832
- this.status = 402;
833
- }
834
- };
835
- __name(_PaymentRequiredException, "PaymentRequiredException");
836
- PaymentRequiredException = _PaymentRequiredException;
837
- _ForbiddenException = class _ForbiddenException extends ResponseException {
838
- constructor() {
839
- super(...arguments);
840
- this.status = 403;
841
- }
842
- };
843
- __name(_ForbiddenException, "ForbiddenException");
844
- ForbiddenException = _ForbiddenException;
845
- _NotFoundException = class _NotFoundException extends ResponseException {
846
- constructor() {
847
- super(...arguments);
848
- this.status = 404;
849
- }
850
- };
851
- __name(_NotFoundException, "NotFoundException");
852
- NotFoundException = _NotFoundException;
853
- _MethodNotAllowedException = class _MethodNotAllowedException extends ResponseException {
854
- constructor() {
855
- super(...arguments);
856
- this.status = 405;
857
- }
858
- };
859
- __name(_MethodNotAllowedException, "MethodNotAllowedException");
860
- MethodNotAllowedException = _MethodNotAllowedException;
861
- _NotAcceptableException = class _NotAcceptableException extends ResponseException {
862
- constructor() {
863
- super(...arguments);
864
- this.status = 406;
865
- }
866
- };
867
- __name(_NotAcceptableException, "NotAcceptableException");
868
- NotAcceptableException = _NotAcceptableException;
869
- _RequestTimeoutException = class _RequestTimeoutException extends ResponseException {
870
- constructor() {
871
- super(...arguments);
872
- this.status = 408;
873
- }
874
- };
875
- __name(_RequestTimeoutException, "RequestTimeoutException");
876
- RequestTimeoutException = _RequestTimeoutException;
877
- _ConflictException = class _ConflictException extends ResponseException {
878
- constructor() {
879
- super(...arguments);
880
- this.status = 409;
881
- }
882
- };
883
- __name(_ConflictException, "ConflictException");
884
- ConflictException = _ConflictException;
885
- _UpgradeRequiredException = class _UpgradeRequiredException extends ResponseException {
886
- constructor() {
887
- super(...arguments);
888
- this.status = 426;
889
- }
890
- };
891
- __name(_UpgradeRequiredException, "UpgradeRequiredException");
892
- UpgradeRequiredException = _UpgradeRequiredException;
893
- _TooManyRequestsException = class _TooManyRequestsException extends ResponseException {
894
- constructor() {
895
- super(...arguments);
896
- this.status = 429;
897
- }
898
- };
899
- __name(_TooManyRequestsException, "TooManyRequestsException");
900
- TooManyRequestsException = _TooManyRequestsException;
901
- _InternalServerException = class _InternalServerException extends ResponseException {
902
- constructor() {
903
- super(...arguments);
904
- this.status = 500;
905
- }
906
- };
907
- __name(_InternalServerException, "InternalServerException");
908
- InternalServerException = _InternalServerException;
909
- _NotImplementedException = class _NotImplementedException extends ResponseException {
910
- constructor() {
911
- super(...arguments);
912
- this.status = 501;
913
- }
914
- };
915
- __name(_NotImplementedException, "NotImplementedException");
916
- NotImplementedException = _NotImplementedException;
917
- _BadGatewayException = class _BadGatewayException extends ResponseException {
918
- constructor() {
919
- super(...arguments);
920
- this.status = 502;
921
- }
922
- };
923
- __name(_BadGatewayException, "BadGatewayException");
924
- BadGatewayException = _BadGatewayException;
925
- _ServiceUnavailableException = class _ServiceUnavailableException extends ResponseException {
926
- constructor() {
927
- super(...arguments);
928
- this.status = 503;
929
- }
930
- };
931
- __name(_ServiceUnavailableException, "ServiceUnavailableException");
932
- ServiceUnavailableException = _ServiceUnavailableException;
933
- _GatewayTimeoutException = class _GatewayTimeoutException extends ResponseException {
934
- constructor() {
935
- super(...arguments);
936
- this.status = 504;
937
- }
938
- };
939
- __name(_GatewayTimeoutException, "GatewayTimeoutException");
940
- GatewayTimeoutException = _GatewayTimeoutException;
941
- _HttpVersionNotSupportedException = class _HttpVersionNotSupportedException extends ResponseException {
942
- constructor() {
943
- super(...arguments);
944
- this.status = 505;
945
- }
946
- };
947
- __name(_HttpVersionNotSupportedException, "HttpVersionNotSupportedException");
948
- HttpVersionNotSupportedException = _HttpVersionNotSupportedException;
949
- _VariantAlsoNegotiatesException = class _VariantAlsoNegotiatesException extends ResponseException {
950
- constructor() {
951
- super(...arguments);
952
- this.status = 506;
953
- }
954
- };
955
- __name(_VariantAlsoNegotiatesException, "VariantAlsoNegotiatesException");
956
- VariantAlsoNegotiatesException = _VariantAlsoNegotiatesException;
957
- _InsufficientStorageException = class _InsufficientStorageException extends ResponseException {
958
- constructor() {
959
- super(...arguments);
960
- this.status = 507;
961
- }
962
- };
963
- __name(_InsufficientStorageException, "InsufficientStorageException");
964
- InsufficientStorageException = _InsufficientStorageException;
965
- _LoopDetectedException = class _LoopDetectedException extends ResponseException {
966
- constructor() {
967
- super(...arguments);
968
- this.status = 508;
685
+ /**
686
+ * Matches a child node against a given segment.
687
+ * This method checks if the segment matches any of the children nodes.
688
+ * @param segment - The segment to match against the children of this node.
689
+ * @returns A child node that matches the segment, or undefined if no match is found.
690
+ */
691
+ matchChild(segment) {
692
+ for (const child of this.children) {
693
+ if (child.isParam || segment.startsWith(child.segment))
694
+ return child;
695
+ }
696
+ return void 0;
697
+ }
698
+ /**
699
+ * Finds a child node that matches the segment exactly.
700
+ * This method checks if there is a child node that matches the segment exactly.
701
+ * @param segment - The segment to find an exact match for among the children of this node.
702
+ * @returns A child node that matches the segment exactly, or undefined if no match is found.
703
+ */
704
+ findExactChild(segment) {
705
+ return this.children.find((c) => c.segment === segment);
706
+ }
707
+ /**
708
+ * Adds a child node to this node's children.
709
+ * This method adds a new child node to the list of children for this node.
710
+ * @param node - The child node to add to this node's children.
711
+ */
712
+ addChild(node) {
713
+ this.children.push(node);
714
+ }
715
+ };
716
+ __name(_RadixNode, "RadixNode");
717
+ var RadixNode = _RadixNode;
718
+ var _RadixTree = class _RadixTree {
719
+ constructor() {
720
+ this.root = new RadixNode("");
721
+ }
722
+ /**
723
+ * Inserts a path and its associated value into the Radix Tree.
724
+ * This method normalizes the path and inserts it into the tree, associating it with
725
+ * @param path - The path to insert into the tree.
726
+ * @param value - The value to associate with the path.
727
+ */
728
+ insert(path2, value) {
729
+ const segments = this.normalize(path2);
730
+ this.insertRecursive(this.root, segments, value);
731
+ }
732
+ /**
733
+ * Recursively inserts a path into the Radix Tree.
734
+ * This method traverses the tree and inserts the segments of the path, creating new nodes
735
+ * @param node - The node to start inserting from.
736
+ * @param segments - The segments of the path to insert.
737
+ * @param value - The value to associate with the path.
738
+ */
739
+ insertRecursive(node, segments, value) {
740
+ if (segments.length === 0) {
741
+ node.value = value;
742
+ return;
743
+ }
744
+ const segment = segments[0] ?? "";
745
+ let child = node.children.find(
746
+ (c) => c.isParam === segment.startsWith(":") && (c.isParam || c.segment === segment)
747
+ );
748
+ if (!child) {
749
+ child = new RadixNode(segment);
750
+ node.addChild(child);
751
+ }
752
+ this.insertRecursive(child, segments.slice(1), value);
753
+ }
754
+ /**
755
+ * Searches for a path in the Radix Tree.
756
+ * This method normalizes the path and searches for it in the tree, returning the node
757
+ * @param path - The path to search for in the Radix Tree.
758
+ * @returns An ISearchResult containing the node and parameters if a match is found, otherwise undefined.
759
+ */
760
+ search(path2) {
761
+ const segments = this.normalize(path2);
762
+ return this.searchRecursive(this.root, segments, {});
763
+ }
764
+ collectValues(node, values = []) {
765
+ if (!node) {
766
+ node = this.root;
767
+ }
768
+ if (node.value !== void 0) {
769
+ values.push(node.value);
770
+ }
771
+ for (const child of node.children) {
772
+ this.collectValues(child, values);
773
+ }
774
+ return values;
775
+ }
776
+ /**
777
+ * Recursively searches for a path in the Radix Tree.
778
+ * This method traverses the tree and searches for the segments of the path, collecting parameters
779
+ * @param node - The node to start searching from.
780
+ * @param segments - The segments of the path to search for.
781
+ * @param params - The parameters collected during the search.
782
+ * @returns An ISearchResult containing the node and parameters if a match is found, otherwise undefined.
783
+ */
784
+ searchRecursive(node, segments, params) {
785
+ if (segments.length === 0) {
786
+ if (node.value !== void 0) {
787
+ return {
788
+ node,
789
+ params
790
+ };
969
791
  }
970
- };
971
- __name(_LoopDetectedException, "LoopDetectedException");
972
- LoopDetectedException = _LoopDetectedException;
973
- _NotExtendedException = class _NotExtendedException extends ResponseException {
974
- constructor() {
975
- super(...arguments);
976
- this.status = 510;
792
+ return void 0;
793
+ }
794
+ const [segment, ...rest] = segments;
795
+ const staticChildren = [];
796
+ const paramChildren = [];
797
+ for (const child of node.children) {
798
+ if (child.isParam) {
799
+ paramChildren.push(child);
800
+ } else if (segment === child.segment) {
801
+ staticChildren.push(child);
977
802
  }
978
- };
979
- __name(_NotExtendedException, "NotExtendedException");
980
- NotExtendedException = _NotExtendedException;
981
- _NetworkAuthenticationRequiredException = class _NetworkAuthenticationRequiredException extends ResponseException {
982
- constructor() {
983
- super(...arguments);
984
- this.status = 511;
803
+ }
804
+ for (const child of staticChildren) {
805
+ if (rest.length === 0) {
806
+ if (child.value !== void 0 || child.children.length > 0) {
807
+ return { node: child, params };
808
+ }
985
809
  }
986
- };
987
- __name(_NetworkAuthenticationRequiredException, "NetworkAuthenticationRequiredException");
988
- NetworkAuthenticationRequiredException = _NetworkAuthenticationRequiredException;
989
- _NetworkConnectTimeoutException = class _NetworkConnectTimeoutException extends ResponseException {
990
- constructor() {
991
- super(...arguments);
992
- this.status = 599;
810
+ const result = this.searchRecursive(child, rest, params);
811
+ if (result) return result;
812
+ }
813
+ for (const child of paramChildren) {
814
+ const paramName = child.paramName;
815
+ const childParams = {
816
+ ...params,
817
+ [paramName]: segment ?? ""
818
+ };
819
+ if (rest.length === 0) {
820
+ if (child.value !== void 0 || child.children.length > 0) {
821
+ return { node: child, params: childParams };
822
+ }
993
823
  }
994
- };
995
- __name(_NetworkConnectTimeoutException, "NetworkConnectTimeoutException");
996
- NetworkConnectTimeoutException = _NetworkConnectTimeoutException;
824
+ const result = this.searchRecursive(child, rest, childParams);
825
+ if (result) return result;
826
+ }
827
+ return void 0;
997
828
  }
998
- });
829
+ /**
830
+ * Normalizes a path into an array of segments.
831
+ * This method removes leading and trailing slashes, splits the path by slashes, and
832
+ * @param path - The path to normalize.
833
+ * @returns An array of normalized path segments.
834
+ */
835
+ normalize(path2) {
836
+ const segments = path2.replace(/^\/+|\/+$/g, "").split("/").filter(Boolean);
837
+ return ["", ...segments];
838
+ }
839
+ };
840
+ __name(_RadixTree, "RadixTree");
841
+ var RadixTree = _RadixTree;
842
+
843
+ // src/internal/exceptions.ts
844
+ var _ResponseException = class _ResponseException extends Error {
845
+ constructor(statusOrMessage, message) {
846
+ let statusCode;
847
+ if (typeof statusOrMessage === "number") {
848
+ statusCode = statusOrMessage;
849
+ } else if (typeof statusOrMessage === "string") {
850
+ message = statusOrMessage;
851
+ }
852
+ super(message ?? "");
853
+ this.status = 0;
854
+ if (statusCode !== void 0) {
855
+ this.status = statusCode;
856
+ }
857
+ this.name = this.constructor.name.replace(/([A-Z])/g, " $1");
858
+ }
859
+ };
860
+ __name(_ResponseException, "ResponseException");
861
+ var ResponseException = _ResponseException;
862
+ var _BadRequestException = class _BadRequestException extends ResponseException {
863
+ constructor() {
864
+ super(...arguments);
865
+ this.status = 400;
866
+ }
867
+ };
868
+ __name(_BadRequestException, "BadRequestException");
869
+ var BadRequestException = _BadRequestException;
870
+ var _UnauthorizedException = class _UnauthorizedException extends ResponseException {
871
+ constructor() {
872
+ super(...arguments);
873
+ this.status = 401;
874
+ }
875
+ };
876
+ __name(_UnauthorizedException, "UnauthorizedException");
877
+ var UnauthorizedException = _UnauthorizedException;
878
+ var _PaymentRequiredException = class _PaymentRequiredException extends ResponseException {
879
+ constructor() {
880
+ super(...arguments);
881
+ this.status = 402;
882
+ }
883
+ };
884
+ __name(_PaymentRequiredException, "PaymentRequiredException");
885
+ var PaymentRequiredException = _PaymentRequiredException;
886
+ var _ForbiddenException = class _ForbiddenException extends ResponseException {
887
+ constructor() {
888
+ super(...arguments);
889
+ this.status = 403;
890
+ }
891
+ };
892
+ __name(_ForbiddenException, "ForbiddenException");
893
+ var ForbiddenException = _ForbiddenException;
894
+ var _NotFoundException = class _NotFoundException extends ResponseException {
895
+ constructor() {
896
+ super(...arguments);
897
+ this.status = 404;
898
+ }
899
+ };
900
+ __name(_NotFoundException, "NotFoundException");
901
+ var NotFoundException = _NotFoundException;
902
+ var _MethodNotAllowedException = class _MethodNotAllowedException extends ResponseException {
903
+ constructor() {
904
+ super(...arguments);
905
+ this.status = 405;
906
+ }
907
+ };
908
+ __name(_MethodNotAllowedException, "MethodNotAllowedException");
909
+ var MethodNotAllowedException = _MethodNotAllowedException;
910
+ var _NotAcceptableException = class _NotAcceptableException extends ResponseException {
911
+ constructor() {
912
+ super(...arguments);
913
+ this.status = 406;
914
+ }
915
+ };
916
+ __name(_NotAcceptableException, "NotAcceptableException");
917
+ var NotAcceptableException = _NotAcceptableException;
918
+ var _RequestTimeoutException = class _RequestTimeoutException extends ResponseException {
919
+ constructor() {
920
+ super(...arguments);
921
+ this.status = 408;
922
+ }
923
+ };
924
+ __name(_RequestTimeoutException, "RequestTimeoutException");
925
+ var RequestTimeoutException = _RequestTimeoutException;
926
+ var _ConflictException = class _ConflictException extends ResponseException {
927
+ constructor() {
928
+ super(...arguments);
929
+ this.status = 409;
930
+ }
931
+ };
932
+ __name(_ConflictException, "ConflictException");
933
+ var ConflictException = _ConflictException;
934
+ var _UpgradeRequiredException = class _UpgradeRequiredException extends ResponseException {
935
+ constructor() {
936
+ super(...arguments);
937
+ this.status = 426;
938
+ }
939
+ };
940
+ __name(_UpgradeRequiredException, "UpgradeRequiredException");
941
+ var UpgradeRequiredException = _UpgradeRequiredException;
942
+ var _TooManyRequestsException = class _TooManyRequestsException extends ResponseException {
943
+ constructor() {
944
+ super(...arguments);
945
+ this.status = 429;
946
+ }
947
+ };
948
+ __name(_TooManyRequestsException, "TooManyRequestsException");
949
+ var TooManyRequestsException = _TooManyRequestsException;
950
+ var _InternalServerException = class _InternalServerException extends ResponseException {
951
+ constructor() {
952
+ super(...arguments);
953
+ this.status = 500;
954
+ }
955
+ };
956
+ __name(_InternalServerException, "InternalServerException");
957
+ var InternalServerException = _InternalServerException;
958
+ var _NotImplementedException = class _NotImplementedException extends ResponseException {
959
+ constructor() {
960
+ super(...arguments);
961
+ this.status = 501;
962
+ }
963
+ };
964
+ __name(_NotImplementedException, "NotImplementedException");
965
+ var NotImplementedException = _NotImplementedException;
966
+ var _BadGatewayException = class _BadGatewayException extends ResponseException {
967
+ constructor() {
968
+ super(...arguments);
969
+ this.status = 502;
970
+ }
971
+ };
972
+ __name(_BadGatewayException, "BadGatewayException");
973
+ var BadGatewayException = _BadGatewayException;
974
+ var _ServiceUnavailableException = class _ServiceUnavailableException extends ResponseException {
975
+ constructor() {
976
+ super(...arguments);
977
+ this.status = 503;
978
+ }
979
+ };
980
+ __name(_ServiceUnavailableException, "ServiceUnavailableException");
981
+ var ServiceUnavailableException = _ServiceUnavailableException;
982
+ var _GatewayTimeoutException = class _GatewayTimeoutException extends ResponseException {
983
+ constructor() {
984
+ super(...arguments);
985
+ this.status = 504;
986
+ }
987
+ };
988
+ __name(_GatewayTimeoutException, "GatewayTimeoutException");
989
+ var GatewayTimeoutException = _GatewayTimeoutException;
990
+ var _HttpVersionNotSupportedException = class _HttpVersionNotSupportedException extends ResponseException {
991
+ constructor() {
992
+ super(...arguments);
993
+ this.status = 505;
994
+ }
995
+ };
996
+ __name(_HttpVersionNotSupportedException, "HttpVersionNotSupportedException");
997
+ var HttpVersionNotSupportedException = _HttpVersionNotSupportedException;
998
+ var _VariantAlsoNegotiatesException = class _VariantAlsoNegotiatesException extends ResponseException {
999
+ constructor() {
1000
+ super(...arguments);
1001
+ this.status = 506;
1002
+ }
1003
+ };
1004
+ __name(_VariantAlsoNegotiatesException, "VariantAlsoNegotiatesException");
1005
+ var VariantAlsoNegotiatesException = _VariantAlsoNegotiatesException;
1006
+ var _InsufficientStorageException = class _InsufficientStorageException extends ResponseException {
1007
+ constructor() {
1008
+ super(...arguments);
1009
+ this.status = 507;
1010
+ }
1011
+ };
1012
+ __name(_InsufficientStorageException, "InsufficientStorageException");
1013
+ var InsufficientStorageException = _InsufficientStorageException;
1014
+ var _LoopDetectedException = class _LoopDetectedException extends ResponseException {
1015
+ constructor() {
1016
+ super(...arguments);
1017
+ this.status = 508;
1018
+ }
1019
+ };
1020
+ __name(_LoopDetectedException, "LoopDetectedException");
1021
+ var LoopDetectedException = _LoopDetectedException;
1022
+ var _NotExtendedException = class _NotExtendedException extends ResponseException {
1023
+ constructor() {
1024
+ super(...arguments);
1025
+ this.status = 510;
1026
+ }
1027
+ };
1028
+ __name(_NotExtendedException, "NotExtendedException");
1029
+ var NotExtendedException = _NotExtendedException;
1030
+ var _NetworkAuthenticationRequiredException = class _NetworkAuthenticationRequiredException extends ResponseException {
1031
+ constructor() {
1032
+ super(...arguments);
1033
+ this.status = 511;
1034
+ }
1035
+ };
1036
+ __name(_NetworkAuthenticationRequiredException, "NetworkAuthenticationRequiredException");
1037
+ var NetworkAuthenticationRequiredException = _NetworkAuthenticationRequiredException;
1038
+ var _NetworkConnectTimeoutException = class _NetworkConnectTimeoutException extends ResponseException {
1039
+ constructor() {
1040
+ super(...arguments);
1041
+ this.status = 599;
1042
+ }
1043
+ };
1044
+ __name(_NetworkConnectTimeoutException, "NetworkConnectTimeoutException");
1045
+ var NetworkConnectTimeoutException = _NetworkConnectTimeoutException;
999
1046
 
1000
1047
  // src/internal/request.ts
1048
+ init_app_injector();
1049
+ var _Request = class _Request {
1050
+ constructor(event, senderId, id, method, path2, body, query) {
1051
+ this.event = event;
1052
+ this.senderId = senderId;
1053
+ this.id = id;
1054
+ this.method = method;
1055
+ this.path = path2;
1056
+ this.body = body;
1057
+ this.context = RootInjector.createScope();
1058
+ this.params = {};
1059
+ this.path = path2.replace(/^\/|\/$/g, "");
1060
+ this.query = query ?? {};
1061
+ }
1062
+ };
1063
+ __name(_Request, "Request");
1064
+ var Request = _Request;
1065
+ var RENDERER_EVENT_TYPE = "noxus:event";
1001
1066
  function createRendererEventMessage(event, payload) {
1002
1067
  return {
1003
1068
  type: RENDERER_EVENT_TYPE,
@@ -1005,6 +1070,7 @@ function createRendererEventMessage(event, payload) {
1005
1070
  payload
1006
1071
  };
1007
1072
  }
1073
+ __name(createRendererEventMessage, "createRendererEventMessage");
1008
1074
  function isRendererEventMessage(value) {
1009
1075
  if (value === null || typeof value !== "object") {
1010
1076
  return false;
@@ -1012,306 +1078,268 @@ function isRendererEventMessage(value) {
1012
1078
  const possibleMessage = value;
1013
1079
  return possibleMessage.type === RENDERER_EVENT_TYPE && typeof possibleMessage.event === "string";
1014
1080
  }
1015
- var _Request, Request, RENDERER_EVENT_TYPE;
1016
- var init_request = __esm({
1017
- "src/internal/request.ts"() {
1018
- "use strict";
1019
- init_app_injector();
1020
- _Request = class _Request {
1021
- constructor(event, senderId, id, method, path2, body) {
1022
- this.event = event;
1023
- this.senderId = senderId;
1024
- this.id = id;
1025
- this.method = method;
1026
- this.path = path2;
1027
- this.body = body;
1028
- this.context = RootInjector.createScope();
1029
- this.params = {};
1030
- this.path = path2.replace(/^\/|\/$/g, "");
1031
- }
1032
- };
1033
- __name(_Request, "Request");
1034
- Request = _Request;
1035
- RENDERER_EVENT_TYPE = "noxus:event";
1036
- __name(createRendererEventMessage, "createRendererEventMessage");
1037
- __name(isRendererEventMessage, "isRendererEventMessage");
1038
- }
1039
- });
1081
+ __name(isRendererEventMessage, "isRendererEventMessage");
1040
1082
 
1041
1083
  // src/internal/router.ts
1042
- var router_exports = {};
1043
- __export(router_exports, {
1044
- Router: () => Router
1045
- });
1046
- var Router;
1047
- var init_router = __esm({
1048
- "src/internal/router.ts"() {
1049
- "use strict";
1050
- init_controller_decorator();
1051
- init_injectable_decorator();
1052
- init_method_decorator();
1053
- init_injector_explorer();
1054
- init_logger();
1055
- init_radix_tree();
1056
- init_exceptions();
1057
- init_request();
1058
- Router = class {
1059
- constructor() {
1060
- this.routes = new RadixTree();
1061
- this.rootMiddlewares = [];
1062
- this.lazyRoutes = /* @__PURE__ */ new Map();
1063
- }
1064
- // -------------------------------------------------------------------------
1065
- // Registration
1066
- // -------------------------------------------------------------------------
1067
- registerController(controllerClass, pathPrefix, routeGuards = [], routeMiddlewares = []) {
1068
- const meta = getControllerMetadata(controllerClass);
1069
- if (!meta) {
1070
- throw new Error(`[Noxus] Missing @Controller decorator on ${controllerClass.name}`);
1071
- }
1072
- const routeMeta = getRouteMetadata(controllerClass);
1073
- for (const def of routeMeta) {
1074
- const fullPath = `${pathPrefix}/${def.path}`.replace(/\/+/g, "/").replace(/\/$/, "") || "/";
1075
- const guards = [.../* @__PURE__ */ new Set([...routeGuards, ...def.guards])];
1076
- const middlewares = [.../* @__PURE__ */ new Set([...routeMiddlewares, ...def.middlewares])];
1077
- const routeDef = {
1078
- method: def.method,
1079
- path: fullPath,
1080
- controller: controllerClass,
1081
- handler: def.handler,
1082
- guards,
1083
- middlewares
1084
- };
1085
- this.routes.insert(fullPath + "/" + def.method, routeDef);
1086
- const guardInfo = guards.length ? `<${guards.map((g) => g.name).join("|")}>` : "";
1087
- Logger.log(`Mapped {${def.method} /${fullPath}}${guardInfo} route`);
1088
- }
1089
- const ctrlGuardInfo = routeGuards.length ? `<${routeGuards.map((g) => g.name).join("|")}>` : "";
1090
- Logger.log(`Mapped ${controllerClass.name}${ctrlGuardInfo} controller's routes`);
1091
- return this;
1092
- }
1093
- registerLazyRoute(pathPrefix, load, guards = [], middlewares = []) {
1094
- const normalized = pathPrefix.replace(/^\/+|\/+$/g, "");
1095
- this.lazyRoutes.set(normalized, { load, guards, middlewares, loading: null, loaded: false });
1096
- Logger.log(`Registered lazy route prefix {${normalized}}`);
1097
- return this;
1098
- }
1099
- defineRootMiddleware(middleware) {
1100
- this.rootMiddlewares.push(middleware);
1101
- return this;
1102
- }
1103
- // -------------------------------------------------------------------------
1104
- // Request handling
1105
- // -------------------------------------------------------------------------
1106
- async handle(request) {
1107
- return request.method === "BATCH" ? this.handleBatch(request) : this.handleAtomic(request);
1108
- }
1109
- async handleAtomic(request) {
1110
- Logger.comment(`> ${request.method} /${request.path}`);
1111
- const t0 = performance.now();
1112
- const response = { requestId: request.id, status: 200, body: null };
1113
- let isCritical = false;
1114
- try {
1115
- const routeDef = await this.findRoute(request);
1116
- await this.resolveController(request, response, routeDef);
1117
- if (response.status >= 400) throw new ResponseException(response.status, response.error);
1118
- } catch (error) {
1119
- this.fillErrorResponse(response, error, (c) => {
1120
- isCritical = c;
1121
- });
1122
- } finally {
1123
- this.logResponse(request, response, performance.now() - t0, isCritical);
1124
- return response;
1125
- }
1126
- }
1127
- async handleBatch(request) {
1128
- Logger.comment(`> ${request.method} /${request.path}`);
1129
- const t0 = performance.now();
1130
- const response = {
1131
- requestId: request.id,
1132
- status: 200,
1133
- body: { responses: [] }
1134
- };
1135
- let isCritical = false;
1136
- try {
1137
- const payload = this.normalizeBatchPayload(request.body);
1138
- response.body.responses = await Promise.all(
1139
- payload.requests.map((item, i) => {
1140
- const id = item.requestId ?? `${request.id}:${i}`;
1141
- return this.handleAtomic(new Request(request.event, request.senderId, id, item.method, item.path, item.body));
1142
- })
1143
- );
1144
- } catch (error) {
1145
- this.fillErrorResponse(response, error, (c) => {
1146
- isCritical = c;
1147
- });
1148
- } finally {
1149
- this.logResponse(request, response, performance.now() - t0, isCritical);
1150
- return response;
1151
- }
1152
- }
1153
- // -------------------------------------------------------------------------
1154
- // Route resolution
1155
- // -------------------------------------------------------------------------
1156
- tryFindRoute(request) {
1157
- const matched = this.routes.search(request.path);
1158
- if (!matched?.node || matched.node.children.length === 0) return void 0;
1159
- return matched.node.findExactChild(request.method)?.value;
1160
- }
1161
- async findRoute(request) {
1162
- const direct = this.tryFindRoute(request);
1163
- if (direct) return direct;
1164
- await this.tryLoadLazyRoute(request.path);
1165
- const afterLazy = this.tryFindRoute(request);
1166
- if (afterLazy) return afterLazy;
1167
- throw new NotFoundException(`No route matches ${request.method} ${request.path}`);
1168
- }
1169
- async tryLoadLazyRoute(requestPath) {
1170
- const firstSegment = requestPath.replace(/^\/+/, "").split("/")[0] ?? "";
1171
- for (const [prefix, entry] of this.lazyRoutes) {
1172
- if (entry.loaded) continue;
1173
- const normalized = requestPath.replace(/^\/+/, "");
1174
- if (normalized === prefix || normalized.startsWith(prefix + "/") || firstSegment === prefix) {
1175
- if (!entry.loading) entry.loading = this.loadLazyModule(prefix, entry);
1176
- await entry.loading;
1177
- return;
1178
- }
1179
- }
1180
- }
1181
- async loadLazyModule(prefix, entry) {
1182
- const t0 = performance.now();
1183
- InjectorExplorer.beginAccumulate();
1184
- await entry.load?.();
1185
- entry.loading = null;
1186
- entry.load = null;
1187
- InjectorExplorer.flushAccumulated(entry.guards, entry.middlewares, prefix);
1188
- entry.loaded = true;
1189
- Logger.info(`Lazy-loaded module for prefix {${prefix}} in ${Math.round(performance.now() - t0)}ms`);
1190
- }
1191
- // -------------------------------------------------------------------------
1192
- // Pipeline
1193
- // -------------------------------------------------------------------------
1194
- async resolveController(request, response, routeDef) {
1195
- const instance = request.context.resolve(routeDef.controller);
1196
- Object.assign(request.params, this.extractParams(request.path, routeDef.path));
1197
- await this.runPipeline(request, response, routeDef, instance);
1198
- }
1199
- async runPipeline(request, response, routeDef, controllerInstance) {
1200
- const middlewares = [.../* @__PURE__ */ new Set([...this.rootMiddlewares, ...routeDef.middlewares])];
1201
- const mwMax = middlewares.length - 1;
1202
- const guardMax = mwMax + routeDef.guards.length;
1203
- let index = -1;
1204
- const dispatch = /* @__PURE__ */ __name(async (i) => {
1205
- if (i <= index) throw new Error("next() called multiple times");
1206
- index = i;
1207
- if (i <= mwMax) {
1208
- await this.runMiddleware(request, response, dispatch.bind(null, i + 1), middlewares[i]);
1209
- if (response.status >= 400) throw new ResponseException(response.status, response.error);
1210
- return;
1211
- }
1212
- if (i <= guardMax) {
1213
- await this.runGuard(request, routeDef.guards[i - middlewares.length]);
1214
- await dispatch(i + 1);
1215
- return;
1216
- }
1217
- const action = controllerInstance[routeDef.handler];
1218
- response.body = await action.call(controllerInstance, request, response);
1219
- if (response.body === void 0) response.body = {};
1220
- }, "dispatch");
1221
- await dispatch(0);
1222
- }
1223
- async runMiddleware(request, response, next, middleware) {
1224
- await middleware(request, response, next);
1225
- }
1226
- async runGuard(request, guard) {
1227
- if (!await guard(request)) {
1228
- throw new UnauthorizedException(`Unauthorized for ${request.method} ${request.path}`);
1229
- }
1230
- }
1231
- // -------------------------------------------------------------------------
1232
- // Utilities
1233
- // -------------------------------------------------------------------------
1234
- extractParams(actual, template) {
1235
- const aParts = actual.split("/");
1236
- const tParts = template.split("/");
1237
- const params = {};
1238
- tParts.forEach((part, i) => {
1239
- if (part.startsWith(":")) params[part.slice(1)] = aParts[i] ?? "";
1240
- });
1241
- return params;
1242
- }
1243
- normalizeBatchPayload(body) {
1244
- if (body === null || typeof body !== "object") {
1245
- throw new BadRequestException("Batch payload must be an object containing a requests array.");
1246
- }
1247
- const { requests } = body;
1248
- if (!Array.isArray(requests)) throw new BadRequestException("Batch payload must define a requests array.");
1249
- return { requests: requests.map((e, i) => this.normalizeBatchItem(e, i)) };
1250
- }
1251
- normalizeBatchItem(entry, index) {
1252
- if (entry === null || typeof entry !== "object") throw new BadRequestException(`Batch request at index ${index} must be an object.`);
1253
- const { requestId, path: path2, method, body } = entry;
1254
- if (requestId !== void 0 && typeof requestId !== "string") throw new BadRequestException(`Batch request at index ${index} has an invalid requestId.`);
1255
- if (typeof path2 !== "string" || !path2.length) throw new BadRequestException(`Batch request at index ${index} must define a non-empty path.`);
1256
- if (typeof method !== "string") throw new BadRequestException(`Batch request at index ${index} must define an HTTP method.`);
1257
- const normalized = method.toUpperCase();
1258
- if (!isAtomicHttpMethod(normalized)) throw new BadRequestException(`Batch request at index ${index} uses unsupported method ${method}.`);
1259
- return { requestId, path: path2, method: normalized, body };
1084
+ var Router = class {
1085
+ constructor() {
1086
+ this.routes = new RadixTree();
1087
+ this.rootMiddlewares = [];
1088
+ this.lazyRoutes = /* @__PURE__ */ new Map();
1089
+ }
1090
+ // -------------------------------------------------------------------------
1091
+ // Registration
1092
+ // -------------------------------------------------------------------------
1093
+ registerController(controllerClass, pathPrefix, routeGuards = [], routeMiddlewares = []) {
1094
+ const meta = getControllerMetadata(controllerClass);
1095
+ if (!meta) {
1096
+ throw new Error(`[Noxus] Missing @Controller decorator on ${controllerClass.name}`);
1097
+ }
1098
+ const routeMeta = getRouteMetadata(controllerClass);
1099
+ for (const def of routeMeta) {
1100
+ const fullPath = `${pathPrefix}/${def.path}`.replace(/\/+/g, "/").replace(/\/$/, "") || "/";
1101
+ const guards = [.../* @__PURE__ */ new Set([...routeGuards, ...def.guards])];
1102
+ const middlewares = [.../* @__PURE__ */ new Set([...routeMiddlewares, ...def.middlewares])];
1103
+ const routeDef = {
1104
+ method: def.method,
1105
+ path: fullPath,
1106
+ controller: controllerClass,
1107
+ handler: def.handler,
1108
+ guards,
1109
+ middlewares
1110
+ };
1111
+ this.routes.insert(fullPath + "/" + def.method, routeDef);
1112
+ const guardInfo = guards.length ? `<${guards.map((g) => g.name).join("|")}>` : "";
1113
+ Logger.log(`Mapped {${def.method} /${fullPath}}${guardInfo} route`);
1114
+ }
1115
+ const ctrlGuardInfo = routeGuards.length ? `<${routeGuards.map((g) => g.name).join("|")}>` : "";
1116
+ Logger.log(`Mapped ${controllerClass.name}${ctrlGuardInfo} controller's routes`);
1117
+ return this;
1118
+ }
1119
+ registerLazyRoute(pathPrefix, load, guards = [], middlewares = []) {
1120
+ const normalized = pathPrefix.replace(/^\/+|\/+$/g, "");
1121
+ this.lazyRoutes.set(normalized, { load, guards, middlewares, loading: null, loaded: false });
1122
+ Logger.log(`Registered lazy route prefix {${normalized}}`);
1123
+ return this;
1124
+ }
1125
+ defineRootMiddleware(middleware) {
1126
+ this.rootMiddlewares.push(middleware);
1127
+ return this;
1128
+ }
1129
+ getRegisteredRoutes() {
1130
+ const allRoutes = this.routes.collectValues();
1131
+ return allRoutes.map((r) => ({ method: r.method, path: r.path }));
1132
+ }
1133
+ getLazyRoutes() {
1134
+ return [...this.lazyRoutes.entries()].map(([prefix, entry]) => ({
1135
+ prefix,
1136
+ loaded: entry.loaded
1137
+ }));
1138
+ }
1139
+ // -------------------------------------------------------------------------
1140
+ // Request handling
1141
+ // -------------------------------------------------------------------------
1142
+ async handle(request) {
1143
+ return request.method === "BATCH" ? this.handleBatch(request) : this.handleAtomic(request);
1144
+ }
1145
+ async handleAtomic(request) {
1146
+ Logger.comment(`> ${request.method} /${request.path}`);
1147
+ const t0 = performance.now();
1148
+ const response = { requestId: request.id, status: 200, body: null };
1149
+ let isCritical = false;
1150
+ try {
1151
+ const routeDef = await this.findRoute(request);
1152
+ await this.resolveController(request, response, routeDef);
1153
+ if (response.status >= 400) throw new ResponseException(response.status, response.error);
1154
+ } catch (error) {
1155
+ this.fillErrorResponse(response, error, (c) => {
1156
+ isCritical = c;
1157
+ });
1158
+ } finally {
1159
+ this.logResponse(request, response, performance.now() - t0, isCritical);
1160
+ return response;
1161
+ }
1162
+ }
1163
+ async handleBatch(request) {
1164
+ Logger.comment(`> ${request.method} /${request.path}`);
1165
+ const t0 = performance.now();
1166
+ const response = {
1167
+ requestId: request.id,
1168
+ status: 200,
1169
+ body: { responses: [] }
1170
+ };
1171
+ let isCritical = false;
1172
+ try {
1173
+ const payload = this.normalizeBatchPayload(request.body);
1174
+ response.body.responses = await Promise.all(
1175
+ payload.requests.map((item, i) => {
1176
+ const id = item.requestId ?? `${request.id}:${i}`;
1177
+ return this.handleAtomic(new Request(request.event, request.senderId, id, item.method, item.path, item.body, item.query));
1178
+ })
1179
+ );
1180
+ } catch (error) {
1181
+ this.fillErrorResponse(response, error, (c) => {
1182
+ isCritical = c;
1183
+ });
1184
+ } finally {
1185
+ this.logResponse(request, response, performance.now() - t0, isCritical);
1186
+ return response;
1187
+ }
1188
+ }
1189
+ // -------------------------------------------------------------------------
1190
+ // Route resolution
1191
+ // -------------------------------------------------------------------------
1192
+ tryFindRoute(request) {
1193
+ const matched = this.routes.search(request.path);
1194
+ if (!matched?.node || matched.node.children.length === 0) return void 0;
1195
+ return matched.node.findExactChild(request.method)?.value;
1196
+ }
1197
+ async findRoute(request) {
1198
+ const direct = this.tryFindRoute(request);
1199
+ if (direct) return direct;
1200
+ await this.tryLoadLazyRoute(request.path);
1201
+ const afterLazy = this.tryFindRoute(request);
1202
+ if (afterLazy) return afterLazy;
1203
+ throw new NotFoundException(`No route matches ${request.method} ${request.path}`);
1204
+ }
1205
+ async tryLoadLazyRoute(requestPath) {
1206
+ const firstSegment = requestPath.replace(/^\/+/, "").split("/")[0] ?? "";
1207
+ for (const [prefix, entry] of this.lazyRoutes) {
1208
+ if (entry.loaded) continue;
1209
+ const normalized = requestPath.replace(/^\/+/, "");
1210
+ if (normalized === prefix || normalized.startsWith(prefix + "/") || firstSegment === prefix) {
1211
+ if (!entry.loading) entry.loading = this.loadLazyModule(prefix, entry);
1212
+ await entry.loading;
1213
+ return;
1260
1214
  }
1261
- fillErrorResponse(response, error, setCritical) {
1262
- response.body = void 0;
1263
- if (error instanceof ResponseException) {
1264
- response.status = error.status;
1265
- response.error = error.message;
1266
- response.stack = error.stack;
1267
- } else if (error instanceof Error) {
1268
- setCritical(true);
1269
- response.status = 500;
1270
- response.error = error.message || "Internal Server Error";
1271
- response.stack = error.stack;
1272
- } else {
1273
- setCritical(true);
1274
- response.status = 500;
1275
- response.error = "Unknown error occurred";
1276
- }
1215
+ }
1216
+ }
1217
+ async loadLazyModule(prefix, entry) {
1218
+ const t0 = performance.now();
1219
+ InjectorExplorer.beginAccumulate();
1220
+ await entry.load?.();
1221
+ entry.loading = null;
1222
+ entry.load = null;
1223
+ await InjectorExplorer.flushAccumulated(entry.guards, entry.middlewares, prefix);
1224
+ entry.loaded = true;
1225
+ Logger.info(`Lazy-loaded module for prefix {${prefix}} in ${Math.round(performance.now() - t0)}ms`);
1226
+ }
1227
+ // -------------------------------------------------------------------------
1228
+ // Pipeline
1229
+ // -------------------------------------------------------------------------
1230
+ async resolveController(request, response, routeDef) {
1231
+ const instance = request.context.resolve(routeDef.controller);
1232
+ Object.assign(request.params, this.extractParams(request.path, routeDef.path));
1233
+ await this.runPipeline(request, response, routeDef, instance);
1234
+ }
1235
+ async runPipeline(request, response, routeDef, controllerInstance) {
1236
+ const middlewares = [.../* @__PURE__ */ new Set([...this.rootMiddlewares, ...routeDef.middlewares])];
1237
+ const mwMax = middlewares.length - 1;
1238
+ const guardMax = mwMax + routeDef.guards.length;
1239
+ let index = -1;
1240
+ const dispatch = /* @__PURE__ */ __name(async (i) => {
1241
+ if (i <= index) throw new Error("next() called multiple times");
1242
+ index = i;
1243
+ if (i <= mwMax) {
1244
+ await this.runMiddleware(request, response, dispatch.bind(null, i + 1), middlewares[i]);
1245
+ if (response.status >= 400) throw new ResponseException(response.status, response.error);
1246
+ return;
1277
1247
  }
1278
- logResponse(request, response, ms, isCritical) {
1279
- const msg = `< ${response.status} ${request.method} /${request.path} ${Logger.colors.yellow}${Math.round(ms)}ms${Logger.colors.initial}`;
1280
- if (response.status < 400) Logger.log(msg);
1281
- else if (response.status < 500) Logger.warn(msg);
1282
- else isCritical ? Logger.critical(msg) : Logger.error(msg);
1283
- if (response.error) {
1284
- isCritical ? Logger.critical(response.error) : Logger.error(response.error);
1285
- if (response.stack) Logger.errorStack(response.stack);
1286
- }
1248
+ if (i <= guardMax) {
1249
+ await this.runGuard(request, routeDef.guards[i - middlewares.length]);
1250
+ await dispatch(i + 1);
1251
+ return;
1287
1252
  }
1288
- };
1289
- __name(Router, "Router");
1290
- Router = __decorateClass([
1291
- Injectable({ lifetime: "singleton" })
1292
- ], Router);
1253
+ const action = controllerInstance[routeDef.handler];
1254
+ response.body = await action.call(controllerInstance, request, response);
1255
+ if (response.body === void 0) response.body = {};
1256
+ }, "dispatch");
1257
+ await dispatch(0);
1293
1258
  }
1294
- });
1295
-
1296
- // src/main.ts
1297
- init_app_injector();
1298
- init_token();
1299
- init_router();
1259
+ async runMiddleware(request, response, next, middleware) {
1260
+ await middleware(request, response, next);
1261
+ }
1262
+ async runGuard(request, guard) {
1263
+ if (!await guard(request)) {
1264
+ throw new UnauthorizedException(`Unauthorized for ${request.method} ${request.path}`);
1265
+ }
1266
+ }
1267
+ // -------------------------------------------------------------------------
1268
+ // Utilities
1269
+ // -------------------------------------------------------------------------
1270
+ extractParams(actual, template) {
1271
+ const aParts = actual.split("/");
1272
+ const tParts = template.split("/");
1273
+ const params = {};
1274
+ tParts.forEach((part, i) => {
1275
+ if (part.startsWith(":")) params[part.slice(1)] = aParts[i] ?? "";
1276
+ });
1277
+ return params;
1278
+ }
1279
+ normalizeBatchPayload(body) {
1280
+ if (body === null || typeof body !== "object") {
1281
+ throw new BadRequestException("Batch payload must be an object containing a requests array.");
1282
+ }
1283
+ const { requests } = body;
1284
+ if (!Array.isArray(requests)) throw new BadRequestException("Batch payload must define a requests array.");
1285
+ return { requests: requests.map((e, i) => this.normalizeBatchItem(e, i)) };
1286
+ }
1287
+ normalizeBatchItem(entry, index) {
1288
+ if (entry === null || typeof entry !== "object") throw new BadRequestException(`Batch request at index ${index} must be an object.`);
1289
+ const { requestId, path: path2, method, body } = entry;
1290
+ if (requestId !== void 0 && typeof requestId !== "string") throw new BadRequestException(`Batch request at index ${index} has an invalid requestId.`);
1291
+ if (typeof path2 !== "string" || !path2.length) throw new BadRequestException(`Batch request at index ${index} must define a non-empty path.`);
1292
+ if (typeof method !== "string") throw new BadRequestException(`Batch request at index ${index} must define an HTTP method.`);
1293
+ const normalized = method.toUpperCase();
1294
+ if (!isAtomicHttpMethod(normalized)) throw new BadRequestException(`Batch request at index ${index} uses unsupported method ${method}.`);
1295
+ return { requestId, path: path2, method: normalized, body };
1296
+ }
1297
+ fillErrorResponse(response, error, setCritical) {
1298
+ response.body = void 0;
1299
+ if (error instanceof ResponseException) {
1300
+ response.status = error.status;
1301
+ response.error = error.message;
1302
+ response.stack = error.stack;
1303
+ } else if (error instanceof Error) {
1304
+ setCritical(true);
1305
+ response.status = 500;
1306
+ response.error = error.message || "Internal Server Error";
1307
+ response.stack = error.stack;
1308
+ } else {
1309
+ setCritical(true);
1310
+ response.status = 500;
1311
+ response.error = "Unknown error occurred";
1312
+ }
1313
+ }
1314
+ logResponse(request, response, ms, isCritical) {
1315
+ const msg = `< ${response.status} ${request.method} /${request.path} ${Logger.colors.yellow}${Math.round(ms)}ms${Logger.colors.initial}`;
1316
+ if (response.status < 400) Logger.log(msg);
1317
+ else if (response.status < 500) Logger.warn(msg);
1318
+ else isCritical ? Logger.critical(msg) : Logger.error(msg);
1319
+ if (response.error) {
1320
+ isCritical ? Logger.critical(response.error) : Logger.error(response.error);
1321
+ if (response.stack) Logger.errorStack(response.stack);
1322
+ }
1323
+ }
1324
+ };
1325
+ __name(Router, "Router");
1326
+ Router = __decorateClass([
1327
+ Injectable({ lifetime: "singleton" })
1328
+ ], Router);
1300
1329
 
1301
1330
  // src/internal/app.ts
1302
- init_injectable_decorator();
1331
+ import { app, BrowserWindow as BrowserWindow2, ipcMain, MessageChannelMain } from "electron/main";
1303
1332
  init_app_injector();
1304
1333
  init_injector_explorer();
1305
1334
  init_logger();
1306
- import { app, BrowserWindow as BrowserWindow2, ipcMain, MessageChannelMain } from "electron/main";
1307
1335
 
1308
1336
  // src/window/window-manager.ts
1309
- init_injectable_decorator();
1337
+ import { BrowserWindow } from "electron/main";
1310
1338
  init_logger();
1311
- import { BrowserWindow, screen } from "electron/main";
1312
1339
  var WindowManager = class {
1313
1340
  constructor() {
1314
1341
  this._windows = /* @__PURE__ */ new Map();
1342
+ this.listeners = /* @__PURE__ */ new Map();
1315
1343
  }
1316
1344
  // -------------------------------------------------------------------------
1317
1345
  // Creation
@@ -1361,18 +1389,24 @@ var WindowManager = class {
1361
1389
  * win.loadFile('index.html');
1362
1390
  */
1363
1391
  async createSplash(options = {}) {
1364
- const { animationDuration = 600, ...bwOptions } = options;
1392
+ const {
1393
+ animationDuration = 10,
1394
+ expandToWorkArea = true,
1395
+ ...bwOptions
1396
+ } = options;
1365
1397
  const win = new BrowserWindow({
1366
1398
  width: 600,
1367
1399
  height: 600,
1368
1400
  center: true,
1369
- frame: false,
1370
1401
  show: true,
1371
1402
  ...bwOptions
1372
1403
  });
1373
1404
  this._register(win, true);
1374
1405
  Logger.log(`[WindowManager] Splash window #${win.id} created`);
1375
- await this._expandToWorkArea(win, animationDuration);
1406
+ if (expandToWorkArea) {
1407
+ await (() => new Promise((r) => setTimeout(r, 500)))();
1408
+ await this._expandToWorkArea(win, animationDuration);
1409
+ }
1376
1410
  return win;
1377
1411
  }
1378
1412
  // -------------------------------------------------------------------------
@@ -1431,10 +1465,24 @@ var WindowManager = class {
1431
1465
  */
1432
1466
  broadcast(channel, ...args) {
1433
1467
  for (const win of this._windows.values()) {
1434
- if (!win.isDestroyed()) win.webContents.send(channel, ...args);
1468
+ if (!win.isDestroyed()) {
1469
+ win.webContents.send(channel, ...args);
1470
+ }
1435
1471
  }
1436
1472
  }
1437
1473
  // -------------------------------------------------------------------------
1474
+ // Events
1475
+ // -------------------------------------------------------------------------
1476
+ on(event, handler) {
1477
+ const set = this.listeners.get(event) ?? /* @__PURE__ */ new Set();
1478
+ set.add(handler);
1479
+ this.listeners.set(event, set);
1480
+ return () => set.delete(handler);
1481
+ }
1482
+ _emit(event, win) {
1483
+ this.listeners.get(event)?.forEach((h) => h(win));
1484
+ }
1485
+ // -------------------------------------------------------------------------
1438
1486
  // Private
1439
1487
  // -------------------------------------------------------------------------
1440
1488
  _register(win, isMain) {
@@ -1442,10 +1490,16 @@ var WindowManager = class {
1442
1490
  if (isMain && this._mainWindowId === void 0) {
1443
1491
  this._mainWindowId = win.id;
1444
1492
  }
1493
+ this._emit("created", win);
1494
+ win.on("focus", () => this._emit("focused", win));
1495
+ win.on("blur", () => this._emit("blurred", win));
1445
1496
  win.once("closed", () => {
1446
1497
  this._windows.delete(win.id);
1447
- if (this._mainWindowId === win.id) this._mainWindowId = void 0;
1498
+ if (this._mainWindowId === win.id) {
1499
+ this._mainWindowId = void 0;
1500
+ }
1448
1501
  Logger.log(`[WindowManager] Window #${win.id} closed`);
1502
+ this._emit("closed", win);
1449
1503
  });
1450
1504
  }
1451
1505
  /**
@@ -1455,17 +1509,18 @@ var WindowManager = class {
1455
1509
  */
1456
1510
  _expandToWorkArea(win, animationDuration) {
1457
1511
  return new Promise((resolve) => {
1458
- const { x, y, width, height } = screen.getPrimaryDisplay().workArea;
1459
- win.setBounds({ x, y, width, height }, true);
1512
+ win.maximize();
1460
1513
  let resolved = false;
1461
1514
  const done = /* @__PURE__ */ __name(() => {
1462
- if (resolved) return;
1515
+ if (resolved) {
1516
+ return;
1517
+ }
1463
1518
  resolved = true;
1464
1519
  win.removeListener("resize", done);
1465
1520
  resolve();
1466
1521
  }, "done");
1467
1522
  win.once("resize", done);
1468
- setTimeout(done, animationDuration + 100);
1523
+ setTimeout(done, animationDuration);
1469
1524
  });
1470
1525
  }
1471
1526
  };
@@ -1474,14 +1529,8 @@ WindowManager = __decorateClass([
1474
1529
  Injectable({ lifetime: "singleton" })
1475
1530
  ], WindowManager);
1476
1531
 
1477
- // src/internal/app.ts
1478
- init_request();
1479
- init_router();
1480
-
1481
1532
  // src/internal/socket.ts
1482
- init_injectable_decorator();
1483
1533
  init_logger();
1484
- init_request();
1485
1534
  var NoxSocket = class {
1486
1535
  constructor() {
1487
1536
  this.channels = /* @__PURE__ */ new Map();
@@ -1504,7 +1553,6 @@ var NoxSocket = class {
1504
1553
  throw new Error("Renderer event name must be a non-empty string.");
1505
1554
  }
1506
1555
  const recipients = targetSenderIds ?? this.getSenderIds();
1507
- let delivered = 0;
1508
1556
  for (const senderId of recipients) {
1509
1557
  const channel = this.channels.get(senderId);
1510
1558
  if (!channel) {
@@ -1513,15 +1561,17 @@ var NoxSocket = class {
1513
1561
  }
1514
1562
  try {
1515
1563
  channel.socket.port1.postMessage(createRendererEventMessage(normalizedEvent, payload));
1516
- delivered++;
1517
1564
  } catch (error) {
1518
1565
  Logger.error(`[Noxus] Failed to emit "${normalizedEvent}" to sender ${senderId}.`, error);
1519
1566
  }
1520
1567
  }
1521
- return delivered;
1522
1568
  }
1523
1569
  emitToRenderer(senderId, eventName, payload) {
1524
- return this.emit(eventName, payload, [senderId]) > 0;
1570
+ if (!this.channels.has(senderId)) {
1571
+ return false;
1572
+ }
1573
+ this.emit(eventName, payload, [senderId]);
1574
+ return true;
1525
1575
  }
1526
1576
  };
1527
1577
  __name(NoxSocket, "NoxSocket");
@@ -1531,22 +1581,22 @@ NoxSocket = __decorateClass([
1531
1581
 
1532
1582
  // src/internal/app.ts
1533
1583
  var NoxApp = class {
1534
- constructor() {
1535
- this.router = inject(Router);
1536
- this.socket = inject(NoxSocket);
1537
- this.windowManager = inject(WindowManager);
1584
+ constructor(router, socket, windowManager) {
1585
+ this.router = router;
1586
+ this.socket = socket;
1587
+ this.windowManager = windowManager;
1538
1588
  // -------------------------------------------------------------------------
1539
1589
  // IPC
1540
1590
  // -------------------------------------------------------------------------
1541
1591
  this.onRendererMessage = /* @__PURE__ */ __name(async (event) => {
1542
- const { senderId, requestId, path: path2, method, body } = event.data;
1592
+ const { senderId, requestId, path: path2, method, body, query } = event.data;
1543
1593
  const channels = this.socket.get(senderId);
1544
1594
  if (!channels) {
1545
1595
  Logger.error(`No message channel found for sender ID: ${senderId}`);
1546
1596
  return;
1547
1597
  }
1548
1598
  try {
1549
- const request = new Request(event, senderId, requestId, method, path2, body);
1599
+ const request = new Request(event, senderId, requestId, method, path2, body, query);
1550
1600
  const response = await this.router.handle(request);
1551
1601
  channels.request.port1.postMessage(response);
1552
1602
  } catch (err) {
@@ -1597,7 +1647,7 @@ var NoxApp = class {
1597
1647
  async load(importFns) {
1598
1648
  InjectorExplorer.beginAccumulate();
1599
1649
  await Promise.all(importFns.map((fn) => fn()));
1600
- InjectorExplorer.flushAccumulated();
1650
+ await InjectorExplorer.flushAccumulated();
1601
1651
  return this;
1602
1652
  }
1603
1653
  /**
@@ -1674,19 +1724,35 @@ NoxApp = __decorateClass([
1674
1724
  // src/internal/bootstrap.ts
1675
1725
  init_app_injector();
1676
1726
  init_injector_explorer();
1727
+ init_logger();
1677
1728
  import { app as app2 } from "electron/main";
1678
1729
  async function bootstrapApplication(config = {}) {
1679
1730
  await app2.whenReady();
1731
+ if (config.logLevel !== void 0) {
1732
+ if (config.logLevel === "none") {
1733
+ Logger.setLogLevel([]);
1734
+ } else if (Array.isArray(config.logLevel)) {
1735
+ Logger.setLogLevel(config.logLevel);
1736
+ } else {
1737
+ Logger.setLogLevel(config.logLevel);
1738
+ }
1739
+ }
1680
1740
  const overrides = /* @__PURE__ */ new Map();
1681
1741
  for (const { token: token2, useValue } of config.singletons ?? []) {
1682
1742
  overrides.set(token2, useValue);
1683
1743
  RootInjector.singletons.set(token2, useValue);
1684
1744
  }
1745
+ InjectorExplorer.setControllerRegistrar((controllerClass, pathPrefix, routeGuards, routeMiddlewares) => {
1746
+ const router = inject(Router);
1747
+ router.registerController(controllerClass, pathPrefix, routeGuards, routeMiddlewares);
1748
+ });
1685
1749
  InjectorExplorer.processPending(overrides);
1686
1750
  const noxApp = inject(NoxApp);
1687
1751
  if (config.routes?.length) {
1688
1752
  for (const route of config.routes) {
1689
- noxApp.lazy(route.path, route.load, route.guards, route.middlewares);
1753
+ if (route.load) {
1754
+ noxApp.lazy(route.path, route.load, route.guards, route.middlewares);
1755
+ }
1690
1756
  }
1691
1757
  }
1692
1758
  if (config.eagerLoad?.length) {
@@ -1698,29 +1764,53 @@ async function bootstrapApplication(config = {}) {
1698
1764
  __name(bootstrapApplication, "bootstrapApplication");
1699
1765
 
1700
1766
  // src/main.ts
1701
- init_exceptions();
1702
- init_controller_decorator();
1703
- init_injectable_decorator();
1704
- init_method_decorator();
1705
1767
  init_logger();
1706
1768
  init_forward_ref();
1707
- init_request();
1708
1769
 
1709
1770
  // src/internal/routes.ts
1710
1771
  function defineRoutes(routes) {
1711
- const paths = routes.map((r) => r.path.replace(/^\/+|\/+$/g, ""));
1772
+ const flat = flattenRoutes(routes);
1773
+ const paths = flat.map((r) => r.path);
1712
1774
  const duplicates = paths.filter((p, i) => paths.indexOf(p) !== i);
1713
1775
  if (duplicates.length > 0) {
1714
1776
  throw new Error(
1715
1777
  `[Noxus] Duplicate route prefixes detected: ${[...new Set(duplicates)].map((d) => `"${d}"`).join(", ")}`
1716
1778
  );
1717
1779
  }
1718
- return routes.map((r) => ({
1719
- ...r,
1720
- path: r.path.replace(/^\/+|\/+$/g, "")
1721
- }));
1780
+ const sorted = [...paths].sort();
1781
+ for (let i = 0; i < sorted.length - 1; i++) {
1782
+ const a = sorted[i];
1783
+ const b = sorted[i + 1];
1784
+ if (b.startsWith(a + "/")) {
1785
+ throw new Error(
1786
+ `[Noxus] Overlapping route prefixes detected: "${a}" and "${b}". Use nested children under "${a}" instead of declaring both as top-level routes.`
1787
+ );
1788
+ }
1789
+ }
1790
+ return flat;
1722
1791
  }
1723
1792
  __name(defineRoutes, "defineRoutes");
1793
+ function flattenRoutes(routes, parentPath = "", parentGuards = [], parentMiddlewares = []) {
1794
+ const result = [];
1795
+ for (const route of routes) {
1796
+ const path2 = [parentPath, route.path.replace(/^\/+|\/+$/g, "")].filter(Boolean).join("/");
1797
+ const guards = [.../* @__PURE__ */ new Set([...parentGuards, ...route.guards ?? []])];
1798
+ const middlewares = [.../* @__PURE__ */ new Set([...parentMiddlewares, ...route.middlewares ?? []])];
1799
+ if (route.load) {
1800
+ result.push({ ...route, path: path2, guards, middlewares });
1801
+ }
1802
+ if (route.children?.length) {
1803
+ result.push(...flattenRoutes(route.children, path2, guards, middlewares));
1804
+ }
1805
+ if (!route.load && !route.children?.length) {
1806
+ throw new Error(
1807
+ `[Noxus] Route "${path2}" has neither a load function nor children. It must have at least one of them.`
1808
+ );
1809
+ }
1810
+ }
1811
+ return result;
1812
+ }
1813
+ __name(flattenRoutes, "flattenRoutes");
1724
1814
  export {
1725
1815
  AppInjector,
1726
1816
  BadGatewayException,
@@ -1773,6 +1863,7 @@ export {
1773
1863
  inject,
1774
1864
  isAtomicHttpMethod,
1775
1865
  isRendererEventMessage,
1866
+ resetRootInjector,
1776
1867
  token
1777
1868
  };
1778
1869
  /**