@noxfly/noxus 1.0.5 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -402,7 +402,84 @@ const instance: MyClass = inject(MyClass);
402
402
 
403
403
  ### Middlewares
404
404
 
405
- § TODO
405
+ Declare middlewares as follow :
406
+
407
+ ```ts
408
+ // renderer/middlewares.ts
409
+
410
+ import { IMiddleware, Injectable, Request, IResponse, NextFunction } from '@noxfly/noxus';
411
+
412
+ @Injectable()
413
+ export class MiddlewareA implements IMiddleware {
414
+ public async invoke(request: Request, response: IResponse, next: NextFunction): Promise<void> {
415
+ console.log(`[Middleware A] before next()`);
416
+ await next();
417
+ console.log(`[Middleware A] after next()`);
418
+ }
419
+ }
420
+
421
+ @Injectable()
422
+ export class MiddlewareB implements IMiddleware {
423
+ public async invoke(request: Request, response: IResponse, next: NextFunction): Promise<void> {
424
+ console.log(`[Middleware B] before next()`);
425
+ await next();
426
+ console.log(`[Middleware B] after next()`);
427
+ }
428
+ }
429
+ ```
430
+
431
+ It is highly recommended to `await` the call of the `next` function.
432
+
433
+ Register these by 3 possible ways :
434
+
435
+ 1. For a root scope. Will be present for each routes.
436
+
437
+ ```ts
438
+ const noxApp = bootstrapApplication(AppModule);
439
+
440
+ noxApp.configure(Application);
441
+
442
+ noxApp.use(MiddlewareA);
443
+ noxApp.use(MiddlewareB);
444
+
445
+ noxApp.start();
446
+ ```
447
+
448
+ 2. Or for a controller or action's scope :
449
+
450
+ ```ts
451
+ @Controller('user')
452
+ @UseMiddlewares([MiddlewareA, MiddlewareB])
453
+ export class UserController {
454
+ @Get('all')
455
+ @UseMiddlewares([MiddlewareA, MiddlewareB])
456
+ public getAll(): Promise<void> {
457
+ // ...
458
+ }
459
+ }
460
+ ```
461
+
462
+ Note that if, for a given action, it has registered multiples times the same middleware, only the first registration will be saved.
463
+
464
+ For instance, registering MiddlewareA for root, on the controller and on the action is useless.
465
+
466
+ The order of declaration of use of middlewares is important.
467
+
468
+ assume we do this :
469
+ 1. Use Middleware A for root
470
+ 2. Use Middleware B for root just after MiddlewareA
471
+ 3. Use Middleware C for controller
472
+ 4. Use Middleware D for action
473
+ 5. Use AuthGuard on the controller
474
+ 6. Use RoleGuard on the action
475
+
476
+ Then the executing pipeline will be as follow :
477
+
478
+ ```r
479
+ A -> B -> C -> D -> AuthGuard -> RoleGuard -> [action] -> D -> C -> B -> A.
480
+ ```
481
+
482
+ if a middleware throw any exception or put the response status higher or equal to 400, the pipeline immediatly stops and the response is returned, weither it is done before or after the call to the next function.
406
483
 
407
484
  ## Contributing
408
485
 
package/dist/noxus.d.mts CHANGED
@@ -62,7 +62,7 @@ declare class Request {
62
62
  readonly method: HttpMethod;
63
63
  readonly path: string;
64
64
  readonly body: any;
65
- readonly context: any;
65
+ readonly context: AppInjector;
66
66
  readonly params: Record<string, string>;
67
67
  constructor(event: Electron.MessageEvent, id: string, method: HttpMethod, path: string, body: any);
68
68
  }
@@ -93,21 +93,62 @@ declare function getGuardForController(controllerName: string): Type<IGuard>[];
93
93
  declare function getGuardForControllerAction(controllerName: string, actionName: string): Type<IGuard>[];
94
94
 
95
95
 
96
+ type NextFunction = () => Promise<void>;
97
+ interface IMiddleware {
98
+ invoke(request: Request, response: IResponse, next: NextFunction): MaybeAsync<void>;
99
+ }
100
+ declare function UseMiddlewares(mdlw: Type<IMiddleware>[]): ClassDecorator & MethodDecorator;
101
+ declare function getMiddlewaresForController(controllerName: string): Type<IMiddleware>[];
102
+ declare function getMiddlewaresForControllerAction(controllerName: string, actionName: string): Type<IMiddleware>[];
103
+
104
+
96
105
  interface IRouteDefinition {
97
106
  method: string;
98
107
  path: string;
99
108
  controller: Type<any>;
100
109
  handler: string;
101
110
  guards: Type<IGuard>[];
111
+ middlewares: Type<IMiddleware>[];
102
112
  }
103
113
  type ControllerAction = (request: Request, response: IResponse) => any;
104
114
  declare class Router {
105
115
  private readonly routes;
116
+ private readonly rootMiddlewares;
117
+ /**
118
+ *
119
+ */
106
120
  registerController(controllerClass: Type<unknown>): Router;
121
+ /**
122
+ *
123
+ */
124
+ defineRootMiddleware(middleware: Type<IMiddleware>): Router;
125
+ /**
126
+ *
127
+ */
107
128
  handle(request: Request): Promise<IResponse>;
129
+ /**
130
+ *
131
+ */
108
132
  private findRoute;
133
+ /**
134
+ *
135
+ */
109
136
  private resolveController;
110
- private verifyRequestBody;
137
+ /**
138
+ *
139
+ */
140
+ private runRequestPipeline;
141
+ /**
142
+ *
143
+ */
144
+ private runMiddleware;
145
+ /**
146
+ *
147
+ */
148
+ private runGuard;
149
+ /**
150
+ *
151
+ */
111
152
  private extractParams;
112
153
  }
113
154
 
@@ -145,6 +186,7 @@ declare class NoxApp {
145
186
  */
146
187
  private onAllWindowsClosed;
147
188
  configure(app: Type<IApp>): NoxApp;
189
+ use(middleware: Type<IMiddleware>): NoxApp;
148
190
  /**
149
191
  * Should be called after the bootstrapApplication function is called.
150
192
  */
@@ -157,9 +199,10 @@ declare class NoxApp {
157
199
  */
158
200
  declare function bootstrapApplication(rootModule: Type<any>): Promise<NoxApp>;
159
201
 
160
- declare abstract class ResponseException extends Error {
161
- abstract readonly status: number;
202
+ declare class ResponseException extends Error {
203
+ readonly status: number;
162
204
  constructor(message?: string);
205
+ constructor(statusCode?: number, message?: string);
163
206
  }
164
207
  declare class BadRequestException extends ResponseException {
165
208
  readonly status = 400;
@@ -284,4 +327,4 @@ declare namespace Logger {
284
327
  function debug(...args: any[]): void;
285
328
  }
286
329
 
287
- export { Authorize, BadGatewayException, BadRequestException, CONTROLLER_METADATA_KEY, ConflictException, Controller, type ControllerAction, Delete, ForbiddenException, GatewayTimeoutException, Get, type HttpMethod, HttpVersionNotSupportedException, type IApp, type IBinding, type IControllerMetadata, type IGuard, type IModuleMetadata, INJECTABLE_METADATA_KEY, type IRequest, type IResponse, type IRouteDefinition, type IRouteMetadata, Injectable, InsufficientStorageException, InternalServerException, type Lifetime, type LogLevel, Logger, LoopDetectedException, MODULE_METADATA_KEY, type MaybeAsync, MethodNotAllowedException, Module, NetworkAuthenticationRequiredException, NetworkConnectTimeoutException, NotAcceptableException, NotExtendedException, NotFoundException, NotImplementedException, NoxApp, Patch, PaymentRequiredException, Post, Put, ROUTE_METADATA_KEY, Request, RequestTimeoutException, ResponseException, RootInjector, Router, ServiceUnavailableException, TooManyRequestsException, type Type, UnauthorizedException, UpgradeRequiredException, VariantAlsoNegotiatesException, bootstrapApplication, getControllerMetadata, getGuardForController, getGuardForControllerAction, getInjectableMetadata, getModuleMetadata, getRouteMetadata, inject };
330
+ export { AppInjector, Authorize, BadGatewayException, BadRequestException, CONTROLLER_METADATA_KEY, ConflictException, Controller, type ControllerAction, Delete, ForbiddenException, GatewayTimeoutException, Get, type HttpMethod, HttpVersionNotSupportedException, type IApp, type IBinding, type IControllerMetadata, type IGuard, type IMiddleware, type IModuleMetadata, INJECTABLE_METADATA_KEY, type IRequest, type IResponse, type IRouteDefinition, type IRouteMetadata, Injectable, InsufficientStorageException, InternalServerException, type Lifetime, type LogLevel, Logger, LoopDetectedException, MODULE_METADATA_KEY, type MaybeAsync, MethodNotAllowedException, Module, NetworkAuthenticationRequiredException, NetworkConnectTimeoutException, type NextFunction, NotAcceptableException, NotExtendedException, NotFoundException, NotImplementedException, NoxApp, Patch, PaymentRequiredException, Post, Put, ROUTE_METADATA_KEY, Request, RequestTimeoutException, ResponseException, RootInjector, Router, ServiceUnavailableException, TooManyRequestsException, type Type, UnauthorizedException, UpgradeRequiredException, UseMiddlewares, VariantAlsoNegotiatesException, bootstrapApplication, getControllerMetadata, getGuardForController, getGuardForControllerAction, getInjectableMetadata, getMiddlewaresForController, getMiddlewaresForControllerAction, getModuleMetadata, getRouteMetadata, inject };
package/dist/noxus.d.ts CHANGED
@@ -62,7 +62,7 @@ declare class Request {
62
62
  readonly method: HttpMethod;
63
63
  readonly path: string;
64
64
  readonly body: any;
65
- readonly context: any;
65
+ readonly context: AppInjector;
66
66
  readonly params: Record<string, string>;
67
67
  constructor(event: Electron.MessageEvent, id: string, method: HttpMethod, path: string, body: any);
68
68
  }
@@ -93,21 +93,62 @@ declare function getGuardForController(controllerName: string): Type<IGuard>[];
93
93
  declare function getGuardForControllerAction(controllerName: string, actionName: string): Type<IGuard>[];
94
94
 
95
95
 
96
+ type NextFunction = () => Promise<void>;
97
+ interface IMiddleware {
98
+ invoke(request: Request, response: IResponse, next: NextFunction): MaybeAsync<void>;
99
+ }
100
+ declare function UseMiddlewares(mdlw: Type<IMiddleware>[]): ClassDecorator & MethodDecorator;
101
+ declare function getMiddlewaresForController(controllerName: string): Type<IMiddleware>[];
102
+ declare function getMiddlewaresForControllerAction(controllerName: string, actionName: string): Type<IMiddleware>[];
103
+
104
+
96
105
  interface IRouteDefinition {
97
106
  method: string;
98
107
  path: string;
99
108
  controller: Type<any>;
100
109
  handler: string;
101
110
  guards: Type<IGuard>[];
111
+ middlewares: Type<IMiddleware>[];
102
112
  }
103
113
  type ControllerAction = (request: Request, response: IResponse) => any;
104
114
  declare class Router {
105
115
  private readonly routes;
116
+ private readonly rootMiddlewares;
117
+ /**
118
+ *
119
+ */
106
120
  registerController(controllerClass: Type<unknown>): Router;
121
+ /**
122
+ *
123
+ */
124
+ defineRootMiddleware(middleware: Type<IMiddleware>): Router;
125
+ /**
126
+ *
127
+ */
107
128
  handle(request: Request): Promise<IResponse>;
129
+ /**
130
+ *
131
+ */
108
132
  private findRoute;
133
+ /**
134
+ *
135
+ */
109
136
  private resolveController;
110
- private verifyRequestBody;
137
+ /**
138
+ *
139
+ */
140
+ private runRequestPipeline;
141
+ /**
142
+ *
143
+ */
144
+ private runMiddleware;
145
+ /**
146
+ *
147
+ */
148
+ private runGuard;
149
+ /**
150
+ *
151
+ */
111
152
  private extractParams;
112
153
  }
113
154
 
@@ -145,6 +186,7 @@ declare class NoxApp {
145
186
  */
146
187
  private onAllWindowsClosed;
147
188
  configure(app: Type<IApp>): NoxApp;
189
+ use(middleware: Type<IMiddleware>): NoxApp;
148
190
  /**
149
191
  * Should be called after the bootstrapApplication function is called.
150
192
  */
@@ -157,9 +199,10 @@ declare class NoxApp {
157
199
  */
158
200
  declare function bootstrapApplication(rootModule: Type<any>): Promise<NoxApp>;
159
201
 
160
- declare abstract class ResponseException extends Error {
161
- abstract readonly status: number;
202
+ declare class ResponseException extends Error {
203
+ readonly status: number;
162
204
  constructor(message?: string);
205
+ constructor(statusCode?: number, message?: string);
163
206
  }
164
207
  declare class BadRequestException extends ResponseException {
165
208
  readonly status = 400;
@@ -284,4 +327,4 @@ declare namespace Logger {
284
327
  function debug(...args: any[]): void;
285
328
  }
286
329
 
287
- export { Authorize, BadGatewayException, BadRequestException, CONTROLLER_METADATA_KEY, ConflictException, Controller, type ControllerAction, Delete, ForbiddenException, GatewayTimeoutException, Get, type HttpMethod, HttpVersionNotSupportedException, type IApp, type IBinding, type IControllerMetadata, type IGuard, type IModuleMetadata, INJECTABLE_METADATA_KEY, type IRequest, type IResponse, type IRouteDefinition, type IRouteMetadata, Injectable, InsufficientStorageException, InternalServerException, type Lifetime, type LogLevel, Logger, LoopDetectedException, MODULE_METADATA_KEY, type MaybeAsync, MethodNotAllowedException, Module, NetworkAuthenticationRequiredException, NetworkConnectTimeoutException, NotAcceptableException, NotExtendedException, NotFoundException, NotImplementedException, NoxApp, Patch, PaymentRequiredException, Post, Put, ROUTE_METADATA_KEY, Request, RequestTimeoutException, ResponseException, RootInjector, Router, ServiceUnavailableException, TooManyRequestsException, type Type, UnauthorizedException, UpgradeRequiredException, VariantAlsoNegotiatesException, bootstrapApplication, getControllerMetadata, getGuardForController, getGuardForControllerAction, getInjectableMetadata, getModuleMetadata, getRouteMetadata, inject };
330
+ export { AppInjector, Authorize, BadGatewayException, BadRequestException, CONTROLLER_METADATA_KEY, ConflictException, Controller, type ControllerAction, Delete, ForbiddenException, GatewayTimeoutException, Get, type HttpMethod, HttpVersionNotSupportedException, type IApp, type IBinding, type IControllerMetadata, type IGuard, type IMiddleware, type IModuleMetadata, INJECTABLE_METADATA_KEY, type IRequest, type IResponse, type IRouteDefinition, type IRouteMetadata, Injectable, InsufficientStorageException, InternalServerException, type Lifetime, type LogLevel, Logger, LoopDetectedException, MODULE_METADATA_KEY, type MaybeAsync, MethodNotAllowedException, Module, NetworkAuthenticationRequiredException, NetworkConnectTimeoutException, type NextFunction, NotAcceptableException, NotExtendedException, NotFoundException, NotImplementedException, NoxApp, Patch, PaymentRequiredException, Post, Put, ROUTE_METADATA_KEY, Request, RequestTimeoutException, ResponseException, RootInjector, Router, ServiceUnavailableException, TooManyRequestsException, type Type, UnauthorizedException, UpgradeRequiredException, UseMiddlewares, VariantAlsoNegotiatesException, bootstrapApplication, getControllerMetadata, getGuardForController, getGuardForControllerAction, getInjectableMetadata, getMiddlewaresForController, getMiddlewaresForControllerAction, getModuleMetadata, getRouteMetadata, inject };
package/dist/noxus.js CHANGED
@@ -28,6 +28,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
28
28
  // src/index.ts
29
29
  var src_exports = {};
30
30
  __export(src_exports, {
31
+ AppInjector: () => AppInjector,
31
32
  Authorize: () => Authorize,
32
33
  BadGatewayException: () => BadGatewayException,
33
34
  BadRequestException: () => BadRequestException,
@@ -69,12 +70,15 @@ __export(src_exports, {
69
70
  TooManyRequestsException: () => TooManyRequestsException,
70
71
  UnauthorizedException: () => UnauthorizedException,
71
72
  UpgradeRequiredException: () => UpgradeRequiredException,
73
+ UseMiddlewares: () => UseMiddlewares,
72
74
  VariantAlsoNegotiatesException: () => VariantAlsoNegotiatesException,
73
75
  bootstrapApplication: () => bootstrapApplication,
74
76
  getControllerMetadata: () => getControllerMetadata,
75
77
  getGuardForController: () => getGuardForController,
76
78
  getGuardForControllerAction: () => getGuardForControllerAction,
77
79
  getInjectableMetadata: () => getInjectableMetadata,
80
+ getMiddlewaresForController: () => getMiddlewaresForController,
81
+ getMiddlewaresForControllerAction: () => getMiddlewaresForControllerAction,
78
82
  getModuleMetadata: () => getModuleMetadata,
79
83
  getRouteMetadata: () => getRouteMetadata,
80
84
  inject: () => inject
@@ -86,8 +90,18 @@ var import_reflect_metadata = require("reflect-metadata");
86
90
 
87
91
  // src/exceptions.ts
88
92
  var _ResponseException = class _ResponseException extends Error {
89
- constructor(message = "") {
90
- super(message);
93
+ constructor(statusOrMessage, message) {
94
+ let statusCode;
95
+ if (typeof statusOrMessage === "number") {
96
+ statusCode = statusOrMessage;
97
+ } else if (typeof statusOrMessage === "string") {
98
+ message = statusOrMessage;
99
+ }
100
+ super(message ?? "");
101
+ __publicField(this, "status", 0);
102
+ if (statusCode !== void 0) {
103
+ this.status = statusCode;
104
+ }
91
105
  this.name = this.constructor.name.replace(/([A-Z])/g, " $1");
92
106
  }
93
107
  };
@@ -279,8 +293,7 @@ __name(_NetworkConnectTimeoutException, "NetworkConnectTimeoutException");
279
293
  var NetworkConnectTimeoutException = _NetworkConnectTimeoutException;
280
294
 
281
295
  // src/DI/app-injector.ts
282
- var _a;
283
- var AppInjector = (_a = class {
296
+ var _AppInjector = class _AppInjector {
284
297
  constructor(name = null) {
285
298
  __publicField(this, "name");
286
299
  __publicField(this, "bindings", /* @__PURE__ */ new Map());
@@ -293,7 +306,7 @@ var AppInjector = (_a = class {
293
306
  * au niveau "scope" (donc durée de vie d'une requête)
294
307
  */
295
308
  createScope() {
296
- const scope = new _a();
309
+ const scope = new _AppInjector();
297
310
  scope.bindings = this.bindings;
298
311
  scope.singletons = this.singletons;
299
312
  return scope;
@@ -304,7 +317,8 @@ var AppInjector = (_a = class {
304
317
  */
305
318
  resolve(target) {
306
319
  const binding = this.bindings.get(target);
307
- if (!binding) throw new InternalServerException(`Failed to resolve a dependency injection : No binding for type ${target.name}`);
320
+ if (!binding) throw new InternalServerException(`Failed to resolve a dependency injection : No binding for type ${target.name}.
321
+ Did you forget to use @Injectable() decorator ?`);
308
322
  switch (binding.lifetime) {
309
323
  case "transient":
310
324
  return this.instantiate(binding.implementation);
@@ -333,7 +347,9 @@ var AppInjector = (_a = class {
333
347
  const params = paramTypes.map((p) => this.resolve(p));
334
348
  return new target(...params);
335
349
  }
336
- }, __name(_a, "AppInjector"), _a);
350
+ };
351
+ __name(_AppInjector, "AppInjector");
352
+ var AppInjector = _AppInjector;
337
353
  var RootInjector = new AppInjector("root");
338
354
  function inject(t) {
339
355
  return RootInjector.resolve(t);
@@ -473,7 +489,7 @@ function Authorize(...guardClasses) {
473
489
  if (authorizations.has(key)) {
474
490
  throw new Error(`Guard(s) already registered for ${key}`);
475
491
  }
476
- Logger.debug(`Registering guards for ${key}: ${guardClasses.map((c) => c.name).join(", ")}`);
492
+ Logger.debug(`Registering guard(s) for ${key}: ${guardClasses.map((c) => c.name).join(", ")}`);
477
493
  authorizations.set(key, guardClasses);
478
494
  };
479
495
  }
@@ -636,9 +652,41 @@ function getControllerMetadata(target) {
636
652
  }
637
653
  __name(getControllerMetadata, "getControllerMetadata");
638
654
 
655
+ // src/decorators/middleware.decorator.ts
656
+ var middlewares = /* @__PURE__ */ new Map();
657
+ function UseMiddlewares(mdlw) {
658
+ return (target, propertyKey) => {
659
+ let key;
660
+ if (propertyKey) {
661
+ const ctrlName = target.constructor.name;
662
+ const actionName = propertyKey;
663
+ key = `${ctrlName}.${actionName}`;
664
+ } else {
665
+ const ctrlName = target.name;
666
+ key = `${ctrlName}`;
667
+ }
668
+ if (middlewares.has(key)) {
669
+ throw new Error(`Middlewares(s) already registered for ${key}`);
670
+ }
671
+ Logger.debug(`Registering middleware(s) for ${key}: ${mdlw.map((c) => c.name).join(", ")}`);
672
+ middlewares.set(key, mdlw);
673
+ };
674
+ }
675
+ __name(UseMiddlewares, "UseMiddlewares");
676
+ function getMiddlewaresForController(controllerName) {
677
+ const key = `${controllerName}`;
678
+ return middlewares.get(key) ?? [];
679
+ }
680
+ __name(getMiddlewaresForController, "getMiddlewaresForController");
681
+ function getMiddlewaresForControllerAction(controllerName, actionName) {
682
+ const key = `${controllerName}.${actionName}`;
683
+ return middlewares.get(key) ?? [];
684
+ }
685
+ __name(getMiddlewaresForControllerAction, "getMiddlewaresForControllerAction");
686
+
639
687
  // src/utils/radix-tree.ts
640
- var _a2;
641
- var RadixNode = (_a2 = class {
688
+ var _a;
689
+ var RadixNode = (_a = class {
642
690
  constructor(segment) {
643
691
  __publicField(this, "segment");
644
692
  __publicField(this, "children", []);
@@ -663,7 +711,7 @@ var RadixNode = (_a2 = class {
663
711
  addChild(node) {
664
712
  this.children.push(node);
665
713
  }
666
- }, __name(_a2, "RadixNode"), _a2);
714
+ }, __name(_a, "RadixNode"), _a);
667
715
  var _RadixTree = class _RadixTree {
668
716
  constructor() {
669
717
  __publicField(this, "root", new RadixNode(""));
@@ -750,19 +798,29 @@ __name(_ts_decorate, "_ts_decorate");
750
798
  var _Router = class _Router {
751
799
  constructor() {
752
800
  __publicField(this, "routes", new RadixTree());
801
+ __publicField(this, "rootMiddlewares", []);
753
802
  }
803
+ /**
804
+ *
805
+ */
754
806
  registerController(controllerClass) {
755
807
  const controllerMeta = getControllerMetadata(controllerClass);
756
808
  const controllerGuards = getGuardForController(controllerClass.name);
809
+ const controllerMiddlewares = getMiddlewaresForController(controllerClass.name);
757
810
  if (!controllerMeta) throw new Error(`Missing @Controller decorator on ${controllerClass.name}`);
758
811
  const routeMetadata = getRouteMetadata(controllerClass);
759
812
  for (const def of routeMetadata) {
760
813
  const fullPath = `${controllerMeta.path}/${def.path}`.replace(/\/+/g, "/");
761
814
  const routeGuards = getGuardForControllerAction(controllerClass.name, def.handler);
815
+ const routeMiddlewares = getMiddlewaresForControllerAction(controllerClass.name, def.handler);
762
816
  const guards = /* @__PURE__ */ new Set([
763
817
  ...controllerGuards,
764
818
  ...routeGuards
765
819
  ]);
820
+ const middlewares2 = /* @__PURE__ */ new Set([
821
+ ...controllerMiddlewares,
822
+ ...routeMiddlewares
823
+ ]);
766
824
  const routeDef = {
767
825
  method: def.method,
768
826
  path: fullPath,
@@ -770,6 +828,9 @@ var _Router = class _Router {
770
828
  handler: def.handler,
771
829
  guards: [
772
830
  ...guards
831
+ ],
832
+ middlewares: [
833
+ ...middlewares2
773
834
  ]
774
835
  };
775
836
  this.routes.insert(fullPath + "/" + def.method, routeDef);
@@ -782,6 +843,17 @@ var _Router = class _Router {
782
843
  Logger.log(`Mapped ${controllerClass.name}${controllerGuardsInfo} controller's routes`);
783
844
  return this;
784
845
  }
846
+ /**
847
+ *
848
+ */
849
+ defineRootMiddleware(middleware) {
850
+ Logger.debug(`Registering root middleware: ${middleware.name}`);
851
+ this.rootMiddlewares.push(middleware);
852
+ return this;
853
+ }
854
+ /**
855
+ *
856
+ */
785
857
  async handle(request) {
786
858
  Logger.log(`> Received request: {${request.method} /${request.path}}`);
787
859
  const t0 = performance.now();
@@ -793,10 +865,10 @@ var _Router = class _Router {
793
865
  };
794
866
  try {
795
867
  const routeDef = this.findRoute(request);
796
- const controllerInstance = await this.resolveController(request, routeDef);
797
- const action = controllerInstance[routeDef.handler];
798
- this.verifyRequestBody(request, action);
799
- response.body = await action.call(controllerInstance, request, response);
868
+ await this.resolveController(request, response, routeDef);
869
+ if (response.status > 400) {
870
+ throw new ResponseException(response.status, response.error);
871
+ }
800
872
  } catch (error) {
801
873
  if (error instanceof ResponseException) {
802
874
  response.status = error.status;
@@ -820,6 +892,9 @@ var _Router = class _Router {
820
892
  return response;
821
893
  }
822
894
  }
895
+ /**
896
+ *
897
+ */
823
898
  findRoute(request) {
824
899
  const matchedRoutes = this.routes.search(request.path);
825
900
  if (matchedRoutes?.node === void 0 || matchedRoutes.node.children.length === 0) {
@@ -831,21 +906,68 @@ var _Router = class _Router {
831
906
  }
832
907
  return routeDef.value;
833
908
  }
834
- async resolveController(request, routeDef) {
909
+ /**
910
+ *
911
+ */
912
+ async resolveController(request, response, routeDef) {
835
913
  const controllerInstance = request.context.resolve(routeDef.controller);
836
914
  Object.assign(request.params, this.extractParams(request.path, routeDef.path));
837
- if (routeDef.guards.length > 0) {
838
- for (const guardType of routeDef.guards) {
839
- const guard = request.context.resolve(guardType);
840
- const allowed = await guard.canActivate(request);
841
- if (!allowed) throw new UnauthorizedException(`Unauthorized for ${request.method} ${request.path}`);
915
+ await this.runRequestPipeline(request, response, routeDef, controllerInstance);
916
+ }
917
+ /**
918
+ *
919
+ */
920
+ async runRequestPipeline(request, response, routeDef, controllerInstance) {
921
+ const middlewares2 = [
922
+ .../* @__PURE__ */ new Set([
923
+ ...this.rootMiddlewares,
924
+ ...routeDef.middlewares
925
+ ])
926
+ ];
927
+ const middlewareMaxIndex = middlewares2.length - 1;
928
+ const guardsMaxIndex = middlewareMaxIndex + routeDef.guards.length;
929
+ let index = -1;
930
+ const dispatch = /* @__PURE__ */ __name(async (i) => {
931
+ if (i <= index) throw new Error("next() called multiple times");
932
+ index = i;
933
+ if (i <= middlewareMaxIndex) {
934
+ const nextFn = dispatch.bind(null, i + 1);
935
+ await this.runMiddleware(request, response, nextFn, middlewares2[i]);
936
+ if (response.status >= 400) {
937
+ throw new ResponseException(response.status, response.error);
938
+ }
939
+ return;
842
940
  }
843
- }
844
- return controllerInstance;
941
+ if (i <= guardsMaxIndex) {
942
+ const guardIndex = i - middlewares2.length;
943
+ const guardType = routeDef.guards[guardIndex];
944
+ await this.runGuard(request, guardType);
945
+ dispatch(i + 1);
946
+ return;
947
+ }
948
+ const action = controllerInstance[routeDef.handler];
949
+ response.body = await action.call(controllerInstance, request, response);
950
+ }, "dispatch");
951
+ await dispatch(0);
952
+ }
953
+ /**
954
+ *
955
+ */
956
+ async runMiddleware(request, response, next, middlewareType) {
957
+ const middleware = request.context.resolve(middlewareType);
958
+ await middleware.invoke(request, response, next);
845
959
  }
846
- verifyRequestBody(request, action) {
847
- const requiredParams = Reflect.getMetadata("design:paramtypes", action) || [];
960
+ /**
961
+ *
962
+ */
963
+ async runGuard(request, guardType) {
964
+ const guard = request.context.resolve(guardType);
965
+ const allowed = await guard.canActivate(request);
966
+ if (!allowed) throw new UnauthorizedException(`Unauthorized for ${request.method} ${request.path}`);
848
967
  }
968
+ /**
969
+ *
970
+ */
849
971
  extractParams(actual, template) {
850
972
  const aParts = actual.split("/");
851
973
  const tParts = template.split("/");
@@ -998,6 +1120,10 @@ var _NoxApp = class _NoxApp {
998
1120
  this.app = inject(app3);
999
1121
  return this;
1000
1122
  }
1123
+ use(middleware) {
1124
+ this.router.defineRootMiddleware(middleware);
1125
+ return this;
1126
+ }
1001
1127
  /**
1002
1128
  * Should be called after the bootstrapApplication function is called.
1003
1129
  */
@@ -1030,6 +1156,7 @@ async function bootstrapApplication(rootModule) {
1030
1156
  __name(bootstrapApplication, "bootstrapApplication");
1031
1157
  // Annotate the CommonJS export names for ESM import in node:
1032
1158
  0 && (module.exports = {
1159
+ AppInjector,
1033
1160
  Authorize,
1034
1161
  BadGatewayException,
1035
1162
  BadRequestException,
@@ -1071,12 +1198,15 @@ __name(bootstrapApplication, "bootstrapApplication");
1071
1198
  TooManyRequestsException,
1072
1199
  UnauthorizedException,
1073
1200
  UpgradeRequiredException,
1201
+ UseMiddlewares,
1074
1202
  VariantAlsoNegotiatesException,
1075
1203
  bootstrapApplication,
1076
1204
  getControllerMetadata,
1077
1205
  getGuardForController,
1078
1206
  getGuardForControllerAction,
1079
1207
  getInjectableMetadata,
1208
+ getMiddlewaresForController,
1209
+ getMiddlewaresForControllerAction,
1080
1210
  getModuleMetadata,
1081
1211
  getRouteMetadata,
1082
1212
  inject