@noxfly/noxus 1.1.1 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/router.ts CHANGED
@@ -16,8 +16,11 @@ import { Logger } from 'src/utils/logger';
16
16
  import { RadixTree } from 'src/utils/radix-tree';
17
17
  import { Type } from 'src/utils/types';
18
18
 
19
- // types & interfaces
20
-
19
+ /**
20
+ * IRouteDefinition interface defines the structure of a route in the application.
21
+ * It includes the HTTP method, path, controller class, handler method name,
22
+ * guards, and middlewares associated with the route.
23
+ */
21
24
  export interface IRouteDefinition {
22
25
  method: string;
23
26
  path: string;
@@ -27,23 +30,34 @@ export interface IRouteDefinition {
27
30
  middlewares: Type<IMiddleware>[];
28
31
  }
29
32
 
33
+ /**
34
+ * This type defines a function that represents an action in a controller.
35
+ * It takes a Request and an IResponse as parameters and returns a value or a Promise.
36
+ */
30
37
  export type ControllerAction = (request: Request, response: IResponse) => any;
31
38
 
32
39
 
40
+ /**
41
+ * Router class is responsible for managing the application's routing.
42
+ * It registers controllers, handles requests, and manages middlewares and guards.
43
+ */
33
44
  @Injectable('singleton')
34
45
  export class Router {
35
46
  private readonly routes = new RadixTree<IRouteDefinition>();
36
47
  private readonly rootMiddlewares: Type<IMiddleware>[] = [];
37
48
 
38
49
  /**
39
- *
50
+ * Registers a controller class with the router.
51
+ * This method extracts the route metadata from the controller class and registers it in the routing tree.
52
+ * It also handles the guards and middlewares associated with the controller.
53
+ * @param controllerClass - The controller class to register.
40
54
  */
41
55
  public registerController(controllerClass: Type<unknown>): Router {
42
56
  const controllerMeta = getControllerMetadata(controllerClass);
43
57
 
44
58
  const controllerGuards = getGuardForController(controllerClass.name);
45
59
  const controllerMiddlewares = getMiddlewaresForController(controllerClass.name);
46
-
60
+
47
61
  if(!controllerMeta)
48
62
  throw new Error(`Missing @Controller decorator on ${controllerClass.name}`);
49
63
 
@@ -66,7 +80,7 @@ export class Router {
66
80
  guards: [...guards],
67
81
  middlewares: [...middlewares],
68
82
  };
69
-
83
+
70
84
  this.routes.insert(fullPath + '/' + def.method, routeDef);
71
85
 
72
86
  const hasActionGuards = routeDef.guards.length > 0;
@@ -79,7 +93,7 @@ export class Router {
79
93
  }
80
94
 
81
95
  const hasCtrlGuards = controllerMeta.guards.length > 0;
82
-
96
+
83
97
  const controllerGuardsInfo = hasCtrlGuards
84
98
  ? '<' + controllerMeta.guards.map(g => g.name).join('|') + '>'
85
99
  : '';
@@ -90,7 +104,10 @@ export class Router {
90
104
  }
91
105
 
92
106
  /**
93
- *
107
+ * Defines a middleware for the root of the application.
108
+ * This method allows you to register a middleware that will be applied to all requests
109
+ * to the application, regardless of the controller or action.
110
+ * @param middleware - The middleware class to register.
94
111
  */
95
112
  public defineRootMiddleware(middleware: Type<IMiddleware>): Router {
96
113
  Logger.debug(`Registering root middleware: ${middleware.name}`);
@@ -99,13 +116,16 @@ export class Router {
99
116
  }
100
117
 
101
118
  /**
102
- *
119
+ * Shuts down the message channel for a specific sender ID.
120
+ * This method closes the IPC channel for the specified sender ID and
121
+ * removes it from the messagePorts map.
122
+ * @param channelSenderId - The ID of the sender channel to shut down.
103
123
  */
104
124
  public async handle(request: Request): Promise<IResponse> {
105
125
  Logger.log(`> Received request: {${request.method} /${request.path}}`);
106
126
 
107
127
  const t0 = performance.now();
108
-
128
+
109
129
  const response: IResponse = {
110
130
  requestId: request.id,
111
131
  status: 200,
@@ -156,7 +176,11 @@ export class Router {
156
176
  }
157
177
 
158
178
  /**
159
- *
179
+ * Finds the route definition for a given request.
180
+ * This method searches the routing tree for a matching route based on the request's path and method.
181
+ * If no matching route is found, it throws a NotFoundException.
182
+ * @param request - The Request object containing the method and path to search for.
183
+ * @returns The IRouteDefinition for the matched route.
160
184
  */
161
185
  private findRoute(request: Request): IRouteDefinition {
162
186
  const matchedRoutes = this.routes.search(request.path);
@@ -175,7 +199,14 @@ export class Router {
175
199
  }
176
200
 
177
201
  /**
178
- *
202
+ * Resolves the controller for a given route definition.
203
+ * This method creates an instance of the controller class and prepares the request parameters.
204
+ * It also runs the request pipeline, which includes executing middlewares and guards.
205
+ * @param request - The Request object containing the request data.
206
+ * @param response - The IResponse object to populate with the response data.
207
+ * @param routeDef - The IRouteDefinition for the matched route.
208
+ * @return A Promise that resolves when the controller action has been executed.
209
+ * @throws UnauthorizedException if the request is not authorized by the guards.
179
210
  */
180
211
  private async resolveController(request: Request, response: IResponse, routeDef: IRouteDefinition): Promise<void> {
181
212
  const controllerInstance = request.context.resolve(routeDef.controller);
@@ -186,7 +217,15 @@ export class Router {
186
217
  }
187
218
 
188
219
  /**
189
- *
220
+ * Runs the request pipeline for a given request.
221
+ * This method executes the middlewares and guards associated with the route,
222
+ * and finally calls the controller action.
223
+ * @param request - The Request object containing the request data.
224
+ * @param response - The IResponse object to populate with the response data.
225
+ * @param routeDef - The IRouteDefinition for the matched route.
226
+ * @param controllerInstance - The instance of the controller class.
227
+ * @return A Promise that resolves when the request pipeline has been executed.
228
+ * @throws ResponseException if the response status is not successful.
190
229
  */
191
230
  private async runRequestPipeline(request: Request, response: IResponse, routeDef: IRouteDefinition, controllerInstance: any): Promise<void> {
192
231
  const middlewares = [...new Set([...this.rootMiddlewares, ...routeDef.middlewares])];
@@ -199,7 +238,7 @@ export class Router {
199
238
  const dispatch = async (i: number): Promise<void> => {
200
239
  if(i <= index)
201
240
  throw new Error("next() called multiple times");
202
-
241
+
203
242
  index = i;
204
243
 
205
244
  // middlewares
@@ -213,7 +252,7 @@ export class Router {
213
252
 
214
253
  return;
215
254
  }
216
-
255
+
217
256
  // guards
218
257
  if(i <= guardsMaxIndex) {
219
258
  const guardIndex = i - middlewares.length;
@@ -232,7 +271,14 @@ export class Router {
232
271
  }
233
272
 
234
273
  /**
235
- *
274
+ * Runs a middleware function in the request pipeline.
275
+ * This method creates an instance of the middleware and invokes its `invoke` method,
276
+ * passing the request, response, and next function.
277
+ * @param request - The Request object containing the request data.
278
+ * @param response - The IResponse object to populate with the response data.
279
+ * @param next - The NextFunction to call to continue the middleware chain.
280
+ * @param middlewareType - The type of the middleware to run.
281
+ * @return A Promise that resolves when the middleware has been executed.
236
282
  */
237
283
  private async runMiddleware(request: Request, response: IResponse, next: NextFunction, middlewareType: Type<IMiddleware>): Promise<void> {
238
284
  const middleware = request.context.resolve(middlewareType);
@@ -240,7 +286,13 @@ export class Router {
240
286
  }
241
287
 
242
288
  /**
243
- *
289
+ * Runs a guard to check if the request is authorized.
290
+ * This method creates an instance of the guard and calls its `canActivate` method.
291
+ * If the guard returns false, it throws an UnauthorizedException.
292
+ * @param request - The Request object containing the request data.
293
+ * @param guardType - The type of the guard to run.
294
+ * @return A Promise that resolves if the guard allows the request, or throws an UnauthorizedException if not.
295
+ * @throws UnauthorizedException if the guard denies access to the request.
244
296
  */
245
297
  private async runGuard(request: Request, guardType: Type<IGuard>): Promise<void> {
246
298
  const guard = request.context.resolve(guardType);
@@ -251,19 +303,24 @@ export class Router {
251
303
  }
252
304
 
253
305
  /**
254
- *
306
+ * Extracts parameters from the actual request path based on the template path.
307
+ * This method splits the actual path and the template path into segments,
308
+ * then maps the segments to parameters based on the template.
309
+ * @param actual - The actual request path.
310
+ * @param template - The template path to extract parameters from.
311
+ * @returns An object containing the extracted parameters.
255
312
  */
256
313
  private extractParams(actual: string, template: string): Record<string, string> {
257
314
  const aParts = actual.split('/');
258
315
  const tParts = template.split('/');
259
316
  const params: Record<string, string> = {};
260
-
317
+
261
318
  tParts.forEach((part, i) => {
262
319
  if(part.startsWith(':')) {
263
320
  params[part.slice(1)] = aParts[i] ?? '';
264
321
  }
265
322
  });
266
-
323
+
267
324
  return params;
268
325
  }
269
326
  }
@@ -4,12 +4,27 @@
4
4
  * @author NoxFly
5
5
  */
6
6
 
7
+ /**
8
+ * Logger is a utility class for logging messages to the console.
9
+ */
10
+ export type LogLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';
11
+
12
+ /**
13
+ * Returns a formatted timestamp for logging.
14
+ */
7
15
  function getPrettyTimestamp(): string {
8
16
  const now = new Date();
9
17
  return `${now.getDate().toString().padStart(2, '0')}/${(now.getMonth() + 1).toString().padStart(2, '0')}/${now.getFullYear()}`
10
18
  + ` ${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
11
19
  }
12
20
 
21
+ /**
22
+ * Generates a log prefix for the console output.
23
+ * @param callee - The name of the function or class that is logging the message.
24
+ * @param messageType - The type of message being logged (e.g., 'log', 'info', 'warn', 'error', 'debug').
25
+ * @param color - The color to use for the log message.
26
+ * @returns A formatted string that includes the timestamp, process ID, message type, and callee name.
27
+ */
13
28
  function getLogPrefix(callee: string, messageType: string, color: string): string {
14
29
  const timestamp = getPrettyTimestamp();
15
30
 
@@ -21,9 +36,16 @@ function getLogPrefix(callee: string, messageType: string, color: string): strin
21
36
  + `${Logger.colors.yellow}[${callee}]${Logger.colors.initial}`;
22
37
  }
23
38
 
39
+ /**
40
+ * Formats an object into a string representation for logging.
41
+ * It converts the object to JSON and adds indentation for readability.
42
+ * @param prefix - The prefix to use for the formatted object.
43
+ * @param arg - The object to format.
44
+ * @returns A formatted string representation of the object, with each line prefixed by the specified prefix.
45
+ */
24
46
  function formatObject(prefix: string, arg: object): string {
25
47
  const json = JSON.stringify(arg, null, 2);
26
-
48
+
27
49
  const prefixedJson = json
28
50
  .split('\n')
29
51
  .map((line, idx) => idx === 0 ? `${Logger.colors.darkGrey}${line}` : `${prefix} ${Logger.colors.grey}${line}`)
@@ -32,6 +54,15 @@ function formatObject(prefix: string, arg: object): string {
32
54
  return prefixedJson;
33
55
  }
34
56
 
57
+ /**
58
+ * Formats the arguments for logging.
59
+ * It colors strings and formats objects with indentation.
60
+ * This function is used to prepare the arguments for console output.
61
+ * @param prefix - The prefix to use for the formatted arguments.
62
+ * @param args - The arguments to format.
63
+ * @param color - The color to use for the formatted arguments.
64
+ * @returns An array of formatted arguments, where strings are colored and objects are formatted with indentation.
65
+ */
35
66
  function formattedArgs(prefix: string, args: any[], color: string): any[] {
36
67
  return args.map(arg => {
37
68
  if(typeof arg === 'string') {
@@ -46,13 +77,29 @@ function formattedArgs(prefix: string, args: any[], color: string): any[] {
46
77
  });
47
78
  }
48
79
 
80
+ /**
81
+ * Gets the name of the caller function or class from the stack trace.
82
+ * This function is used to determine the context of the log message.
83
+ * @returns The name of the caller function or class.
84
+ */
49
85
  function getCallee(): string {
50
86
  const stack = new Error().stack?.split('\n') ?? [];
51
87
  const caller = stack[3]?.trim().match(/at (.+?)(?:\..+)? .+$/)?.[1]?.replace('Object', '') || "App";
52
88
  return caller;
53
89
  }
54
90
 
55
- export type LogLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';
91
+ /**
92
+ * Checks if the current log level allows logging the specified level.
93
+ * This function compares the current log level with the specified level to determine if logging should occur.
94
+ * @param level - The log level to check.
95
+ * @returns A boolean indicating whether the log level is enabled.
96
+ */
97
+ function canLog(level: LogLevel): boolean {
98
+ return logLevelRank[level] >= logLevelRank[logLevel];
99
+ }
100
+
101
+
102
+ let logLevel: LogLevel = 'log';
56
103
 
57
104
  const logLevelRank: Record<LogLevel, number> = {
58
105
  debug: 0,
@@ -62,39 +109,24 @@ const logLevelRank: Record<LogLevel, number> = {
62
109
  error: 4,
63
110
  };
64
111
 
65
- function canLog(level: LogLevel): boolean {
66
- return logLevelRank[level] >= logLevelRank[logLevel];
67
- }
68
-
69
- let logLevel: LogLevel = 'log';
70
-
71
112
  export namespace Logger {
72
-
113
+
114
+ /**
115
+ * Sets the log level for the logger.
116
+ * This function allows you to change the log level dynamically at runtime.
117
+ * This won't affect the startup logs.
118
+ * @param level Sets the log level for the logger.
119
+ */
73
120
  export function setLogLevel(level: LogLevel): void {
74
121
  logLevel = level;
75
122
  }
76
123
 
77
- export const colors = {
78
- black: '\x1b[0;30m',
79
- grey: '\x1b[0;37m',
80
- red: '\x1b[0;31m',
81
- green: '\x1b[0;32m',
82
- brown: '\x1b[0;33m',
83
- blue: '\x1b[0;34m',
84
- purple: '\x1b[0;35m',
85
-
86
- darkGrey: '\x1b[1;30m',
87
- lightRed: '\x1b[1;31m',
88
- lightGreen: '\x1b[1;32m',
89
- yellow: '\x1b[1;33m',
90
- lightBlue: '\x1b[1;34m',
91
- magenta: '\x1b[1;35m',
92
- cyan: '\x1b[1;36m',
93
- white: '\x1b[1;37m',
94
-
95
- initial: '\x1b[0m'
96
- };
97
-
124
+ /**
125
+ * Logs a message to the console with log level LOG.
126
+ * This function formats the message with a timestamp, process ID, and the name of the caller function or class.
127
+ * It uses different colors for different log levels to enhance readability.
128
+ * @param args The arguments to log.
129
+ */
98
130
  export function log(...args: any[]): void {
99
131
  if(!canLog('log'))
100
132
  return;
@@ -104,6 +136,12 @@ export namespace Logger {
104
136
  console.log(prefix, ...formattedArgs(prefix, args, colors.green));
105
137
  }
106
138
 
139
+ /**
140
+ * Logs a message to the console with log level INFO.
141
+ * This function formats the message with a timestamp, process ID, and the name of the caller function or class.
142
+ * It uses different colors for different log levels to enhance readability.
143
+ * @param args The arguments to log.
144
+ */
107
145
  export function info(...args: any[]): void {
108
146
  if(!canLog('info'))
109
147
  return;
@@ -113,6 +151,12 @@ export namespace Logger {
113
151
  console.info(prefix, ...formattedArgs(prefix, args, colors.blue));
114
152
  }
115
153
 
154
+ /**
155
+ * Logs a message to the console with log level WARN.
156
+ * This function formats the message with a timestamp, process ID, and the name of the caller function or class.
157
+ * It uses different colors for different log levels to enhance readability.
158
+ * @param args The arguments to log.
159
+ */
116
160
  export function warn(...args: any[]): void {
117
161
  if(!canLog('warn'))
118
162
  return;
@@ -122,6 +166,12 @@ export namespace Logger {
122
166
  console.warn(prefix, ...formattedArgs(prefix, args, colors.brown));
123
167
  }
124
168
 
169
+ /**
170
+ * Logs a message to the console with log level ERROR.
171
+ * This function formats the message with a timestamp, process ID, and the name of the caller function or class.
172
+ * It uses different colors for different log levels to enhance readability.
173
+ * @param args The arguments to log.
174
+ */
125
175
  export function error(...args: any[]): void {
126
176
  if(!canLog('error'))
127
177
  return;
@@ -131,6 +181,12 @@ export namespace Logger {
131
181
  console.error(prefix, ...formattedArgs(prefix, args, colors.red));
132
182
  }
133
183
 
184
+ /**
185
+ * Logs a message to the console with log level DEBUG.
186
+ * This function formats the message with a timestamp, process ID, and the name of the caller function or class.
187
+ * It uses different colors for different log levels to enhance readability.
188
+ * @param args The arguments to log.
189
+ */
134
190
  export function debug(...args: any[]): void {
135
191
  if(!canLog('debug'))
136
192
  return;
@@ -139,4 +195,26 @@ export namespace Logger {
139
195
  const prefix = getLogPrefix(callee, "debug", colors.purple);
140
196
  console.debug(prefix, ...formattedArgs(prefix, args, colors.purple));
141
197
  }
142
- }
198
+
199
+
200
+ export const colors = {
201
+ black: '\x1b[0;30m',
202
+ grey: '\x1b[0;37m',
203
+ red: '\x1b[0;31m',
204
+ green: '\x1b[0;32m',
205
+ brown: '\x1b[0;33m',
206
+ blue: '\x1b[0;34m',
207
+ purple: '\x1b[0;35m',
208
+
209
+ darkGrey: '\x1b[1;30m',
210
+ lightRed: '\x1b[1;31m',
211
+ lightGreen: '\x1b[1;32m',
212
+ yellow: '\x1b[1;33m',
213
+ lightBlue: '\x1b[1;34m',
214
+ magenta: '\x1b[1;35m',
215
+ cyan: '\x1b[1;36m',
216
+ white: '\x1b[1;37m',
217
+
218
+ initial: '\x1b[0m'
219
+ };
220
+ }
@@ -4,13 +4,23 @@
4
4
  * @author NoxFly
5
5
  */
6
6
 
7
+ /**
8
+ *
9
+ */
7
10
  type Params = Record<string, string>;
8
11
 
12
+ /**
13
+ * Represents a search result in the Radix Tree.
14
+ */
9
15
  interface ISearchResult<T> {
10
16
  node: RadixNode<T>;
11
17
  params: Params;
12
18
  }
13
19
 
20
+ /**
21
+ * Represents a node in the Radix Tree.
22
+ * The represents a path segment
23
+ */
14
24
  class RadixNode<T> {
15
25
  public segment: string;
16
26
  public children: RadixNode<T>[] = [];
@@ -18,15 +28,25 @@ class RadixNode<T> {
18
28
  public isParam: boolean;
19
29
  public paramName?: string;
20
30
 
31
+ /**
32
+ * Creates a new RadixNode.
33
+ * @param segment - The segment of the path this node represents.
34
+ */
21
35
  constructor(segment: string) {
22
36
  this.segment = segment;
23
37
  this.isParam = segment.startsWith(":");
24
-
38
+
25
39
  if(this.isParam) {
26
40
  this.paramName = segment.slice(1);
27
41
  }
28
42
  }
29
43
 
44
+ /**
45
+ * Matches a child node against a given segment.
46
+ * This method checks if the segment matches any of the children nodes.
47
+ * @param segment - The segment to match against the children of this node.
48
+ * @returns A child node that matches the segment, or undefined if no match is found.
49
+ */
30
50
  public matchChild(segment: string): RadixNode<T> | undefined {
31
51
  for(const child of this.children) {
32
52
  if(child.isParam || segment.startsWith(child.segment))
@@ -36,23 +56,50 @@ class RadixNode<T> {
36
56
  return undefined;
37
57
  }
38
58
 
59
+ /**
60
+ * Finds a child node that matches the segment exactly.
61
+ * This method checks if there is a child node that matches the segment exactly.
62
+ * @param segment - The segment to find an exact match for among the children of this node.
63
+ * @returns A child node that matches the segment exactly, or undefined if no match is found.
64
+ */
39
65
  public findExactChild(segment: string): RadixNode<T> | undefined {
40
66
  return this.children.find(c => c.segment === segment);
41
67
  }
42
68
 
69
+ /**
70
+ * Adds a child node to this node's children.
71
+ * This method adds a new child node to the list of children for this node.
72
+ * @param node - The child node to add to this node's children.
73
+ */
43
74
  public addChild(node: RadixNode<T>): void {
44
75
  this.children.push(node);
45
76
  }
46
77
  }
47
78
 
79
+ /**
80
+ *
81
+ */
48
82
  export class RadixTree<T> {
49
83
  private readonly root = new RadixNode<T>("");
50
84
 
85
+ /**
86
+ * Inserts a path and its associated value into the Radix Tree.
87
+ * This method normalizes the path and inserts it into the tree, associating it with
88
+ * @param path - The path to insert into the tree.
89
+ * @param value - The value to associate with the path.
90
+ */
51
91
  public insert(path: string, value: T): void {
52
92
  const segments = this.normalize(path);
53
93
  this.insertRecursive(this.root, segments, value);
54
94
  }
55
95
 
96
+ /**
97
+ * Recursively inserts a path into the Radix Tree.
98
+ * This method traverses the tree and inserts the segments of the path, creating new nodes
99
+ * @param node - The node to start inserting from.
100
+ * @param segments - The segments of the path to insert.
101
+ * @param value - The value to associate with the path.
102
+ */
56
103
  private insertRecursive(node: RadixNode<T>, segments: string[], value: T): void {
57
104
  if(segments.length === 0) {
58
105
  node.value = value;
@@ -74,11 +121,25 @@ export class RadixTree<T> {
74
121
  this.insertRecursive(child, segments.slice(1), value);
75
122
  }
76
123
 
124
+ /**
125
+ * Searches for a path in the Radix Tree.
126
+ * This method normalizes the path and searches for it in the tree, returning the node
127
+ * @param path - The path to search for in the Radix Tree.
128
+ * @returns An ISearchResult containing the node and parameters if a match is found, otherwise undefined.
129
+ */
77
130
  public search(path: string): ISearchResult<T> | undefined {
78
131
  const segments = this.normalize(path);
79
132
  return this.searchRecursive(this.root, segments, {});
80
133
  }
81
134
 
135
+ /**
136
+ * Recursively searches for a path in the Radix Tree.
137
+ * This method traverses the tree and searches for the segments of the path, collecting parameters
138
+ * @param node - The node to start searching from.
139
+ * @param segments - The segments of the path to search for.
140
+ * @param params - The parameters collected during the search.
141
+ * @returns An ISearchResult containing the node and parameters if a match is found, otherwise undefined.
142
+ */
82
143
  private searchRecursive(node: RadixNode<T>, segments: string[], params: Params): ISearchResult<T> | undefined {
83
144
  if(segments.length === 0) {
84
145
  if(node.value !== undefined) {
@@ -96,7 +157,7 @@ export class RadixTree<T> {
96
157
  for(const child of node.children) {
97
158
  if(child.isParam) {
98
159
  const paramName = child.paramName!;
99
-
160
+
100
161
  const childParams: Params = {
101
162
  ...params,
102
163
  [paramName]: segment ?? "",
@@ -110,7 +171,7 @@ export class RadixTree<T> {
110
171
  }
111
172
 
112
173
  const result = this.searchRecursive(child, rest, childParams);
113
-
174
+
114
175
  if(result)
115
176
  return result;
116
177
  }
@@ -132,6 +193,12 @@ export class RadixTree<T> {
132
193
  return undefined;
133
194
  }
134
195
 
196
+ /**
197
+ * Normalizes a path into an array of segments.
198
+ * This method removes leading and trailing slashes, splits the path by slashes, and
199
+ * @param path - The path to normalize.
200
+ * @returns An array of normalized path segments.
201
+ */
135
202
  private normalize(path: string): string[] {
136
203
  const segments = path
137
204
  .replace(/^\/+|\/+$/g, "")
@@ -139,5 +206,5 @@ export class RadixTree<T> {
139
206
  .filter(Boolean);
140
207
 
141
208
  return ['', ...segments];
142
- }
209
+ }
143
210
  }
@@ -4,13 +4,18 @@
4
4
  * @author NoxFly
5
5
  */
6
6
 
7
- /* eslint-disable @typescript-eslint/no-unsafe-function-type */
8
-
9
7
 
8
+ /**
9
+ * Represents a generic type that can be either a class or a function.
10
+ * This is used to define types that can be instantiated or called.
11
+ */
10
12
  declare const Type: FunctionConstructor;
11
13
  export interface Type<T> extends Function {
12
14
  // eslint-disable-next-line @typescript-eslint/prefer-function-type
13
15
  new (...args: any[]): T;
14
16
  }
15
17
 
16
- export type MaybeAsync<T> = T | Promise<T>;
18
+ /**
19
+ * Represents a generic type that can be either a value or a promise resolving to that value.
20
+ */
21
+ export type MaybeAsync<T> = T | Promise<T>;
package/tsup.config.ts CHANGED
@@ -13,6 +13,7 @@ export default defineConfig({
13
13
  noxus: "src/index.ts"
14
14
  },
15
15
  keepNames: true,
16
+ minifyIdentifiers: false,
16
17
  name: "noxus",
17
18
  format: ["cjs", "esm"],
18
19
  dts: true,
@@ -28,4 +29,4 @@ export default defineConfig({
28
29
  banner: {
29
30
  js: copyrights,
30
31
  }
31
- });
32
+ });