@deepkit/http 1.0.1-alpha.98 → 1.0.1
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/CHANGELOG.md +240 -0
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/src/controllers.js +1 -1
- package/dist/cjs/src/decorator.d.ts +7 -6
- package/dist/cjs/src/decorator.js +55 -42
- package/dist/cjs/src/decorator.js.map +1 -1
- package/dist/cjs/src/filter.js +4 -4
- package/dist/cjs/src/filter.js.map +1 -1
- package/dist/cjs/src/http-app.d.ts +8 -0
- package/dist/cjs/src/http-app.js +16 -0
- package/dist/cjs/src/http-app.js.map +1 -0
- package/dist/cjs/src/http.d.ts +2 -4
- package/dist/cjs/src/http.js +63 -51
- package/dist/cjs/src/http.js.map +1 -1
- package/dist/cjs/src/kernel.d.ts +43 -3
- package/dist/cjs/src/kernel.js +58 -8
- package/dist/cjs/src/kernel.js.map +1 -1
- package/dist/cjs/src/logger.js +2 -2
- package/dist/cjs/src/logger.js.map +1 -1
- package/dist/cjs/src/middleware.d.ts +2 -2
- package/dist/cjs/src/middleware.js +5 -6
- package/dist/cjs/src/middleware.js.map +1 -1
- package/dist/cjs/src/model.d.ts +154 -31
- package/dist/cjs/src/model.js +81 -41
- package/dist/cjs/src/model.js.map +1 -1
- package/dist/cjs/src/module.config.d.ts +67 -2
- package/dist/cjs/src/module.config.js +2 -2
- package/dist/cjs/src/module.config.js.map +1 -1
- package/dist/cjs/src/module.js +18 -13
- package/dist/cjs/src/module.js.map +1 -1
- package/dist/cjs/src/request-parser.d.ts +2 -1
- package/dist/cjs/src/request-parser.js +138 -60
- package/dist/cjs/src/request-parser.js.map +1 -1
- package/dist/cjs/src/router.d.ts +12 -6
- package/dist/cjs/src/router.js +40 -28
- package/dist/cjs/src/router.js.map +1 -1
- package/dist/cjs/src/static-serving.d.ts +4 -0
- package/dist/cjs/src/static-serving.js +44 -40
- package/dist/cjs/src/static-serving.js.map +1 -1
- package/dist/cjs/src/utils.js +1 -2
- package/dist/cjs/src/utils.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/src/controllers.js +2 -2
- package/dist/esm/src/controllers.js.map +1 -1
- package/dist/esm/src/decorator.d.ts +7 -6
- package/dist/esm/src/decorator.js +66 -54
- package/dist/esm/src/decorator.js.map +1 -1
- package/dist/esm/src/filter.js +5 -5
- package/dist/esm/src/filter.js.map +1 -1
- package/dist/esm/src/http-app.d.ts +8 -0
- package/dist/esm/src/http-app.js +12 -0
- package/dist/esm/src/http-app.js.map +1 -0
- package/dist/esm/src/http.d.ts +2 -4
- package/dist/esm/src/http.js +65 -53
- package/dist/esm/src/http.js.map +1 -1
- package/dist/esm/src/kernel.d.ts +43 -3
- package/dist/esm/src/kernel.js +60 -10
- package/dist/esm/src/kernel.js.map +1 -1
- package/dist/esm/src/logger.js +2 -2
- package/dist/esm/src/logger.js.map +1 -1
- package/dist/esm/src/middleware.d.ts +2 -2
- package/dist/esm/src/middleware.js +6 -7
- package/dist/esm/src/middleware.js.map +1 -1
- package/dist/esm/src/model.d.ts +154 -31
- package/dist/esm/src/model.js +59 -31
- package/dist/esm/src/model.js.map +1 -1
- package/dist/esm/src/module.config.d.ts +67 -2
- package/dist/esm/src/module.config.js +2 -2
- package/dist/esm/src/module.config.js.map +1 -1
- package/dist/esm/src/module.js +20 -15
- package/dist/esm/src/module.js.map +1 -1
- package/dist/esm/src/request-parser.d.ts +2 -1
- package/dist/esm/src/request-parser.js +139 -61
- package/dist/esm/src/request-parser.js.map +1 -1
- package/dist/esm/src/router.d.ts +12 -6
- package/dist/esm/src/router.js +46 -36
- package/dist/esm/src/router.js.map +1 -1
- package/dist/esm/src/static-serving.d.ts +4 -0
- package/dist/esm/src/static-serving.js +41 -39
- package/dist/esm/src/static-serving.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/index.ts +1 -0
- package/package.json +23 -16
- package/src/decorator.ts +20 -2
- package/src/http-app.ts +11 -0
- package/src/http.ts +30 -10
- package/src/kernel.ts +81 -9
- package/src/middleware.ts +1 -1
- package/src/model.ts +175 -16
- package/src/module.config.ts +75 -1
- package/src/module.ts +20 -12
- package/src/request-parser.ts +147 -62
- package/src/router.ts +36 -10
- package/src/static-serving.ts +49 -38
- package/tsconfig.json +9 -3
- package/tsconfig.spec.json +8 -0
package/src/middleware.ts
CHANGED
package/src/model.ts
CHANGED
|
@@ -12,8 +12,8 @@ import { IncomingMessage, OutgoingHttpHeader, OutgoingHttpHeaders, ServerRespons
|
|
|
12
12
|
import { UploadedFile } from './router.js';
|
|
13
13
|
import * as querystring from 'querystring';
|
|
14
14
|
import { Writable } from 'stream';
|
|
15
|
-
import {
|
|
16
|
-
import { asyncOperation, isArray } from '@deepkit/core';
|
|
15
|
+
import { ReflectionKind, Type, typeAnnotation, ValidationErrorItem } from '@deepkit/type';
|
|
16
|
+
import { asyncOperation, isArray, TypeAnnotation } from '@deepkit/core';
|
|
17
17
|
|
|
18
18
|
export class HttpResponse extends ServerResponse {
|
|
19
19
|
status(code: number) {
|
|
@@ -103,7 +103,7 @@ export class BodyValidationError {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
getErrorMessageForPath(path: string): string {
|
|
106
|
-
return this.getErrorsForPath(path).map(v => v.
|
|
106
|
+
return this.getErrorsForPath(path).map(v => v.toString()).join(', ');
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
@@ -116,12 +116,151 @@ export class ValidatedBody<T> {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Marks a parameter as HTTP body and reads the value from the request body.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* class Controller {
|
|
125
|
+
* @http.GET('/api')
|
|
126
|
+
* route(body: HttpBody<{username: string}>) {
|
|
127
|
+
* //body is {username: string} and required
|
|
128
|
+
* //could also reference an interface or type alias via `HttpBody<MyInterface>`
|
|
129
|
+
* }
|
|
130
|
+
* }
|
|
131
|
+
*
|
|
132
|
+
* // curl /api -d '{"username":"Peter"}'
|
|
133
|
+
*/
|
|
134
|
+
export type HttpBody<T> = T & TypeAnnotation<'httpBody'>;
|
|
135
|
+
export type HttpBodyValidation<T> = ValidatedBody<T> & TypeAnnotation<'httpBodyValidation'>;
|
|
136
|
+
|
|
137
|
+
export interface HttpRequestParserOptions {
|
|
138
|
+
withPath?: boolean;
|
|
139
|
+
withBody?: boolean;
|
|
140
|
+
withQuery?: boolean;
|
|
141
|
+
withHeader?: boolean;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Delays the parsing of the path/body/query/header to the very last moment, when the parameter is actually used.
|
|
146
|
+
*
|
|
147
|
+
* If no options are provided, the parser will receive data from path, header, body, and query, in this order.
|
|
148
|
+
* This basically allows to fetch data from all possible HTTP sources in one go.
|
|
149
|
+
*
|
|
150
|
+
* You can disable various sources by providing the options, e.g. `{withBody: false}` to disable body parsing.
|
|
151
|
+
* Or `{withQuery: false}` to disable query parsing. Or `{withHeader: false}` to disable header parsing.
|
|
152
|
+
* To only parse the body, use `{withQuery: false, withHeader: false}`.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```typescript
|
|
156
|
+
* async route(parser: HttpRequestParser<{authorization: string}>) {
|
|
157
|
+
* const data = await parser();
|
|
158
|
+
* console.log(data.authorization);
|
|
159
|
+
* }
|
|
160
|
+
* ```
|
|
161
|
+
*
|
|
162
|
+
* Note that the parsers is based on all defined parameters (e.g. `userId: HttpQuery<string>` => {userId: string}),
|
|
163
|
+
* and then starts from there applying header, body, and then query values.
|
|
164
|
+
* This means you also get access to defined path parameters, like:
|
|
165
|
+
*
|
|
166
|
+
* ```typescript
|
|
167
|
+
* @http.GET('teams/:teamId')
|
|
168
|
+
* async route(teamId: string) {
|
|
169
|
+
* //teamId is string
|
|
170
|
+
* }
|
|
171
|
+
*
|
|
172
|
+
* httpWorkflow.onController.listen((event, parser: HttpRequestParser<{teamId: string}>) => {
|
|
173
|
+
* const data = await parser();
|
|
174
|
+
* console.log(data.teamId);
|
|
175
|
+
* });
|
|
176
|
+
* ```
|
|
177
|
+
*
|
|
178
|
+
* HttpRequestParser is necessary in event listeners, since they are instantiated synchronously,
|
|
179
|
+
* but body is parsed asynchronously. So use in event listeners HttpRequestParser instead of HttpBody.
|
|
180
|
+
*/
|
|
181
|
+
export type HttpRequestParser<T> = ((options?: HttpRequestParserOptions) => Promise<T>) & TypeAnnotation<'httpRequestParser', T>;
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Marks a parameter as HTTP path and reads the value from the request path.
|
|
185
|
+
* This is normally not requires since the parameter name automatically maps to the path parameter,
|
|
186
|
+
* but in case of requesting the path parameter in for example listeners, this is required.
|
|
187
|
+
* The name option can be used to change the parameter name.
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```typescript
|
|
191
|
+
* app.listen(httpWorkflow.onController, (event, request: HttpRequest, groupId: HttpPath<number>) => {
|
|
192
|
+
* console.log(groupId); //123
|
|
193
|
+
* });
|
|
194
|
+
*
|
|
195
|
+
* class Controller {
|
|
196
|
+
* @http.GET('/api/:groupId')
|
|
197
|
+
* route(groupId: number) {
|
|
198
|
+
* //groupId is number and required
|
|
199
|
+
* //same as `groupId: HttpPath<number>`
|
|
200
|
+
* }
|
|
201
|
+
* }
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
export type HttpPath<T, Options extends { name?: string } = {}> = T & TypeAnnotation<'httpPath', Options>;
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Marks a parameter as HTTP header and reads the value from the request header.
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```typescript
|
|
211
|
+
* class Controller {
|
|
212
|
+
* @http.GET('/api')
|
|
213
|
+
* route(authorization: HttpHeader<string>) {
|
|
214
|
+
* //authorization is string and required
|
|
215
|
+
* //use `authorization?: HttpHeader<string>` to make it optional
|
|
216
|
+
* }
|
|
217
|
+
* }
|
|
218
|
+
*
|
|
219
|
+
* // curl /api -H 'Authorization: 123'
|
|
220
|
+
* ```
|
|
221
|
+
*
|
|
222
|
+
* To change the header name, use `param: HttpHeader<string, {name: 'X-My-Header'}>`.
|
|
223
|
+
*/
|
|
224
|
+
export type HttpHeader<T, Options extends { name?: string } = {}> = T & TypeAnnotation<'httpHeader', Options>;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Marks a parameter as HTTP query and reads the value from the request query string.
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```typescript
|
|
231
|
+
* class Controller {
|
|
232
|
+
* @http.GET('/api')
|
|
233
|
+
* route(limit: HttpQuery<number>) {
|
|
234
|
+
* //limit is number and required
|
|
235
|
+
* //use `limit?: HttpQuery<number>` to make it optional
|
|
236
|
+
* }
|
|
237
|
+
* }
|
|
238
|
+
*
|
|
239
|
+
* // curl /api?limit=10
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
export type HttpQuery<T, Options extends { name?: string } = {}> = T & TypeAnnotation<'httpQuery', Options>;
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Marks a parameter as HTTP query objects and reads multiple values from the request query string into an object.
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```typescript
|
|
249
|
+
* interface Query {
|
|
250
|
+
* limit?: number;
|
|
251
|
+
* offset?: number;
|
|
252
|
+
* sort?: 'asc' | 'desc';
|
|
253
|
+
* }
|
|
254
|
+
*
|
|
255
|
+
* class Controller {
|
|
256
|
+
* @http.GET('/api')
|
|
257
|
+
* route(query: HttpQueries<Query>) {
|
|
258
|
+
* //query is an object containing limit, offset, and sort
|
|
259
|
+
* }
|
|
260
|
+
*
|
|
261
|
+
* // curl /api?limit=10&offset=20&sort=asc
|
|
262
|
+
*/
|
|
263
|
+
export type HttpQueries<T, Options extends { name?: string } = {}> = T & TypeAnnotation<'httpQueries', Options>;
|
|
125
264
|
|
|
126
265
|
/**
|
|
127
266
|
* For all parameters used in the URL path, a regular expression of /[^/]+/ is used. To change that, use getRegExp.
|
|
@@ -137,13 +276,13 @@ export type HttpQueries<T, Options extends { name?: string } = {}> = T & { __met
|
|
|
137
276
|
* }
|
|
138
277
|
* ```
|
|
139
278
|
*/
|
|
140
|
-
export type HttpRegExp<T, Pattern extends string | RegExp> = T &
|
|
279
|
+
export type HttpRegExp<T, Pattern extends string | RegExp> = T & TypeAnnotation<'httpRegExp', Pattern>;
|
|
141
280
|
|
|
142
281
|
export function getRegExp(type: Type): string | RegExp | undefined {
|
|
143
|
-
const options =
|
|
144
|
-
if (!options
|
|
145
|
-
if (options
|
|
146
|
-
if (options
|
|
282
|
+
const options = typeAnnotation.getType(type, 'httpRegExp');
|
|
283
|
+
if (!options) return;
|
|
284
|
+
if (options.kind === ReflectionKind.literal && 'string' === typeof options.literal) return options.literal;
|
|
285
|
+
if (options.kind === ReflectionKind.literal && options.literal instanceof RegExp) return options.literal;
|
|
147
286
|
return;
|
|
148
287
|
}
|
|
149
288
|
|
|
@@ -226,8 +365,12 @@ Content-Type: application/json\r
|
|
|
226
365
|
return this;
|
|
227
366
|
}
|
|
228
367
|
|
|
229
|
-
query(query: any): this {
|
|
230
|
-
|
|
368
|
+
query(query: any | string): this {
|
|
369
|
+
if ('string' === typeof query) {
|
|
370
|
+
this.queryPath = query;
|
|
371
|
+
} else {
|
|
372
|
+
this.queryPath = querystring.stringify(query);
|
|
373
|
+
}
|
|
231
374
|
return this;
|
|
232
375
|
}
|
|
233
376
|
}
|
|
@@ -240,6 +383,8 @@ export class HttpRequest extends IncomingMessage {
|
|
|
240
383
|
|
|
241
384
|
public uploadedFiles: { [name: string]: UploadedFile } = {};
|
|
242
385
|
|
|
386
|
+
public throwErrorOnNotFound: boolean = false;
|
|
387
|
+
|
|
243
388
|
/**
|
|
244
389
|
* The router sets the body when it was read.
|
|
245
390
|
*/
|
|
@@ -299,6 +444,20 @@ export class HttpRequest extends IncomingMessage {
|
|
|
299
444
|
}
|
|
300
445
|
}
|
|
301
446
|
|
|
447
|
+
export function incomingMessageToHttpRequest(request: IncomingMessage): HttpRequest {
|
|
448
|
+
if (request instanceof HttpRequest) return request;
|
|
449
|
+
Object.setPrototypeOf(request, HttpRequest.prototype);
|
|
450
|
+
HttpRequest.constructor.call(request);
|
|
451
|
+
return request as HttpRequest;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export function serverResponseToHttpResponse(response: ServerResponse): HttpResponse {
|
|
455
|
+
if (response instanceof HttpResponse) return response;
|
|
456
|
+
Object.setPrototypeOf(response, HttpResponse.prototype);
|
|
457
|
+
MemoryHttpResponse.constructor.call(response);
|
|
458
|
+
return response as HttpResponse;
|
|
459
|
+
}
|
|
460
|
+
|
|
302
461
|
export class MemoryHttpResponse extends HttpResponse {
|
|
303
462
|
public body: Buffer = Buffer.alloc(0);
|
|
304
463
|
public headers: { [name: string]: number | string | string[] | undefined } = Object.create(null);
|
package/src/module.config.ts
CHANGED
|
@@ -4,7 +4,7 @@ export interface HttpParserOptions {
|
|
|
4
4
|
*
|
|
5
5
|
* @default 'utf-8'
|
|
6
6
|
*/
|
|
7
|
-
encoding?:
|
|
7
|
+
encoding?: string | undefined;
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* the directory for placing file uploads in. You can move them later by using fs.rename()
|
|
@@ -90,6 +90,80 @@ export interface HttpParserOptions {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
export class HttpConfig {
|
|
93
|
+
|
|
93
94
|
debug: boolean = false;
|
|
95
|
+
|
|
94
96
|
parser: HttpParserOptions = {};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Limits maximum incoming headers count. If set to 0, no limit will be applied.
|
|
100
|
+
*/
|
|
101
|
+
maxHeadersCount?: number;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The maximum number of requests socket can handle
|
|
105
|
+
* before closing keep alive connection.
|
|
106
|
+
*
|
|
107
|
+
* A value of `0` will disable the limit.
|
|
108
|
+
*
|
|
109
|
+
* When the limit is reached it will set the `Connection` header value to `close`,
|
|
110
|
+
* but will not actually close the connection, subsequent requests sent
|
|
111
|
+
* after the limit is reached will get `503 Service Unavailable` as a response.
|
|
112
|
+
*/
|
|
113
|
+
maxRequestsPerSocket?: number;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The number of milliseconds of inactivity before a socket is presumed
|
|
117
|
+
* to have timed out.
|
|
118
|
+
*
|
|
119
|
+
* A value of `0` will disable the timeout behavior on incoming connections.
|
|
120
|
+
*
|
|
121
|
+
* The socket timeout logic is set up on connection, so changing this
|
|
122
|
+
* value only affects new connections to the server, not any existing connections.
|
|
123
|
+
*/
|
|
124
|
+
timeout?: number;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Limit the amount of time the parser will wait to receive the complete HTTP
|
|
128
|
+
* headers.
|
|
129
|
+
*
|
|
130
|
+
* If the timeout expires, the server responds with status 408 without
|
|
131
|
+
* forwarding the request to the request listener and then closes the connection.
|
|
132
|
+
*
|
|
133
|
+
* It must be set to a non-zero value (e.g. 120 seconds) to protect against
|
|
134
|
+
* potential Denial-of-Service attacks in case the server is deployed without a
|
|
135
|
+
* reverse proxy in front.
|
|
136
|
+
*/
|
|
137
|
+
headersTimeout?: number;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The number of milliseconds of inactivity a server needs to wait for additional
|
|
141
|
+
* incoming data, after it has finished writing the last response, before a socket
|
|
142
|
+
* will be destroyed. If the server receives new data before the keep-alive
|
|
143
|
+
* timeout has fired, it will reset the regular inactivity timeout, i.e., `server.timeout`.
|
|
144
|
+
*
|
|
145
|
+
* A value of `0` will disable the keep-alive timeout behavior on incoming
|
|
146
|
+
* connections.
|
|
147
|
+
* A value of `0` makes the http server behave similarly to Node.js versions prior
|
|
148
|
+
* to 8.0.0, which did not have a keep-alive timeout.
|
|
149
|
+
*
|
|
150
|
+
* The socket timeout logic is set up on connection, so changing this value only
|
|
151
|
+
* affects new connections to the server, not any existing connections.
|
|
152
|
+
*/
|
|
153
|
+
keepAliveTimeout?: number;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Sets the timeout value in milliseconds for receiving the entire request from
|
|
157
|
+
* the client.
|
|
158
|
+
*
|
|
159
|
+
* If the timeout expires, the server responds with status 408 without
|
|
160
|
+
* forwarding the request to the request listener and then closes the connection.
|
|
161
|
+
*
|
|
162
|
+
* It must be set to a non-zero value (e.g. 120 seconds) to protect against
|
|
163
|
+
* potential Denial-of-Service attacks in case the server is deployed without a
|
|
164
|
+
* reverse proxy in front.
|
|
165
|
+
*
|
|
166
|
+
* Default is 5 minutes.
|
|
167
|
+
*/
|
|
168
|
+
requestTimeout?: number;
|
|
95
169
|
}
|
package/src/module.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HttpListener, HttpResultFormatter, httpWorkflow } from './http.js';
|
|
2
2
|
import { HttpConfig } from './module.config.js';
|
|
3
|
-
import { AddedListener, AppModule, ControllerConfig,
|
|
3
|
+
import { AddedListener, AppModule, ControllerConfig, createModuleClass, stringifyListener } from '@deepkit/app';
|
|
4
4
|
import { HttpRouter, HttpRouterRegistry, RouteConfig } from './router.js';
|
|
5
5
|
import { HttpKernel } from './kernel.js';
|
|
6
6
|
import { HttpRouterFilterResolver } from './filter.js';
|
|
@@ -10,16 +10,21 @@ import { HttpRequest, HttpResponse } from './model.js';
|
|
|
10
10
|
import '@deepkit/type';
|
|
11
11
|
import { httpClass } from './decorator.js';
|
|
12
12
|
import { EventToken } from '@deepkit/event';
|
|
13
|
-
import {
|
|
13
|
+
import { ReflectionKind, ReflectionParameter, Type, typeAnnotation } from '@deepkit/type';
|
|
14
14
|
import { buildRequestParser } from './request-parser.js';
|
|
15
15
|
import { InjectorContext } from '@deepkit/injector';
|
|
16
16
|
|
|
17
17
|
function parameterRequiresRequest(parameter: ReflectionParameter): boolean {
|
|
18
|
-
return Boolean(
|
|
19
|
-
||
|
|
18
|
+
return Boolean(typeAnnotation.getType(parameter.type, 'httpQueries')
|
|
19
|
+
|| typeAnnotation.getType(parameter.type, 'httpQuery')
|
|
20
|
+
|| typeAnnotation.getType(parameter.type, 'httpBody')
|
|
21
|
+
|| typeAnnotation.getType(parameter.type, 'httpRequestParser')
|
|
22
|
+
|| typeAnnotation.getType(parameter.type, 'httpPath')
|
|
23
|
+
|| typeAnnotation.getType(parameter.type, 'httpHeader'));
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
|
|
27
|
+
export class HttpModule extends createModuleClass({
|
|
23
28
|
config: HttpConfig,
|
|
24
29
|
providers: [
|
|
25
30
|
HttpRouter,
|
|
@@ -67,7 +72,7 @@ export class HttpModule extends createModule({
|
|
|
67
72
|
const params = listener.reflection.getParameters().slice(1);
|
|
68
73
|
|
|
69
74
|
for (const parameter of params) {
|
|
70
|
-
if (
|
|
75
|
+
if (typeAnnotation.getType(parameter.type, 'httpBody')) needsAsync = true;
|
|
71
76
|
if (parameterRequiresRequest(parameter)) requiresHttpRequest = true;
|
|
72
77
|
}
|
|
73
78
|
|
|
@@ -76,27 +81,30 @@ export class HttpModule extends createModule({
|
|
|
76
81
|
throw new Error(`Listener ${stringifyListener(listener)} requires async HttpBody. This is not yet supported. You have to parse the request manually by injecting HttpRequest.`);
|
|
77
82
|
}
|
|
78
83
|
|
|
84
|
+
const parserCache = new Map<RouteConfig, Function>();
|
|
85
|
+
|
|
79
86
|
for (let index = 0; index < params.length; index++) {
|
|
80
87
|
const parameter = params[index];
|
|
81
88
|
if (!parameterRequiresRequest(parameter)) continue;
|
|
82
89
|
|
|
83
90
|
//change the reflection type so that we create a unique injection token for that type.
|
|
84
|
-
const unique = Symbol('
|
|
91
|
+
const unique = Symbol('event.parameter:' + parameter.name);
|
|
85
92
|
const uniqueType: Type = { kind: ReflectionKind.literal, literal: unique };
|
|
86
|
-
|
|
87
|
-
let build: Function;
|
|
93
|
+
typeAnnotation.registerType(parameter.type, { name: 'inject', options: uniqueType });
|
|
88
94
|
let i = index;
|
|
89
95
|
|
|
90
96
|
this.addProvider({
|
|
91
97
|
provide: uniqueType, useFactory: (httpConfig: HttpConfig, request: HttpRequest, injector: InjectorContext, config?: RouteConfig) => {
|
|
98
|
+
let build = config && parserCache.get(config);
|
|
92
99
|
if (!build) {
|
|
93
100
|
const params = listener.reflection.getParameters().slice(1);
|
|
94
|
-
build = buildRequestParser(httpConfig.parser, params, config
|
|
101
|
+
build = buildRequestParser(httpConfig.parser, params, config);
|
|
102
|
+
if (config) parserCache.set(config, build);
|
|
95
103
|
}
|
|
96
104
|
|
|
97
105
|
const parser = build(request);
|
|
98
|
-
const
|
|
99
|
-
return
|
|
106
|
+
const result = parser(injector);
|
|
107
|
+
return result.arguments[i];
|
|
100
108
|
}, scope: 'http'
|
|
101
109
|
});
|
|
102
110
|
this.addExport(uniqueType);
|