@noxfly/noxus 1.0.5 → 1.1.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.
- package/README.md +78 -1
- package/dist/noxus.d.mts +48 -5
- package/dist/noxus.d.ts +48 -5
- package/dist/noxus.js +155 -25
- package/dist/noxus.mjs +151 -25
- package/dist/noxus.mjs.map +1 -1
- package/eslint.config.js +1 -0
- package/package.json +1 -1
- package/src/DI/app-injector.ts +5 -2
- package/src/app.ts +6 -0
- package/src/decorators/guards.decorator.ts +2 -2
- package/src/decorators/middleware.decorator.ts +54 -0
- package/src/decorators/module.decorator.ts +0 -2
- package/src/exceptions.ts +42 -27
- package/src/index.ts +1 -0
- package/src/request.ts +2 -2
- package/src/router.ts +97 -19
- package/dist/noxus.js.map +0 -1
- package/images/screenshot-requests.png +0 -0
- package/images/screenshot-startup.png +0 -0
package/dist/noxus.mjs
CHANGED
|
@@ -13,8 +13,18 @@ import "reflect-metadata";
|
|
|
13
13
|
|
|
14
14
|
// src/exceptions.ts
|
|
15
15
|
var _ResponseException = class _ResponseException extends Error {
|
|
16
|
-
constructor(message
|
|
17
|
-
|
|
16
|
+
constructor(statusOrMessage, message) {
|
|
17
|
+
let statusCode;
|
|
18
|
+
if (typeof statusOrMessage === "number") {
|
|
19
|
+
statusCode = statusOrMessage;
|
|
20
|
+
} else if (typeof statusOrMessage === "string") {
|
|
21
|
+
message = statusOrMessage;
|
|
22
|
+
}
|
|
23
|
+
super(message ?? "");
|
|
24
|
+
__publicField(this, "status", 0);
|
|
25
|
+
if (statusCode !== void 0) {
|
|
26
|
+
this.status = statusCode;
|
|
27
|
+
}
|
|
18
28
|
this.name = this.constructor.name.replace(/([A-Z])/g, " $1");
|
|
19
29
|
}
|
|
20
30
|
};
|
|
@@ -206,8 +216,7 @@ __name(_NetworkConnectTimeoutException, "NetworkConnectTimeoutException");
|
|
|
206
216
|
var NetworkConnectTimeoutException = _NetworkConnectTimeoutException;
|
|
207
217
|
|
|
208
218
|
// src/DI/app-injector.ts
|
|
209
|
-
var
|
|
210
|
-
var AppInjector = (_a = class {
|
|
219
|
+
var _AppInjector = class _AppInjector {
|
|
211
220
|
constructor(name = null) {
|
|
212
221
|
__publicField(this, "name");
|
|
213
222
|
__publicField(this, "bindings", /* @__PURE__ */ new Map());
|
|
@@ -220,7 +229,7 @@ var AppInjector = (_a = class {
|
|
|
220
229
|
* au niveau "scope" (donc durée de vie d'une requête)
|
|
221
230
|
*/
|
|
222
231
|
createScope() {
|
|
223
|
-
const scope = new
|
|
232
|
+
const scope = new _AppInjector();
|
|
224
233
|
scope.bindings = this.bindings;
|
|
225
234
|
scope.singletons = this.singletons;
|
|
226
235
|
return scope;
|
|
@@ -231,7 +240,8 @@ var AppInjector = (_a = class {
|
|
|
231
240
|
*/
|
|
232
241
|
resolve(target) {
|
|
233
242
|
const binding = this.bindings.get(target);
|
|
234
|
-
if (!binding) throw new InternalServerException(`Failed to resolve a dependency injection : No binding for type ${target.name}
|
|
243
|
+
if (!binding) throw new InternalServerException(`Failed to resolve a dependency injection : No binding for type ${target.name}.
|
|
244
|
+
Did you forget to use @Injectable() decorator ?`);
|
|
235
245
|
switch (binding.lifetime) {
|
|
236
246
|
case "transient":
|
|
237
247
|
return this.instantiate(binding.implementation);
|
|
@@ -260,7 +270,9 @@ var AppInjector = (_a = class {
|
|
|
260
270
|
const params = paramTypes.map((p) => this.resolve(p));
|
|
261
271
|
return new target(...params);
|
|
262
272
|
}
|
|
263
|
-
}
|
|
273
|
+
};
|
|
274
|
+
__name(_AppInjector, "AppInjector");
|
|
275
|
+
var AppInjector = _AppInjector;
|
|
264
276
|
var RootInjector = new AppInjector("root");
|
|
265
277
|
function inject(t) {
|
|
266
278
|
return RootInjector.resolve(t);
|
|
@@ -400,7 +412,7 @@ function Authorize(...guardClasses) {
|
|
|
400
412
|
if (authorizations.has(key)) {
|
|
401
413
|
throw new Error(`Guard(s) already registered for ${key}`);
|
|
402
414
|
}
|
|
403
|
-
Logger.debug(`Registering
|
|
415
|
+
Logger.debug(`Registering guard(s) for ${key}: ${guardClasses.map((c) => c.name).join(", ")}`);
|
|
404
416
|
authorizations.set(key, guardClasses);
|
|
405
417
|
};
|
|
406
418
|
}
|
|
@@ -563,9 +575,41 @@ function getControllerMetadata(target) {
|
|
|
563
575
|
}
|
|
564
576
|
__name(getControllerMetadata, "getControllerMetadata");
|
|
565
577
|
|
|
578
|
+
// src/decorators/middleware.decorator.ts
|
|
579
|
+
var middlewares = /* @__PURE__ */ new Map();
|
|
580
|
+
function UseMiddlewares(mdlw) {
|
|
581
|
+
return (target, propertyKey) => {
|
|
582
|
+
let key;
|
|
583
|
+
if (propertyKey) {
|
|
584
|
+
const ctrlName = target.constructor.name;
|
|
585
|
+
const actionName = propertyKey;
|
|
586
|
+
key = `${ctrlName}.${actionName}`;
|
|
587
|
+
} else {
|
|
588
|
+
const ctrlName = target.name;
|
|
589
|
+
key = `${ctrlName}`;
|
|
590
|
+
}
|
|
591
|
+
if (middlewares.has(key)) {
|
|
592
|
+
throw new Error(`Middlewares(s) already registered for ${key}`);
|
|
593
|
+
}
|
|
594
|
+
Logger.debug(`Registering middleware(s) for ${key}: ${mdlw.map((c) => c.name).join(", ")}`);
|
|
595
|
+
middlewares.set(key, mdlw);
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
__name(UseMiddlewares, "UseMiddlewares");
|
|
599
|
+
function getMiddlewaresForController(controllerName) {
|
|
600
|
+
const key = `${controllerName}`;
|
|
601
|
+
return middlewares.get(key) ?? [];
|
|
602
|
+
}
|
|
603
|
+
__name(getMiddlewaresForController, "getMiddlewaresForController");
|
|
604
|
+
function getMiddlewaresForControllerAction(controllerName, actionName) {
|
|
605
|
+
const key = `${controllerName}.${actionName}`;
|
|
606
|
+
return middlewares.get(key) ?? [];
|
|
607
|
+
}
|
|
608
|
+
__name(getMiddlewaresForControllerAction, "getMiddlewaresForControllerAction");
|
|
609
|
+
|
|
566
610
|
// src/utils/radix-tree.ts
|
|
567
|
-
var
|
|
568
|
-
var RadixNode = (
|
|
611
|
+
var _a;
|
|
612
|
+
var RadixNode = (_a = class {
|
|
569
613
|
constructor(segment) {
|
|
570
614
|
__publicField(this, "segment");
|
|
571
615
|
__publicField(this, "children", []);
|
|
@@ -590,7 +634,7 @@ var RadixNode = (_a2 = class {
|
|
|
590
634
|
addChild(node) {
|
|
591
635
|
this.children.push(node);
|
|
592
636
|
}
|
|
593
|
-
}, __name(
|
|
637
|
+
}, __name(_a, "RadixNode"), _a);
|
|
594
638
|
var _RadixTree = class _RadixTree {
|
|
595
639
|
constructor() {
|
|
596
640
|
__publicField(this, "root", new RadixNode(""));
|
|
@@ -677,19 +721,29 @@ __name(_ts_decorate, "_ts_decorate");
|
|
|
677
721
|
var _Router = class _Router {
|
|
678
722
|
constructor() {
|
|
679
723
|
__publicField(this, "routes", new RadixTree());
|
|
724
|
+
__publicField(this, "rootMiddlewares", []);
|
|
680
725
|
}
|
|
726
|
+
/**
|
|
727
|
+
*
|
|
728
|
+
*/
|
|
681
729
|
registerController(controllerClass) {
|
|
682
730
|
const controllerMeta = getControllerMetadata(controllerClass);
|
|
683
731
|
const controllerGuards = getGuardForController(controllerClass.name);
|
|
732
|
+
const controllerMiddlewares = getMiddlewaresForController(controllerClass.name);
|
|
684
733
|
if (!controllerMeta) throw new Error(`Missing @Controller decorator on ${controllerClass.name}`);
|
|
685
734
|
const routeMetadata = getRouteMetadata(controllerClass);
|
|
686
735
|
for (const def of routeMetadata) {
|
|
687
736
|
const fullPath = `${controllerMeta.path}/${def.path}`.replace(/\/+/g, "/");
|
|
688
737
|
const routeGuards = getGuardForControllerAction(controllerClass.name, def.handler);
|
|
738
|
+
const routeMiddlewares = getMiddlewaresForControllerAction(controllerClass.name, def.handler);
|
|
689
739
|
const guards = /* @__PURE__ */ new Set([
|
|
690
740
|
...controllerGuards,
|
|
691
741
|
...routeGuards
|
|
692
742
|
]);
|
|
743
|
+
const middlewares2 = /* @__PURE__ */ new Set([
|
|
744
|
+
...controllerMiddlewares,
|
|
745
|
+
...routeMiddlewares
|
|
746
|
+
]);
|
|
693
747
|
const routeDef = {
|
|
694
748
|
method: def.method,
|
|
695
749
|
path: fullPath,
|
|
@@ -697,6 +751,9 @@ var _Router = class _Router {
|
|
|
697
751
|
handler: def.handler,
|
|
698
752
|
guards: [
|
|
699
753
|
...guards
|
|
754
|
+
],
|
|
755
|
+
middlewares: [
|
|
756
|
+
...middlewares2
|
|
700
757
|
]
|
|
701
758
|
};
|
|
702
759
|
this.routes.insert(fullPath + "/" + def.method, routeDef);
|
|
@@ -709,6 +766,17 @@ var _Router = class _Router {
|
|
|
709
766
|
Logger.log(`Mapped ${controllerClass.name}${controllerGuardsInfo} controller's routes`);
|
|
710
767
|
return this;
|
|
711
768
|
}
|
|
769
|
+
/**
|
|
770
|
+
*
|
|
771
|
+
*/
|
|
772
|
+
defineRootMiddleware(middleware) {
|
|
773
|
+
Logger.debug(`Registering root middleware: ${middleware.name}`);
|
|
774
|
+
this.rootMiddlewares.push(middleware);
|
|
775
|
+
return this;
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
*
|
|
779
|
+
*/
|
|
712
780
|
async handle(request) {
|
|
713
781
|
Logger.log(`> Received request: {${request.method} /${request.path}}`);
|
|
714
782
|
const t0 = performance.now();
|
|
@@ -720,10 +788,10 @@ var _Router = class _Router {
|
|
|
720
788
|
};
|
|
721
789
|
try {
|
|
722
790
|
const routeDef = this.findRoute(request);
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
791
|
+
await this.resolveController(request, response, routeDef);
|
|
792
|
+
if (response.status > 400) {
|
|
793
|
+
throw new ResponseException(response.status, response.error);
|
|
794
|
+
}
|
|
727
795
|
} catch (error) {
|
|
728
796
|
if (error instanceof ResponseException) {
|
|
729
797
|
response.status = error.status;
|
|
@@ -747,6 +815,9 @@ var _Router = class _Router {
|
|
|
747
815
|
return response;
|
|
748
816
|
}
|
|
749
817
|
}
|
|
818
|
+
/**
|
|
819
|
+
*
|
|
820
|
+
*/
|
|
750
821
|
findRoute(request) {
|
|
751
822
|
const matchedRoutes = this.routes.search(request.path);
|
|
752
823
|
if (matchedRoutes?.node === void 0 || matchedRoutes.node.children.length === 0) {
|
|
@@ -758,21 +829,68 @@ var _Router = class _Router {
|
|
|
758
829
|
}
|
|
759
830
|
return routeDef.value;
|
|
760
831
|
}
|
|
761
|
-
|
|
832
|
+
/**
|
|
833
|
+
*
|
|
834
|
+
*/
|
|
835
|
+
async resolveController(request, response, routeDef) {
|
|
762
836
|
const controllerInstance = request.context.resolve(routeDef.controller);
|
|
763
837
|
Object.assign(request.params, this.extractParams(request.path, routeDef.path));
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
838
|
+
await this.runRequestPipeline(request, response, routeDef, controllerInstance);
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
*
|
|
842
|
+
*/
|
|
843
|
+
async runRequestPipeline(request, response, routeDef, controllerInstance) {
|
|
844
|
+
const middlewares2 = [
|
|
845
|
+
.../* @__PURE__ */ new Set([
|
|
846
|
+
...this.rootMiddlewares,
|
|
847
|
+
...routeDef.middlewares
|
|
848
|
+
])
|
|
849
|
+
];
|
|
850
|
+
const middlewareMaxIndex = middlewares2.length - 1;
|
|
851
|
+
const guardsMaxIndex = middlewareMaxIndex + routeDef.guards.length;
|
|
852
|
+
let index = -1;
|
|
853
|
+
const dispatch = /* @__PURE__ */ __name(async (i) => {
|
|
854
|
+
if (i <= index) throw new Error("next() called multiple times");
|
|
855
|
+
index = i;
|
|
856
|
+
if (i <= middlewareMaxIndex) {
|
|
857
|
+
const nextFn = dispatch.bind(null, i + 1);
|
|
858
|
+
await this.runMiddleware(request, response, nextFn, middlewares2[i]);
|
|
859
|
+
if (response.status >= 400) {
|
|
860
|
+
throw new ResponseException(response.status, response.error);
|
|
861
|
+
}
|
|
862
|
+
return;
|
|
769
863
|
}
|
|
770
|
-
|
|
771
|
-
|
|
864
|
+
if (i <= guardsMaxIndex) {
|
|
865
|
+
const guardIndex = i - middlewares2.length;
|
|
866
|
+
const guardType = routeDef.guards[guardIndex];
|
|
867
|
+
await this.runGuard(request, guardType);
|
|
868
|
+
dispatch(i + 1);
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
const action = controllerInstance[routeDef.handler];
|
|
872
|
+
response.body = await action.call(controllerInstance, request, response);
|
|
873
|
+
}, "dispatch");
|
|
874
|
+
await dispatch(0);
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
*
|
|
878
|
+
*/
|
|
879
|
+
async runMiddleware(request, response, next, middlewareType) {
|
|
880
|
+
const middleware = request.context.resolve(middlewareType);
|
|
881
|
+
await middleware.invoke(request, response, next);
|
|
772
882
|
}
|
|
773
|
-
|
|
774
|
-
|
|
883
|
+
/**
|
|
884
|
+
*
|
|
885
|
+
*/
|
|
886
|
+
async runGuard(request, guardType) {
|
|
887
|
+
const guard = request.context.resolve(guardType);
|
|
888
|
+
const allowed = await guard.canActivate(request);
|
|
889
|
+
if (!allowed) throw new UnauthorizedException(`Unauthorized for ${request.method} ${request.path}`);
|
|
775
890
|
}
|
|
891
|
+
/**
|
|
892
|
+
*
|
|
893
|
+
*/
|
|
776
894
|
extractParams(actual, template) {
|
|
777
895
|
const aParts = actual.split("/");
|
|
778
896
|
const tParts = template.split("/");
|
|
@@ -925,6 +1043,10 @@ var _NoxApp = class _NoxApp {
|
|
|
925
1043
|
this.app = inject(app3);
|
|
926
1044
|
return this;
|
|
927
1045
|
}
|
|
1046
|
+
use(middleware) {
|
|
1047
|
+
this.router.defineRootMiddleware(middleware);
|
|
1048
|
+
return this;
|
|
1049
|
+
}
|
|
928
1050
|
/**
|
|
929
1051
|
* Should be called after the bootstrapApplication function is called.
|
|
930
1052
|
*/
|
|
@@ -956,6 +1078,7 @@ async function bootstrapApplication(rootModule) {
|
|
|
956
1078
|
}
|
|
957
1079
|
__name(bootstrapApplication, "bootstrapApplication");
|
|
958
1080
|
export {
|
|
1081
|
+
AppInjector,
|
|
959
1082
|
Authorize,
|
|
960
1083
|
BadGatewayException,
|
|
961
1084
|
BadRequestException,
|
|
@@ -997,12 +1120,15 @@ export {
|
|
|
997
1120
|
TooManyRequestsException,
|
|
998
1121
|
UnauthorizedException,
|
|
999
1122
|
UpgradeRequiredException,
|
|
1123
|
+
UseMiddlewares,
|
|
1000
1124
|
VariantAlsoNegotiatesException,
|
|
1001
1125
|
bootstrapApplication,
|
|
1002
1126
|
getControllerMetadata,
|
|
1003
1127
|
getGuardForController,
|
|
1004
1128
|
getGuardForControllerAction,
|
|
1005
1129
|
getInjectableMetadata,
|
|
1130
|
+
getMiddlewaresForController,
|
|
1131
|
+
getMiddlewaresForControllerAction,
|
|
1006
1132
|
getModuleMetadata,
|
|
1007
1133
|
getRouteMetadata,
|
|
1008
1134
|
inject
|