@deepkit/http 1.0.1-alpha.16 → 1.0.1-alpha.160

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 (124) hide show
  1. package/CHANGELOG.md +236 -0
  2. package/dist/cjs/index.d.ts +14 -10
  3. package/dist/cjs/index.js +19 -11
  4. package/dist/cjs/index.js.map +1 -1
  5. package/dist/cjs/src/controllers.d.ts +10 -3
  6. package/dist/cjs/src/controllers.js +7 -3
  7. package/dist/cjs/src/controllers.js.map +1 -1
  8. package/dist/cjs/src/decorator.d.ts +144 -70
  9. package/dist/cjs/src/decorator.js +277 -102
  10. package/dist/cjs/src/decorator.js.map +1 -1
  11. package/dist/cjs/src/filter.d.ts +49 -0
  12. package/dist/cjs/src/filter.js +215 -0
  13. package/dist/cjs/src/filter.js.map +1 -0
  14. package/dist/cjs/src/http-app.d.ts +8 -0
  15. package/dist/cjs/src/http-app.js +16 -0
  16. package/dist/cjs/src/http-app.js.map +1 -0
  17. package/dist/cjs/src/http.d.ts +131 -32
  18. package/dist/cjs/src/http.js +523 -122
  19. package/dist/cjs/src/http.js.map +1 -1
  20. package/dist/cjs/src/kernel.d.ts +50 -9
  21. package/dist/cjs/src/kernel.js +109 -98
  22. package/dist/cjs/src/kernel.js.map +1 -1
  23. package/dist/cjs/src/logger.d.ts +3 -3
  24. package/dist/cjs/src/logger.js +9 -12
  25. package/dist/cjs/src/logger.js.map +1 -1
  26. package/dist/cjs/src/middleware.d.ts +61 -0
  27. package/dist/cjs/src/middleware.js +95 -0
  28. package/dist/cjs/src/middleware.js.map +1 -0
  29. package/dist/cjs/src/model.d.ts +270 -4
  30. package/dist/cjs/src/model.js +401 -3
  31. package/dist/cjs/src/model.js.map +1 -1
  32. package/dist/cjs/src/module.config.d.ts +149 -4
  33. package/dist/cjs/src/module.config.js +11 -6
  34. package/dist/cjs/src/module.config.js.map +1 -1
  35. package/dist/cjs/src/module.d.ts +14 -32
  36. package/dist/cjs/src/module.js +122 -7
  37. package/dist/cjs/src/module.js.map +1 -1
  38. package/dist/cjs/src/request-parser.d.ts +37 -0
  39. package/dist/cjs/src/request-parser.js +382 -0
  40. package/dist/cjs/src/request-parser.js.map +1 -0
  41. package/dist/cjs/src/router.d.ts +174 -75
  42. package/dist/cjs/src/router.js +469 -288
  43. package/dist/cjs/src/router.js.map +1 -1
  44. package/dist/cjs/src/static-serving.d.ts +32 -4
  45. package/dist/cjs/src/static-serving.js +115 -71
  46. package/dist/cjs/src/static-serving.js.map +1 -1
  47. package/dist/cjs/src/utils.js +2 -2
  48. package/dist/cjs/src/utils.js.map +1 -1
  49. package/dist/cjs/tsconfig.tsbuildinfo +1 -2916
  50. package/dist/esm/index.d.ts +14 -10
  51. package/dist/esm/index.js +14 -10
  52. package/dist/esm/index.js.map +1 -1
  53. package/dist/esm/src/controllers.d.ts +10 -3
  54. package/dist/esm/src/controllers.js +7 -3
  55. package/dist/esm/src/controllers.js.map +1 -1
  56. package/dist/esm/src/decorator.d.ts +144 -70
  57. package/dist/esm/src/decorator.js +272 -99
  58. package/dist/esm/src/decorator.js.map +1 -1
  59. package/dist/esm/src/filter.d.ts +49 -0
  60. package/dist/esm/src/filter.js +209 -0
  61. package/dist/esm/src/filter.js.map +1 -0
  62. package/dist/esm/src/http-app.d.ts +8 -0
  63. package/dist/esm/src/http-app.js +12 -0
  64. package/dist/esm/src/http-app.js.map +1 -0
  65. package/dist/esm/src/http.d.ts +131 -32
  66. package/dist/esm/src/http.js +504 -121
  67. package/dist/esm/src/http.js.map +1 -1
  68. package/dist/esm/src/kernel.d.ts +50 -9
  69. package/dist/esm/src/kernel.js +110 -100
  70. package/dist/esm/src/kernel.js.map +1 -1
  71. package/dist/esm/src/logger.d.ts +3 -3
  72. package/dist/esm/src/logger.js +7 -11
  73. package/dist/esm/src/logger.js.map +1 -1
  74. package/dist/esm/src/middleware.d.ts +61 -0
  75. package/dist/esm/src/middleware.js +90 -0
  76. package/dist/esm/src/middleware.js.map +1 -0
  77. package/dist/esm/src/model.d.ts +270 -4
  78. package/dist/esm/src/model.js +357 -1
  79. package/dist/esm/src/model.js.map +1 -1
  80. package/dist/esm/src/module.config.d.ts +149 -4
  81. package/dist/esm/src/module.config.js +9 -5
  82. package/dist/esm/src/module.config.js.map +1 -1
  83. package/dist/esm/src/module.d.ts +14 -32
  84. package/dist/esm/src/module.js +121 -7
  85. package/dist/esm/src/module.js.map +1 -1
  86. package/dist/esm/src/request-parser.d.ts +37 -0
  87. package/dist/esm/src/request-parser.js +372 -0
  88. package/dist/esm/src/request-parser.js.map +1 -0
  89. package/dist/esm/src/router.d.ts +174 -75
  90. package/dist/esm/src/router.js +463 -285
  91. package/dist/esm/src/router.js.map +1 -1
  92. package/dist/esm/src/static-serving.d.ts +32 -4
  93. package/dist/esm/src/static-serving.js +107 -65
  94. package/dist/esm/src/static-serving.js.map +1 -1
  95. package/dist/esm/src/utils.js +1 -0
  96. package/dist/esm/src/utils.js.map +1 -1
  97. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -2916
  98. package/index.ts +14 -10
  99. package/package.json +30 -19
  100. package/src/controllers.ts +4 -3
  101. package/src/decorator.ts +242 -104
  102. package/src/filter.ts +224 -0
  103. package/src/http-app.ts +11 -0
  104. package/src/http.ts +501 -100
  105. package/src/kernel.ts +128 -80
  106. package/src/logger.ts +4 -6
  107. package/src/middleware.ts +116 -0
  108. package/src/model.ts +516 -5
  109. package/src/module.config.ts +168 -5
  110. package/src/module.ts +120 -7
  111. package/src/request-parser.ts +430 -0
  112. package/src/router.ts +546 -317
  113. package/src/static-serving.ts +120 -50
  114. package/src/utils.ts +0 -2
  115. package/tsconfig.esm.json +7 -7
  116. package/tsconfig.json +18 -11
  117. package/tsconfig.spec.json +8 -0
  118. package/dist/cjs/tests/router.spec.d.ts +0 -1
  119. package/dist/cjs/tests/router.spec.js +0 -481
  120. package/dist/cjs/tests/router.spec.js.map +0 -1
  121. package/dist/esm/tests/router.spec.d.ts +0 -1
  122. package/dist/esm/tests/router.spec.js +0 -479
  123. package/dist/esm/tests/router.spec.js.map +0 -1
  124. package/tests/router.spec.ts +0 -347
package/index.ts CHANGED
@@ -1,10 +1,14 @@
1
- export * from './src/decorator';
2
- export * from './src/http';
3
- export * from './src/model';
4
- export * from './src/logger';
5
- export * from './src/router';
6
- export * from './src/static-serving';
7
- export * from './src/kernel';
8
- export * from './src/module';
9
- export * from './src/module.config';
10
- export * from './src/controllers';
1
+ export * from './src/decorator.js';
2
+ export * from './src/http.js';
3
+ export * from './src/http-app.js';
4
+ export * from './src/model.js';
5
+ export * from './src/logger.js';
6
+ export * from './src/router.js';
7
+ export * from './src/static-serving.js';
8
+ export * from './src/kernel.js';
9
+ export * from './src/module.js';
10
+ export * from './src/module.config.js';
11
+ export * from './src/controllers.js';
12
+ export * from './src/middleware.js';
13
+ export * from './src/filter.js';
14
+ export * from './src/utils.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepkit/http",
3
- "version": "1.0.1-alpha.16",
3
+ "version": "1.0.1-alpha.160",
4
4
  "description": "Deepkit HTTP server and router.",
5
5
  "type": "commonjs",
6
6
  "main": "./dist/cjs/index.js",
@@ -8,6 +8,7 @@
8
8
  "types": "./dist/cjs/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
+ "types": "./dist/cjs/index.d.ts",
11
12
  "require": "./dist/cjs/index.js",
12
13
  "default": "./dist/esm/index.js"
13
14
  }
@@ -30,34 +31,44 @@
30
31
  "@deepkit/stopwatch": "^1.0.1-alpha.13",
31
32
  "@deepkit/template": "^1.0.1-alpha.13",
32
33
  "@deepkit/type": "^1.0.1-alpha.13",
33
- "@deepkit/workflow": "^1.0.1-alpha.13",
34
- "reflect-metadata": "^0.1.13"
34
+ "@deepkit/workflow": "^1.0.1-alpha.13"
35
35
  },
36
36
  "dependencies": {
37
- "formidable": "^1.2.2",
38
- "send": "^0.17.1"
37
+ "formidable": "^3.5.2",
38
+ "qs": "^6.14.0",
39
+ "send": "^1.1.0"
39
40
  },
40
41
  "devDependencies": {
41
- "@deepkit/app": "^1.0.1-alpha.16",
42
- "@deepkit/core": "^1.0.1-alpha.16",
43
- "@deepkit/event": "^1.0.1-alpha.16",
44
- "@deepkit/injector": "^1.0.1-alpha.16",
45
- "@deepkit/logger": "^1.0.1-alpha.16",
46
- "@deepkit/stopwatch": "^1.0.1-alpha.16",
47
- "@deepkit/template": "^1.0.1-alpha.16",
48
- "@deepkit/type": "^1.0.1-alpha.16",
49
- "@deepkit/workflow": "^1.0.1-alpha.16",
50
- "@types/node": "14.14.28",
51
- "@types/send": "^0.14.6",
52
- "reflect-metadata": "^0.1.13"
42
+ "@deepkit/app": "^1.0.1-alpha.160",
43
+ "@deepkit/core": "^1.0.1-alpha.158",
44
+ "@deepkit/event": "^1.0.1-alpha.160",
45
+ "@deepkit/injector": "^1.0.1-alpha.160",
46
+ "@deepkit/logger": "^1.0.1-alpha.160",
47
+ "@deepkit/stopwatch": "^1.0.1-alpha.160",
48
+ "@deepkit/template": "^1.0.1-alpha.160",
49
+ "@deepkit/type": "^1.0.1-alpha.160",
50
+ "@deepkit/workflow": "^1.0.1-alpha.160",
51
+ "@types/formidable": "^3.4.5",
52
+ "@types/node": "^22.13.1",
53
+ "@types/send": "^0.14.6"
53
54
  },
54
55
  "jest": {
56
+ "testEnvironment": "node",
55
57
  "transform": {
56
- "^.+\\.(ts|tsx)$": "ts-jest"
58
+ "^.+\\.(ts|tsx)$": [
59
+ "ts-jest",
60
+ {
61
+ "tsconfig": "<rootDir>/tsconfig.spec.json"
62
+ }
63
+ ]
57
64
  },
65
+ "moduleNameMapper": {
66
+ "(.+)\\.js": "$1"
67
+ },
68
+ "resolver": "../../jest-resolver.js",
58
69
  "testMatch": [
59
70
  "**/tests/**/*.spec.ts"
60
71
  ]
61
72
  },
62
- "gitHead": "0f3c1766cbd9d1ac489d8f396a52f21f1a3da6ff"
73
+ "gitHead": "52a703a1f6747c59e77cbf44d80d3bc5b1424ee6"
63
74
  }
@@ -1,10 +1,11 @@
1
1
  import { ClassType } from '@deepkit/core';
2
+ import { InjectorModule } from '@deepkit/injector';
2
3
 
3
4
  export class HttpControllers {
4
- constructor(public readonly controllers: ClassType[]) {
5
+ constructor(public readonly controllers: {controller: ClassType, module: InjectorModule<any>}[] = []) {
5
6
  }
6
7
 
7
- public add(controller: ClassType) {
8
- this.controllers.push(controller);
8
+ public add(controller: ClassType, module: InjectorModule<any>) {
9
+ this.controllers.push({controller, module});
9
10
  }
10
11
  }
package/src/decorator.ts CHANGED
@@ -8,21 +8,48 @@
8
8
  * You should have received a copy of the MIT License along with this program.
9
9
  */
10
10
 
11
- import { ClassType } from '@deepkit/core';
12
- import { ClassDecoratorResult, createClassDecoratorContext, createPropertyDecoratorContext, getClassSchema, mergeDecorator, PropertyDecoratorResult } from '@deepkit/type';
13
- import { join } from 'path';
14
-
15
- export interface ControllerOptions {
16
- name: string;
11
+ import { ClassType, isClass, urlJoin } from '@deepkit/core';
12
+ import {
13
+ ClassDecoratorResult,
14
+ createClassDecoratorContext,
15
+ createPropertyDecoratorContext,
16
+ DecoratorAndFetchSignature,
17
+ DualDecorator,
18
+ ExtractApiDataType,
19
+ ExtractClass,
20
+ isDecoratorContext,
21
+ mergeDecorator,
22
+ PropertyDecoratorFn,
23
+ ReceiveType,
24
+ resolveReceiveType,
25
+ SerializationOptions,
26
+ Serializer,
27
+ Type,
28
+ UnionToIntersection
29
+ } from '@deepkit/type';
30
+ import { RouteParameterResolver } from './router.js';
31
+ import { httpMiddleware, HttpMiddleware, HttpMiddlewareConfig, HttpMiddlewareFn } from './middleware.js';
32
+
33
+ type HttpActionMiddleware = (() => HttpMiddlewareConfig) | ClassType<HttpMiddleware> | HttpMiddlewareFn;
34
+
35
+ function isMiddlewareClassTypeOrFn(v: HttpActionMiddleware): v is ClassType<HttpMiddleware> | HttpMiddlewareFn {
36
+ return isClass(v) || !isDecoratorContext(httpMiddleware, v);
17
37
  }
18
38
 
19
- class HttpController {
39
+ export class HttpController {
20
40
  baseUrl: string = '';
21
- protected actions = new Set<HttpAction>();
41
+ actions = new Set<HttpAction>();
22
42
  groups: string[] = [];
23
43
 
44
+ middlewares: (() => HttpMiddlewareConfig)[] = [];
45
+
46
+ resolverForToken: Map<any, ClassType> = new Map();
47
+ resolverForParameterName: Map<string, ClassType> = new Map();
48
+
49
+ private actionsProcessed = new Set<HttpAction>();
50
+
24
51
  getUrl(action: HttpAction): string {
25
- return join('/', this.baseUrl, action.path);
52
+ return urlJoin('/', this.baseUrl, action.path);
26
53
  }
27
54
 
28
55
  addAction(action: HttpAction) {
@@ -30,15 +57,25 @@ class HttpController {
30
57
  }
31
58
 
32
59
  getActions(): Set<HttpAction> {
33
- for (const a of this.actions) {
34
- for (const g of this.groups) {
35
- if (!a.groups.includes(g)) a.groups.push(g);
60
+ for (const action of this.actions) {
61
+ if (!this.actionsProcessed.has(action)) {
62
+ action.groups = [
63
+ ...this.groups,
64
+ ...action.groups.filter((g) => !this.groups.includes(g)),
65
+ ];
66
+ this.actionsProcessed.add(action);
36
67
  }
37
68
  }
38
69
 
39
70
  return this.actions;
40
71
  }
41
72
 
73
+ removeAction(methodName: string): void {
74
+ const action = this.getAction(methodName);
75
+ this.actions.delete(action);
76
+ this.actionsProcessed.delete(action);
77
+ }
78
+
42
79
  getAction(methodName: string): HttpAction {
43
80
  for (const a of this.getActions()) {
44
81
  if (a.methodName === methodName) return a;
@@ -49,7 +86,7 @@ class HttpController {
49
86
 
50
87
  export class HttpActionParameter {
51
88
  name: string = '';
52
- type?: 'body' | 'query';
89
+ type?: 'body' | 'query' | 'queries';
53
90
 
54
91
  /**
55
92
  * undefined = propertyName, '' === root, else given path
@@ -63,47 +100,120 @@ export class HttpAction {
63
100
  description: string = '';
64
101
  category: string = '';
65
102
  path: string = '';
66
- httpMethod: string = 'GET';
103
+ httpMethods: string[] = [];
67
104
  methodName: string = '';
68
105
  groups: string[] = [];
106
+ serializer?: Serializer;
107
+ middlewares: (() => HttpMiddlewareConfig)[] = [];
108
+ serializationOptions?: SerializationOptions;
69
109
 
70
- parameterRegularExpressions: { [name: string]: any } = {};
71
-
72
- /**
73
- * This is only filled when the user used @http.body() for example on an method argument.
74
- */
75
- parameters: { [name: string]: HttpActionParameter } = {};
110
+ resolverForToken: Map<any, ClassType> = new Map();
111
+ resolverForParameterName: Map<string, ClassType> = new Map();
76
112
 
77
113
  /**
78
114
  * An arbitrary data container the user can use to store app specific settings/values.
79
115
  */
80
116
  data = new Map<any, any>();
81
117
 
82
- throws: { errorType: ClassType, message?: string }[] = [];
118
+ responses: { statusCode: number, description: string, type?: Type }[] = [];
83
119
  }
84
120
 
85
- export class HttpDecorator {
121
+ export class HttpControllerDecorator {
86
122
  t = new HttpController;
87
123
 
88
124
  controller(baseUrl: string = '') {
89
125
  this.t.baseUrl = baseUrl;
90
126
  }
91
127
 
92
- groupAll(...group: string[]) {
128
+ group(...group: string[]) {
93
129
  this.t.groups.push(...group);
94
130
  }
95
131
 
132
+ middleware(...middlewares: HttpActionMiddleware[]) {
133
+ this.t.middlewares.push(...middlewares.map(v => isMiddlewareClassTypeOrFn(v) ? httpMiddleware.for(v) : v));
134
+ }
135
+
136
+ /**
137
+ * Adds a parameter resolver for parameters based on the class type. Use .resolveParameterByName() for name-based resolving.
138
+ *
139
+ * ```typescript
140
+ *
141
+ * class UserResolver {
142
+ * resolve(context: RouteParameterResolverContext): any | Promise<any> {
143
+ * return new User();
144
+ * }
145
+ * }
146
+ *
147
+ * @http.resolveParameter(User, UserResolver)
148
+ * class MyController {
149
+ *
150
+ * @http.GET()
151
+ * myAction(user: User) {
152
+ * }
153
+ * }
154
+ *
155
+ * new App({providers: [UserResolver]}).run();
156
+ * ```
157
+ */
158
+ resolveParameter(classType: ClassType | string | any, resolver: ClassType<RouteParameterResolver>) {
159
+ this.t.resolverForToken.set(classType, resolver);
160
+ }
161
+
162
+ /**
163
+ * Adds a parameter resolver for parameters based on its name. Use .resolveParameter() for class-based resolving.
164
+ *
165
+ * ```typescript
166
+ *
167
+ * class UserResolver {
168
+ * resolve(context: RouteParameterResolverContext): any | Promise<any> {
169
+ * return new User();
170
+ * }
171
+ * }
172
+ *
173
+ * @http.resolveParameterByName('user', UserResolver)
174
+ * class MyController {
175
+ *
176
+ * @http.GET()
177
+ * myAction(user: User) {
178
+ * }
179
+ * }
180
+ *
181
+ * new App({providers: [UserResolver]}).run();
182
+ * ```
183
+ */
184
+ resolveParameterByName(name: string, resolver: ClassType<RouteParameterResolver>) {
185
+ this.t.resolverForParameterName.set(name, resolver);
186
+ }
187
+
96
188
  setAction(action: HttpAction) {
97
189
  this.t.addAction(action);
98
190
  }
99
191
  }
100
192
 
101
- export const httpClass: ClassDecoratorResult<typeof HttpDecorator> = createClassDecoratorContext(HttpDecorator);
193
+ export const httpClass: ClassDecoratorResult<typeof HttpControllerDecorator> = createClassDecoratorContext(HttpControllerDecorator);
194
+
195
+ export function getActions(target: ClassType): HttpAction[] {
196
+ const parent = Object.getPrototypeOf(target);
197
+ const results: HttpAction[] = parent ? getActions(parent) : [];
198
+
199
+ const config = httpClass._fetch(target);
200
+ if (config) {
201
+ for (const action of config.getActions()) {
202
+ const existing = results.findIndex(v => v.methodName === action.methodName);
203
+ if (existing !== -1) results.splice(existing, 1);
204
+ results.push(action);
205
+ }
206
+ }
207
+
208
+ return results;
209
+ }
102
210
 
103
211
  export class HttpActionDecorator {
104
212
  t = new HttpAction;
105
213
 
106
- onDecorator(target: ClassType, property: string) {
214
+ onDecorator(target: ClassType, property: string | undefined, parameterIndexOrDescriptor?: any) {
215
+ if (!property) return;
216
+ if (target === Object) return;
107
217
  this.t.methodName = property;
108
218
  httpClass.setAction(this.t)(target);
109
219
  }
@@ -112,14 +222,22 @@ export class HttpActionDecorator {
112
222
  this.t.name = name;
113
223
  }
114
224
 
115
- setParameter(name: string, parameter: HttpActionParameter) {
116
- this.t.parameters[name] = parameter;
117
- }
118
-
119
225
  description(description: string) {
120
226
  this.t.description = description;
121
227
  }
122
228
 
229
+ serialization(options: SerializationOptions) {
230
+ this.t.serializationOptions = options;
231
+ }
232
+
233
+ serializer(serializer: Serializer) {
234
+ this.t.serializer = serializer;
235
+ }
236
+
237
+ middleware(...middlewares: HttpActionMiddleware[]) {
238
+ this.t.middlewares.push(...middlewares.map(v => isMiddlewareClassTypeOrFn(v) ? httpMiddleware.for(v) : v));
239
+ }
240
+
123
241
  /**
124
242
  * Allows to change the HttpAction object and composite multiple properties into one function.
125
243
  *
@@ -154,7 +272,7 @@ export class HttpActionDecorator {
154
272
  * assets() {}
155
273
  * }
156
274
  * ```
157
- */
275
+ */
158
276
  data(name: string, value: any) {
159
277
  this.t.data.set(name, value);
160
278
  }
@@ -168,124 +286,144 @@ export class HttpActionDecorator {
168
286
  }
169
287
 
170
288
  GET(path: string = '') {
171
- this.t.httpMethod = 'GET';
172
- this.t.path = path;
289
+ this.t.httpMethods.push('GET');
290
+ if (path) this.t.path = path;
291
+ }
292
+
293
+ HEAD(path: string = '') {
294
+ this.t.httpMethods.push('HEAD');
295
+ if (path) this.t.path = path;
173
296
  }
174
297
 
175
298
  POST(path: string = '') {
176
- this.t.httpMethod = 'POST';
177
- this.t.path = path;
299
+ this.t.httpMethods.push('POST');
300
+ if (path) this.t.path = path;
178
301
  }
179
302
 
180
303
  PUT(path: string = '') {
181
- this.t.httpMethod = 'PUT';
182
- this.t.path = path;
304
+ this.t.httpMethods.push('PUT');
305
+ if (path) this.t.path = path;
183
306
  }
184
307
 
185
308
  DELETE(path: string = '') {
186
- this.t.httpMethod = 'DELETE';
187
- this.t.path = path;
309
+ this.t.httpMethods.push('DELETE');
310
+ if (path) this.t.path = path;
188
311
  }
189
312
 
190
- ANY(path: string = '') {
191
- this.t.httpMethod = 'ANY';
192
- this.t.path = path;
313
+ OPTIONS(path: string = '') {
314
+ this.t.httpMethods.push('OPTIONS');
315
+ if (path) this.t.path = path;
193
316
  }
194
317
 
195
- throws(errorType: ClassType, message?: string) {
196
- this.t.throws.push({ errorType, message });
318
+ TRACE(path: string = '') {
319
+ this.t.httpMethods.push('TRACE');
320
+ if (path) this.t.path = path;
197
321
  }
198
322
 
199
- regexp(parameterName: string, regex: any) {
200
- this.t.parameterRegularExpressions[parameterName] = regex;
323
+ PATCH(path: string = '') {
324
+ this.t.httpMethods.push('PATCH');
325
+ if (path) this.t.path = path;
201
326
  }
202
- }
203
327
 
204
- class HttpActionParameterDecorator {
205
- t = new HttpActionParameter();
206
-
207
- onDecorator(target: ClassType, propertyName?: string, parameterIndex?: number) {
208
- if (!propertyName) throw new Error('@http action parameter decorator can only be used on method arguments.');
209
- if (parameterIndex === undefined) throw new Error('@http action parameter decorator can only be used on method arguments.');
210
- const schema = getClassSchema(target);
211
- const property = schema.getMethodProperties(propertyName)[parameterIndex];
212
- this.t.name = property.name;
213
- if (this.t.typePath === undefined) {
214
- this.t.typePath = property.name;
215
- }
216
- httpAction.setParameter(property.name, this.t)(target.prototype, propertyName);
328
+ ANY(path: string = '') {
329
+ this.t.httpMethods = [];
330
+ if (path) this.t.path = path;
217
331
  }
218
332
 
219
333
  /**
220
- * Marks the argument as body parameter. Data from the client sent in the body
221
- * will be tried to parsed (JSON/form data) and deserialized to the defined type.
222
- * Make sure the class type as a schema assigned.
334
+ * Adds additional information about what HTTP status codes are available in this route.
335
+ * You can add additionally a description and a response body type.
336
+ *
337
+ * The type is used for serialization for responses with the given statusCode.
338
+ *
339
+ * This information is available in Deepkit API console.
223
340
  *
224
- * @example
225
341
  * ```typescript
226
- * class MyActionBody {
227
- * @t name!: string;
342
+ *
343
+ * http.GET().response<boolean>(200, 'All ok')
344
+ *
345
+ * interface User {
346
+ * username: string;
228
347
  * }
348
+ * http.GET().response<User>(200, 'User object')
229
349
  *
230
- * class Controller {
231
- * @http.GET()
232
- * myAction(@http.body() body: MyActionBody) {
233
- * console.log('body', body.name);
234
- * }
350
+ * interface HttpErrorMessage {
351
+ * error: string;
235
352
  * }
353
+ * http.GET().response<HttpErrorMessage>(500, 'Error')
236
354
  * ```
237
355
  */
238
- body() {
239
- this.t.type = 'body';
240
- this.t.typePath = ''; //root
241
- }
242
-
243
- query(path?: string) {
244
- this.t.typePath = path; //undefined === propertyName
245
- this.t.type = 'query';
356
+ response<T>(statusCode: number, description: string = '', type?: ReceiveType<T>) {
357
+ this.t.responses.push({ statusCode, description, type: type ? resolveReceiveType(type) : undefined });
246
358
  }
247
359
 
248
- get optional() {
249
- this.t.optional = true;
250
- return;
360
+ /**
361
+ * Adds a parameter resolver for parameters based on the class type. Use .resolveParameterByName() for name-based resolving.
362
+ *
363
+ * ```typescript
364
+ *
365
+ * class UserResolver {
366
+ * resolve(context: RouteParameterResolverContext): any | Promise<any> {
367
+ * return new User();
368
+ * }
369
+ * }
370
+ *
371
+ * class MyController {
372
+ * @http.GET()
373
+ * @http.resolveParameter(User, UserResolver)
374
+ * myAction(user: User) {
375
+ * }
376
+ * }
377
+ *
378
+ * new App({providers: [UserResolver]}).run();
379
+ * ```
380
+ */
381
+ resolveParameter(classType: ClassType | string | any, resolver: ClassType<RouteParameterResolver>) {
382
+ this.t.resolverForToken.set(classType, resolver);
251
383
  }
252
384
 
253
385
  /**
254
- * Marks the argument as query parameter. Data from the query string is parsed
255
- * and deserialized to the defined type.
256
- * Define a `path` if you want to parse a subset of the query string only.
257
- *
258
- * Note: Make sure the defined parameter type has optional properties,
259
- * otherwise it's always required to pass a query string.
386
+ * Adds a parameter resolver for parameters based on its name. Use .resolveParameter() for class-based resolving.
260
387
  *
261
- * @example
262
388
  * ```typescript
263
- * class MyActionQueries {
264
- * @t.optional name?: string;
389
+ *
390
+ * class UserResolver {
391
+ * resolve(context: RouteParameterResolverContext): any | Promise<any> {
392
+ * return new User();
393
+ * }
265
394
  * }
266
395
  *
267
- * class Controller {
268
- * @http.GET('my-action')
269
- * myAction(@http.queries() query: MyActionQueries) {
270
- * console.log('query', query.name);
396
+ * class MyController {
397
+ * @http.GET()
398
+ * @http.resolveParameterByName('user', UserResolver)
399
+ * myAction(user: User) {
271
400
  * }
272
401
  * }
273
402
  *
274
- * // Open via, e.g.
275
- * // -> /my-action?name=Peter
403
+ * new App({providers: [UserResolver]}).run();
276
404
  * ```
277
- *
278
405
  */
279
- queries(path: string = '') {
280
- this.t.typePath = path; //'' === root
281
- this.t.type = 'query';
406
+ resolveParameterByName(name: string, resolver: ClassType<RouteParameterResolver>) {
407
+ this.t.resolverForParameterName.set(name, resolver);
282
408
  }
283
409
  }
284
410
 
285
- createPropertyDecoratorContext(HttpActionDecorator);
411
+ //this workaround is necessary since generic functions (necessary for response<T>) are lost during a mapped type and changed ReturnType
412
+ type HttpActionFluidDecorator<T, D extends Function> = {
413
+ [name in keyof T]: name extends 'response' ? <T2>(statusCode: number, description?: string, type?: ReceiveType<T2>) => D & HttpActionFluidDecorator<T, D>
414
+ : T[name] extends (...args: infer K) => any ? (...args: K) => D & HttpActionFluidDecorator<T, D>
415
+ : D & HttpActionFluidDecorator<T, D> & { _data: ExtractApiDataType<T> };
416
+ };
417
+ type HttpActionPropertyDecoratorResult =
418
+ HttpActionFluidDecorator<ExtractClass<typeof HttpActionDecorator>, PropertyDecoratorFn>
419
+ & DecoratorAndFetchSignature<typeof HttpActionDecorator, PropertyDecoratorFn>;
420
+
421
+ export const httpAction: HttpActionPropertyDecoratorResult = createPropertyDecoratorContext(HttpActionDecorator);
286
422
 
287
- export const httpAction: PropertyDecoratorResult<typeof HttpActionDecorator> = createPropertyDecoratorContext(HttpActionDecorator);
423
+ //this workaround is necessary since generic functions are lost during a mapped type and changed ReturnType
424
+ type HttpMerge<U> = { [K in keyof U]: K extends 'response' ? <T2>(statusCode: number, description?: string, type?: ReceiveType<T2>) => PropertyDecoratorFn & U : U[K] extends ((...a: infer A) => infer R) ? R extends DualDecorator ? (...a: A) => PropertyDecoratorFn & R & U : (...a: A) => R : never };
425
+ type MergedHttp<T extends any[]> = HttpMerge<Omit<UnionToIntersection<T[number]>, '_fetch' | 't'>>
288
426
 
289
- export const httpActionParameter: PropertyDecoratorResult<typeof HttpActionParameterDecorator> = createPropertyDecoratorContext(HttpActionParameterDecorator);
427
+ export type HttpDecorator = PropertyDecoratorFn & HttpActionFluidDecorator<HttpActionDecorator, PropertyDecoratorFn>;
290
428
 
291
- export const http = mergeDecorator(httpClass, httpAction, httpActionParameter);
429
+ export const http: MergedHttp<[typeof httpClass, typeof httpAction]> = mergeDecorator(httpClass, httpAction) as any as MergedHttp<[typeof httpClass, typeof httpAction]>;