@noxfly/noxus 3.0.0-dev.0 → 3.0.0-dev.10

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