@noxfly/noxus 1.0.4 → 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/src/bootstrap.ts CHANGED
@@ -1,128 +1,29 @@
1
- import { ipcMain } from "electron";
2
- import { app, BrowserWindow, MessageChannelMain } from "electron/main";
3
- import { IApp } from "src/app";
4
- import { getInjectableMetadata } from "src/decorators/injectable.decorator";
1
+ /**
2
+ * @copyright 2025 NoxFly
3
+ * @license MIT
4
+ * @author NoxFly
5
+ */
6
+
7
+ import { app } from "electron/main";
8
+ import { NoxApp } from "src/app";
5
9
  import { getModuleMetadata } from "src/decorators/module.decorator";
6
- import { RootInjector } from "src/DI/app-injector";
7
- import { IRequest, IResponse, Request } from "src/request";
8
- import { Router } from "src/router";
10
+ import { inject } from "src/DI/app-injector";
9
11
  import { Type } from "src/utils/types";
10
12
 
11
13
  /**
12
14
  *
13
15
  */
14
- export async function bootstrapApplication(root: Type<IApp>, rootModule: Type<any>): Promise<IApp> {
16
+ export async function bootstrapApplication(rootModule: Type<any>): Promise<NoxApp> {
15
17
  if(!getModuleMetadata(rootModule)) {
16
18
  throw new Error(`Root module must be decorated with @Module`);
17
19
  }
18
20
 
19
- if(!getInjectableMetadata(root)) {
20
- throw new Error(`Root application must be decorated with @Injectable`);
21
- }
22
-
23
21
  await app.whenReady();
24
22
 
25
- RootInjector.resolve(Router);
26
-
27
- const noxEngine = new Nox(root, rootModule);
28
-
29
- const application = await noxEngine.init();
30
-
31
- return application;
32
- }
23
+ const noxApp = inject(NoxApp);
33
24
 
25
+ await noxApp.init();
34
26
 
35
- class Nox {
36
- private messagePort: Electron.MessageChannelMain | undefined;
37
-
38
- constructor(
39
- public readonly root: Type<IApp>,
40
- public readonly rootModule: Type<any>
41
- ) {}
42
-
43
- /**
44
- *
45
- */
46
- public async init(): Promise<IApp> {
47
- const application = RootInjector.resolve(this.root);
48
-
49
- ipcMain.on('gimme-my-port', this.giveTheClientAPort.bind(this, application));
50
-
51
- app.once('activate', this.onAppActivated.bind(this, application));
52
- app.once('window-all-closed', this.onAllWindowsClosed.bind(this, application));
53
-
54
- await application.onReady();
55
-
56
- console.log(''); // create a new line in the console to separate setup logs from the future logs
57
-
58
- return application;
59
- }
60
-
61
- /**
62
- *
63
- */
64
- private giveTheClientAPort(application: IApp, event: Electron.IpcMainInvokeEvent): void {
65
- if(this.messagePort) {
66
- this.messagePort.port1.close();
67
- this.messagePort.port2.close();
68
- this.messagePort = undefined;
69
- }
70
-
71
- this.messagePort = new MessageChannelMain();
72
-
73
- this.messagePort.port1.on('message', event => this.onClientMessage(application, event));
74
- this.messagePort.port1.start();
75
-
76
- event.sender.postMessage('port', null, [this.messagePort.port2]);
77
- }
78
-
79
- /**
80
- * Electron specific message handling.
81
- * Replaces HTTP calls by using Electron's IPC mechanism.
82
- */
83
- private async onClientMessage(application: IApp, event: Electron.MessageEvent): Promise<void> {
84
- const { requestId, path, method, body }: IRequest = event.data;
85
-
86
- try {
87
-
88
- const request = new Request(application, event, requestId, method, path, body);
89
- const router = RootInjector.resolve(Router);
90
-
91
- const response = await router.handle(request);
92
-
93
- this.messagePort?.port1.postMessage(response);
94
- }
95
- catch(err: any) {
96
- const response: IResponse = {
97
- requestId,
98
- status: 500,
99
- body: null,
100
- error: err.message || 'Internal Server Error',
101
- };
102
-
103
- this.messagePort?.port1.postMessage(response);
104
- }
105
- }
106
-
107
- /**
108
- *
109
- */
110
- private onAppActivated(application: IApp): void {
111
- if(BrowserWindow.getAllWindows().length === 0) {
112
- application.onReady();
113
- }
114
- }
115
-
116
- /**
117
- *
118
- */
119
- private async onAllWindowsClosed(application: IApp): Promise<void> {
120
- this.messagePort?.port1.close();
121
- await application.dispose();
122
-
123
- if(process.platform !== 'darwin') {
124
- app.quit();
125
- }
126
- }
27
+ return noxApp;
127
28
  }
128
29
 
@@ -1,3 +1,9 @@
1
+ /**
2
+ * @copyright 2025 NoxFly
3
+ * @license MIT
4
+ * @author NoxFly
5
+ */
6
+
1
7
  import { getGuardForController, IGuard } from "src/decorators/guards.decorator";
2
8
  import { Injectable } from "src/decorators/injectable.decorator";
3
9
  import { Type } from "src/utils/types";
@@ -1,3 +1,9 @@
1
+ /**
2
+ * @copyright 2025 NoxFly
3
+ * @license MIT
4
+ * @author NoxFly
5
+ */
6
+
1
7
  import { Request } from 'src/request';
2
8
  import { Logger } from 'src/utils/logger';
3
9
  import { MaybeAsync, Type } from 'src/utils/types';
@@ -13,7 +19,7 @@ const authorizations = new Map<string, Type<IGuard>[]>();
13
19
  * Peut être utilisé sur une classe controleur, ou sur une méthode de contrôleur.
14
20
  */
15
21
  export function Authorize(...guardClasses: Type<IGuard>[]): MethodDecorator & ClassDecorator {
16
- return (target: any, propertyKey?: string | symbol) => {
22
+ return (target: Function | object, propertyKey?: string | symbol) => {
17
23
  let key: string;
18
24
 
19
25
  // Method decorator
@@ -32,7 +38,7 @@ export function Authorize(...guardClasses: Type<IGuard>[]): MethodDecorator & Cl
32
38
  throw new Error(`Guard(s) already registered for ${key}`);
33
39
  }
34
40
 
35
- Logger.debug(`Registering guards for ${key}: ${guardClasses.map(c => c.name).join(', ')}`);
41
+ Logger.debug(`Registering guard(s) for ${key}: ${guardClasses.map(c => c.name).join(', ')}`);
36
42
 
37
43
  authorizations.set(key, guardClasses);
38
44
  };
@@ -1,3 +1,9 @@
1
+ /**
2
+ * @copyright 2025 NoxFly
3
+ * @license MIT
4
+ * @author NoxFly
5
+ */
6
+
1
7
  import { Lifetime } from "src/DI/app-injector";
2
8
  import { InjectorExplorer } from "src/DI/injector-explorer";
3
9
  import { Type } from "src/utils/types";
@@ -1,3 +1,9 @@
1
+ /**
2
+ * @copyright 2025 NoxFly
3
+ * @license MIT
4
+ * @author NoxFly
5
+ */
6
+
1
7
  import { getGuardForControllerAction, IGuard } from "src/decorators/guards.decorator";
2
8
  import { Type } from "src/utils/types";
3
9
 
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @copyright 2025 NoxFly
3
+ * @license MIT
4
+ * @author NoxFly
5
+ */
6
+
7
+ import { IResponse, Request } from "src/request";
8
+ import { Logger } from "src/utils/logger";
9
+ import { MaybeAsync, Type } from "src/utils/types";
10
+
11
+
12
+ export type NextFunction = () => Promise<void>;
13
+
14
+ export interface IMiddleware {
15
+ invoke(request: Request, response: IResponse, next: NextFunction): MaybeAsync<void>;
16
+ }
17
+
18
+ const middlewares = new Map<string, Type<IMiddleware>[]>();
19
+
20
+ export function UseMiddlewares(mdlw: Type<IMiddleware>[]): ClassDecorator & MethodDecorator {
21
+ return (target: Function | object, propertyKey?: string | symbol) => {
22
+ let key: string;
23
+
24
+ // Method decorator
25
+ if(propertyKey) {
26
+ const ctrlName = target.constructor.name;
27
+ const actionName = propertyKey as string;
28
+ key = `${ctrlName}.${actionName}`;
29
+ }
30
+ // Class decorator
31
+ else {
32
+ const ctrlName = (target as Type<unknown>).name;
33
+ key = `${ctrlName}`;
34
+ }
35
+
36
+ if(middlewares.has(key)) {
37
+ throw new Error(`Middlewares(s) already registered for ${key}`);
38
+ }
39
+
40
+ Logger.debug(`Registering middleware(s) for ${key}: ${mdlw.map(c => c.name).join(', ')}`);
41
+
42
+ middlewares.set(key, mdlw);
43
+ };
44
+ }
45
+
46
+ export function getMiddlewaresForController(controllerName: string): Type<IMiddleware>[] {
47
+ const key = `${controllerName}`;
48
+ return middlewares.get(key) ?? [];
49
+ }
50
+
51
+ export function getMiddlewaresForControllerAction(controllerName: string, actionName: string): Type<IMiddleware>[] {
52
+ const key = `${controllerName}.${actionName}`;
53
+ return middlewares.get(key) ?? [];
54
+ }
@@ -1,4 +1,8 @@
1
- /* eslint-disable @typescript-eslint/no-unsafe-function-type */
1
+ /**
2
+ * @copyright 2025 NoxFly
3
+ * @license MIT
4
+ * @author NoxFly
5
+ */
2
6
 
3
7
  import { CONTROLLER_METADATA_KEY } from "src/decorators/controller.decorator";
4
8
  import { Injectable, INJECTABLE_METADATA_KEY } from "src/decorators/injectable.decorator";
package/src/exceptions.ts CHANGED
@@ -1,8 +1,29 @@
1
- export abstract class ResponseException extends Error {
2
- public abstract readonly status: number;
1
+ /**
2
+ * @copyright 2025 NoxFly
3
+ * @license MIT
4
+ * @author NoxFly
5
+ */
3
6
 
4
- constructor(message: string = "") {
5
- super(message);
7
+ export class ResponseException extends Error {
8
+ public readonly status: number = 0;
9
+
10
+ constructor(message?: string);
11
+ constructor(statusCode?: number, message?: string);
12
+ constructor(statusOrMessage?: number | string, message?: string) {
13
+ let statusCode: number | undefined;
14
+
15
+ if(typeof statusOrMessage === 'number') {
16
+ statusCode = statusOrMessage;
17
+ }
18
+ else if(typeof statusOrMessage === 'string') {
19
+ message = statusOrMessage;
20
+ }
21
+
22
+ super(message ?? "");
23
+
24
+ if(statusCode !== undefined) {
25
+ this.status = statusCode;
26
+ }
6
27
 
7
28
  this.name = this.constructor.name
8
29
  .replace(/([A-Z])/g, ' $1');
@@ -10,26 +31,27 @@ export abstract class ResponseException extends Error {
10
31
  }
11
32
 
12
33
  // 4XX
13
- export class BadRequestException extends ResponseException { public readonly status = 400; }
14
- export class UnauthorizedException extends ResponseException { public readonly status = 401; }
15
- export class ForbiddenException extends ResponseException { public readonly status = 403; }
16
- export class NotFoundException extends ResponseException { public readonly status = 404; }
17
- export class MethodNotAllowedException extends ResponseException { public readonly status = 405; }
18
- export class NotAcceptableException extends ResponseException { public readonly status = 406; }
19
- export class RequestTimeoutException extends ResponseException { public readonly status = 408; }
20
- export class ConflictException extends ResponseException { public readonly status = 409; }
21
- export class UpgradeRequiredException extends ResponseException { public readonly status = 426; }
22
- export class TooManyRequestsException extends ResponseException { public readonly status = 429; }
34
+ export class BadRequestException extends ResponseException { public override readonly status = 400; }
35
+ export class UnauthorizedException extends ResponseException { public override readonly status = 401; }
36
+ export class PaymentRequiredException extends ResponseException { public override readonly status = 402; }
37
+ export class ForbiddenException extends ResponseException { public override readonly status = 403; }
38
+ export class NotFoundException extends ResponseException { public override readonly status = 404; }
39
+ export class MethodNotAllowedException extends ResponseException { public override readonly status = 405; }
40
+ export class NotAcceptableException extends ResponseException { public override readonly status = 406; }
41
+ export class RequestTimeoutException extends ResponseException { public override readonly status = 408; }
42
+ export class ConflictException extends ResponseException { public override readonly status = 409; }
43
+ export class UpgradeRequiredException extends ResponseException { public override readonly status = 426; }
44
+ export class TooManyRequestsException extends ResponseException { public override readonly status = 429; }
23
45
  // 5XX
24
- export class InternalServerException extends ResponseException { public readonly status = 500; }
25
- export class NotImplementedException extends ResponseException { public readonly status = 501; }
26
- export class BadGatewayException extends ResponseException { public readonly status = 502; }
27
- export class ServiceUnavailableException extends ResponseException { public readonly status = 503; }
28
- export class GatewayTimeoutException extends ResponseException { public readonly status = 504; }
29
- export class HttpVersionNotSupportedException extends ResponseException { public readonly status = 505; }
30
- export class VariantAlsoNegotiatesException extends ResponseException { public readonly status = 506; }
31
- export class InsufficientStorageException extends ResponseException { public readonly status = 507; }
32
- export class LoopDetectedException extends ResponseException { public readonly status = 508; }
33
- export class NotExtendedException extends ResponseException { public readonly status = 510; }
34
- export class NetworkAuthenticationRequiredException extends ResponseException { public readonly status = 511; }
35
- export class NetworkConnectTimeoutException extends ResponseException { public readonly status = 599; }
46
+ export class InternalServerException extends ResponseException { public override readonly status = 500; }
47
+ export class NotImplementedException extends ResponseException { public override readonly status = 501; }
48
+ export class BadGatewayException extends ResponseException { public override readonly status = 502; }
49
+ export class ServiceUnavailableException extends ResponseException { public override readonly status = 503; }
50
+ export class GatewayTimeoutException extends ResponseException { public override readonly status = 504; }
51
+ export class HttpVersionNotSupportedException extends ResponseException { public override readonly status = 505; }
52
+ export class VariantAlsoNegotiatesException extends ResponseException { public override readonly status = 506; }
53
+ export class InsufficientStorageException extends ResponseException { public override readonly status = 507; }
54
+ export class LoopDetectedException extends ResponseException { public override readonly status = 508; }
55
+ export class NotExtendedException extends ResponseException { public override readonly status = 510; }
56
+ export class NetworkAuthenticationRequiredException extends ResponseException { public override readonly status = 511; }
57
+ export class NetworkConnectTimeoutException extends ResponseException { public override readonly status = 599; }
package/src/index.ts CHANGED
@@ -1,8 +1,15 @@
1
+ /**
2
+ * @copyright 2025 NoxFly
3
+ * @license MIT
4
+ * @author NoxFly
5
+ */
6
+
1
7
  export * from './DI/app-injector';
2
8
  export * from './router';
3
9
  export * from './app';
4
10
  export * from './bootstrap';
5
11
  export * from './exceptions';
12
+ export * from './decorators/middleware.decorator';
6
13
  export * from './decorators/guards.decorator';
7
14
  export * from './decorators/controller.decorator';
8
15
  export * from './decorators/injectable.decorator';
package/src/request.ts CHANGED
@@ -1,18 +1,19 @@
1
+ /**
2
+ * @copyright 2025 NoxFly
3
+ * @license MIT
4
+ * @author NoxFly
5
+ */
6
+
1
7
  import 'reflect-metadata';
2
- import { IApp } from 'src/app';
3
8
  import { HttpMethod } from 'src/decorators/method.decorator';
4
- import { RootInjector } from 'src/DI/app-injector';
5
-
6
-
7
- //
9
+ import { AppInjector, RootInjector } from 'src/DI/app-injector';
8
10
 
9
11
  export class Request {
10
- public readonly context: any = RootInjector.createScope();
12
+ public readonly context: AppInjector = RootInjector.createScope();
11
13
 
12
14
  public readonly params: Record<string, string> = {};
13
15
 
14
16
  constructor(
15
- public readonly app: IApp,
16
17
  public readonly event: Electron.MessageEvent,
17
18
  public readonly id: string,
18
19
  public readonly method: HttpMethod,
@@ -24,6 +25,7 @@ export class Request {
24
25
  }
25
26
 
26
27
  export interface IRequest<T = any> {
28
+ senderId: number;
27
29
  requestId: string;
28
30
  path: string;
29
31
  method: HttpMethod;
package/src/router.ts CHANGED
@@ -1,8 +1,15 @@
1
+ /**
2
+ * @copyright 2025 NoxFly
3
+ * @license MIT
4
+ * @author NoxFly
5
+ */
6
+
1
7
  import 'reflect-metadata';
2
8
  import { getControllerMetadata } from 'src/decorators/controller.decorator';
3
9
  import { getGuardForController, getGuardForControllerAction, IGuard } from 'src/decorators/guards.decorator';
4
10
  import { Injectable } from 'src/decorators/injectable.decorator';
5
11
  import { getRouteMetadata } from 'src/decorators/method.decorator';
12
+ import { getMiddlewaresForController, getMiddlewaresForControllerAction, IMiddleware, NextFunction } from 'src/decorators/middleware.decorator';
6
13
  import { MethodNotAllowedException, NotFoundException, ResponseException, UnauthorizedException } from 'src/exceptions';
7
14
  import { IResponse, Request } from 'src/request';
8
15
  import { Logger } from 'src/utils/logger';
@@ -17,6 +24,7 @@ export interface IRouteDefinition {
17
24
  controller: Type<any>;
18
25
  handler: string;
19
26
  guards: Type<IGuard>[];
27
+ middlewares: Type<IMiddleware>[];
20
28
  }
21
29
 
22
30
  export type ControllerAction = (request: Request, response: IResponse) => any;
@@ -25,11 +33,16 @@ export type ControllerAction = (request: Request, response: IResponse) => any;
25
33
  @Injectable('singleton')
26
34
  export class Router {
27
35
  private readonly routes = new RadixTree<IRouteDefinition>();
36
+ private readonly rootMiddlewares: Type<IMiddleware>[] = [];
28
37
 
38
+ /**
39
+ *
40
+ */
29
41
  public registerController(controllerClass: Type<unknown>): Router {
30
42
  const controllerMeta = getControllerMetadata(controllerClass);
31
43
 
32
44
  const controllerGuards = getGuardForController(controllerClass.name);
45
+ const controllerMiddlewares = getMiddlewaresForController(controllerClass.name);
33
46
 
34
47
  if(!controllerMeta)
35
48
  throw new Error(`Missing @Controller decorator on ${controllerClass.name}`);
@@ -40,8 +53,10 @@ export class Router {
40
53
  const fullPath = `${controllerMeta.path}/${def.path}`.replace(/\/+/g, '/');
41
54
 
42
55
  const routeGuards = getGuardForControllerAction(controllerClass.name, def.handler);
56
+ const routeMiddlewares = getMiddlewaresForControllerAction(controllerClass.name, def.handler);
43
57
 
44
58
  const guards = new Set([...controllerGuards, ...routeGuards]);
59
+ const middlewares = new Set([...controllerMiddlewares, ...routeMiddlewares]);
45
60
 
46
61
  const routeDef: IRouteDefinition = {
47
62
  method: def.method,
@@ -49,6 +64,7 @@ export class Router {
49
64
  controller: controllerClass,
50
65
  handler: def.handler,
51
66
  guards: [...guards],
67
+ middlewares: [...middlewares],
52
68
  };
53
69
 
54
70
  this.routes.insert(fullPath + '/' + def.method, routeDef);
@@ -73,6 +89,18 @@ export class Router {
73
89
  return this;
74
90
  }
75
91
 
92
+ /**
93
+ *
94
+ */
95
+ public defineRootMiddleware(middleware: Type<IMiddleware>): Router {
96
+ Logger.debug(`Registering root middleware: ${middleware.name}`);
97
+ this.rootMiddlewares.push(middleware);
98
+ return this;
99
+ }
100
+
101
+ /**
102
+ *
103
+ */
76
104
  public async handle(request: Request): Promise<IResponse> {
77
105
  Logger.log(`> Received request: {${request.method} /${request.path}}`);
78
106
 
@@ -87,13 +115,11 @@ export class Router {
87
115
 
88
116
  try {
89
117
  const routeDef = this.findRoute(request);
90
- const controllerInstance = await this.resolveController(request, routeDef);
91
-
92
- const action = controllerInstance[routeDef.handler] as ControllerAction;
93
-
94
- this.verifyRequestBody(request, action);
118
+ await this.resolveController(request, response, routeDef);
95
119
 
96
- response.body = await action.call(controllerInstance, request, response);
120
+ if(response.status > 400) {
121
+ throw new ResponseException(response.status, response.error);
122
+ }
97
123
  }
98
124
  catch(error: unknown) {
99
125
  if(error instanceof ResponseException) {
@@ -129,6 +155,9 @@ export class Router {
129
155
  }
130
156
  }
131
157
 
158
+ /**
159
+ *
160
+ */
132
161
  private findRoute(request: Request): IRouteDefinition {
133
162
  const matchedRoutes = this.routes.search(request.path);
134
163
 
@@ -145,30 +174,85 @@ export class Router {
145
174
  return routeDef.value;
146
175
  }
147
176
 
148
- private async resolveController(request: Request, routeDef: IRouteDefinition): Promise<any> {
177
+ /**
178
+ *
179
+ */
180
+ private async resolveController(request: Request, response: IResponse, routeDef: IRouteDefinition): Promise<void> {
149
181
  const controllerInstance = request.context.resolve(routeDef.controller);
150
182
 
151
183
  Object.assign(request.params, this.extractParams(request.path, routeDef.path));
152
184
 
153
- if(routeDef.guards.length > 0) {
154
- for(const guardType of routeDef.guards) {
155
- const guard = request.context.resolve(guardType);
156
- const allowed = await guard.canActivate(request);
185
+ await this.runRequestPipeline(request, response, routeDef, controllerInstance);
186
+ }
187
+
188
+ /**
189
+ *
190
+ */
191
+ private async runRequestPipeline(request: Request, response: IResponse, routeDef: IRouteDefinition, controllerInstance: any): Promise<void> {
192
+ const middlewares = [...new Set([...this.rootMiddlewares, ...routeDef.middlewares])];
157
193
 
158
- if(!allowed)
159
- throw new UnauthorizedException(`Unauthorized for ${request.method} ${request.path}`);
194
+ const middlewareMaxIndex = middlewares.length - 1;
195
+ const guardsMaxIndex = middlewareMaxIndex + routeDef.guards.length;
196
+
197
+ let index = -1;
198
+
199
+ const dispatch = async (i: number): Promise<void> => {
200
+ if(i <= index)
201
+ throw new Error("next() called multiple times");
202
+
203
+ index = i;
204
+
205
+ // middlewares
206
+ if(i <= middlewareMaxIndex) {
207
+ const nextFn = dispatch.bind(null, i + 1);
208
+ await this.runMiddleware(request, response, nextFn, middlewares[i]!);
209
+
210
+ if(response.status >= 400) {
211
+ throw new ResponseException(response.status, response.error);
212
+ }
213
+
214
+ return;
215
+ }
216
+
217
+ // guards
218
+ if(i <= guardsMaxIndex) {
219
+ const guardIndex = i - middlewares.length;
220
+ const guardType = routeDef.guards[guardIndex]!;
221
+ await this.runGuard(request, guardType);
222
+ dispatch(i + 1);
223
+ return;
160
224
  }
161
- }
162
225
 
163
- return controllerInstance;
226
+ // endpoint action
227
+ const action = controllerInstance[routeDef.handler] as ControllerAction;
228
+ response.body = await action.call(controllerInstance, request, response);
229
+ };
230
+
231
+ await dispatch(0);
164
232
  }
165
233
 
166
- private verifyRequestBody(request: Request, action: ControllerAction): void {
167
- const requiredParams = Reflect.getMetadata('design:paramtypes', action) || [];
168
- // peut être à faire plus tard. problème du TS, c'est qu'en JS pas de typage.
169
- // donc il faudrait passer par des décorateurs mais pas sûr que ce soit bien.
234
+ /**
235
+ *
236
+ */
237
+ private async runMiddleware(request: Request, response: IResponse, next: NextFunction, middlewareType: Type<IMiddleware>): Promise<void> {
238
+ const middleware = request.context.resolve(middlewareType);
239
+ await middleware.invoke(request, response, next);
240
+ }
241
+
242
+ /**
243
+ *
244
+ */
245
+ private async runGuard(request: Request, guardType: Type<IGuard>): Promise<void> {
246
+ const guard = request.context.resolve(guardType);
247
+ const allowed = await guard.canActivate(request);
248
+
249
+ if(!allowed)
250
+ throw new UnauthorizedException(`Unauthorized for ${request.method} ${request.path}`);
170
251
  }
171
252
 
253
+ /**
254
+ *
255
+ */
172
256
  private extractParams(actual: string, template: string): Record<string, string> {
173
257
  const aParts = actual.split('/');
174
258
  const tParts = template.split('/');
@@ -1,3 +1,9 @@
1
+ /**
2
+ * @copyright 2025 NoxFly
3
+ * @license MIT
4
+ * @author NoxFly
5
+ */
6
+
1
7
  function getPrettyTimestamp(): string {
2
8
  const now = new Date();
3
9
  return `${now.getDate().toString().padStart(2, '0')}/${(now.getMonth() + 1).toString().padStart(2, '0')}/${now.getFullYear()}`
@@ -1,3 +1,9 @@
1
+ /**
2
+ * @copyright 2025 NoxFly
3
+ * @license MIT
4
+ * @author NoxFly
5
+ */
6
+
1
7
  type Params = Record<string, string>;
2
8
 
3
9
  interface ISearchResult<T> {
@@ -1,3 +1,9 @@
1
+ /**
2
+ * @copyright 2025 NoxFly
3
+ * @license MIT
4
+ * @author NoxFly
5
+ */
6
+
1
7
  /* eslint-disable @typescript-eslint/no-unsafe-function-type */
2
8
 
3
9
 
package/tsup.config.ts CHANGED
@@ -1,5 +1,13 @@
1
1
  import { defineConfig } from "tsup";
2
2
 
3
+ const copyrights = `
4
+ /**
5
+ * @copyright 2025 NoxFly
6
+ * @license MIT
7
+ * @author NoxFly
8
+ */
9
+ `.trim()
10
+
3
11
  export default defineConfig({
4
12
  entry: {
5
13
  noxus: "src/index.ts"
@@ -17,4 +25,7 @@ export default defineConfig({
17
25
  splitting: false,
18
26
  shims: false,
19
27
  treeshake: false,
28
+ banner: {
29
+ js: copyrights,
30
+ }
20
31
  });