@noxfly/noxus 3.0.0-dev.4 → 3.0.0-dev.5
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/dist/child.js.map +1 -0
- package/dist/child.mjs.map +1 -0
- package/dist/main.js.map +1 -0
- package/dist/main.mjs.map +1 -0
- package/dist/preload.js.map +1 -0
- package/dist/preload.mjs.map +1 -0
- package/dist/renderer.js.map +1 -0
- package/dist/renderer.mjs.map +1 -0
- package/package.json +10 -9
- package/.editorconfig +0 -16
- package/.github/copilot-instructions.md +0 -128
- package/.vscode/settings.json +0 -3
- package/AGENTS.md +0 -5
- package/eslint.config.js +0 -109
- package/scripts/postbuild.js +0 -31
- package/src/DI/app-injector.ts +0 -173
- package/src/DI/injector-explorer.ts +0 -201
- 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 -219
- package/src/internal/bootstrap.ts +0 -141
- package/src/internal/exceptions.ts +0 -57
- package/src/internal/preload-bridge.ts +0 -75
- package/src/internal/renderer-client.ts +0 -374
- package/src/internal/renderer-events.ts +0 -110
- package/src/internal/request.ts +0 -102
- package/src/internal/router.ts +0 -365
- package/src/internal/routes.ts +0 -142
- package/src/internal/socket.ts +0 -75
- 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 -243
- package/src/utils/types.ts +0 -21
- package/src/window/window-manager.ts +0 -302
- package/tsconfig.json +0 -29
- package/tsup.config.ts +0 -50
package/src/DI/token.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { Type } from '../utils/types';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* A DI token uniquely identifies a dependency.
|
|
11
|
-
* It can wrap a class (Type<T>) or be a named symbol token.
|
|
12
|
-
*
|
|
13
|
-
* Using tokens instead of reflect-metadata means dependencies are
|
|
14
|
-
* declared explicitly — no magic type inference, no emitDecoratorMetadata.
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* // Class token (most common)
|
|
18
|
-
* const MY_SERVICE = token(MyService);
|
|
19
|
-
*
|
|
20
|
-
* // Named symbol token (for interfaces or non-class values)
|
|
21
|
-
* const DB_URL = token<string>('DB_URL');
|
|
22
|
-
*/
|
|
23
|
-
export class Token<T> {
|
|
24
|
-
public readonly description: string;
|
|
25
|
-
|
|
26
|
-
constructor(
|
|
27
|
-
public readonly target: Type<T> | string,
|
|
28
|
-
) {
|
|
29
|
-
this.description = typeof target === 'string' ? target : target.name;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
public toString(): string {
|
|
33
|
-
return `Token(${this.description})`;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Creates a DI token for a class type or a named value.
|
|
39
|
-
*
|
|
40
|
-
* @example
|
|
41
|
-
* export const MY_SERVICE = token(MyService);
|
|
42
|
-
* export const DB_URL = token<string>('DB_URL');
|
|
43
|
-
*/
|
|
44
|
-
export function token<T>(target: Type<T> | string): Token<T> {
|
|
45
|
-
return new Token<T>(target);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* The key used to look up a class token in the registry.
|
|
50
|
-
* For class tokens, the key is the class constructor itself.
|
|
51
|
-
* For named tokens, the key is the Token instance.
|
|
52
|
-
*/
|
|
53
|
-
export type TokenKey<T = unknown> = Type<T> | Token<T>;
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { InjectorExplorer } from '../DI/injector-explorer';
|
|
8
|
-
import { TokenKey } from '../DI/token';
|
|
9
|
-
import { Type } from '../utils/types';
|
|
10
|
-
|
|
11
|
-
export interface ControllerOptions {
|
|
12
|
-
/**
|
|
13
|
-
* Explicit constructor dependencies.
|
|
14
|
-
*/
|
|
15
|
-
deps?: ReadonlyArray<TokenKey>;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface IControllerMetadata {
|
|
19
|
-
deps: ReadonlyArray<TokenKey>;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const controllerMetaMap = new WeakMap<object, IControllerMetadata>();
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Marks a class as a Noxus controller.
|
|
26
|
-
* Controllers are always scope-scoped injectables.
|
|
27
|
-
* The route prefix and guards/middlewares are declared in defineRoutes(), not here.
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* @Controller({ deps: [UserService] })
|
|
31
|
-
* export class UserController {
|
|
32
|
-
* constructor(private svc: UserService) {}
|
|
33
|
-
*
|
|
34
|
-
* @Get('byId/:userId')
|
|
35
|
-
* getUserById(req: Request) { ... }
|
|
36
|
-
* }
|
|
37
|
-
*/
|
|
38
|
-
export function Controller(options: ControllerOptions = {}): ClassDecorator {
|
|
39
|
-
return (target) => {
|
|
40
|
-
const meta: IControllerMetadata = {
|
|
41
|
-
deps: options.deps ?? [],
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
controllerMetaMap.set(target, meta);
|
|
45
|
-
|
|
46
|
-
InjectorExplorer.enqueue({
|
|
47
|
-
key: target as unknown as Type<unknown>,
|
|
48
|
-
implementation: target as unknown as Type<unknown>,
|
|
49
|
-
lifetime: 'scope',
|
|
50
|
-
deps: options.deps ?? [],
|
|
51
|
-
isController: true,
|
|
52
|
-
});
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function getControllerMetadata(target: object): IControllerMetadata | undefined {
|
|
57
|
-
return controllerMetaMap.get(target);
|
|
58
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { Request } from '../internal/request';
|
|
8
|
-
import { MaybeAsync } from '../utils/types';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* A guard decides whether an incoming request should reach the handler.
|
|
12
|
-
* Implement this interface and pass the class to @Controller({ guards }) or @Get('path', { guards }).
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
export type Guard = (request: Request) => MaybeAsync<boolean>;
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { Lifetime } from '../DI/app-injector';
|
|
8
|
-
import { InjectorExplorer } from '../DI/injector-explorer';
|
|
9
|
-
import { Token, TokenKey } from '../DI/token';
|
|
10
|
-
import { Type } from '../utils/types';
|
|
11
|
-
|
|
12
|
-
export interface InjectableOptions {
|
|
13
|
-
/**
|
|
14
|
-
* Lifetime of this injectable.
|
|
15
|
-
* @default 'scope'
|
|
16
|
-
*/
|
|
17
|
-
lifetime?: Lifetime;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Explicit list of constructor dependencies, in the same order as the constructor parameters.
|
|
21
|
-
* Each entry is either a class constructor or a Token created with token().
|
|
22
|
-
*
|
|
23
|
-
* This replaces reflect-metadata / emitDecoratorMetadata entirely.
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* @Injectable({ lifetime: 'singleton', deps: [MyRepo, DB_URL] })
|
|
27
|
-
* class MyService {
|
|
28
|
-
* constructor(private repo: MyRepo, private dbUrl: string) {}
|
|
29
|
-
* }
|
|
30
|
-
*/
|
|
31
|
-
deps?: ReadonlyArray<TokenKey>;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Marks a class as injectable into the Noxus DI container.
|
|
36
|
-
*
|
|
37
|
-
* Unlike the v2 @Injectable, this decorator:
|
|
38
|
-
* - Does NOT require reflect-metadata or emitDecoratorMetadata.
|
|
39
|
-
* - Requires you to declare deps explicitly when the class has constructor parameters.
|
|
40
|
-
* - Supports standalone usage — no module declaration needed.
|
|
41
|
-
*
|
|
42
|
-
* @example
|
|
43
|
-
* // No dependencies
|
|
44
|
-
* @Injectable()
|
|
45
|
-
* class Logger {}
|
|
46
|
-
*
|
|
47
|
-
* // With dependencies
|
|
48
|
-
* @Injectable({ lifetime: 'singleton', deps: [Logger, MyRepo] })
|
|
49
|
-
* class MyService {
|
|
50
|
-
* constructor(private logger: Logger, private repo: MyRepo) {}
|
|
51
|
-
* }
|
|
52
|
-
*
|
|
53
|
-
* // With a named token
|
|
54
|
-
* const DB_URL = token<string>('DB_URL');
|
|
55
|
-
*
|
|
56
|
-
* @Injectable({ deps: [DB_URL] })
|
|
57
|
-
* class DbService {
|
|
58
|
-
* constructor(private url: string) {}
|
|
59
|
-
* }
|
|
60
|
-
*/
|
|
61
|
-
export function Injectable(options: InjectableOptions = {}): ClassDecorator {
|
|
62
|
-
const { lifetime = 'scope', deps = [] } = options;
|
|
63
|
-
|
|
64
|
-
return (target) => {
|
|
65
|
-
if (typeof target !== 'function' || !target.prototype) {
|
|
66
|
-
throw new Error(`@Injectable can only be applied to classes, not ${typeof target}`);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const key = target as unknown as Type<unknown>;
|
|
70
|
-
|
|
71
|
-
InjectorExplorer.enqueue({
|
|
72
|
-
key,
|
|
73
|
-
implementation: key,
|
|
74
|
-
lifetime,
|
|
75
|
-
deps,
|
|
76
|
-
isController: false,
|
|
77
|
-
});
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export { Token, TokenKey };
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { Guard } from './guards.decorator';
|
|
8
|
-
import { Middleware } from './middleware.decorator';
|
|
9
|
-
|
|
10
|
-
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'BATCH';
|
|
11
|
-
export type AtomicHttpMethod = Exclude<HttpMethod, 'BATCH'>;
|
|
12
|
-
|
|
13
|
-
const ATOMIC_METHODS = new Set<AtomicHttpMethod>(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']);
|
|
14
|
-
export function isAtomicHttpMethod(m: unknown): m is AtomicHttpMethod {
|
|
15
|
-
return typeof m === 'string' && ATOMIC_METHODS.has(m as AtomicHttpMethod);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface IRouteOptions {
|
|
19
|
-
/**
|
|
20
|
-
* Guards specific to this route (merged with controller guards).
|
|
21
|
-
*/
|
|
22
|
-
guards?: Guard[];
|
|
23
|
-
/**
|
|
24
|
-
* Middlewares specific to this route (merged with controller middlewares).
|
|
25
|
-
*/
|
|
26
|
-
middlewares?: Middleware[];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface IRouteMetadata {
|
|
30
|
-
method: HttpMethod;
|
|
31
|
-
path: string;
|
|
32
|
-
handler: string;
|
|
33
|
-
guards: Guard[];
|
|
34
|
-
middlewares: Middleware[];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const routeMetaMap = new WeakMap<object, IRouteMetadata[]>();
|
|
38
|
-
|
|
39
|
-
function createRouteDecorator(verb: HttpMethod) {
|
|
40
|
-
return (path: string, options: IRouteOptions = {}): MethodDecorator => {
|
|
41
|
-
return (target, propertyKey) => {
|
|
42
|
-
const ctor = target.constructor;
|
|
43
|
-
const existing: IRouteMetadata[] = routeMetaMap.get(ctor) ?? [];
|
|
44
|
-
|
|
45
|
-
existing.push({
|
|
46
|
-
method: verb,
|
|
47
|
-
path: (path ?? '').trim().replace(/^\/|\/$/g, ''),
|
|
48
|
-
handler: propertyKey as string,
|
|
49
|
-
guards: options.guards ?? [],
|
|
50
|
-
middlewares: options.middlewares ?? [],
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
routeMetaMap.set(ctor, existing);
|
|
54
|
-
};
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function getRouteMetadata(target: object): IRouteMetadata[] {
|
|
59
|
-
return routeMetaMap.get(target) ?? [];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export const Get = createRouteDecorator('GET');
|
|
63
|
-
export const Post = createRouteDecorator('POST');
|
|
64
|
-
export const Put = createRouteDecorator('PUT');
|
|
65
|
-
export const Patch = createRouteDecorator('PATCH');
|
|
66
|
-
export const Delete = createRouteDecorator('DELETE');
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { IResponse, Request } from '../internal/request';
|
|
8
|
-
import { MaybeAsync } from '../utils/types';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* A middleware intercepts requests before they reach guards and the handler.
|
|
12
|
-
* Implement this interface and pass the class to @Controller({ middlewares }) or per-route options.
|
|
13
|
-
*/
|
|
14
|
-
export type Middleware = (request: Request, response: IResponse, next: NextFunction) => MaybeAsync<void>;
|
|
15
|
-
export type NextFunction = () => Promise<void>;
|
package/src/index.ts
DELETED
package/src/internal/app.ts
DELETED
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { app, BrowserWindow, ipcMain, MessageChannelMain } from 'electron/main';
|
|
8
|
-
import { Guard } from "../decorators/guards.decorator";
|
|
9
|
-
import { Injectable } from '../decorators/injectable.decorator';
|
|
10
|
-
import { Middleware } from '../decorators/middleware.decorator';
|
|
11
|
-
import { inject } from '../DI/app-injector';
|
|
12
|
-
import { InjectorExplorer } from '../DI/injector-explorer';
|
|
13
|
-
import { Logger } from '../utils/logger';
|
|
14
|
-
import { Type } from '../utils/types';
|
|
15
|
-
import { WindowManager } from '../window/window-manager';
|
|
16
|
-
import { IResponse, Request } from './request';
|
|
17
|
-
import { Router } from './router';
|
|
18
|
-
import { NoxSocket } from './socket';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Your application service should implement IApp.
|
|
22
|
-
* Noxus calls these lifecycle methods at the appropriate time.
|
|
23
|
-
*
|
|
24
|
-
* Unlike v2, IApp no longer receives a BrowserWindow in onReady.
|
|
25
|
-
* Use the injected WindowManager instead — it is more flexible and
|
|
26
|
-
* does not couple the lifecycle to a single pre-created window.
|
|
27
|
-
*
|
|
28
|
-
* @example
|
|
29
|
-
* @Injectable({ lifetime: 'singleton', deps: [WindowManager, MyService] })
|
|
30
|
-
* class AppService implements IApp {
|
|
31
|
-
* constructor(private wm: WindowManager, private svc: MyService) {}
|
|
32
|
-
*
|
|
33
|
-
* async onReady() {
|
|
34
|
-
* const win = await this.wm.createSplash({ webPreferences: { preload: ... } });
|
|
35
|
-
* win.loadFile('index.html');
|
|
36
|
-
* }
|
|
37
|
-
*
|
|
38
|
-
* async onActivated() { ... }
|
|
39
|
-
* async dispose() { ... }
|
|
40
|
-
* }
|
|
41
|
-
*/
|
|
42
|
-
export interface IApp {
|
|
43
|
-
dispose(): Promise<void>;
|
|
44
|
-
onReady(): Promise<void>;
|
|
45
|
-
onActivated(): Promise<void>;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
@Injectable({ lifetime: 'singleton', deps: [Router, NoxSocket, WindowManager] })
|
|
49
|
-
export class NoxApp {
|
|
50
|
-
private appService: IApp | undefined;
|
|
51
|
-
|
|
52
|
-
constructor(
|
|
53
|
-
private readonly router: Router,
|
|
54
|
-
private readonly socket: NoxSocket,
|
|
55
|
-
public readonly windowManager: WindowManager,
|
|
56
|
-
) {}
|
|
57
|
-
|
|
58
|
-
// -------------------------------------------------------------------------
|
|
59
|
-
// Initialisation
|
|
60
|
-
// -------------------------------------------------------------------------
|
|
61
|
-
|
|
62
|
-
public async init(): Promise<this> {
|
|
63
|
-
ipcMain.on('gimme-my-port', this.giveTheRendererAPort.bind(this));
|
|
64
|
-
app.once('activate', this.onAppActivated.bind(this));
|
|
65
|
-
app.once('window-all-closed', this.onAllWindowsClosed.bind(this));
|
|
66
|
-
console.log('');
|
|
67
|
-
return this;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// -------------------------------------------------------------------------
|
|
71
|
-
// Public API
|
|
72
|
-
// -------------------------------------------------------------------------
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Registers a lazy route. The file behind this prefix is dynamically
|
|
76
|
-
* imported on the first IPC request that targets it.
|
|
77
|
-
*
|
|
78
|
-
* The import function should NOT statically reference heavy modules —
|
|
79
|
-
* the whole point is to defer their loading.
|
|
80
|
-
*
|
|
81
|
-
* @example
|
|
82
|
-
* noxApp.lazy('auth', () => import('./modules/auth/auth.controller.js'));
|
|
83
|
-
* noxApp.lazy('reporting', () => import('./modules/reporting/index.js'));
|
|
84
|
-
*/
|
|
85
|
-
public lazy(
|
|
86
|
-
pathPrefix: string,
|
|
87
|
-
load: () => Promise<unknown>,
|
|
88
|
-
guards: Guard[] = [],
|
|
89
|
-
middlewares: Middleware[] = [],
|
|
90
|
-
): this {
|
|
91
|
-
this.router.registerLazyRoute(pathPrefix, load, guards, middlewares);
|
|
92
|
-
return this;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Eagerly loads a set of modules (controllers + services) before start().
|
|
97
|
-
* Use this for modules that provide services needed by your IApp.onReady().
|
|
98
|
-
*
|
|
99
|
-
* All imports run in parallel; DI is flushed with the two-phase guarantee.
|
|
100
|
-
*/
|
|
101
|
-
public async load(importFns: Array<() => Promise<unknown>>): Promise<this> {
|
|
102
|
-
InjectorExplorer.beginAccumulate();
|
|
103
|
-
await Promise.all(importFns.map((fn) => fn()));
|
|
104
|
-
await InjectorExplorer.flushAccumulated();
|
|
105
|
-
return this;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Registers a global middleware applied to every route.
|
|
110
|
-
*/
|
|
111
|
-
public use(middleware: Middleware): this {
|
|
112
|
-
this.router.defineRootMiddleware(middleware);
|
|
113
|
-
return this;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Sets the application service (implements IApp) that receives lifecycle events.
|
|
118
|
-
* @param appClass - Class decorated with @Injectable that implements IApp.
|
|
119
|
-
*/
|
|
120
|
-
public configure(appClass: Type<IApp>): this {
|
|
121
|
-
this.appService = inject(appClass);
|
|
122
|
-
return this;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Calls IApp.onReady(). Should be called after configure() and any lazy()
|
|
127
|
-
* registrations are set up.
|
|
128
|
-
*/
|
|
129
|
-
public start(): this {
|
|
130
|
-
this.appService?.onReady();
|
|
131
|
-
return this;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// -------------------------------------------------------------------------
|
|
135
|
-
// IPC
|
|
136
|
-
// -------------------------------------------------------------------------
|
|
137
|
-
|
|
138
|
-
private readonly onRendererMessage = async (event: Electron.MessageEvent): Promise<void> => {
|
|
139
|
-
const { senderId, requestId, path, method, body, query }: import('./request').IRequest = event.data;
|
|
140
|
-
const channels = this.socket.get(senderId);
|
|
141
|
-
|
|
142
|
-
if (!channels) {
|
|
143
|
-
Logger.error(`No message channel found for sender ID: ${senderId}`);
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
try {
|
|
148
|
-
const request = new Request(event, senderId, requestId, method, path, body, query);
|
|
149
|
-
const response = await this.router.handle(request);
|
|
150
|
-
channels.request.port1.postMessage(response);
|
|
151
|
-
}
|
|
152
|
-
catch (err: unknown) {
|
|
153
|
-
const response: IResponse = {
|
|
154
|
-
requestId,
|
|
155
|
-
status: 500,
|
|
156
|
-
body: null,
|
|
157
|
-
error: err instanceof Error ? err.message : 'Internal Server Error',
|
|
158
|
-
};
|
|
159
|
-
channels.request.port1.postMessage(response);
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
private giveTheRendererAPort(event: Electron.IpcMainInvokeEvent): void {
|
|
164
|
-
const senderId = event.sender.id;
|
|
165
|
-
|
|
166
|
-
if (this.socket.get(senderId)) {
|
|
167
|
-
this.shutdownChannel(senderId);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const requestChannel = new MessageChannelMain();
|
|
171
|
-
const socketChannel = new MessageChannelMain();
|
|
172
|
-
|
|
173
|
-
requestChannel.port1.on('message', this.onRendererMessage);
|
|
174
|
-
requestChannel.port1.start();
|
|
175
|
-
socketChannel.port1.start();
|
|
176
|
-
|
|
177
|
-
event.sender.once('destroyed', () => this.shutdownChannel(senderId));
|
|
178
|
-
|
|
179
|
-
this.socket.register(senderId, requestChannel, socketChannel);
|
|
180
|
-
event.sender.postMessage('port', { senderId }, [requestChannel.port2, socketChannel.port2]);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// -------------------------------------------------------------------------
|
|
184
|
-
// Lifecycle
|
|
185
|
-
// -------------------------------------------------------------------------
|
|
186
|
-
|
|
187
|
-
private onAppActivated(): void {
|
|
188
|
-
if (process.platform === 'darwin' && BrowserWindow.getAllWindows().length === 0) {
|
|
189
|
-
this.appService?.onActivated();
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
private async onAllWindowsClosed(): Promise<void> {
|
|
194
|
-
for (const senderId of this.socket.getSenderIds()) {
|
|
195
|
-
this.shutdownChannel(senderId);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
Logger.info('All windows closed, shutting down application...');
|
|
199
|
-
await this.appService?.dispose();
|
|
200
|
-
|
|
201
|
-
if (process.platform !== 'darwin') app.quit();
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
private shutdownChannel(channelSenderId: number): void {
|
|
205
|
-
const channels = this.socket.get(channelSenderId);
|
|
206
|
-
|
|
207
|
-
if (!channels) {
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
channels.request.port1.off('message', this.onRendererMessage);
|
|
212
|
-
channels.request.port1.close();
|
|
213
|
-
channels.request.port2.close();
|
|
214
|
-
channels.socket.port1.close();
|
|
215
|
-
channels.socket.port2.close();
|
|
216
|
-
|
|
217
|
-
this.socket.unregister(channelSenderId);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { app } from 'electron/main';
|
|
8
|
-
import { inject, RootInjector } from '../DI/app-injector';
|
|
9
|
-
import { InjectorExplorer } from '../DI/injector-explorer';
|
|
10
|
-
import { TokenKey } from '../DI/token';
|
|
11
|
-
import { Logger } from '../utils/logger';
|
|
12
|
-
import { NoxApp } from './app';
|
|
13
|
-
import { RouteDefinition } from "./routes";
|
|
14
|
-
import { Router } from './router';
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* A singleton value override: provides an already-constructed instance
|
|
18
|
-
* for a given token, bypassing the DI factory.
|
|
19
|
-
*
|
|
20
|
-
* Useful for injecting external singletons (e.g. a database connection,
|
|
21
|
-
* a logger already configured, a third-party SDK wrapper) that cannot
|
|
22
|
-
* or should not be constructed by the DI container.
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* { token: MikroORM, useValue: await MikroORM.init(config) }
|
|
26
|
-
* { token: DB_URL, useValue: process.env.DATABASE_URL }
|
|
27
|
-
*/
|
|
28
|
-
export interface SingletonOverride<T = unknown> {
|
|
29
|
-
token: TokenKey<T>;
|
|
30
|
-
useValue: T;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Configuration object for bootstrapApplication.
|
|
35
|
-
*/
|
|
36
|
-
export interface BootstrapConfig {
|
|
37
|
-
/**
|
|
38
|
-
* Application routing table, produced by defineRoutes().
|
|
39
|
-
* All lazy routes are registered before the app starts.
|
|
40
|
-
*/
|
|
41
|
-
routes?: RouteDefinition[];
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Pre-built singleton instances to inject into the DI container
|
|
45
|
-
* before the application starts.
|
|
46
|
-
*
|
|
47
|
-
* This replaces the v2 module/provider declaration pattern for
|
|
48
|
-
* external singletons.
|
|
49
|
-
*
|
|
50
|
-
* @example
|
|
51
|
-
* singletons: [
|
|
52
|
-
* { token: MikroORM, useValue: await MikroORM.init(ormConfig) },
|
|
53
|
-
* { token: DB_URL, useValue: process.env.DATABASE_URL! },
|
|
54
|
-
* ]
|
|
55
|
-
*/
|
|
56
|
-
singletons?: SingletonOverride[];
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Controllers and services to eagerly load before NoxApp.start() is called.
|
|
60
|
-
* Each entry is a dynamic import function — files are imported in parallel.
|
|
61
|
-
*
|
|
62
|
-
* Use this only for things needed at startup (e.g. if your IApp service
|
|
63
|
-
* depends on a service in an otherwise lazy module).
|
|
64
|
-
*
|
|
65
|
-
* Everything else should be registered via noxApp.lazy().
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* eagerLoad: [
|
|
69
|
-
* () => import('./modules/auth/auth.controller.js'),
|
|
70
|
-
* ]
|
|
71
|
-
*/
|
|
72
|
-
eagerLoad?: Array<() => Promise<unknown>>;
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Controls framework log verbosity.
|
|
76
|
-
* - `'debug'`: all messages (default during development).
|
|
77
|
-
* - `'info'`: info, warn, error, critical only.
|
|
78
|
-
* - `'none'`: completely silent — no framework logs.
|
|
79
|
-
*
|
|
80
|
-
* You can also pass an array of specific log levels to enable.
|
|
81
|
-
*/
|
|
82
|
-
logLevel?: 'debug' | 'info' | 'none' | import('../utils/logger').LogLevel[];
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Bootstraps the Noxus application.
|
|
87
|
-
*/
|
|
88
|
-
export async function bootstrapApplication(config: BootstrapConfig = {}): Promise<NoxApp> {
|
|
89
|
-
await app.whenReady();
|
|
90
|
-
|
|
91
|
-
// Apply log level configuration
|
|
92
|
-
if (config.logLevel !== undefined) {
|
|
93
|
-
if (config.logLevel === 'none') {
|
|
94
|
-
Logger.setLogLevel([]);
|
|
95
|
-
} else if (Array.isArray(config.logLevel)) {
|
|
96
|
-
Logger.setLogLevel(config.logLevel);
|
|
97
|
-
} else {
|
|
98
|
-
Logger.setLogLevel(config.logLevel);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Build override map for the DI flush phase
|
|
103
|
-
const overrides = new Map<TokenKey, unknown>();
|
|
104
|
-
|
|
105
|
-
for (const { token, useValue } of config.singletons ?? []) {
|
|
106
|
-
overrides.set(token, useValue);
|
|
107
|
-
// Pre-register the binding so the injector knows the token exists
|
|
108
|
-
RootInjector.singletons.set(token as any, useValue);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Flush all classes enqueued by decorators at import time (two-phase)
|
|
112
|
-
// Wire the controller registrar so InjectorExplorer can register controllers
|
|
113
|
-
// without directly importing Router (avoids circular dependency).
|
|
114
|
-
InjectorExplorer.setControllerRegistrar((controllerClass, pathPrefix, routeGuards, routeMiddlewares) => {
|
|
115
|
-
const router = inject(Router);
|
|
116
|
-
router.registerController(controllerClass, pathPrefix, routeGuards, routeMiddlewares);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
InjectorExplorer.processPending(overrides);
|
|
120
|
-
|
|
121
|
-
// Resolve core framework singletons
|
|
122
|
-
const noxApp = inject(NoxApp);
|
|
123
|
-
|
|
124
|
-
// Register routes from the routing table
|
|
125
|
-
if (config.routes?.length) {
|
|
126
|
-
for (const route of config.routes) {
|
|
127
|
-
if (route.load) {
|
|
128
|
-
noxApp.lazy(route.path, route.load, route.guards, route.middlewares);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Eagerly load optional modules
|
|
134
|
-
if (config.eagerLoad?.length) {
|
|
135
|
-
await noxApp.load(config.eagerLoad);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
await noxApp.init();
|
|
139
|
-
|
|
140
|
-
return noxApp;
|
|
141
|
-
}
|