@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/internal/router.ts
DELETED
|
@@ -1,353 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { getControllerMetadata } from '../decorators/controller.decorator';
|
|
8
|
-
import { Guard } from '../decorators/guards.decorator';
|
|
9
|
-
import { Injectable } from '../decorators/injectable.decorator';
|
|
10
|
-
import { getRouteMetadata, isAtomicHttpMethod } from '../decorators/method.decorator';
|
|
11
|
-
import { Middleware, NextFunction } from '../decorators/middleware.decorator';
|
|
12
|
-
import { InjectorExplorer } from '../DI/injector-explorer';
|
|
13
|
-
import { Logger } from '../utils/logger';
|
|
14
|
-
import { RadixTree } from '../utils/radix-tree';
|
|
15
|
-
import { Type } from '../utils/types';
|
|
16
|
-
import {
|
|
17
|
-
BadRequestException,
|
|
18
|
-
NotFoundException,
|
|
19
|
-
ResponseException,
|
|
20
|
-
UnauthorizedException
|
|
21
|
-
} from './exceptions';
|
|
22
|
-
import { IBatchRequestItem, IBatchRequestPayload, IBatchResponsePayload, IResponse, Request } from './request';
|
|
23
|
-
|
|
24
|
-
export interface ILazyRoute {
|
|
25
|
-
load: () => Promise<unknown>;
|
|
26
|
-
guards: Guard[];
|
|
27
|
-
middlewares: Middleware[];
|
|
28
|
-
loading: Promise<void> | null;
|
|
29
|
-
loaded: boolean;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
interface LazyRouteEntry {
|
|
33
|
-
load: (() => Promise<unknown>) | null;
|
|
34
|
-
guards: Guard[];
|
|
35
|
-
middlewares: Middleware[];
|
|
36
|
-
loading: Promise<void> | null;
|
|
37
|
-
loaded: boolean;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface IRouteDefinition {
|
|
41
|
-
method: string;
|
|
42
|
-
path: string;
|
|
43
|
-
controller: Type<unknown>;
|
|
44
|
-
handler: string;
|
|
45
|
-
guards: Guard[];
|
|
46
|
-
middlewares: Middleware[];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export type ControllerAction = (request: Request, response: IResponse) => unknown;
|
|
50
|
-
|
|
51
|
-
@Injectable({ lifetime: 'singleton' })
|
|
52
|
-
export class Router {
|
|
53
|
-
private readonly routes = new RadixTree<IRouteDefinition>();
|
|
54
|
-
private readonly rootMiddlewares: Middleware[] = [];
|
|
55
|
-
private readonly lazyRoutes = new Map<string, LazyRouteEntry>();
|
|
56
|
-
|
|
57
|
-
// -------------------------------------------------------------------------
|
|
58
|
-
// Registration
|
|
59
|
-
// -------------------------------------------------------------------------
|
|
60
|
-
|
|
61
|
-
public registerController(
|
|
62
|
-
controllerClass: Type<unknown>,
|
|
63
|
-
pathPrefix: string,
|
|
64
|
-
routeGuards: Guard[] = [],
|
|
65
|
-
routeMiddlewares: Middleware[] = [],
|
|
66
|
-
): this {
|
|
67
|
-
const meta = getControllerMetadata(controllerClass);
|
|
68
|
-
|
|
69
|
-
if (!meta) {
|
|
70
|
-
throw new Error(`[Noxus] Missing @Controller decorator on ${controllerClass.name}`);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const routeMeta = getRouteMetadata(controllerClass);
|
|
74
|
-
|
|
75
|
-
for (const def of routeMeta) {
|
|
76
|
-
const fullPath = `${pathPrefix}/${def.path}`.replace(/\/+/g, '/').replace(/\/$/, '') || '/';
|
|
77
|
-
|
|
78
|
-
// Route-level guards/middlewares from defineRoutes() + action-level ones
|
|
79
|
-
const guards = [...new Set([...routeGuards, ...def.guards])];
|
|
80
|
-
const middlewares = [...new Set([...routeMiddlewares, ...def.middlewares])];
|
|
81
|
-
|
|
82
|
-
const routeDef: IRouteDefinition = {
|
|
83
|
-
method: def.method,
|
|
84
|
-
path: fullPath,
|
|
85
|
-
controller: controllerClass,
|
|
86
|
-
handler: def.handler,
|
|
87
|
-
guards,
|
|
88
|
-
middlewares,
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
this.routes.insert(fullPath + '/' + def.method, routeDef);
|
|
92
|
-
|
|
93
|
-
const guardInfo = guards.length ? `<${guards.map(g => g.name).join('|')}>` : '';
|
|
94
|
-
Logger.log(`Mapped {${def.method} /${fullPath}}${guardInfo} route`);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const ctrlGuardInfo = routeGuards.length
|
|
98
|
-
? `<${routeGuards.map(g => g.name).join('|')}>`
|
|
99
|
-
: '';
|
|
100
|
-
Logger.log(`Mapped ${controllerClass.name}${ctrlGuardInfo} controller's routes`);
|
|
101
|
-
|
|
102
|
-
return this;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
public registerLazyRoute(
|
|
106
|
-
pathPrefix: string,
|
|
107
|
-
load: () => Promise<unknown>,
|
|
108
|
-
guards: Guard[] = [],
|
|
109
|
-
middlewares: Middleware[] = [],
|
|
110
|
-
): this {
|
|
111
|
-
const normalized = pathPrefix.replace(/^\/+|\/+$/g, '');
|
|
112
|
-
this.lazyRoutes.set(normalized, { load, guards, middlewares, loading: null, loaded: false });
|
|
113
|
-
Logger.log(`Registered lazy route prefix {${normalized}}`);
|
|
114
|
-
return this;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
public defineRootMiddleware(middleware: Middleware): this {
|
|
118
|
-
this.rootMiddlewares.push(middleware);
|
|
119
|
-
return this;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// -------------------------------------------------------------------------
|
|
123
|
-
// Request handling
|
|
124
|
-
// -------------------------------------------------------------------------
|
|
125
|
-
|
|
126
|
-
public async handle(request: Request): Promise<IResponse> {
|
|
127
|
-
return request.method === 'BATCH'
|
|
128
|
-
? this.handleBatch(request)
|
|
129
|
-
: this.handleAtomic(request);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
private async handleAtomic(request: Request): Promise<IResponse> {
|
|
133
|
-
Logger.comment(`> ${request.method} /${request.path}`);
|
|
134
|
-
const t0 = performance.now();
|
|
135
|
-
|
|
136
|
-
const response: IResponse = { requestId: request.id, status: 200, body: null };
|
|
137
|
-
let isCritical = false;
|
|
138
|
-
|
|
139
|
-
try {
|
|
140
|
-
const routeDef = await this.findRoute(request);
|
|
141
|
-
await this.resolveController(request, response, routeDef);
|
|
142
|
-
|
|
143
|
-
if (response.status >= 400) throw new ResponseException(response.status, response.error);
|
|
144
|
-
}
|
|
145
|
-
catch (error) {
|
|
146
|
-
this.fillErrorResponse(response, error, (c) => { isCritical = c; });
|
|
147
|
-
}
|
|
148
|
-
finally {
|
|
149
|
-
this.logResponse(request, response, performance.now() - t0, isCritical);
|
|
150
|
-
return response;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
private async handleBatch(request: Request): Promise<IResponse> {
|
|
155
|
-
Logger.comment(`> ${request.method} /${request.path}`);
|
|
156
|
-
const t0 = performance.now();
|
|
157
|
-
|
|
158
|
-
const response: IResponse<IBatchResponsePayload> = {
|
|
159
|
-
requestId: request.id,
|
|
160
|
-
status: 200,
|
|
161
|
-
body: { responses: [] },
|
|
162
|
-
};
|
|
163
|
-
let isCritical = false;
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
const payload = this.normalizeBatchPayload(request.body);
|
|
167
|
-
response.body!.responses = await Promise.all(
|
|
168
|
-
payload.requests.map((item, i) => {
|
|
169
|
-
const id = item.requestId ?? `${request.id}:${i}`;
|
|
170
|
-
return this.handleAtomic(new Request(request.event, request.senderId, id, item.method, item.path, item.body));
|
|
171
|
-
}),
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
catch (error) {
|
|
175
|
-
this.fillErrorResponse(response, error, (c) => { isCritical = c; });
|
|
176
|
-
}
|
|
177
|
-
finally {
|
|
178
|
-
this.logResponse(request, response, performance.now() - t0, isCritical);
|
|
179
|
-
return response;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// -------------------------------------------------------------------------
|
|
184
|
-
// Route resolution
|
|
185
|
-
// -------------------------------------------------------------------------
|
|
186
|
-
|
|
187
|
-
private tryFindRoute(request: Request): IRouteDefinition | undefined {
|
|
188
|
-
const matched = this.routes.search(request.path);
|
|
189
|
-
if (!matched?.node || matched.node.children.length === 0) return undefined;
|
|
190
|
-
return matched.node.findExactChild(request.method)?.value;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
private async findRoute(request: Request): Promise<IRouteDefinition> {
|
|
194
|
-
const direct = this.tryFindRoute(request);
|
|
195
|
-
if (direct) return direct;
|
|
196
|
-
|
|
197
|
-
await this.tryLoadLazyRoute(request.path);
|
|
198
|
-
|
|
199
|
-
const afterLazy = this.tryFindRoute(request);
|
|
200
|
-
if (afterLazy) return afterLazy;
|
|
201
|
-
|
|
202
|
-
throw new NotFoundException(`No route matches ${request.method} ${request.path}`);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
private async tryLoadLazyRoute(requestPath: string): Promise<void> {
|
|
206
|
-
const firstSegment = requestPath.replace(/^\/+/, '').split('/')[0] ?? '';
|
|
207
|
-
|
|
208
|
-
for (const [prefix, entry] of this.lazyRoutes) {
|
|
209
|
-
if (entry.loaded) continue;
|
|
210
|
-
const normalized = requestPath.replace(/^\/+/, '');
|
|
211
|
-
if (normalized === prefix || normalized.startsWith(prefix + '/') || firstSegment === prefix) {
|
|
212
|
-
if (!entry.loading) entry.loading = this.loadLazyModule(prefix, entry);
|
|
213
|
-
await entry.loading;
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
private async loadLazyModule(prefix: string, entry: LazyRouteEntry): Promise<void> {
|
|
220
|
-
const t0 = performance.now();
|
|
221
|
-
InjectorExplorer.beginAccumulate();
|
|
222
|
-
|
|
223
|
-
await entry.load?.();
|
|
224
|
-
|
|
225
|
-
entry.loading = null;
|
|
226
|
-
entry.load = null;
|
|
227
|
-
|
|
228
|
-
InjectorExplorer.flushAccumulated(entry.guards, entry.middlewares, prefix);
|
|
229
|
-
|
|
230
|
-
entry.loaded = true;
|
|
231
|
-
|
|
232
|
-
Logger.info(`Lazy-loaded module for prefix {${prefix}} in ${Math.round(performance.now() - t0)}ms`);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// -------------------------------------------------------------------------
|
|
236
|
-
// Pipeline
|
|
237
|
-
// -------------------------------------------------------------------------
|
|
238
|
-
|
|
239
|
-
private async resolveController(request: Request, response: IResponse, routeDef: IRouteDefinition): Promise<void> {
|
|
240
|
-
const instance = request.context.resolve(routeDef.controller);
|
|
241
|
-
Object.assign(request.params, this.extractParams(request.path, routeDef.path));
|
|
242
|
-
await this.runPipeline(request, response, routeDef, instance);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
private async runPipeline(
|
|
246
|
-
request: Request,
|
|
247
|
-
response: IResponse,
|
|
248
|
-
routeDef: IRouteDefinition,
|
|
249
|
-
controllerInstance: unknown,
|
|
250
|
-
): Promise<void> {
|
|
251
|
-
const middlewares = [...new Set([...this.rootMiddlewares, ...routeDef.middlewares])];
|
|
252
|
-
const mwMax = middlewares.length - 1;
|
|
253
|
-
const guardMax = mwMax + routeDef.guards.length;
|
|
254
|
-
let index = -1;
|
|
255
|
-
|
|
256
|
-
const dispatch = async (i: number): Promise<void> => {
|
|
257
|
-
if (i <= index) throw new Error('next() called multiple times');
|
|
258
|
-
index = i;
|
|
259
|
-
|
|
260
|
-
if (i <= mwMax) {
|
|
261
|
-
await this.runMiddleware(request, response, dispatch.bind(null, i + 1), middlewares[i]!);
|
|
262
|
-
if (response.status >= 400) throw new ResponseException(response.status, response.error);
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
if (i <= guardMax) {
|
|
267
|
-
await this.runGuard(request, routeDef.guards[i - middlewares.length]!);
|
|
268
|
-
await dispatch(i + 1);
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
const action = (controllerInstance as Record<string, ControllerAction>)[routeDef.handler]!;
|
|
273
|
-
response.body = await action.call(controllerInstance, request, response);
|
|
274
|
-
if (response.body === undefined) response.body = {};
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
await dispatch(0);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
private async runMiddleware(request: Request, response: IResponse, next: NextFunction, middleware: Middleware): Promise<void> {
|
|
281
|
-
await middleware(request, response, next);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
private async runGuard(request: Request, guard: Guard): Promise<void> {
|
|
285
|
-
if (!await guard(request)) {
|
|
286
|
-
throw new UnauthorizedException(`Unauthorized for ${request.method} ${request.path}`);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// -------------------------------------------------------------------------
|
|
291
|
-
// Utilities
|
|
292
|
-
// -------------------------------------------------------------------------
|
|
293
|
-
|
|
294
|
-
private extractParams(actual: string, template: string): Record<string, string> {
|
|
295
|
-
const aParts = actual.split('/');
|
|
296
|
-
const tParts = template.split('/');
|
|
297
|
-
const params: Record<string, string> = {};
|
|
298
|
-
tParts.forEach((part, i) => {
|
|
299
|
-
if (part.startsWith(':')) params[part.slice(1)] = aParts[i] ?? '';
|
|
300
|
-
});
|
|
301
|
-
return params;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
private normalizeBatchPayload(body: unknown): IBatchRequestPayload {
|
|
305
|
-
if (body === null || typeof body !== 'object') {
|
|
306
|
-
throw new BadRequestException('Batch payload must be an object containing a requests array.');
|
|
307
|
-
}
|
|
308
|
-
const { requests } = body as Partial<IBatchRequestPayload>;
|
|
309
|
-
if (!Array.isArray(requests)) throw new BadRequestException('Batch payload must define a requests array.');
|
|
310
|
-
return { requests: requests.map((e, i) => this.normalizeBatchItem(e, i)) };
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
private normalizeBatchItem(entry: unknown, index: number): IBatchRequestItem {
|
|
314
|
-
if (entry === null || typeof entry !== 'object') throw new BadRequestException(`Batch request at index ${index} must be an object.`);
|
|
315
|
-
const { requestId, path, method, body } = entry as Partial<IBatchRequestItem> & { method?: unknown };
|
|
316
|
-
if (requestId !== undefined && typeof requestId !== 'string') throw new BadRequestException(`Batch request at index ${index} has an invalid requestId.`);
|
|
317
|
-
if (typeof path !== 'string' || !path.length) throw new BadRequestException(`Batch request at index ${index} must define a non-empty path.`);
|
|
318
|
-
if (typeof method !== 'string') throw new BadRequestException(`Batch request at index ${index} must define an HTTP method.`);
|
|
319
|
-
const normalized = method.toUpperCase();
|
|
320
|
-
if (!isAtomicHttpMethod(normalized)) throw new BadRequestException(`Batch request at index ${index} uses unsupported method ${method}.`);
|
|
321
|
-
return { requestId, path, method: normalized, body };
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
private fillErrorResponse(response: IResponse, error: unknown, setCritical: (v: boolean) => void): void {
|
|
325
|
-
response.body = undefined;
|
|
326
|
-
if (error instanceof ResponseException) {
|
|
327
|
-
response.status = error.status;
|
|
328
|
-
response.error = error.message;
|
|
329
|
-
response.stack = error.stack;
|
|
330
|
-
} else if (error instanceof Error) {
|
|
331
|
-
setCritical(true);
|
|
332
|
-
response.status = 500;
|
|
333
|
-
response.error = error.message || 'Internal Server Error';
|
|
334
|
-
response.stack = error.stack;
|
|
335
|
-
} else {
|
|
336
|
-
setCritical(true);
|
|
337
|
-
response.status = 500;
|
|
338
|
-
response.error = 'Unknown error occurred';
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
private logResponse(request: Request, response: IResponse, ms: number, isCritical: boolean): void {
|
|
343
|
-
const msg = `< ${response.status} ${request.method} /${request.path} ${Logger.colors.yellow}${Math.round(ms)}ms${Logger.colors.initial}`;
|
|
344
|
-
if (response.status < 400) Logger.log(msg);
|
|
345
|
-
else if (response.status < 500) Logger.warn(msg);
|
|
346
|
-
else isCritical ? Logger.critical(msg) : Logger.error(msg);
|
|
347
|
-
|
|
348
|
-
if (response.error) {
|
|
349
|
-
isCritical ? Logger.critical(response.error) : Logger.error(response.error);
|
|
350
|
-
if (response.stack) Logger.errorStack(response.stack);
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
}
|
package/src/internal/routes.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { Guard } from '../decorators/guards.decorator';
|
|
8
|
-
import { Middleware } from '../decorators/middleware.decorator';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* A single route entry in the application routing table.
|
|
12
|
-
*/
|
|
13
|
-
export interface RouteDefinition {
|
|
14
|
-
/**
|
|
15
|
-
* The path prefix for this route (e.g. 'users', 'orders').
|
|
16
|
-
* All actions defined in the controller will be prefixed with this path.
|
|
17
|
-
*/
|
|
18
|
-
path: string;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Dynamic import function returning the controller file.
|
|
22
|
-
* The controller is loaded lazily on the first IPC request targeting this prefix.
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* load: () => import('./modules/users/users.controller')
|
|
26
|
-
*/
|
|
27
|
-
load: () => Promise<unknown>;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Guards applied to every action in this controller.
|
|
31
|
-
* Merged with action-level guards.
|
|
32
|
-
*/
|
|
33
|
-
guards?: Guard[];
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Middlewares applied to every action in this controller.
|
|
37
|
-
* Merged with action-level middlewares.
|
|
38
|
-
*/
|
|
39
|
-
middlewares?: Middleware[];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Defines the application routing table.
|
|
44
|
-
* Each entry maps a path prefix to a lazily-loaded controller.
|
|
45
|
-
*
|
|
46
|
-
* This is the single source of truth for routing — no path is declared
|
|
47
|
-
* in @Controller(), preventing duplicate route prefixes across controllers.
|
|
48
|
-
*
|
|
49
|
-
* @example
|
|
50
|
-
* export const routes = defineRoutes([
|
|
51
|
-
* {
|
|
52
|
-
* path: 'users',
|
|
53
|
-
* load: () => import('./modules/users/users.controller'),
|
|
54
|
-
* guards: [authGuard],
|
|
55
|
-
* },
|
|
56
|
-
* {
|
|
57
|
-
* path: 'orders',
|
|
58
|
-
* load: () => import('./modules/orders/orders.controller'),
|
|
59
|
-
* guards: [authGuard],
|
|
60
|
-
* middlewares: [logMiddleware],
|
|
61
|
-
* },
|
|
62
|
-
* ]);
|
|
63
|
-
*/
|
|
64
|
-
export function defineRoutes(routes: RouteDefinition[]): RouteDefinition[] {
|
|
65
|
-
const paths = routes.map(r => r.path.replace(/^\/+|\/+$/g, ''));
|
|
66
|
-
const duplicates = paths.filter((p, i) => paths.indexOf(p) !== i);
|
|
67
|
-
|
|
68
|
-
if (duplicates.length > 0) {
|
|
69
|
-
throw new Error(
|
|
70
|
-
`[Noxus] Duplicate route prefixes detected: ${[...new Set(duplicates)].map(d => `"${d}"`).join(', ')}`
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return routes.map(r => ({
|
|
75
|
-
...r,
|
|
76
|
-
path: r.path.replace(/^\/+|\/+$/g, ''),
|
|
77
|
-
}));
|
|
78
|
-
}
|
package/src/internal/socket.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Centralizes MessagePort storage for renderer communication and handles
|
|
9
|
-
* push-event delivery back to renderer processes.
|
|
10
|
-
*/
|
|
11
|
-
import { Injectable } from '../decorators/injectable.decorator';
|
|
12
|
-
import { Logger } from '../utils/logger';
|
|
13
|
-
import { createRendererEventMessage } from './request';
|
|
14
|
-
|
|
15
|
-
interface RendererChannels {
|
|
16
|
-
request: Electron.MessageChannelMain;
|
|
17
|
-
socket: Electron.MessageChannelMain;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
@Injectable({ lifetime: 'singleton' })
|
|
21
|
-
export class NoxSocket {
|
|
22
|
-
private readonly channels = new Map<number, RendererChannels>();
|
|
23
|
-
|
|
24
|
-
public register(senderId: number, requestChannel: Electron.MessageChannelMain, socketChannel: Electron.MessageChannelMain): void {
|
|
25
|
-
this.channels.set(senderId, { request: requestChannel, socket: socketChannel });
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
public get(senderId: number): RendererChannels | undefined {
|
|
29
|
-
return this.channels.get(senderId);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
public unregister(senderId: number): void {
|
|
33
|
-
this.channels.delete(senderId);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
public getSenderIds(): number[] {
|
|
37
|
-
return [...this.channels.keys()];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
public emit<TPayload = unknown>(eventName: string, payload?: TPayload, targetSenderIds?: number[]): number {
|
|
41
|
-
const normalizedEvent = eventName.trim();
|
|
42
|
-
|
|
43
|
-
if(normalizedEvent.length === 0) {
|
|
44
|
-
throw new Error('Renderer event name must be a non-empty string.');
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const recipients = targetSenderIds ?? this.getSenderIds();
|
|
48
|
-
let delivered = 0;
|
|
49
|
-
|
|
50
|
-
for(const senderId of recipients) {
|
|
51
|
-
const channel = this.channels.get(senderId);
|
|
52
|
-
|
|
53
|
-
if(!channel) {
|
|
54
|
-
Logger.warn(`No message channel found for sender ID: ${senderId} while emitting "${normalizedEvent}".`);
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
try {
|
|
59
|
-
channel.socket.port1.postMessage(createRendererEventMessage(normalizedEvent, payload));
|
|
60
|
-
delivered++;
|
|
61
|
-
}
|
|
62
|
-
catch(error) {
|
|
63
|
-
Logger.error(`[Noxus] Failed to emit "${normalizedEvent}" to sender ${senderId}.`, error);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return delivered;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
public emitToRenderer<TPayload = unknown>(senderId: number, eventName: string, payload?: TPayload): boolean {
|
|
71
|
-
return this.emit(eventName, payload, [senderId]) > 0;
|
|
72
|
-
}
|
|
73
|
-
}
|
package/src/main.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*
|
|
6
|
-
* Entry point for Electron main-process consumers.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export * from './DI/app-injector';
|
|
10
|
-
export * from './DI/token';
|
|
11
|
-
export * from './internal/router';
|
|
12
|
-
export * from './internal/app';
|
|
13
|
-
export * from './internal/bootstrap';
|
|
14
|
-
export * from './internal/exceptions';
|
|
15
|
-
export * from './decorators/middleware.decorator';
|
|
16
|
-
export * from './decorators/guards.decorator';
|
|
17
|
-
export * from './decorators/controller.decorator';
|
|
18
|
-
export * from './decorators/injectable.decorator';
|
|
19
|
-
export * from './decorators/method.decorator';
|
|
20
|
-
export * from './utils/logger';
|
|
21
|
-
export * from './utils/types';
|
|
22
|
-
export * from './utils/forward-ref';
|
|
23
|
-
export * from './internal/request';
|
|
24
|
-
export * from './internal/socket';
|
|
25
|
-
export * from './window/window-manager';
|
|
26
|
-
export * from './internal/routes';
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Entry point for nodeJS non-electron process consumers.
|
|
9
|
-
* For instance, if main process creates a child process that
|
|
10
|
-
* wants to use Logger and DI.
|
|
11
|
-
* Child processes must not try to communicate with the renderer
|
|
12
|
-
* process.
|
|
13
|
-
* order of exports here matters and can affect module resolution.
|
|
14
|
-
* Please be cautious when modifying.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
export * from './DI/app-injector';
|
|
18
|
-
export * from './internal/exceptions';
|
|
19
|
-
export * from './decorators/injectable.decorator';
|
|
20
|
-
export * from './utils/logger';
|
|
21
|
-
export * from './utils/types';
|
|
22
|
-
export * from './utils/forward-ref';
|
package/src/preload.ts
DELETED
package/src/renderer.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*
|
|
6
|
-
* Entry point for renderer web consumers (Angular, React, Vue, Vanilla...).
|
|
7
|
-
* No Electron imports — safe to bundle with any web bundler.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
export * from './internal/renderer-client';
|
|
11
|
-
export * from './internal/renderer-events';
|
|
12
|
-
export * from './internal/request';
|
|
13
|
-
export type { HttpMethod, AtomicHttpMethod } from './decorators/method.decorator';
|
package/src/utils/forward-ref.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { Type } from "./types";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* A function that returns a type.
|
|
11
|
-
* Used for forward references to types that are not yet defined.
|
|
12
|
-
*/
|
|
13
|
-
export interface ForwardRefFn<T = any> {
|
|
14
|
-
(): Type<T>;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* A wrapper class for forward referenced types.
|
|
19
|
-
*/
|
|
20
|
-
export class ForwardReference<T = any> {
|
|
21
|
-
constructor(public readonly forwardRefFn: ForwardRefFn<T>) {}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Creates a forward reference to a type.
|
|
26
|
-
* @param fn A function that returns the type.
|
|
27
|
-
* @returns A ForwardReference instance.
|
|
28
|
-
*/
|
|
29
|
-
export function forwardRef<T = any>(fn: ForwardRefFn<T>): ForwardReference<T> {
|
|
30
|
-
return new ForwardReference(fn);
|
|
31
|
-
}
|