@noxfly/noxus 3.0.0-dev.3 → 3.0.0-dev.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +114 -7
  2. package/dist/child.d.mts +7 -1
  3. package/dist/child.d.ts +7 -1
  4. package/dist/child.js +402 -862
  5. package/dist/child.js.map +1 -0
  6. package/dist/child.mjs +389 -850
  7. package/dist/child.mjs.map +1 -0
  8. package/dist/main.d.mts +171 -125
  9. package/dist/main.d.ts +171 -125
  10. package/dist/main.js +967 -886
  11. package/dist/main.js.map +1 -0
  12. package/dist/main.mjs +914 -834
  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 +17 -2
  17. package/dist/renderer.d.ts +17 -2
  18. package/dist/renderer.js +161 -118
  19. package/dist/renderer.js.map +1 -0
  20. package/dist/renderer.mjs +150 -106
  21. package/dist/renderer.mjs.map +1 -0
  22. package/package.json +10 -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 -160
  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 -109
  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 -268
  56. package/tsconfig.json +0 -29
  57. package/tsup.config.ts +0 -50
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();
1310
- init_logger();
1311
1337
  import { BrowserWindow } from "electron/main";
1338
+ init_logger();
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
@@ -1443,6 +1471,18 @@ var WindowManager = class {
1443
1471
  }
1444
1472
  }
1445
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
+ // -------------------------------------------------------------------------
1446
1486
  // Private
1447
1487
  // -------------------------------------------------------------------------
1448
1488
  _register(win, isMain) {
@@ -1450,12 +1490,16 @@ var WindowManager = class {
1450
1490
  if (isMain && this._mainWindowId === void 0) {
1451
1491
  this._mainWindowId = win.id;
1452
1492
  }
1493
+ this._emit("created", win);
1494
+ win.on("focus", () => this._emit("focused", win));
1495
+ win.on("blur", () => this._emit("blurred", win));
1453
1496
  win.once("closed", () => {
1454
1497
  this._windows.delete(win.id);
1455
1498
  if (this._mainWindowId === win.id) {
1456
1499
  this._mainWindowId = void 0;
1457
1500
  }
1458
1501
  Logger.log(`[WindowManager] Window #${win.id} closed`);
1502
+ this._emit("closed", win);
1459
1503
  });
1460
1504
  }
1461
1505
  /**
@@ -1485,14 +1529,8 @@ WindowManager = __decorateClass([
1485
1529
  Injectable({ lifetime: "singleton" })
1486
1530
  ], WindowManager);
1487
1531
 
1488
- // src/internal/app.ts
1489
- init_request();
1490
- init_router();
1491
-
1492
1532
  // src/internal/socket.ts
1493
- init_injectable_decorator();
1494
1533
  init_logger();
1495
- init_request();
1496
1534
  var NoxSocket = class {
1497
1535
  constructor() {
1498
1536
  this.channels = /* @__PURE__ */ new Map();
@@ -1515,7 +1553,6 @@ var NoxSocket = class {
1515
1553
  throw new Error("Renderer event name must be a non-empty string.");
1516
1554
  }
1517
1555
  const recipients = targetSenderIds ?? this.getSenderIds();
1518
- let delivered = 0;
1519
1556
  for (const senderId of recipients) {
1520
1557
  const channel = this.channels.get(senderId);
1521
1558
  if (!channel) {
@@ -1524,15 +1561,17 @@ var NoxSocket = class {
1524
1561
  }
1525
1562
  try {
1526
1563
  channel.socket.port1.postMessage(createRendererEventMessage(normalizedEvent, payload));
1527
- delivered++;
1528
1564
  } catch (error) {
1529
1565
  Logger.error(`[Noxus] Failed to emit "${normalizedEvent}" to sender ${senderId}.`, error);
1530
1566
  }
1531
1567
  }
1532
- return delivered;
1533
1568
  }
1534
1569
  emitToRenderer(senderId, eventName, payload) {
1535
- 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;
1536
1575
  }
1537
1576
  };
1538
1577
  __name(NoxSocket, "NoxSocket");
@@ -1542,22 +1581,22 @@ NoxSocket = __decorateClass([
1542
1581
 
1543
1582
  // src/internal/app.ts
1544
1583
  var NoxApp = class {
1545
- constructor() {
1546
- this.router = inject(Router);
1547
- this.socket = inject(NoxSocket);
1548
- this.windowManager = inject(WindowManager);
1584
+ constructor(router, socket, windowManager) {
1585
+ this.router = router;
1586
+ this.socket = socket;
1587
+ this.windowManager = windowManager;
1549
1588
  // -------------------------------------------------------------------------
1550
1589
  // IPC
1551
1590
  // -------------------------------------------------------------------------
1552
1591
  this.onRendererMessage = /* @__PURE__ */ __name(async (event) => {
1553
- const { senderId, requestId, path: path2, method, body } = event.data;
1592
+ const { senderId, requestId, path: path2, method, body, query } = event.data;
1554
1593
  const channels = this.socket.get(senderId);
1555
1594
  if (!channels) {
1556
1595
  Logger.error(`No message channel found for sender ID: ${senderId}`);
1557
1596
  return;
1558
1597
  }
1559
1598
  try {
1560
- const request = new Request(event, senderId, requestId, method, path2, body);
1599
+ const request = new Request(event, senderId, requestId, method, path2, body, query);
1561
1600
  const response = await this.router.handle(request);
1562
1601
  channels.request.port1.postMessage(response);
1563
1602
  } catch (err) {
@@ -1608,7 +1647,7 @@ var NoxApp = class {
1608
1647
  async load(importFns) {
1609
1648
  InjectorExplorer.beginAccumulate();
1610
1649
  await Promise.all(importFns.map((fn) => fn()));
1611
- InjectorExplorer.flushAccumulated();
1650
+ await InjectorExplorer.flushAccumulated();
1612
1651
  return this;
1613
1652
  }
1614
1653
  /**
@@ -1685,19 +1724,35 @@ NoxApp = __decorateClass([
1685
1724
  // src/internal/bootstrap.ts
1686
1725
  init_app_injector();
1687
1726
  init_injector_explorer();
1727
+ init_logger();
1688
1728
  import { app as app2 } from "electron/main";
1689
1729
  async function bootstrapApplication(config = {}) {
1690
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
+ }
1691
1740
  const overrides = /* @__PURE__ */ new Map();
1692
1741
  for (const { token: token2, useValue } of config.singletons ?? []) {
1693
1742
  overrides.set(token2, useValue);
1694
1743
  RootInjector.singletons.set(token2, useValue);
1695
1744
  }
1745
+ InjectorExplorer.setControllerRegistrar((controllerClass, pathPrefix, routeGuards, routeMiddlewares) => {
1746
+ const router = inject(Router);
1747
+ router.registerController(controllerClass, pathPrefix, routeGuards, routeMiddlewares);
1748
+ });
1696
1749
  InjectorExplorer.processPending(overrides);
1697
1750
  const noxApp = inject(NoxApp);
1698
1751
  if (config.routes?.length) {
1699
1752
  for (const route of config.routes) {
1700
- 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
+ }
1701
1756
  }
1702
1757
  }
1703
1758
  if (config.eagerLoad?.length) {
@@ -1709,29 +1764,53 @@ async function bootstrapApplication(config = {}) {
1709
1764
  __name(bootstrapApplication, "bootstrapApplication");
1710
1765
 
1711
1766
  // src/main.ts
1712
- init_exceptions();
1713
- init_controller_decorator();
1714
- init_injectable_decorator();
1715
- init_method_decorator();
1716
1767
  init_logger();
1717
1768
  init_forward_ref();
1718
- init_request();
1719
1769
 
1720
1770
  // src/internal/routes.ts
1721
1771
  function defineRoutes(routes) {
1722
- const paths = routes.map((r) => r.path.replace(/^\/+|\/+$/g, ""));
1772
+ const flat = flattenRoutes(routes);
1773
+ const paths = flat.map((r) => r.path);
1723
1774
  const duplicates = paths.filter((p, i) => paths.indexOf(p) !== i);
1724
1775
  if (duplicates.length > 0) {
1725
1776
  throw new Error(
1726
1777
  `[Noxus] Duplicate route prefixes detected: ${[...new Set(duplicates)].map((d) => `"${d}"`).join(", ")}`
1727
1778
  );
1728
1779
  }
1729
- return routes.map((r) => ({
1730
- ...r,
1731
- path: r.path.replace(/^\/+|\/+$/g, "")
1732
- }));
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;
1733
1791
  }
1734
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");
1735
1814
  export {
1736
1815
  AppInjector,
1737
1816
  BadGatewayException,
@@ -1784,6 +1863,7 @@ export {
1784
1863
  inject,
1785
1864
  isAtomicHttpMethod,
1786
1865
  isRendererEventMessage,
1866
+ resetRootInjector,
1787
1867
  token
1788
1868
  };
1789
1869
  /**