@deepkit/http 1.0.1-alpha.123 → 1.0.1-alpha.128
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 +10 -0
- package/dist/cjs/src/model.d.ts +30 -1
- package/dist/cjs/src/model.js +5 -1
- package/dist/cjs/src/model.js.map +1 -1
- package/dist/cjs/src/module.js +8 -4
- package/dist/cjs/src/module.js.map +1 -1
- package/dist/cjs/src/request-parser.d.ts +1 -0
- package/dist/cjs/src/request-parser.js +38 -7
- package/dist/cjs/src/request-parser.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/src/model.d.ts +30 -1
- package/dist/esm/src/model.js +4 -0
- package/dist/esm/src/model.js.map +1 -1
- package/dist/esm/src/module.js +8 -4
- package/dist/esm/src/module.js.map +1 -1
- package/dist/esm/src/request-parser.d.ts +1 -0
- package/dist/esm/src/request-parser.js +38 -7
- package/dist/esm/src/request-parser.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +11 -11
- package/src/model.ts +30 -1
- package/src/module.ts +8 -4
- package/src/request-parser.ts +39 -6
package/src/model.ts
CHANGED
|
@@ -12,7 +12,7 @@ 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 { metaAnnotation, ReflectionKind, Type,
|
|
15
|
+
import { metaAnnotation, ReflectionKind, Type, TypeAnnotation, ValidationErrorItem } from '@deepkit/type';
|
|
16
16
|
import { asyncOperation, isArray } from '@deepkit/core';
|
|
17
17
|
|
|
18
18
|
export class HttpResponse extends ServerResponse {
|
|
@@ -134,6 +134,35 @@ export class ValidatedBody<T> {
|
|
|
134
134
|
export type HttpBody<T> = T & TypeAnnotation<'httpBody'>;
|
|
135
135
|
export type HttpBodyValidation<T> = ValidatedBody<T> & TypeAnnotation<'httpBodyValidation'>;
|
|
136
136
|
|
|
137
|
+
export interface HttpRequestParserOptions {
|
|
138
|
+
withBody?: boolean;
|
|
139
|
+
withQuery?: boolean;
|
|
140
|
+
withHeader?: boolean;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Delays the parsing of the body/query/header to the very last moment, when the parameter is actually used.
|
|
145
|
+
*
|
|
146
|
+
* If no options are provided, the parser will receive data from header, body, and query, in this order.
|
|
147
|
+
* This basically allows to fetch data from all possible HTTP sources in one go.
|
|
148
|
+
*
|
|
149
|
+
* You can disable various sources by providing the options, e.g. `{withBody: false}` to disable body parsing.
|
|
150
|
+
* Or `{withQuery: false}` to disable query parsing. Or `{withHeader: false}` to disable header parsing.
|
|
151
|
+
* To only parse the body, use `{withQuery: false, withHeader: false}`.
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* async route(parser: HttpRequestParser<{authorization: string}>) {
|
|
156
|
+
* const data = await parser();
|
|
157
|
+
* console.log(data.authorization);
|
|
158
|
+
* }
|
|
159
|
+
* ```
|
|
160
|
+
*
|
|
161
|
+
* This is necessary in event listeners, since they are instantiated synchronously,
|
|
162
|
+
* but body is parsed asynchronously. So use in event listeners HttpRequestParser instead of HttpBody.
|
|
163
|
+
*/
|
|
164
|
+
export type HttpRequestParser<T> = ((options?: HttpRequestParserOptions) => Promise<T>) & TypeAnnotation<'httpRequestParser', T>;
|
|
165
|
+
|
|
137
166
|
/**
|
|
138
167
|
* Marks a parameter as HTTP path and reads the value from the request path.
|
|
139
168
|
* This is normally not requires since the parameter name automatically maps to the path parameter,
|
package/src/module.ts
CHANGED
|
@@ -15,8 +15,12 @@ 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(metaAnnotation.getForName(parameter.type, 'httpQueries')
|
|
19
|
-
|| metaAnnotation.getForName(parameter.type, '
|
|
18
|
+
return Boolean(metaAnnotation.getForName(parameter.type, 'httpQueries')
|
|
19
|
+
|| metaAnnotation.getForName(parameter.type, 'httpQuery')
|
|
20
|
+
|| metaAnnotation.getForName(parameter.type, 'httpBody')
|
|
21
|
+
|| metaAnnotation.getForName(parameter.type, 'httpRequestParser')
|
|
22
|
+
|| metaAnnotation.getForName(parameter.type, 'httpPath')
|
|
23
|
+
|| metaAnnotation.getForName(parameter.type, 'httpHeader'));
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
export class HttpModule extends createModule({
|
|
@@ -76,15 +80,15 @@ export class HttpModule extends createModule({
|
|
|
76
80
|
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
81
|
}
|
|
78
82
|
|
|
83
|
+
let build: Function;
|
|
79
84
|
for (let index = 0; index < params.length; index++) {
|
|
80
85
|
const parameter = params[index];
|
|
81
86
|
if (!parameterRequiresRequest(parameter)) continue;
|
|
82
87
|
|
|
83
88
|
//change the reflection type so that we create a unique injection token for that type.
|
|
84
|
-
const unique = Symbol('
|
|
89
|
+
const unique = Symbol('event.parameter:' + parameter.name);
|
|
85
90
|
const uniqueType: Type = { kind: ReflectionKind.literal, literal: unique };
|
|
86
91
|
metaAnnotation.registerType(parameter.type, { name: 'inject', options: [uniqueType] });
|
|
87
|
-
let build: Function;
|
|
88
92
|
let i = index;
|
|
89
93
|
|
|
90
94
|
this.addProvider({
|
package/src/request-parser.ts
CHANGED
|
@@ -89,11 +89,20 @@ export class ParameterForRequestParser {
|
|
|
89
89
|
return metaAnnotation.getForName(this.parameter.type, 'httpBody') !== undefined;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
get requestParser() {
|
|
93
|
+
return metaAnnotation.getForName(this.parameter.type, 'httpRequestParser') !== undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
92
96
|
get bodyValidation() {
|
|
93
97
|
return metaAnnotation.getForName(this.parameter.type, 'httpBodyValidation') !== undefined;
|
|
94
98
|
}
|
|
95
99
|
|
|
96
100
|
getType(): Type {
|
|
101
|
+
const parser = metaAnnotation.getForName(this.parameter.type, 'httpRequestParser');
|
|
102
|
+
if (parser && parser[0]) {
|
|
103
|
+
return parser[0];
|
|
104
|
+
}
|
|
105
|
+
|
|
97
106
|
if (this.bodyValidation) {
|
|
98
107
|
assertType(this.parameter.type, ReflectionKind.class);
|
|
99
108
|
const valueType = findMember('value', this.parameter.type.types);
|
|
@@ -180,7 +189,7 @@ export function buildRequestParser(parseOptions: HttpParserOptions, parameters:
|
|
|
180
189
|
compiler.context.set('ValidationError', ValidationError);
|
|
181
190
|
compiler.context.set('qs', qs);
|
|
182
191
|
|
|
183
|
-
let needsQueryString = !!params.find(v => v.query || v.queries);
|
|
192
|
+
let needsQueryString = !!params.find(v => v.query || v.queries || v.requestParser);
|
|
184
193
|
const query = needsQueryString ? '_qPosition === -1 ? {} : qs.parse(_url.substr(_qPosition + 1))' : '{}';
|
|
185
194
|
|
|
186
195
|
const regexVar = compiler.reserveVariable('regex', new RegExp('^' + pathRegex + '$'));
|
|
@@ -219,14 +228,11 @@ export function getRequestParserCodeForParameters(
|
|
|
219
228
|
let bodyValidationErrorHandling = `if (bodyErrors.length) throw ValidationError.from(bodyErrors);`;
|
|
220
229
|
|
|
221
230
|
for (const parameter of parameters) {
|
|
222
|
-
if (parameter.body || parameter.bodyValidation) {
|
|
231
|
+
if (parameter.requestParser || parameter.body || parameter.bodyValidation) {
|
|
223
232
|
const type = parameter.getType();
|
|
224
233
|
const validatorVar = compiler.reserveVariable('argumentValidator', getValidatorFunction(undefined, type));
|
|
225
234
|
const converterVar = compiler.reserveVariable('argumentConverter', getSerializeFunction(type, serializer.deserializeRegistry));
|
|
226
235
|
|
|
227
|
-
enableParseBody = true;
|
|
228
|
-
setParameters.push(`parameters.${parameter.parameter.name} = ${converterVar}(bodyFields, {loosely: true});`);
|
|
229
|
-
parameterValidator.push(`${validatorVar}(parameters.${parameter.parameter.name}, {errors: bodyErrors});`);
|
|
230
236
|
if (parameter.bodyValidation) {
|
|
231
237
|
compiler.context.set('BodyValidation', ValidatedBody);
|
|
232
238
|
compiler.context.set('BodyValidationError', BodyValidationError);
|
|
@@ -235,6 +241,32 @@ export function getRequestParserCodeForParameters(
|
|
|
235
241
|
} else {
|
|
236
242
|
parameterNames.push(`parameters.${parameter.parameter.name}`);
|
|
237
243
|
}
|
|
244
|
+
|
|
245
|
+
if (parameter.requestParser) {
|
|
246
|
+
const parseOptionsVar = compiler.reserveVariable('parseOptions', parseOptions);
|
|
247
|
+
const parseBodyVar = compiler.reserveVariable('parseBody', parseBody);
|
|
248
|
+
setParameters.push(`parameters.${parameter.parameter.name} = async (options = {}) => {
|
|
249
|
+
let res = {};
|
|
250
|
+
if (options.withHeader !== false) {
|
|
251
|
+
Object.assign(res, _headers);
|
|
252
|
+
}
|
|
253
|
+
if (options.withBody !== false) {
|
|
254
|
+
bodyFields = bodyFields || (await ${parseBodyVar}(${parseOptionsVar}, request, uploadedFiles));
|
|
255
|
+
Object.assign(res, bodyFields);
|
|
256
|
+
}
|
|
257
|
+
if (options.withQuery !== false) {
|
|
258
|
+
Object.assign(res, _query);
|
|
259
|
+
}
|
|
260
|
+
res = ${converterVar}(res, {loosely: true});
|
|
261
|
+
${validatorVar}(res, {errors: bodyErrors});
|
|
262
|
+
if (bodyErrors.length) throw ValidationError.from(bodyErrors);
|
|
263
|
+
return res;
|
|
264
|
+
}`);
|
|
265
|
+
} else {
|
|
266
|
+
enableParseBody = true;
|
|
267
|
+
setParameters.push(`parameters.${parameter.parameter.name} = ${converterVar}(bodyFields, {loosely: true});`);
|
|
268
|
+
parameterValidator.push(`${validatorVar}(parameters.${parameter.parameter.name}, {errors: bodyErrors});`);
|
|
269
|
+
}
|
|
238
270
|
} else if (parameter.query || parameter.queries || parameter.header) {
|
|
239
271
|
const converted = getSerializeFunction(parameter.parameter.parameter, serializer.deserializeRegistry, undefined, parameter.getName());
|
|
240
272
|
const validator = getValidatorFunction(undefined, parameter.parameter.parameter);
|
|
@@ -343,7 +375,7 @@ export function getRequestParserCodeForParameters(
|
|
|
343
375
|
const parseOptionsVar = compiler.reserveVariable('parseOptions', parseOptions);
|
|
344
376
|
const parseBodyVar = compiler.reserveVariable('parseBody', parseBody);
|
|
345
377
|
parseBodyLoading = `
|
|
346
|
-
|
|
378
|
+
bodyFields = bodyFields || (await ${parseBodyVar}(${parseOptionsVar}, request, uploadedFiles));`;
|
|
347
379
|
requiresAsyncParameters = true;
|
|
348
380
|
}
|
|
349
381
|
|
|
@@ -354,6 +386,7 @@ export function getRequestParserCodeForParameters(
|
|
|
354
386
|
const validationErrors = [];
|
|
355
387
|
const bodyErrors = [];
|
|
356
388
|
const parameters = {};
|
|
389
|
+
let bodyFields;
|
|
357
390
|
${setParametersFromPath}
|
|
358
391
|
${parseBodyLoading}
|
|
359
392
|
${setParameters.join('\n')}
|