@tsed/platform-router 8.0.1 → 8.0.3

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/package.json CHANGED
@@ -2,13 +2,14 @@
2
2
  "name": "@tsed/platform-router",
3
3
  "description": "Router",
4
4
  "type": "module",
5
- "version": "8.0.1",
5
+ "version": "8.0.3",
6
6
  "source": "./src/index.ts",
7
7
  "main": "./lib/esm/index.js",
8
8
  "module": "./lib/esm/index.js",
9
9
  "typings": "./lib/types/index.d.ts",
10
10
  "exports": {
11
11
  ".": {
12
+ "@tsed/source": "./src/index.ts",
12
13
  "types": "./lib/types/index.d.ts",
13
14
  "import": "./lib/esm/index.js",
14
15
  "default": "./lib/esm/index.js"
@@ -25,25 +26,25 @@
25
26
  "tslib": "2.7.0"
26
27
  },
27
28
  "devDependencies": {
28
- "@tsed/barrels": "8.0.1",
29
- "@tsed/core": "8.0.1",
30
- "@tsed/di": "8.0.1",
31
- "@tsed/exceptions": "8.0.1",
32
- "@tsed/json-mapper": "8.0.1",
33
- "@tsed/platform-params": "8.0.1",
34
- "@tsed/schema": "8.0.1",
35
- "@tsed/typescript": "8.0.1",
29
+ "@tsed/barrels": "8.0.3",
30
+ "@tsed/core": "8.0.3",
31
+ "@tsed/di": "8.0.3",
32
+ "@tsed/exceptions": "8.0.3",
33
+ "@tsed/json-mapper": "8.0.3",
34
+ "@tsed/platform-params": "8.0.3",
35
+ "@tsed/schema": "8.0.3",
36
+ "@tsed/typescript": "8.0.3",
36
37
  "eslint": "9.12.0",
37
38
  "typescript": "5.4.5",
38
39
  "vitest": "2.1.2"
39
40
  },
40
41
  "peerDependencies": {
41
- "@tsed/core": "8.0.1",
42
- "@tsed/di": "8.0.1",
43
- "@tsed/exceptions": "8.0.1",
44
- "@tsed/json-mapper": "8.0.1",
45
- "@tsed/platform-params": "8.0.1",
46
- "@tsed/schema": "8.0.1"
42
+ "@tsed/core": "8.0.3",
43
+ "@tsed/di": "8.0.3",
44
+ "@tsed/exceptions": "8.0.3",
45
+ "@tsed/json-mapper": "8.0.3",
46
+ "@tsed/platform-params": "8.0.3",
47
+ "@tsed/schema": "8.0.3"
47
48
  },
48
49
  "peerDependenciesMeta": {
49
50
  "@tsed/core": {
@@ -0,0 +1,149 @@
1
+ import {Controller, destroyInjector, injector} from "@tsed/di";
2
+ import {Err, Next, Req} from "@tsed/platform-http";
3
+ import {Middleware} from "@tsed/platform-middlewares";
4
+ import {Get, JsonMethodStore} from "@tsed/schema";
5
+
6
+ import {useContextHandler} from "../utils/useContextHandler.js";
7
+ import {PlatformHandlerMetadata} from "./PlatformHandlerMetadata.js";
8
+ import {PlatformHandlerType} from "./PlatformHandlerType.js";
9
+
10
+ describe("PlatformHandlerMetadata", () => {
11
+ afterEach(() => destroyInjector());
12
+ describe("from()", () => {
13
+ it("should return PlatformMetadata", () => {
14
+ const meta = new PlatformHandlerMetadata({
15
+ type: PlatformHandlerType.CUSTOM,
16
+ handler: () => {}
17
+ });
18
+ const result = PlatformHandlerMetadata.from(meta);
19
+ expect(result).toEqual(meta);
20
+ });
21
+ });
22
+ describe("from useContextHandler", () => {
23
+ it("should create a new handlerMetadata with right metadata", () => {
24
+ // GIVEN
25
+ const handler = useContextHandler((ctx: any) => {});
26
+
27
+ // WHEN
28
+ const handlerMetadata = new PlatformHandlerMetadata({
29
+ handler
30
+ });
31
+
32
+ // THEN
33
+ expect(handlerMetadata.type).toEqual(PlatformHandlerType.CTX_FN);
34
+ expect(handlerMetadata.hasNextFunction).toEqual(false);
35
+ expect(handlerMetadata.hasErrorParam).toEqual(false);
36
+ expect(handlerMetadata.toString()).toEqual("");
37
+ });
38
+ });
39
+ describe("from function", () => {
40
+ it("should create a new handlerMetadata with right metadata", () => {
41
+ // GIVEN
42
+ const options = {
43
+ handler(req: any, res: any, next: any) {}
44
+ };
45
+ // WHEN
46
+ const handlerMetadata = new PlatformHandlerMetadata(options);
47
+
48
+ // THEN
49
+ expect(handlerMetadata.type).toEqual(PlatformHandlerType.RAW_FN);
50
+ expect(handlerMetadata.hasNextFunction).toEqual(true);
51
+ expect(handlerMetadata.hasErrorParam).toEqual(false);
52
+ expect(handlerMetadata.toString()).toEqual("handler");
53
+ });
54
+ });
55
+ describe("from function err", () => {
56
+ it("should create a new handlerMetadata with right metadata", () => {
57
+ // GIVEN
58
+ const options = {
59
+ handler(err: any, req: any, res: any, next: any) {}
60
+ };
61
+ // WHEN
62
+ const handlerMetadata = new PlatformHandlerMetadata(options);
63
+
64
+ // THEN
65
+ expect(handlerMetadata.type).toEqual(PlatformHandlerType.RAW_ERR_FN);
66
+ expect(handlerMetadata.hasNextFunction).toEqual(true);
67
+ expect(handlerMetadata.hasErrorParam).toEqual(true);
68
+ expect(handlerMetadata.propertyKey).toBeUndefined();
69
+ expect(handlerMetadata.toString()).toEqual("handler");
70
+ });
71
+ });
72
+ describe("from function without nextFn", () => {
73
+ it("should create a new handlerMetadata with right metadata", () => {
74
+ // GIVEN
75
+ const options = {
76
+ handler(req: any, res: any) {}
77
+ };
78
+
79
+ // WHEN
80
+ const handlerMetadata = new PlatformHandlerMetadata(options);
81
+
82
+ // THEN
83
+ expect(handlerMetadata.type).toEqual(PlatformHandlerType.RAW_FN);
84
+ expect(handlerMetadata.hasNextFunction).toEqual(false);
85
+ expect(handlerMetadata.hasErrorParam).toEqual(false);
86
+ expect(handlerMetadata.propertyKey).toBeUndefined();
87
+ expect(handlerMetadata.toString()).toEqual("handler");
88
+ });
89
+ });
90
+ describe("from endpoint/middleware with injection", () => {
91
+ it("should create a new handlerMetadata with right metadata", () => {
92
+ // GIVEN
93
+ @Controller("/")
94
+ class Test {
95
+ @Get("/")
96
+ test(@Req() req: Req, @Next() next: Next) {}
97
+ }
98
+
99
+ injector().addProvider(Test);
100
+
101
+ const options = {
102
+ provider: injector().getProvider(Test),
103
+ propertyKey: "test",
104
+ type: PlatformHandlerType.ENDPOINT
105
+ };
106
+ // WHEN
107
+ const handlerMetadata = new PlatformHandlerMetadata(options);
108
+
109
+ // THEN
110
+ expect(handlerMetadata.type).toEqual(PlatformHandlerType.ENDPOINT);
111
+ expect(handlerMetadata.hasNextFunction).toEqual(true);
112
+ expect(handlerMetadata.hasErrorParam).toEqual(false);
113
+ expect(handlerMetadata.propertyKey).toEqual("test");
114
+ expect(handlerMetadata.scope).toEqual("singleton");
115
+ expect(handlerMetadata.toString()).toEqual("Test.test");
116
+ expect(handlerMetadata.store).toBeInstanceOf(JsonMethodStore);
117
+
118
+ expect(handlerMetadata.getParams()[0].paramType).toEqual("REQUEST");
119
+ expect(handlerMetadata.getParams()[1].paramType).toEqual("NEXT_FN");
120
+ });
121
+ });
122
+
123
+ describe("from middleware with injection and error", () => {
124
+ it("should create a new handlerMetadata with right metadata", () => {
125
+ // WHEN
126
+ @Middleware()
127
+ class Test {
128
+ use(@Err() error: any, @Next() next: Next) {}
129
+ }
130
+
131
+ injector().addProvider(Test);
132
+
133
+ const options = {
134
+ provider: injector().getProvider(Test),
135
+ propertyKey: "use",
136
+ type: PlatformHandlerType.MIDDLEWARE
137
+ };
138
+ // WHEN
139
+ const handlerMetadata = new PlatformHandlerMetadata(options);
140
+
141
+ // THEN
142
+ expect(handlerMetadata.type).toEqual(PlatformHandlerType.ERR_MIDDLEWARE);
143
+ expect(handlerMetadata.hasNextFunction).toEqual(true);
144
+ expect(handlerMetadata.hasErrorParam).toEqual(true);
145
+ expect(handlerMetadata.propertyKey).toEqual("use");
146
+ expect(handlerMetadata.toString()).toEqual("Test.use");
147
+ });
148
+ });
149
+ });
@@ -0,0 +1,145 @@
1
+ import {nameOf} from "@tsed/core";
2
+ import {DIContext, injector, Provider, ProviderScope, TokenProvider} from "@tsed/di";
3
+ import {ParamTypes} from "@tsed/platform-params";
4
+ import {EndpointMetadata, JsonEntityStore, JsonParameterStore} from "@tsed/schema";
5
+
6
+ import {PlatformHandlerType} from "./PlatformHandlerType.js";
7
+ import {SinglePathType} from "./SinglePathType.js";
8
+
9
+ export interface PlatformHandlerMetadataOpts extends Record<string, any> {
10
+ token?: TokenProvider;
11
+ }
12
+
13
+ export interface PlatformHandlerMetadataProps {
14
+ provider?: Provider;
15
+ handler?: any;
16
+ opts?: PlatformHandlerMetadataOpts;
17
+ propertyKey?: string | symbol;
18
+ type?: PlatformHandlerType;
19
+ }
20
+
21
+ export class PlatformHandlerMetadata {
22
+ path: SinglePathType;
23
+
24
+ readonly provider?: Provider;
25
+ readonly propertyKey: string | symbol;
26
+ readonly type: PlatformHandlerType = PlatformHandlerType.RAW_FN;
27
+ readonly hasNextFunction: boolean = false;
28
+ readonly opts: PlatformHandlerMetadataOpts = {};
29
+ compiledHandler: ($ctx: DIContext) => any;
30
+ #handler: any;
31
+
32
+ constructor(props: PlatformHandlerMetadataProps) {
33
+ const {propertyKey, type, provider, handler, opts} = props;
34
+
35
+ this.provider = provider;
36
+ this.type = type || handler.type || PlatformHandlerType.RAW_FN;
37
+ this.opts = opts || {};
38
+
39
+ this.#handler = propertyKey ? this.target.prototype[propertyKey] : handler;
40
+
41
+ if (propertyKey) {
42
+ this.propertyKey = propertyKey;
43
+ this.hasNextFunction = this.hasParamType(ParamTypes.NEXT_FN);
44
+
45
+ if (this.hasParamType(ParamTypes.ERR)) {
46
+ this.type = PlatformHandlerType.ERR_MIDDLEWARE;
47
+ }
48
+ } else {
49
+ if (this.#handler.length === 4) {
50
+ this.type = PlatformHandlerType.RAW_ERR_FN;
51
+ }
52
+ this.hasNextFunction = this.#handler.length >= 3;
53
+ }
54
+ }
55
+
56
+ get target() {
57
+ return this.provider?.useClass || this.#handler;
58
+ }
59
+
60
+ get token() {
61
+ return this.provider?.token || this.#handler;
62
+ }
63
+
64
+ get handler() {
65
+ return this.#handler;
66
+ }
67
+
68
+ get scope() {
69
+ return this.provider?.scope || ProviderScope.SINGLETON;
70
+ }
71
+
72
+ get hasErrorParam() {
73
+ return this.type === PlatformHandlerType.ERR_MIDDLEWARE || this.type === PlatformHandlerType.RAW_ERR_FN;
74
+ }
75
+
76
+ get store() {
77
+ return JsonEntityStore.fromMethod(this.provider!.useClass, this.propertyKey!);
78
+ }
79
+
80
+ static from(input: any, opts: PlatformHandlerMetadataOpts = {}): PlatformHandlerMetadata {
81
+ if (input instanceof PlatformHandlerMetadata) {
82
+ return input;
83
+ }
84
+
85
+ if (input instanceof EndpointMetadata) {
86
+ const provider = injector().getProvider(opts.token)!;
87
+
88
+ return new PlatformHandlerMetadata({
89
+ provider,
90
+ type: PlatformHandlerType.ENDPOINT,
91
+ propertyKey: input.propertyKey,
92
+ opts
93
+ });
94
+ }
95
+
96
+ const provider = injector().getProvider(input);
97
+
98
+ if (provider) {
99
+ return new PlatformHandlerMetadata({
100
+ provider,
101
+ type: PlatformHandlerType.MIDDLEWARE,
102
+ propertyKey: "use",
103
+ opts
104
+ });
105
+ }
106
+
107
+ return new PlatformHandlerMetadata({
108
+ handler: input,
109
+ type: input.type,
110
+ opts
111
+ });
112
+ }
113
+
114
+ public getParams() {
115
+ return JsonParameterStore.getParams(this.target, this.propertyKey) || [];
116
+ }
117
+
118
+ public hasParamType(paramType: any): boolean {
119
+ return this.getParams().findIndex((p) => p.paramType === paramType) > -1;
120
+ }
121
+
122
+ public isInjectable() {
123
+ return !(this.isRawFn() || this.isResponseFn());
124
+ }
125
+
126
+ public isRawFn() {
127
+ return this.type === PlatformHandlerType.RAW_ERR_FN || this.type === PlatformHandlerType.RAW_FN;
128
+ }
129
+
130
+ public isEndpoint() {
131
+ return this.type === PlatformHandlerType.ENDPOINT;
132
+ }
133
+
134
+ public isCtxFn() {
135
+ return this.type === PlatformHandlerType.CTX_FN;
136
+ }
137
+
138
+ public isResponseFn() {
139
+ return this.type === PlatformHandlerType.RESPONSE_FN;
140
+ }
141
+
142
+ toString() {
143
+ return [nameOf(this.target), this.propertyKey].filter(Boolean).join(".");
144
+ }
145
+ }
@@ -0,0 +1,10 @@
1
+ export enum PlatformHandlerType {
2
+ CUSTOM = "custom",
3
+ ENDPOINT = "endpoint",
4
+ MIDDLEWARE = "middleware",
5
+ ERR_MIDDLEWARE = "err:middleware",
6
+ CTX_FN = "context",
7
+ RESPONSE_FN = "response",
8
+ RAW_FN = "raw:middleware",
9
+ RAW_ERR_FN = "raw:err:middleware"
10
+ }
@@ -0,0 +1,89 @@
1
+ import {isPrimitive, nameOf, Type} from "@tsed/core";
2
+ import {Provider} from "@tsed/di";
3
+ import type {PlatformParamsCallback} from "@tsed/platform-params";
4
+ import {concatPath} from "@tsed/schema";
5
+
6
+ import {PlatformHandlerMetadata} from "./PlatformHandlerMetadata.js";
7
+ import type {PlatformRouter} from "./PlatformRouter.js";
8
+ import {SinglePathType} from "./SinglePathType.js";
9
+
10
+ export interface PlatformLayerOptions extends Record<string, any> {
11
+ token?: Type<any>;
12
+ }
13
+
14
+ export interface PlatformLayerProps {
15
+ parent: PlatformLayer;
16
+ provider: Provider;
17
+ path: SinglePathType;
18
+ basePath: SinglePathType;
19
+ method: string;
20
+ handlers: any[];
21
+ router: PlatformRouter;
22
+ opts: PlatformLayerOptions;
23
+ }
24
+
25
+ export class PlatformLayer {
26
+ public provider: Provider;
27
+ public path: SinglePathType = "";
28
+ public method: string;
29
+ public handlers: PlatformHandlerMetadata[] = [];
30
+ public router?: PlatformRouter;
31
+ public opts: PlatformLayerOptions = {};
32
+
33
+ public layers: PlatformLayer[] = [];
34
+
35
+ public parent?: PlatformLayer;
36
+ basePath: SinglePathType;
37
+ #args: PlatformParamsCallback[];
38
+
39
+ constructor(props: Partial<PlatformLayerProps> = {}) {
40
+ Object.assign(this, props);
41
+
42
+ this.layers = this.layers.map((layer) => {
43
+ return new PlatformLayer({
44
+ ...layer,
45
+ parent: this
46
+ });
47
+ });
48
+ }
49
+
50
+ set(args: PlatformParamsCallback[]) {
51
+ this.#args = args;
52
+ }
53
+
54
+ getArgs() {
55
+ return [this.path, ...this.#args].filter(Boolean);
56
+ }
57
+
58
+ isProvider() {
59
+ return !!this.provider;
60
+ }
61
+
62
+ inspect() {
63
+ return {
64
+ basePath: this.getBasePath(),
65
+ path: this.path,
66
+ method: this.method,
67
+ handlers: this.handlers.map((item: any) => String(item)),
68
+ opts: Object.entries(this.opts).reduce((obj, [key, value]) => {
69
+ return {
70
+ ...obj,
71
+ [key]: isPrimitive(value) ? value : nameOf(value)
72
+ };
73
+ }, {})
74
+ };
75
+ }
76
+
77
+ getBasePath(): string {
78
+ return concatPath(this.parent?.getBasePath(), this.basePath);
79
+ }
80
+
81
+ setLayers(layers: PlatformLayer[]) {
82
+ this.layers = layers.map((layer) => {
83
+ return new PlatformLayer({
84
+ ...layer,
85
+ parent: this
86
+ });
87
+ });
88
+ }
89
+ }
@@ -0,0 +1,131 @@
1
+ import {isString} from "@tsed/core";
2
+ import {injectable, Provider, ProviderScope} from "@tsed/di";
3
+ import {concatPath} from "@tsed/schema";
4
+
5
+ import {formatMethod} from "../utils/formatMethod.js";
6
+ import {PlatformHandlerMetadata} from "./PlatformHandlerMetadata.js";
7
+ import {PlatformLayer, PlatformLayerOptions} from "./PlatformLayer.js";
8
+ import {SinglePathType} from "./SinglePathType.js";
9
+
10
+ function printHandler(handler: any) {
11
+ return handler.toString().split("{")[0].trim();
12
+ }
13
+
14
+ export class PlatformRouter {
15
+ readonly layers: PlatformLayer[] = [];
16
+ provider: Provider;
17
+ #isBuilt = false;
18
+
19
+ use(...handlers: any[]) {
20
+ const layer = handlers.reduce(
21
+ (layer: PlatformLayer, item) => {
22
+ if (isString(item) || item instanceof RegExp) {
23
+ layer.path = item;
24
+ layer.basePath = item;
25
+ } else {
26
+ if (item instanceof PlatformRouter) {
27
+ layer.router = item;
28
+ layer.setLayers(item.layers);
29
+
30
+ if (!this.provider && item.provider) {
31
+ layer.path = concatPath(layer.path, item.provider.path);
32
+ } else {
33
+ layer.path = layer.path || item.provider.path;
34
+ }
35
+ } else {
36
+ item = PlatformHandlerMetadata.from(item);
37
+ }
38
+
39
+ layer.handlers.push(item);
40
+ }
41
+
42
+ return layer;
43
+ },
44
+ new PlatformLayer({
45
+ method: "use",
46
+ basePath: this.provider?.path || "/",
47
+ provider: this.provider
48
+ })
49
+ );
50
+
51
+ this.layers.push(layer);
52
+
53
+ return this;
54
+ }
55
+
56
+ addRoute(method: string, path: SinglePathType, handlers: any[], opts: PlatformLayerOptions = {}) {
57
+ const layer = new PlatformLayer({
58
+ provider: this.provider,
59
+ method: formatMethod(method),
60
+ path,
61
+ handlers: handlers.map((input) => {
62
+ return PlatformHandlerMetadata.from(input, opts);
63
+ }),
64
+ opts
65
+ });
66
+
67
+ this.layers.push(layer);
68
+
69
+ return this;
70
+ }
71
+
72
+ all(path: SinglePathType, ...handlers: any[]) {
73
+ return this.addRoute("all", path, handlers);
74
+ }
75
+
76
+ get(path: SinglePathType, ...handlers: any[]) {
77
+ return this.addRoute("get", path, handlers);
78
+ }
79
+
80
+ post(path: SinglePathType, ...handlers: any[]) {
81
+ return this.addRoute("post", path, handlers);
82
+ }
83
+
84
+ put(path: SinglePathType, ...handlers: any[]) {
85
+ return this.addRoute("put", path, handlers);
86
+ }
87
+
88
+ delete(path: SinglePathType, ...handlers: any[]) {
89
+ return this.addRoute("delete", path, handlers);
90
+ }
91
+
92
+ patch(path: SinglePathType, ...handlers: any[]) {
93
+ return this.addRoute("patch", path, handlers);
94
+ }
95
+
96
+ head(path: SinglePathType, ...handlers: any[]) {
97
+ return this.addRoute("head", path, handlers);
98
+ }
99
+
100
+ options(path: SinglePathType, ...handlers: any[]) {
101
+ return this.addRoute("options", path, handlers);
102
+ }
103
+
104
+ statics(path: string, options: any) {
105
+ return this.addRoute("statics", path, [], options);
106
+ }
107
+
108
+ inspect() {
109
+ return this.layers.map((layer) => {
110
+ const obj = layer.inspect();
111
+
112
+ return {
113
+ ...obj,
114
+ handlers: obj.handlers.map(printHandler),
115
+ path: concatPath(this.provider?.path, obj.path)
116
+ };
117
+ });
118
+ }
119
+
120
+ isBuilt() {
121
+ if (this.#isBuilt) {
122
+ return true;
123
+ }
124
+
125
+ this.#isBuilt = true;
126
+
127
+ return false;
128
+ }
129
+ }
130
+
131
+ injectable(PlatformRouter).scope(ProviderScope.INSTANCE);
@@ -0,0 +1,196 @@
1
+ import {getValue, Type} from "@tsed/core";
2
+ import {
3
+ constant,
4
+ ControllerProvider,
5
+ inject,
6
+ injectable,
7
+ injector,
8
+ Provider,
9
+ ProviderType,
10
+ ResolvedInvokeOptions,
11
+ TokenProvider
12
+ } from "@tsed/di";
13
+ import {$on, Hooks} from "@tsed/hooks";
14
+ import {PlatformParamsCallback} from "@tsed/platform-params";
15
+ import {concatPath, getOperationsRoutes, JsonMethodStore, OPERATION_HTTP_VERBS} from "@tsed/schema";
16
+
17
+ import {useContextHandler} from "../utils/useContextHandler.js";
18
+ import {PlatformHandlerMetadata} from "./PlatformHandlerMetadata.js";
19
+ import {PlatformLayer} from "./PlatformLayer.js";
20
+ import {PlatformRouter} from "./PlatformRouter.js";
21
+
22
+ let AUTO_INC = 0;
23
+
24
+ function getInjectableRouter(provider: Provider): PlatformRouter {
25
+ return injector().get(provider.tokenRouter)!;
26
+ }
27
+
28
+ function createTokenRouter(provider: ControllerProvider) {
29
+ return (provider.tokenRouter = provider.tokenRouter || `${provider.name}_ROUTER_${AUTO_INC++}`);
30
+ }
31
+
32
+ function createInjectableRouter(provider: ControllerProvider): PlatformRouter {
33
+ const tokenRouter = createTokenRouter(provider);
34
+
35
+ if (injector().has(tokenRouter)) {
36
+ return getInjectableRouter(provider);
37
+ }
38
+
39
+ const router = inject<PlatformRouter>(PlatformRouter);
40
+ router.provider = provider;
41
+
42
+ return injector()
43
+ .add(tokenRouter, {
44
+ useValue: router
45
+ })
46
+ .invoke<PlatformRouter>(tokenRouter);
47
+ }
48
+
49
+ export interface AlterEndpointHandlersArg {
50
+ before: (Type<any> | Function)[];
51
+ endpoint: JsonMethodStore;
52
+ after: (Type<any> | Function)[];
53
+ }
54
+
55
+ export class PlatformRouters {
56
+ readonly hooks = new Hooks();
57
+ readonly allowedVerbs = OPERATION_HTTP_VERBS;
58
+
59
+ prebuild() {
60
+ injector()
61
+ .getProviders(ProviderType.CONTROLLER)
62
+ .forEach((provider: ControllerProvider) => {
63
+ createInjectableRouter(provider);
64
+ });
65
+ }
66
+
67
+ from(token: TokenProvider, parentMiddlewares: any[] = []) {
68
+ const provider = injector().getProvider<ControllerProvider>(token)!;
69
+
70
+ if (!provider) {
71
+ throw new Error("Token not found in the provider registry");
72
+ }
73
+
74
+ const router = createInjectableRouter(provider);
75
+
76
+ if (router.isBuilt()) {
77
+ return router;
78
+ }
79
+
80
+ const useBefore = getValue(provider, "middlewares.useBefore", []);
81
+ const middlewares: any[] = [...parentMiddlewares, ...useBefore];
82
+ const {children} = provider;
83
+
84
+ // Set default to true in next major version
85
+ const appendChildrenRoutesFirst = constant<boolean>("router.appendChildrenRoutesFirst", false);
86
+
87
+ if (appendChildrenRoutesFirst) {
88
+ children.forEach((token: Type<any>) => {
89
+ const nested = this.from(token, middlewares);
90
+ router.use(nested);
91
+ });
92
+ }
93
+
94
+ getOperationsRoutes(provider.token, {allowedVerbs: this.allowedVerbs}).forEach((operationRoute) => {
95
+ const {endpoint} = operationRoute;
96
+ const {beforeMiddlewares, middlewares: mldwrs, afterMiddlewares} = endpoint;
97
+
98
+ const useBefore = getValue(provider, "middlewares.useBefore", []);
99
+ const use = getValue(provider, "middlewares.use", []);
100
+ const useAfter = getValue(provider, "middlewares.useAfter", []);
101
+
102
+ let handlers = this.hooks.alter<AlterEndpointHandlersArg>(
103
+ "alterEndpointHandlers",
104
+ {
105
+ before: [...parentMiddlewares, ...useBefore, ...beforeMiddlewares, ...use, ...mldwrs],
106
+ endpoint,
107
+ after: [...afterMiddlewares, ...useAfter]
108
+ },
109
+ [operationRoute],
110
+ this
111
+ );
112
+
113
+ handlers = this.sortHandlers(handlers);
114
+
115
+ router.addRoute(
116
+ operationRoute.method,
117
+ operationRoute.path || "",
118
+ [
119
+ useContextHandler(($ctx) => {
120
+ $ctx.endpoint = operationRoute.endpoint;
121
+ }),
122
+ ...[...handlers.before, handlers.endpoint, ...handlers.after]
123
+ ],
124
+ operationRoute
125
+ );
126
+ });
127
+
128
+ if (!appendChildrenRoutesFirst) {
129
+ children.forEach((token: Type<any>) => {
130
+ const nested = this.from(token, middlewares);
131
+ router.use(nested);
132
+ });
133
+ }
134
+
135
+ return router;
136
+ }
137
+
138
+ getLayers(router: PlatformRouter): PlatformLayer[] {
139
+ return this.flatMapLayers(router.layers);
140
+ }
141
+
142
+ private sortHandlers(handlers: AlterEndpointHandlersArg) {
143
+ const get = (token: TokenProvider) => {
144
+ return injector().getProvider(token)?.priority || 0;
145
+ };
146
+
147
+ const sort = (p1: TokenProvider, p2: TokenProvider) => (get(p1) < get(p2) ? -1 : get(p1) > get(p2) ? 1 : 0);
148
+
149
+ handlers.before = handlers.before.sort(sort);
150
+ handlers.after = handlers.after.sort(sort);
151
+
152
+ return handlers;
153
+ }
154
+
155
+ private flatMapLayers(layers: PlatformLayer[]): PlatformLayer[] {
156
+ return layers
157
+ .flatMap((layer) => {
158
+ if (layer.router) {
159
+ return this.flatMapLayers(layer.layers).map((subLayer: PlatformLayer) => {
160
+ return new PlatformLayer({
161
+ ...subLayer,
162
+ path: concatPath(layer.path, subLayer.path)
163
+ });
164
+ });
165
+ }
166
+
167
+ return new PlatformLayer(layer);
168
+ })
169
+ .map((layer) => {
170
+ const handlers = layer.handlers.map((handlerMetadata) => {
171
+ // set path on handler metadata to retrieve it later in $ctx
172
+ handlerMetadata.path = layer.path;
173
+
174
+ if (handlerMetadata.isRawFn()) {
175
+ return handlerMetadata.handler;
176
+ }
177
+
178
+ return this.hooks.alter<PlatformHandlerMetadata, PlatformParamsCallback>("alterHandler", handlerMetadata);
179
+ });
180
+
181
+ layer.set(handlers);
182
+
183
+ return layer;
184
+ });
185
+ }
186
+ }
187
+
188
+ injectable(PlatformRouters);
189
+ /**
190
+ * Create injectable router for the current invoked provider.
191
+ * @ignore
192
+ */
193
+ $on(`$beforeInvoke:${ProviderType.CONTROLLER}`, ({provider, locals}: ResolvedInvokeOptions) => {
194
+ const router = createInjectableRouter(provider as ControllerProvider);
195
+ locals.set(PlatformRouter, router);
196
+ });
@@ -0,0 +1 @@
1
+ export type SinglePathType = string | RegExp;
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @file Automatically generated by @tsed/barrels.
3
+ */
4
+ export * from "./domain/PlatformHandlerMetadata.js";
5
+ export * from "./domain/PlatformHandlerType.js";
6
+ export * from "./domain/PlatformLayer.js";
7
+ export * from "./domain/PlatformRouter.js";
8
+ export * from "./domain/PlatformRouters.js";
9
+ export * from "./domain/SinglePathType.js";
10
+ export * from "./utils/formatMethod.js";
11
+ export * from "./utils/useContextHandler.js";
@@ -0,0 +1,5 @@
1
+ import {OperationVerbs} from "@tsed/schema";
2
+
3
+ export function formatMethod(method: string | undefined) {
4
+ return (method === OperationVerbs.CUSTOM ? "use" : method || "use").toLowerCase();
5
+ }
@@ -0,0 +1,22 @@
1
+ import {DIContext} from "@tsed/di";
2
+
3
+ import {PlatformHandlerType} from "../domain/PlatformHandlerType.js";
4
+
5
+ export type PlatformContextHandler<Context = DIContext> = ($ctx: Context) => any | Promise<any>;
6
+
7
+ /**
8
+ * Create Ts.ED context handler
9
+ * @param fn
10
+ * @ignore
11
+ */
12
+ export function useContextHandler(fn: PlatformContextHandler & {type?: PlatformHandlerType}) {
13
+ fn.type = PlatformHandlerType.CTX_FN;
14
+
15
+ return fn;
16
+ }
17
+
18
+ export function useResponseHandler(fn: PlatformContextHandler & {type?: PlatformHandlerType}) {
19
+ fn.type = PlatformHandlerType.RESPONSE_FN;
20
+
21
+ return fn;
22
+ }