@noxfly/noxus 3.0.0-dev.4 → 3.0.0-dev.6
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.d.mts +4 -4
- package/dist/main.d.ts +4 -4
- 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.d.mts +4 -4
- package/dist/renderer.d.ts +4 -4
- 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
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2025 NoxFly
|
|
3
|
-
* @license MIT
|
|
4
|
-
* @author NoxFly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { Lifetime, RootInjector } from './app-injector';
|
|
8
|
-
import { TokenKey } from './token';
|
|
9
|
-
import { Type } from '../utils/types';
|
|
10
|
-
import { Logger } from '../utils/logger';
|
|
11
|
-
import { Guard } from '../decorators/guards.decorator';
|
|
12
|
-
import { Middleware } from '../decorators/middleware.decorator';
|
|
13
|
-
|
|
14
|
-
export interface PendingRegistration {
|
|
15
|
-
key: TokenKey;
|
|
16
|
-
implementation: Type<unknown>;
|
|
17
|
-
lifetime: Lifetime;
|
|
18
|
-
deps: ReadonlyArray<TokenKey>;
|
|
19
|
-
isController: boolean;
|
|
20
|
-
pathPrefix?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Callback invoked for each controller registration discovered during flush.
|
|
25
|
-
* Decouples InjectorExplorer from the Router to avoid circular imports.
|
|
26
|
-
*/
|
|
27
|
-
export type ControllerRegistrar = (
|
|
28
|
-
controllerClass: Type<unknown>,
|
|
29
|
-
pathPrefix: string,
|
|
30
|
-
routeGuards: Guard[],
|
|
31
|
-
routeMiddlewares: Middleware[],
|
|
32
|
-
) => void;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* InjectorExplorer accumulates registrations emitted by decorators
|
|
36
|
-
* at import time, then flushes them in two phases (bind → resolve)
|
|
37
|
-
* once bootstrapApplication triggers processing.
|
|
38
|
-
*
|
|
39
|
-
* Because deps are now explicit arrays (no reflect-metadata), this class
|
|
40
|
-
* no longer needs to introspect constructor parameter types.
|
|
41
|
-
*/
|
|
42
|
-
export class InjectorExplorer {
|
|
43
|
-
private static readonly pending: PendingRegistration[] = [];
|
|
44
|
-
private static processed = false;
|
|
45
|
-
private static accumulating = false;
|
|
46
|
-
private static loadingLock: Promise<void> = Promise.resolve();
|
|
47
|
-
private static controllerRegistrar: ControllerRegistrar | null = null;
|
|
48
|
-
|
|
49
|
-
// -------------------------------------------------------------------------
|
|
50
|
-
// Public API
|
|
51
|
-
// -------------------------------------------------------------------------
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Sets the callback used to register controllers.
|
|
55
|
-
* Must be called once before processPending (typically by bootstrapApplication).
|
|
56
|
-
*/
|
|
57
|
-
public static setControllerRegistrar(registrar: ControllerRegistrar): void {
|
|
58
|
-
InjectorExplorer.controllerRegistrar = registrar;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
public static enqueue(reg: PendingRegistration): void {
|
|
62
|
-
if (InjectorExplorer.processed && !InjectorExplorer.accumulating) {
|
|
63
|
-
InjectorExplorer._registerImmediate(reg);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
InjectorExplorer.pending.push(reg);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Two-phase flush of all pending registrations collected at startup.
|
|
71
|
-
* Called by bootstrapApplication after app.whenReady().
|
|
72
|
-
*/
|
|
73
|
-
public static processPending(singletonOverrides?: Map<TokenKey, unknown>): void {
|
|
74
|
-
const queue = [...InjectorExplorer.pending];
|
|
75
|
-
InjectorExplorer.pending.length = 0;
|
|
76
|
-
|
|
77
|
-
InjectorExplorer._phaseOne(queue);
|
|
78
|
-
InjectorExplorer._phaseTwo(queue, singletonOverrides);
|
|
79
|
-
|
|
80
|
-
InjectorExplorer.processed = true;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** Enters accumulation mode for lazy-loaded batches. */
|
|
84
|
-
public static beginAccumulate(): void {
|
|
85
|
-
InjectorExplorer.accumulating = true;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Exits accumulation mode and flushes queued registrations
|
|
90
|
-
* with the same two-phase guarantee as processPending.
|
|
91
|
-
* Serialised through a lock to prevent concurrent lazy loads from corrupting the queue.
|
|
92
|
-
*/
|
|
93
|
-
public static flushAccumulated(
|
|
94
|
-
routeGuards: Guard[] = [],
|
|
95
|
-
routeMiddlewares: Middleware[] = [],
|
|
96
|
-
pathPrefix = '',
|
|
97
|
-
): Promise<void> {
|
|
98
|
-
InjectorExplorer.loadingLock = InjectorExplorer.loadingLock.then(() => {
|
|
99
|
-
InjectorExplorer.accumulating = false;
|
|
100
|
-
const queue = [...InjectorExplorer.pending];
|
|
101
|
-
InjectorExplorer.pending.length = 0;
|
|
102
|
-
InjectorExplorer._phaseOne(queue);
|
|
103
|
-
|
|
104
|
-
// Stamp the path prefix on controller registrations
|
|
105
|
-
for (const reg of queue) {
|
|
106
|
-
if (reg.isController) reg.pathPrefix = pathPrefix;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
InjectorExplorer._phaseTwo(queue, undefined, routeGuards, routeMiddlewares);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
return InjectorExplorer.loadingLock;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Returns a Promise that resolves once all pending flushAccumulated calls
|
|
117
|
-
* have completed. Useful for awaiting lazy-load serialisation.
|
|
118
|
-
*/
|
|
119
|
-
public static waitForFlush(): Promise<void> {
|
|
120
|
-
return InjectorExplorer.loadingLock;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Resets the explorer state. Intended for tests only.
|
|
125
|
-
*/
|
|
126
|
-
public static reset(): void {
|
|
127
|
-
InjectorExplorer.pending.length = 0;
|
|
128
|
-
InjectorExplorer.processed = false;
|
|
129
|
-
InjectorExplorer.accumulating = false;
|
|
130
|
-
InjectorExplorer.loadingLock = Promise.resolve();
|
|
131
|
-
InjectorExplorer.controllerRegistrar = null;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// -------------------------------------------------------------------------
|
|
135
|
-
// Private helpers
|
|
136
|
-
// -------------------------------------------------------------------------
|
|
137
|
-
|
|
138
|
-
/** Phase 1: register all bindings without instantiating anything. */
|
|
139
|
-
private static _phaseOne(queue: PendingRegistration[]): void {
|
|
140
|
-
for (const reg of queue) {
|
|
141
|
-
RootInjector.register(reg.key, reg.implementation, reg.lifetime, reg.deps);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** Phase 2: validate deps, resolve singletons and register controllers via the registrar callback. */
|
|
146
|
-
private static _phaseTwo(
|
|
147
|
-
queue: PendingRegistration[],
|
|
148
|
-
overrides?: Map<TokenKey, unknown>,
|
|
149
|
-
routeGuards: Guard[] = [],
|
|
150
|
-
routeMiddlewares: Middleware[] = [],
|
|
151
|
-
): void {
|
|
152
|
-
// Early dependency validation: warn about deps that have no binding
|
|
153
|
-
for (const reg of queue) {
|
|
154
|
-
for (const dep of reg.deps) {
|
|
155
|
-
if (!RootInjector.bindings.has(dep as any) && !RootInjector.singletons.has(dep as any)) {
|
|
156
|
-
Logger.warn(`[Noxus DI] "${reg.implementation.name}" declares dep "${(dep as any).name ?? dep}" which has no binding`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
for (const reg of queue) {
|
|
162
|
-
// Apply value overrides (e.g. singleton instances provided via bootstrapApplication config)
|
|
163
|
-
if (overrides?.has(reg.key)) {
|
|
164
|
-
const override = overrides.get(reg.key);
|
|
165
|
-
RootInjector.singletons.set(reg.key as any, override);
|
|
166
|
-
Logger.log(`Registered ${reg.implementation.name} as singleton (overridden)`);
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (reg.lifetime === 'singleton') {
|
|
171
|
-
RootInjector.resolve(reg.key);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (reg.isController) {
|
|
175
|
-
if (!InjectorExplorer.controllerRegistrar) {
|
|
176
|
-
throw new Error('[Noxus DI] No controller registrar set. Call InjectorExplorer.setControllerRegistrar() before processing.');
|
|
177
|
-
}
|
|
178
|
-
InjectorExplorer.controllerRegistrar(
|
|
179
|
-
reg.implementation,
|
|
180
|
-
reg.pathPrefix ?? '',
|
|
181
|
-
routeGuards,
|
|
182
|
-
routeMiddlewares,
|
|
183
|
-
);
|
|
184
|
-
} else if (reg.lifetime !== 'singleton') {
|
|
185
|
-
Logger.log(`Registered ${reg.implementation.name} as ${reg.lifetime}`);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
private static _registerImmediate(reg: PendingRegistration): void {
|
|
191
|
-
RootInjector.register(reg.key, reg.implementation, reg.lifetime, reg.deps);
|
|
192
|
-
|
|
193
|
-
if (reg.lifetime === 'singleton') {
|
|
194
|
-
RootInjector.resolve(reg.key);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (reg.isController && InjectorExplorer.controllerRegistrar) {
|
|
198
|
-
InjectorExplorer.controllerRegistrar(reg.implementation, '', [], []);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
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>;
|