@noxfly/noxus 2.4.0 → 3.0.0-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +403 -341
  2. package/dist/app-injector-Bz3Upc0y.d.mts +125 -0
  3. package/dist/app-injector-Bz3Upc0y.d.ts +125 -0
  4. package/dist/child.d.mts +48 -22
  5. package/dist/child.d.ts +48 -22
  6. package/dist/child.js +1114 -1239
  7. package/dist/child.mjs +1090 -1193
  8. package/dist/main.d.mts +304 -261
  9. package/dist/main.d.ts +304 -261
  10. package/dist/main.js +1473 -1873
  11. package/dist/main.mjs +1423 -1791
  12. package/dist/renderer.d.mts +113 -2
  13. package/dist/renderer.d.ts +113 -2
  14. package/dist/renderer.js +144 -132
  15. package/dist/renderer.mjs +143 -132
  16. package/dist/request-BlTtiHbi.d.ts +112 -0
  17. package/dist/request-qJ9EiDZc.d.mts +112 -0
  18. package/package.json +7 -7
  19. package/src/DI/app-injector.ts +95 -106
  20. package/src/DI/injector-explorer.ts +100 -81
  21. package/src/DI/token.ts +53 -0
  22. package/src/app.ts +141 -131
  23. package/src/bootstrap.ts +79 -40
  24. package/src/decorators/controller.decorator.ts +38 -27
  25. package/src/decorators/guards.decorator.ts +5 -64
  26. package/src/decorators/injectable.decorator.ts +68 -15
  27. package/src/decorators/method.decorator.ts +40 -81
  28. package/src/decorators/middleware.decorator.ts +5 -72
  29. package/src/index.ts +3 -0
  30. package/src/main.ts +4 -11
  31. package/src/non-electron-process.ts +0 -1
  32. package/src/preload-bridge.ts +1 -1
  33. package/src/renderer-client.ts +2 -2
  34. package/src/renderer-events.ts +1 -1
  35. package/src/request.ts +3 -3
  36. package/src/router.ts +221 -369
  37. package/src/routes.ts +78 -0
  38. package/src/socket.ts +4 -4
  39. package/src/window/window-manager.ts +255 -0
  40. package/tsconfig.json +5 -10
  41. package/tsup.config.ts +2 -2
  42. package/dist/app-injector-B3MvgV3k.d.mts +0 -95
  43. package/dist/app-injector-B3MvgV3k.d.ts +0 -95
  44. package/dist/index-BxWQVi6C.d.ts +0 -253
  45. package/dist/index-DQBQQfMw.d.mts +0 -253
  46. package/src/decorators/inject.decorator.ts +0 -24
  47. package/src/decorators/injectable.metadata.ts +0 -15
  48. package/src/decorators/module.decorator.ts +0 -75
package/src/app.ts CHANGED
@@ -4,45 +4,140 @@
4
4
  * @author NoxFly
5
5
  */
6
6
 
7
- import { app, BrowserWindow, ipcMain, MessageChannelMain } from "electron/main";
8
- import { Injectable } from "src/decorators/injectable.decorator";
9
- import { IMiddleware } from "src/decorators/middleware.decorator";
10
- import { inject } from "src/DI/app-injector";
11
- import { IRequest, IResponse, Request } from "src/request";
12
- import { NoxSocket } from "src/socket";
13
- import { Router } from "src/router";
14
- import { Logger } from "src/utils/logger";
15
- import { Type } from "src/utils/types";
7
+ import { app, BrowserWindow, ipcMain, MessageChannelMain } from 'electron/main';
8
+ import { Injectable } from './decorators/injectable.decorator';
9
+ import { Middleware } from './decorators/middleware.decorator';
10
+ import { inject } from './DI/app-injector';
11
+ import { InjectorExplorer } from './DI/injector-explorer';
12
+ import { IResponse, Request } from './request';
13
+ import { Router } from './router';
14
+ import { NoxSocket } from './socket';
15
+ import { Logger } from './utils/logger';
16
+ import { Type } from './utils/types';
17
+ import { WindowManager } from './window/window-manager';
18
+ import { Guard } from "src/main";
16
19
 
17
20
  /**
18
- * The application service should implement this interface, as
19
- * the NoxApp class instance will use it to notify the given service
20
- * about application lifecycle events.
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
+ * }
21
41
  */
22
42
  export interface IApp {
23
43
  dispose(): Promise<void>;
24
- onReady(mainWindow?: BrowserWindow): Promise<void>;
44
+ onReady(): Promise<void>;
25
45
  onActivated(): Promise<void>;
26
46
  }
27
47
 
28
- /**
29
- * NoxApp is the main application class that manages the application lifecycle,
30
- * handles IPC communication, and integrates with the Router.
31
- */
32
- @Injectable('singleton')
48
+ @Injectable({ lifetime: 'singleton', deps: [Router, NoxSocket, WindowManager] })
33
49
  export class NoxApp {
34
- private app: IApp | undefined;
35
- private mainWindow: BrowserWindow | undefined;
50
+ private appService: IApp | undefined;
51
+
52
+ private readonly router = inject(Router);
53
+ private readonly socket = inject(NoxSocket);
54
+ public readonly windowManager = inject(WindowManager);
55
+
56
+ // -------------------------------------------------------------------------
57
+ // Initialisation
58
+ // -------------------------------------------------------------------------
59
+
60
+ public async init(): Promise<this> {
61
+ ipcMain.on('gimme-my-port', this.giveTheRendererAPort.bind(this));
62
+ app.once('activate', this.onAppActivated.bind(this));
63
+ app.once('window-all-closed', this.onAllWindowsClosed.bind(this));
64
+ console.log('');
65
+ return this;
66
+ }
67
+
68
+ // -------------------------------------------------------------------------
69
+ // Public API
70
+ // -------------------------------------------------------------------------
36
71
 
37
72
  /**
73
+ * Registers a lazy route. The file behind this prefix is dynamically
74
+ * imported on the first IPC request that targets it.
75
+ *
76
+ * The import function should NOT statically reference heavy modules —
77
+ * the whole point is to defer their loading.
38
78
  *
79
+ * @example
80
+ * noxApp.lazy('auth', () => import('./modules/auth/auth.controller.js'));
81
+ * noxApp.lazy('reporting', () => import('./modules/reporting/index.js'));
39
82
  */
40
- private readonly onRendererMessage = async (event: Electron.MessageEvent): Promise<void> => {
41
- const { senderId, requestId, path, method, body }: IRequest = event.data;
83
+ public lazy(
84
+ pathPrefix: string,
85
+ load: () => Promise<unknown>,
86
+ guards: Guard[] = [],
87
+ middlewares: Middleware[] = [],
88
+ ): this {
89
+ this.router.registerLazyRoute(pathPrefix, load, guards, middlewares);
90
+ return this;
91
+ }
92
+
93
+ /**
94
+ * Eagerly loads a set of modules (controllers + services) before start().
95
+ * Use this for modules that provide services needed by your IApp.onReady().
96
+ *
97
+ * All imports run in parallel; DI is flushed with the two-phase guarantee.
98
+ */
99
+ public async load(importFns: Array<() => Promise<unknown>>): Promise<this> {
100
+ InjectorExplorer.beginAccumulate();
101
+ await Promise.all(importFns.map((fn) => fn()));
102
+ InjectorExplorer.flushAccumulated();
103
+ return this;
104
+ }
105
+
106
+ /**
107
+ * Registers a global middleware applied to every route.
108
+ */
109
+ public use(middleware: Middleware): this {
110
+ this.router.defineRootMiddleware(middleware);
111
+ return this;
112
+ }
42
113
 
114
+ /**
115
+ * Sets the application service (implements IApp) that receives lifecycle events.
116
+ * @param appClass - Class decorated with @Injectable that implements IApp.
117
+ */
118
+ public configure(appClass: Type<IApp>): this {
119
+ this.appService = inject(appClass);
120
+ return this;
121
+ }
122
+
123
+ /**
124
+ * Calls IApp.onReady(). Should be called after configure() and any lazy()
125
+ * registrations are set up.
126
+ */
127
+ public start(): this {
128
+ this.appService?.onReady();
129
+ return this;
130
+ }
131
+
132
+ // -------------------------------------------------------------------------
133
+ // IPC
134
+ // -------------------------------------------------------------------------
135
+
136
+ private readonly onRendererMessage = async (event: Electron.MessageEvent): Promise<void> => {
137
+ const { senderId, requestId, path, method, body }: import('./request').IRequest = event.data;
43
138
  const channels = this.socket.get(senderId);
44
139
 
45
- if(!channels) {
140
+ if (!channels) {
46
141
  Logger.error(`No message channel found for sender ID: ${senderId}`);
47
142
  return;
48
143
  }
@@ -52,49 +147,21 @@ export class NoxApp {
52
147
  const response = await this.router.handle(request);
53
148
  channels.request.port1.postMessage(response);
54
149
  }
55
- catch(err: any) {
150
+ catch (err: unknown) {
56
151
  const response: IResponse = {
57
152
  requestId,
58
153
  status: 500,
59
154
  body: null,
60
- error: err.message || 'Internal Server Error',
155
+ error: err instanceof Error ? err.message : 'Internal Server Error',
61
156
  };
62
-
63
157
  channels.request.port1.postMessage(response);
64
158
  }
65
159
  };
66
160
 
67
- constructor(
68
- private readonly router: Router,
69
- private readonly socket: NoxSocket,
70
- ) {}
71
-
72
- /**
73
- * Initializes the NoxApp instance.
74
- * This method sets up the IPC communication, registers event listeners,
75
- * and prepares the application for use.
76
- */
77
- public async init(): Promise<NoxApp> {
78
- ipcMain.on('gimme-my-port', this.giveTheRendererAPort.bind(this));
79
-
80
- app.once('activate', this.onAppActivated.bind(this));
81
- app.once('window-all-closed', this.onAllWindowsClosed.bind(this));
82
-
83
- console.log(''); // create a new line in the console to separate setup logs from the future logs
84
-
85
- return this;
86
- }
87
-
88
- /**
89
- * Handles the request from the renderer process.
90
- * This method creates a Request object from the IPC event data,
91
- * processes it through the Router, and sends the response back
92
- * to the renderer process using the MessageChannel.
93
- */
94
161
  private giveTheRendererAPort(event: Electron.IpcMainInvokeEvent): void {
95
162
  const senderId = event.sender.id;
96
163
 
97
- if(this.socket.get(senderId)) {
164
+ if (this.socket.get(senderId)) {
98
165
  this.shutdownChannel(senderId);
99
166
  }
100
167
 
@@ -105,103 +172,46 @@ export class NoxApp {
105
172
  requestChannel.port1.start();
106
173
  socketChannel.port1.start();
107
174
 
108
- this.socket.register(senderId, requestChannel, socketChannel);
175
+ event.sender.once('destroyed', () => this.shutdownChannel(senderId));
109
176
 
177
+ this.socket.register(senderId, requestChannel, socketChannel);
110
178
  event.sender.postMessage('port', { senderId }, [requestChannel.port2, socketChannel.port2]);
111
179
  }
112
180
 
113
- /**
114
- * MacOS specific behavior.
115
- */
181
+ // -------------------------------------------------------------------------
182
+ // Lifecycle
183
+ // -------------------------------------------------------------------------
184
+
116
185
  private onAppActivated(): void {
117
- if(process.platform === 'darwin' && BrowserWindow.getAllWindows().length === 0) {
118
- this.app?.onActivated();
186
+ if (process.platform === 'darwin' && BrowserWindow.getAllWindows().length === 0) {
187
+ this.appService?.onActivated();
119
188
  }
120
189
  }
121
190
 
122
- /**
123
- * Shuts down the message channel for a specific sender ID.
124
- * This method closes the IPC channel for the specified sender ID and
125
- * removes it from the messagePorts map.
126
- * @param channelSenderId - The ID of the sender channel to shut down.
127
- * @param remove - Whether to remove the channel from the messagePorts map.
128
- */
191
+ private async onAllWindowsClosed(): Promise<void> {
192
+ for (const senderId of this.socket.getSenderIds()) {
193
+ this.shutdownChannel(senderId);
194
+ }
195
+
196
+ Logger.info('All windows closed, shutting down application...');
197
+ await this.appService?.dispose();
198
+
199
+ if (process.platform !== 'darwin') app.quit();
200
+ }
201
+
129
202
  private shutdownChannel(channelSenderId: number): void {
130
203
  const channels = this.socket.get(channelSenderId);
131
204
 
132
- if(!channels) {
133
- Logger.warn(`No message channel found for sender ID: ${channelSenderId}`);
205
+ if (!channels) {
134
206
  return;
135
207
  }
136
208
 
137
209
  channels.request.port1.off('message', this.onRendererMessage);
138
210
  channels.request.port1.close();
139
211
  channels.request.port2.close();
140
-
141
212
  channels.socket.port1.close();
142
213
  channels.socket.port2.close();
143
214
 
144
215
  this.socket.unregister(channelSenderId);
145
216
  }
146
-
147
- /**
148
- * Handles the application shutdown process.
149
- * This method is called when all windows are closed, and it cleans up the message channels
150
- */
151
- private async onAllWindowsClosed(): Promise<void> {
152
- for(const senderId of this.socket.getSenderIds()) {
153
- this.shutdownChannel(senderId);
154
- }
155
-
156
- Logger.info('All windows closed, shutting down application...');
157
- await this.app?.dispose();
158
-
159
- if(process.platform !== 'darwin') {
160
- app.quit();
161
- }
162
- }
163
-
164
-
165
- // ---
166
-
167
- /**
168
- * Sets the main BrowserWindow that was created early by bootstrapApplication.
169
- * This window will be passed to IApp.onReady when start() is called.
170
- * @param window - The BrowserWindow created during bootstrap.
171
- */
172
- public setMainWindow(window: BrowserWindow): void {
173
- this.mainWindow = window;
174
- }
175
-
176
- /**
177
- * Configures the NoxApp instance with the provided application class.
178
- * This method allows you to set the application class that will handle lifecycle events.
179
- * @param app - The application class to configure.
180
- * @returns NoxApp instance for method chaining.
181
- */
182
- public configure(app: Type<IApp>): NoxApp {
183
- this.app = inject(app);
184
- return this;
185
- }
186
-
187
- /**
188
- * Registers a middleware for the root of the application.
189
- * This method allows you to define a middleware that will be applied to all requests
190
- * @param middleware - The middleware class to register.
191
- * @returns NoxApp instance for method chaining.
192
- */
193
- public use(middleware: Type<IMiddleware>): NoxApp {
194
- this.router.defineRootMiddleware(middleware);
195
- return this;
196
- }
197
-
198
- /**
199
- * Should be called after the bootstrapApplication function is called.
200
- * Passes the early-created BrowserWindow (if any) to the configured IApp service.
201
- * @returns NoxApp instance for method chaining.
202
- */
203
- public start(): NoxApp {
204
- this.app?.onReady(this.mainWindow);
205
- return this;
206
- }
207
217
  }
package/src/bootstrap.ts CHANGED
@@ -4,66 +4,105 @@
4
4
  * @author NoxFly
5
5
  */
6
6
 
7
- import { app, BrowserWindow } from "electron/main";
8
- import { NoxApp } from "src/app";
9
- import { getModuleMetadata } from "src/decorators/module.decorator";
10
- import { inject } from "src/DI/app-injector";
11
- import { InjectorExplorer } from "src/DI/injector-explorer";
12
- import { Type } from "src/utils/types";
7
+ import { app } from 'electron/main';
8
+ import { NoxApp } from './app';
9
+ import { inject, RootInjector } from './DI/app-injector';
10
+ import { InjectorExplorer } from './DI/injector-explorer';
11
+ import { TokenKey } from './DI/token';
12
+ import { RouteDefinition } from "src/routes";
13
13
 
14
14
  /**
15
- * Options for bootstrapping the Noxus application.
15
+ * A singleton value override: provides an already-constructed instance
16
+ * for a given token, bypassing the DI factory.
17
+ *
18
+ * Useful for injecting external singletons (e.g. a database connection,
19
+ * a logger already configured, a third-party SDK wrapper) that cannot
20
+ * or should not be constructed by the DI container.
21
+ *
22
+ * @example
23
+ * { token: MikroORM, useValue: await MikroORM.init(config) }
24
+ * { token: DB_URL, useValue: process.env.DATABASE_URL }
25
+ */
26
+ export interface SingletonOverride<T = unknown> {
27
+ token: TokenKey<T>;
28
+ useValue: T;
29
+ }
30
+
31
+ /**
32
+ * Configuration object for bootstrapApplication.
16
33
  */
17
- export interface BootstrapOptions {
34
+ export interface BootstrapConfig {
35
+ /**
36
+ * Application routing table, produced by defineRoutes().
37
+ * All lazy routes are registered before the app starts.
38
+ */
39
+ routes?: RouteDefinition[];
40
+
41
+ /**
42
+ * Pre-built singleton instances to inject into the DI container
43
+ * before the application starts.
44
+ *
45
+ * This replaces the v2 module/provider declaration pattern for
46
+ * external singletons.
47
+ *
48
+ * @example
49
+ * singletons: [
50
+ * { token: MikroORM, useValue: await MikroORM.init(ormConfig) },
51
+ * { token: DB_URL, useValue: process.env.DATABASE_URL! },
52
+ * ]
53
+ */
54
+ singletons?: SingletonOverride[];
55
+
18
56
  /**
19
- * If provided, Noxus creates a BrowserWindow immediately after Electron is ready,
20
- * before any DI processing occurs. This window is passed to the configured
21
- * IApp service via onReady(). It allows the user to see a window as fast as possible,
22
- * even before the application is fully initialized.
57
+ * Controllers and services to eagerly load before NoxApp.start() is called.
58
+ * Each entry is a dynamic import function files are imported in parallel.
59
+ *
60
+ * Use this only for things needed at startup (e.g. if your IApp service
61
+ * depends on a service in an otherwise lazy module).
62
+ *
63
+ * Everything else should be registered via noxApp.lazy().
64
+ *
65
+ * @example
66
+ * eagerLoad: [
67
+ * () => import('./modules/auth/auth.controller.js'),
68
+ * ]
23
69
  */
24
- window?: Electron.BrowserWindowConstructorOptions;
70
+ eagerLoad?: Array<() => Promise<unknown>>;
25
71
  }
26
72
 
27
73
  /**
28
74
  * Bootstraps the Noxus application.
29
- * This function initializes the application by creating an instance of NoxApp,
30
- * registering the root module, and starting the application.
31
- *
32
- * When {@link BootstrapOptions.window} is provided, a BrowserWindow is created
33
- * immediately after Electron readiness — before DI resolution — so the user
34
- * sees a window as quickly as possible.
35
- *
36
- * @param rootModule - The root module of the application, decorated with @Module.
37
- * @param options - Optional bootstrap configuration.
38
- * @return A promise that resolves to the NoxApp instance.
39
- * @throws Error if the root module is not decorated with @Module, or if the electron process could not start.
40
75
  */
41
- export async function bootstrapApplication(rootModule: Type<any>, options?: BootstrapOptions): Promise<NoxApp> {
42
- if(!getModuleMetadata(rootModule)) {
43
- throw new Error(`Root module must be decorated with @Module`);
44
- }
45
-
76
+ export async function bootstrapApplication(config: BootstrapConfig = {}): Promise<NoxApp> {
46
77
  await app.whenReady();
47
78
 
48
- // Create window immediately after Electron is ready, before DI processing.
49
- // This gets pixels on screen as fast as possible.
50
- let mainWindow: BrowserWindow | undefined;
51
-
52
- if(options?.window) {
53
- mainWindow = new BrowserWindow(options.window);
79
+ // Build override map for the DI flush phase
80
+ const overrides = new Map<TokenKey, unknown>();
81
+ for (const { token, useValue } of config.singletons ?? []) {
82
+ overrides.set(token, useValue);
83
+ // Pre-register the binding so the injector knows the token exists
84
+ RootInjector.singletons.set(token as any, useValue);
54
85
  }
55
86
 
56
- // Process all deferred injectable registrations (two-phase: bindings then resolution)
57
- InjectorExplorer.processPending();
87
+ // Flush all classes enqueued by decorators at import time (two-phase)
88
+ InjectorExplorer.processPending(overrides);
58
89
 
90
+ // Resolve core framework singletons
59
91
  const noxApp = inject(NoxApp);
60
92
 
61
- if(mainWindow) {
62
- noxApp.setMainWindow(mainWindow);
93
+ // Register routes from the routing table
94
+ if (config.routes?.length) {
95
+ for (const route of config.routes) {
96
+ noxApp.lazy(route.path, route.load, route.guards, route.middlewares);
97
+ }
98
+ }
99
+
100
+ // Eagerly load optional modules
101
+ if (config.eagerLoad?.length) {
102
+ await noxApp.load(config.eagerLoad);
63
103
  }
64
104
 
65
105
  await noxApp.init();
66
106
 
67
107
  return noxApp;
68
108
  }
69
-
@@ -4,44 +4,55 @@
4
4
  * @author NoxFly
5
5
  */
6
6
 
7
- import { getGuardForController, IGuard } from "src/decorators/guards.decorator";
8
- import { Injectable } from "src/decorators/injectable.decorator";
9
- import { Type } from "src/utils/types";
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
+ }
10
17
 
11
- /**
12
- * The configuration that waits a controller's decorator.
13
- */
14
18
  export interface IControllerMetadata {
15
- path: string;
16
- guards: Type<IGuard>[];
19
+ deps: ReadonlyArray<TokenKey>;
17
20
  }
18
21
 
22
+ const controllerMetaMap = new WeakMap<object, IControllerMetadata>();
23
+
19
24
  /**
20
- * Controller decorator is used to define a controller in the application.
21
- * It is a kind of node in the routing tree, that can contains routes and middlewares.
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.
22
28
  *
23
- * @param path - The path for the controller.
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
+ * }
24
37
  */
25
- export function Controller(path: string): ClassDecorator {
38
+ export function Controller(options: ControllerOptions = {}): ClassDecorator {
26
39
  return (target) => {
27
- const data: IControllerMetadata = {
28
- path,
29
- guards: getGuardForController(target.name)
40
+ const meta: IControllerMetadata = {
41
+ deps: options.deps ?? [],
30
42
  };
31
43
 
32
- Reflect.defineMetadata(CONTROLLER_METADATA_KEY, data, target);
33
- Injectable('scope')(target);
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
+ });
34
53
  };
35
54
  }
36
55
 
37
- /**
38
- * Gets the controller metadata for a given target class.
39
- * This metadata includes the path and guards defined by the @Controller decorator.
40
- * @param target - The target class to get the controller metadata from.
41
- * @returns The controller metadata if it exists, otherwise undefined.
42
- */
43
- export function getControllerMetadata(target: Type<unknown>): IControllerMetadata | undefined {
44
- return Reflect.getMetadata(CONTROLLER_METADATA_KEY, target);
56
+ export function getControllerMetadata(target: object): IControllerMetadata | undefined {
57
+ return controllerMetaMap.get(target);
45
58
  }
46
-
47
- export const CONTROLLER_METADATA_KEY = Symbol('CONTROLLER_METADATA_KEY');
@@ -4,71 +4,12 @@
4
4
  * @author NoxFly
5
5
  */
6
6
 
7
- import { Request } from 'src/request';
8
- import { Logger } from 'src/utils/logger';
9
- import { MaybeAsync, Type } from 'src/utils/types';
7
+ import { Request } from '../request';
8
+ import { MaybeAsync } from '../utils/types';
10
9
 
11
10
  /**
12
- * IGuard interface defines a guard that can be used to protect routes.
13
- * It has a `canActivate` method that takes a request and returns a MaybeAsync boolean.
14
- * The `canActivate` method can return either a value or a Promise.
15
- * Use it on a class that should be registered as a guard in the application.
16
- * Guards can be used to protect routes or controller actions.
17
- * For example, you can use guards to check if the user is authenticated or has the right permissions.
18
- * You can use the `Authorize` decorator to register guards for a controller or a controller action.
19
- * @see Authorize
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 }).
20
13
  */
21
- export interface IGuard {
22
- canActivate(request: Request): MaybeAsync<boolean>;
23
- }
24
14
 
25
- /**
26
- * Can be used to protect the routes of a controller.
27
- * Can be used on a controller class or on a controller method.
28
- */
29
- export function Authorize(...guardClasses: Type<IGuard>[]): MethodDecorator & ClassDecorator {
30
- return (target: Function | object, propertyKey?: string | symbol) => {
31
- let key: string;
32
-
33
- // Method decorator
34
- if(propertyKey) {
35
- const ctrlName = target.constructor.name;
36
- const actionName = propertyKey as string;
37
- key = `${ctrlName}.${actionName}`;
38
- }
39
- // Class decorator
40
- else {
41
- const ctrlName = (target as Type<unknown>).name;
42
- key = `${ctrlName}`;
43
- }
44
-
45
- if(authorizations.has(key)) {
46
- throw new Error(`Guard(s) already registered for ${key}`);
47
- }
48
-
49
- authorizations.set(key, guardClasses);
50
- };
51
- }
52
-
53
- /**
54
- * Gets the guards for a controller or a controller action.
55
- * @param controllerName The name of the controller to get the guards for.
56
- * @returns An array of guards for the controller.
57
- */
58
- export function getGuardForController(controllerName: string): Type<IGuard>[] {
59
- const key = `${controllerName}`;
60
- return authorizations.get(key) ?? [];
61
- }
62
-
63
- /**
64
- * Gets the guards for a controller action.
65
- * @param controllerName The name of the controller to get the guards for.
66
- * @param actionName The name of the action to get the guards for.
67
- * @returns An array of guards for the controller action.
68
- */
69
- export function getGuardForControllerAction(controllerName: string, actionName: string): Type<IGuard>[] {
70
- const key = `${controllerName}.${actionName}`;
71
- return authorizations.get(key) ?? [];
72
- }
73
-
74
- const authorizations = new Map<string, Type<IGuard>[]>();
15
+ export type Guard = (request: Request) => MaybeAsync<boolean>;