@noxfly/noxus 3.0.0-dev.1 → 3.0.0-dev.11
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 +121 -8
- package/dist/child.d.mts +8 -2
- package/dist/child.d.ts +8 -2
- package/dist/child.js +405 -862
- package/dist/child.js.map +1 -0
- package/dist/child.mjs +396 -854
- package/dist/child.mjs.map +1 -0
- package/dist/main.d.mts +180 -132
- package/dist/main.d.ts +180 -132
- package/dist/main.js +1087 -926
- package/dist/main.js.map +1 -0
- package/dist/main.mjs +1038 -877
- package/dist/main.mjs.map +1 -0
- package/dist/preload.js.map +1 -0
- package/dist/preload.mjs.map +1 -0
- package/dist/renderer.d.mts +30 -4
- package/dist/renderer.d.ts +30 -4
- package/dist/renderer.js +191 -128
- package/dist/renderer.js.map +1 -0
- package/dist/renderer.mjs +180 -116
- package/dist/renderer.mjs.map +1 -0
- package/package.json +18 -9
- package/.editorconfig +0 -16
- package/.github/copilot-instructions.md +0 -32
- package/.vscode/settings.json +0 -3
- package/eslint.config.js +0 -109
- package/scripts/postbuild.js +0 -31
- package/src/DI/app-injector.ts +0 -151
- package/src/DI/injector-explorer.ts +0 -143
- package/src/DI/token.ts +0 -53
- package/src/decorators/controller.decorator.ts +0 -58
- package/src/decorators/guards.decorator.ts +0 -15
- package/src/decorators/injectable.decorator.ts +0 -81
- package/src/decorators/method.decorator.ts +0 -66
- package/src/decorators/middleware.decorator.ts +0 -15
- package/src/index.ts +0 -10
- package/src/internal/app.ts +0 -217
- package/src/internal/bootstrap.ts +0 -108
- package/src/internal/exceptions.ts +0 -57
- package/src/internal/preload-bridge.ts +0 -75
- package/src/internal/renderer-client.ts +0 -338
- package/src/internal/renderer-events.ts +0 -110
- package/src/internal/request.ts +0 -97
- package/src/internal/router.ts +0 -353
- package/src/internal/routes.ts +0 -78
- package/src/internal/socket.ts +0 -73
- package/src/main.ts +0 -26
- package/src/non-electron-process.ts +0 -22
- package/src/preload.ts +0 -10
- package/src/renderer.ts +0 -13
- package/src/utils/forward-ref.ts +0 -31
- package/src/utils/logger.ts +0 -430
- package/src/utils/radix-tree.ts +0 -210
- package/src/utils/types.ts +0 -21
- package/src/window/window-manager.ts +0 -255
- package/tsconfig.json +0 -29
- package/tsup.config.ts +0 -50
package/src/utils/logger.ts
DELETED
|
@@ -1,430 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import * as fs from 'fs';
|
|
8
|
-
import * as path from 'path';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Logger is a utility class for logging messages to the console.
|
|
12
|
-
*/
|
|
13
|
-
export type LogLevel =
|
|
14
|
-
| 'debug'
|
|
15
|
-
| 'comment'
|
|
16
|
-
| 'log'
|
|
17
|
-
| 'info'
|
|
18
|
-
| 'warn'
|
|
19
|
-
| 'error'
|
|
20
|
-
| 'critical'
|
|
21
|
-
;
|
|
22
|
-
|
|
23
|
-
interface FileLogState {
|
|
24
|
-
queue: string[];
|
|
25
|
-
isWriting: boolean;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Returns a formatted timestamp for logging.
|
|
32
|
-
*/
|
|
33
|
-
function getPrettyTimestamp(): string {
|
|
34
|
-
const now = new Date();
|
|
35
|
-
return `${now.getDate().toString().padStart(2, '0')}/${(now.getMonth() + 1).toString().padStart(2, '0')}/${now.getFullYear()}`
|
|
36
|
-
+ ` ${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Generates a log prefix for the console output.
|
|
41
|
-
* @param callee - The name of the function or class that is logging the message.
|
|
42
|
-
* @param messageType - The type of message being logged (e.g., 'log', 'info', 'warn', 'error', 'debug').
|
|
43
|
-
* @param color - The color to use for the log message.
|
|
44
|
-
* @returns A formatted string that includes the timestamp, process ID, message type, and callee name.
|
|
45
|
-
*/
|
|
46
|
-
function getLogPrefix(callee: string, messageType: string, color?: string): string {
|
|
47
|
-
const timestamp = getPrettyTimestamp();
|
|
48
|
-
|
|
49
|
-
const spaces = " ".repeat(10 - messageType.length);
|
|
50
|
-
|
|
51
|
-
let colReset = Logger.colors.initial;
|
|
52
|
-
let colCallee = Logger.colors.yellow;
|
|
53
|
-
|
|
54
|
-
if(color === undefined) {
|
|
55
|
-
color = "";
|
|
56
|
-
colReset = "";
|
|
57
|
-
colCallee = "";
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return `${color}[APP] ${process.pid} - ${colReset}`
|
|
61
|
-
+ `${timestamp}${spaces}`
|
|
62
|
-
+ `${color}${messageType.toUpperCase()}${colReset} `
|
|
63
|
-
+ `${colCallee}[${callee}]${colReset}`;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Formats an object into a string representation for logging.
|
|
68
|
-
* It converts the object to JSON and adds indentation for readability.
|
|
69
|
-
* @param prefix - The prefix to use for the formatted object.
|
|
70
|
-
* @param arg - The object to format.
|
|
71
|
-
* @returns A formatted string representation of the object, with each line prefixed by the specified prefix.
|
|
72
|
-
*/
|
|
73
|
-
function formatObject(prefix: string, arg: object, enableColor: boolean = true): string {
|
|
74
|
-
const json = JSON.stringify(arg, null, 2);
|
|
75
|
-
|
|
76
|
-
let colStart = "";
|
|
77
|
-
let colLine = "";
|
|
78
|
-
let colReset = "";
|
|
79
|
-
|
|
80
|
-
if(enableColor) {
|
|
81
|
-
colStart = Logger.colors.darkGrey;
|
|
82
|
-
colLine = Logger.colors.grey;
|
|
83
|
-
colReset = Logger.colors.initial;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const prefixedJson = json
|
|
87
|
-
.split('\n')
|
|
88
|
-
.map((line, idx) => idx === 0 ? `${colStart}${line}` : `${prefix} ${colLine}${line}`)
|
|
89
|
-
.join('\n') + colReset;
|
|
90
|
-
|
|
91
|
-
return prefixedJson;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Formats the arguments for logging.
|
|
96
|
-
* It colors strings and formats objects with indentation.
|
|
97
|
-
* This function is used to prepare the arguments for console output.
|
|
98
|
-
* @param prefix - The prefix to use for the formatted arguments.
|
|
99
|
-
* @param args - The arguments to format.
|
|
100
|
-
* @param color - The color to use for the formatted arguments.
|
|
101
|
-
* @returns An array of formatted arguments, where strings are colored and objects are formatted with indentation.
|
|
102
|
-
*/
|
|
103
|
-
function formattedArgs(prefix: string, args: any[], color?: string): any[] {
|
|
104
|
-
let colReset = Logger.colors.initial;
|
|
105
|
-
|
|
106
|
-
if(color === undefined) {
|
|
107
|
-
color = "";
|
|
108
|
-
colReset = "";
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return args.map(arg => {
|
|
112
|
-
if(typeof arg === "string") {
|
|
113
|
-
return `${color}${arg}${colReset}`;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
else if(typeof arg === "object") {
|
|
117
|
-
return formatObject(prefix, arg, color !== "");
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return arg;
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Gets the name of the caller function or class from the stack trace.
|
|
126
|
-
* This function is used to determine the context of the log message.
|
|
127
|
-
* @returns The name of the caller function or class.
|
|
128
|
-
*/
|
|
129
|
-
function getCallee(): string {
|
|
130
|
-
const stack = new Error().stack?.split('\n') ?? [];
|
|
131
|
-
const caller = stack[3]
|
|
132
|
-
?.trim()
|
|
133
|
-
.match(/at (.+?)(?:\..+)? .+$/)
|
|
134
|
-
?.[1]
|
|
135
|
-
?.replace("Object", "")
|
|
136
|
-
.replace(/^_/, "")
|
|
137
|
-
|| "App";
|
|
138
|
-
return caller;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Checks if the current log level allows logging the specified level.
|
|
143
|
-
* This function compares the current log level with the specified level to determine if logging should occur.
|
|
144
|
-
* @param level - The log level to check.
|
|
145
|
-
* @returns A boolean indicating whether the log level is enabled.
|
|
146
|
-
*/
|
|
147
|
-
function canLog(level: LogLevel): boolean {
|
|
148
|
-
return logLevels.has(level);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Writes a log message to a file asynchronously to avoid blocking the event loop.
|
|
153
|
-
* It batches messages if writing is already in progress.
|
|
154
|
-
* @param filepath - The path to the log file.
|
|
155
|
-
*/
|
|
156
|
-
function processLogQueue(filepath: string): void {
|
|
157
|
-
const state = fileStates.get(filepath);
|
|
158
|
-
|
|
159
|
-
if(!state || state.isWriting || state.queue.length === 0) {
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
state.isWriting = true;
|
|
164
|
-
|
|
165
|
-
// Optimization: Grab all pending messages to write in one go
|
|
166
|
-
const messagesToWrite = state.queue.join('\n') + '\n';
|
|
167
|
-
state.queue = []; // Clear the queue immediately
|
|
168
|
-
|
|
169
|
-
const dir = path.dirname(filepath);
|
|
170
|
-
|
|
171
|
-
// Using async IO to allow other operations
|
|
172
|
-
fs.mkdir(dir, { recursive: true }, (err) => {
|
|
173
|
-
if(err) {
|
|
174
|
-
console.error(`[Logger] Failed to create directory ${dir}`, err);
|
|
175
|
-
state.isWriting = false;
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
fs.appendFile(filepath, messagesToWrite, { encoding: "utf-8" }, (err) => {
|
|
180
|
-
state.isWriting = false;
|
|
181
|
-
|
|
182
|
-
if(err) {
|
|
183
|
-
console.error(`[Logger] Failed to write log to ${filepath}`, err);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// If new messages arrived while we were writing, process them now
|
|
187
|
-
if(state.queue.length > 0) {
|
|
188
|
-
processLogQueue(filepath);
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Adds a message to the file queue and triggers processing.
|
|
196
|
-
*/
|
|
197
|
-
function enqueue(filepath: string, message: string): void {
|
|
198
|
-
if(!fileStates.has(filepath)) {
|
|
199
|
-
fileStates.set(filepath, { queue: [], isWriting: false });
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const state = fileStates.get(filepath)!;
|
|
203
|
-
state.queue.push(message);
|
|
204
|
-
|
|
205
|
-
processLogQueue(filepath);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
*
|
|
210
|
-
*/
|
|
211
|
-
function output(level: LogLevel, args: any[]): void {
|
|
212
|
-
if(!canLog(level)) {
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const callee = getCallee();
|
|
217
|
-
|
|
218
|
-
{
|
|
219
|
-
const prefix = getLogPrefix(callee, level, logLevelColors[level]);
|
|
220
|
-
const data = formattedArgs(prefix, args, logLevelColors[level]);
|
|
221
|
-
|
|
222
|
-
logLevelChannel[level](prefix, ...data);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
{
|
|
226
|
-
const prefix = getLogPrefix(callee, level);
|
|
227
|
-
const data = formattedArgs(prefix, args);
|
|
228
|
-
|
|
229
|
-
const filepath = fileSettings.get(level)?.filepath;
|
|
230
|
-
|
|
231
|
-
if(filepath) {
|
|
232
|
-
const message = prefix + " " + data.join(" ").replace(/\x1b\[[0-9;]*m/g, ''); // Remove ANSI codes
|
|
233
|
-
enqueue(filepath, message);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
export namespace Logger {
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Sets the log level for the logger.
|
|
244
|
-
* This function allows you to change the log level dynamically at runtime.
|
|
245
|
-
* This won't affect the startup logs.
|
|
246
|
-
*
|
|
247
|
-
* If the parameter is a single LogLevel, all log levels with equal or higher severity will be enabled.
|
|
248
|
-
|
|
249
|
-
* If the parameter is an array of LogLevels, only the specified levels will be enabled.
|
|
250
|
-
*
|
|
251
|
-
* @param level Sets the log level for the logger.
|
|
252
|
-
*/
|
|
253
|
-
export function setLogLevel(level: LogLevel | LogLevel[]): void {
|
|
254
|
-
logLevels.clear();
|
|
255
|
-
|
|
256
|
-
if(Array.isArray(level)) {
|
|
257
|
-
for(const lvl of level) {
|
|
258
|
-
logLevels.add(lvl);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
else {
|
|
262
|
-
const targetRank = logLevelRank[level];
|
|
263
|
-
|
|
264
|
-
for(const [lvl, rank] of Object.entries(logLevelRank) as [LogLevel, number][]) {
|
|
265
|
-
if(rank >= targetRank) {
|
|
266
|
-
logLevels.add(lvl);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Logs a message to the console with log level LOG.
|
|
274
|
-
* This function formats the message with a timestamp, process ID, and the name of the caller function or class.
|
|
275
|
-
* It uses different colors for different log levels to enhance readability.
|
|
276
|
-
* @param args The arguments to log.
|
|
277
|
-
*/
|
|
278
|
-
export function log(...args: any[]): void {
|
|
279
|
-
output("log", args);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* Logs a message to the console with log level INFO.
|
|
284
|
-
* This function formats the message with a timestamp, process ID, and the name of the caller function or class.
|
|
285
|
-
* It uses different colors for different log levels to enhance readability.
|
|
286
|
-
* @param args The arguments to log.
|
|
287
|
-
*/
|
|
288
|
-
export function info(...args: any[]): void {
|
|
289
|
-
output("info", args);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Logs a message to the console with log level WARN.
|
|
294
|
-
* This function formats the message with a timestamp, process ID, and the name of the caller function or class.
|
|
295
|
-
* It uses different colors for different log levels to enhance readability.
|
|
296
|
-
* @param args The arguments to log.
|
|
297
|
-
*/
|
|
298
|
-
export function warn(...args: any[]): void {
|
|
299
|
-
output("warn", args);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Logs a message to the console with log level ERROR.
|
|
304
|
-
* This function formats the message with a timestamp, process ID, and the name of the caller function or class.
|
|
305
|
-
* It uses different colors for different log levels to enhance readability.
|
|
306
|
-
* @param args The arguments to log.
|
|
307
|
-
*/
|
|
308
|
-
export function error(...args: any[]): void {
|
|
309
|
-
output("error", args);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Logs a message to the console with log level ERROR and a grey color scheme.
|
|
314
|
-
*/
|
|
315
|
-
export function errorStack(...args: any[]): void {
|
|
316
|
-
output("error", args);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Logs a message to the console with log level DEBUG.
|
|
321
|
-
* This function formats the message with a timestamp, process ID, and the name of the caller function or class.
|
|
322
|
-
* It uses different colors for different log levels to enhance readability.
|
|
323
|
-
* @param args The arguments to log.
|
|
324
|
-
*/
|
|
325
|
-
export function debug(...args: any[]): void {
|
|
326
|
-
output("debug", args);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Logs a message to the console with log level COMMENT.
|
|
331
|
-
* This function formats the message with a timestamp, process ID, and the name of the caller function or class.
|
|
332
|
-
* It uses different colors for different log levels to enhance readability.
|
|
333
|
-
* @param args The arguments to log.
|
|
334
|
-
*/
|
|
335
|
-
export function comment(...args: any[]): void {
|
|
336
|
-
output("comment", args);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* Logs a message to the console with log level CRITICAL.
|
|
341
|
-
* This function formats the message with a timestamp, process ID, and the name of the caller function or class.
|
|
342
|
-
* It uses different colors for different log levels to enhance readability.
|
|
343
|
-
* @param args The arguments to log.
|
|
344
|
-
*/
|
|
345
|
-
export function critical(...args: any[]): void {
|
|
346
|
-
output("critical", args);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Enables logging to a file output for the specified log levels.
|
|
351
|
-
* @param filepath The path to the log file.
|
|
352
|
-
* @param levels The log levels to enable file logging for. Defaults to all levels.
|
|
353
|
-
*/
|
|
354
|
-
export function enableFileLogging(filepath: string, levels: LogLevel[] = ["debug", "comment", "log", "info", "warn", "error", "critical"]): void {
|
|
355
|
-
for(const level of levels) {
|
|
356
|
-
fileSettings.set(level, { filepath });
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* Disables logging to a file output for the specified log levels.
|
|
362
|
-
* @param levels The log levels to disable file logging for. Defaults to all levels.
|
|
363
|
-
*/
|
|
364
|
-
export function disableFileLogging(levels: LogLevel[] = ["debug", "comment", "log", "info", "warn", "error", "critical"]): void {
|
|
365
|
-
for(const level of levels) {
|
|
366
|
-
fileSettings.delete(level);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
export const colors = {
|
|
372
|
-
black: "\x1b[0;30m",
|
|
373
|
-
grey: "\x1b[0;37m",
|
|
374
|
-
red: "\x1b[0;31m",
|
|
375
|
-
green: "\x1b[0;32m",
|
|
376
|
-
brown: "\x1b[0;33m",
|
|
377
|
-
blue: "\x1b[0;34m",
|
|
378
|
-
purple: "\x1b[0;35m",
|
|
379
|
-
|
|
380
|
-
darkGrey: "\x1b[1;30m",
|
|
381
|
-
lightRed: "\x1b[1;31m",
|
|
382
|
-
lightGreen: "\x1b[1;32m",
|
|
383
|
-
yellow: "\x1b[1;33m",
|
|
384
|
-
lightBlue: "\x1b[1;34m",
|
|
385
|
-
magenta: "\x1b[1;35m",
|
|
386
|
-
cyan: "\x1b[1;36m",
|
|
387
|
-
white: "\x1b[1;37m",
|
|
388
|
-
|
|
389
|
-
initial: "\x1b[0m"
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const fileSettings: Map<LogLevel, { filepath: string }> = new Map();
|
|
395
|
-
const fileStates: Map<string, FileLogState> = new Map(); // filepath -> state
|
|
396
|
-
|
|
397
|
-
const logLevels: Set<LogLevel> = new Set();
|
|
398
|
-
|
|
399
|
-
const logLevelRank: Record<LogLevel, number> = {
|
|
400
|
-
debug: 0,
|
|
401
|
-
comment: 1,
|
|
402
|
-
log: 2,
|
|
403
|
-
info: 3,
|
|
404
|
-
warn: 4,
|
|
405
|
-
error: 5,
|
|
406
|
-
critical: 6,
|
|
407
|
-
};
|
|
408
|
-
|
|
409
|
-
const logLevelColors: Record<LogLevel, string> = {
|
|
410
|
-
debug: Logger.colors.purple,
|
|
411
|
-
comment: Logger.colors.grey,
|
|
412
|
-
log: Logger.colors.green,
|
|
413
|
-
info: Logger.colors.blue,
|
|
414
|
-
warn: Logger.colors.brown,
|
|
415
|
-
error: Logger.colors.red,
|
|
416
|
-
critical: Logger.colors.lightRed,
|
|
417
|
-
};
|
|
418
|
-
|
|
419
|
-
const logLevelChannel: Record<LogLevel, (message?: any, ...optionalParams: any[]) => void> = {
|
|
420
|
-
debug: console.debug,
|
|
421
|
-
comment: console.debug,
|
|
422
|
-
log: console.log,
|
|
423
|
-
info: console.info,
|
|
424
|
-
warn: console.warn,
|
|
425
|
-
error: console.error,
|
|
426
|
-
critical: console.error,
|
|
427
|
-
};
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
Logger.setLogLevel("debug");
|
package/src/utils/radix-tree.ts
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
*
|
|
9
|
-
*/
|
|
10
|
-
type Params = Record<string, string>;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Represents a search result in the Radix Tree.
|
|
14
|
-
*/
|
|
15
|
-
interface ISearchResult<T> {
|
|
16
|
-
node: RadixNode<T>;
|
|
17
|
-
params: Params;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Represents a node in the Radix Tree.
|
|
22
|
-
* The represents a path segment
|
|
23
|
-
*/
|
|
24
|
-
class RadixNode<T> {
|
|
25
|
-
public segment: string;
|
|
26
|
-
public children: RadixNode<T>[] = [];
|
|
27
|
-
public value?: T;
|
|
28
|
-
public isParam: boolean;
|
|
29
|
-
public paramName?: string;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Creates a new RadixNode.
|
|
33
|
-
* @param segment - The segment of the path this node represents.
|
|
34
|
-
*/
|
|
35
|
-
constructor(segment: string) {
|
|
36
|
-
this.segment = segment;
|
|
37
|
-
this.isParam = segment.startsWith(":");
|
|
38
|
-
|
|
39
|
-
if(this.isParam) {
|
|
40
|
-
this.paramName = segment.slice(1);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
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
|
-
*/
|
|
50
|
-
public matchChild(segment: string): RadixNode<T> | undefined {
|
|
51
|
-
for(const child of this.children) {
|
|
52
|
-
if(child.isParam || segment.startsWith(child.segment))
|
|
53
|
-
return child; // param match
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return undefined;
|
|
57
|
-
}
|
|
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
|
-
*/
|
|
65
|
-
public findExactChild(segment: string): RadixNode<T> | undefined {
|
|
66
|
-
return this.children.find(c => c.segment === segment);
|
|
67
|
-
}
|
|
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
|
-
*/
|
|
74
|
-
public addChild(node: RadixNode<T>): void {
|
|
75
|
-
this.children.push(node);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
*
|
|
81
|
-
*/
|
|
82
|
-
export class RadixTree<T> {
|
|
83
|
-
private readonly root = new RadixNode<T>("");
|
|
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
|
-
*/
|
|
91
|
-
public insert(path: string, value: T): void {
|
|
92
|
-
const segments = this.normalize(path);
|
|
93
|
-
this.insertRecursive(this.root, segments, value);
|
|
94
|
-
}
|
|
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
|
-
*/
|
|
103
|
-
private insertRecursive(node: RadixNode<T>, segments: string[], value: T): void {
|
|
104
|
-
if(segments.length === 0) {
|
|
105
|
-
node.value = value;
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const segment = segments[0] ?? "";
|
|
110
|
-
|
|
111
|
-
let child = node.children.find(c =>
|
|
112
|
-
c.isParam === segment.startsWith(":") &&
|
|
113
|
-
(c.isParam || c.segment === segment)
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
if(!child) {
|
|
117
|
-
child = new RadixNode<T>(segment);
|
|
118
|
-
node.addChild(child);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
this.insertRecursive(child, segments.slice(1), value);
|
|
122
|
-
}
|
|
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
|
-
*/
|
|
130
|
-
public search(path: string): ISearchResult<T> | undefined {
|
|
131
|
-
const segments = this.normalize(path);
|
|
132
|
-
return this.searchRecursive(this.root, segments, {});
|
|
133
|
-
}
|
|
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
|
-
*/
|
|
143
|
-
private searchRecursive(node: RadixNode<T>, segments: string[], params: Params): ISearchResult<T> | undefined {
|
|
144
|
-
if(segments.length === 0) {
|
|
145
|
-
if(node.value !== undefined) {
|
|
146
|
-
return {
|
|
147
|
-
node: node,
|
|
148
|
-
params
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return undefined;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const [segment, ...rest] = segments;
|
|
156
|
-
|
|
157
|
-
for(const child of node.children) {
|
|
158
|
-
if(child.isParam) {
|
|
159
|
-
const paramName = child.paramName!;
|
|
160
|
-
|
|
161
|
-
const childParams: Params = {
|
|
162
|
-
...params,
|
|
163
|
-
[paramName]: segment ?? "",
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
if(rest.length === 0) {
|
|
167
|
-
return {
|
|
168
|
-
node: child,
|
|
169
|
-
params: childParams
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const result = this.searchRecursive(child, rest, childParams);
|
|
174
|
-
|
|
175
|
-
if(result)
|
|
176
|
-
return result;
|
|
177
|
-
}
|
|
178
|
-
else if(segment === child.segment) {
|
|
179
|
-
if(rest.length === 0) {
|
|
180
|
-
return {
|
|
181
|
-
node: child,
|
|
182
|
-
params
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const result = this.searchRecursive(child, rest, params);
|
|
187
|
-
|
|
188
|
-
if(result)
|
|
189
|
-
return result;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return undefined;
|
|
194
|
-
}
|
|
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
|
-
*/
|
|
202
|
-
private normalize(path: string): string[] {
|
|
203
|
-
const segments = path
|
|
204
|
-
.replace(/^\/+|\/+$/g, "")
|
|
205
|
-
.split("/")
|
|
206
|
-
.filter(Boolean);
|
|
207
|
-
|
|
208
|
-
return ['', ...segments];
|
|
209
|
-
}
|
|
210
|
-
}
|
package/src/utils/types.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
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
|
-
*/
|
|
12
|
-
declare const Type: FunctionConstructor;
|
|
13
|
-
export interface Type<T> extends Function {
|
|
14
|
-
// eslint-disable-next-line @typescript-eslint/prefer-function-type
|
|
15
|
-
new (...args: any[]): T;
|
|
16
|
-
}
|
|
17
|
-
|
|
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>;
|