@deepkit/http 1.0.1-alpha.155 → 1.0.1-alpha.158
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 +20 -0
- package/dist/cjs/src/decorator.js +35 -42
- package/dist/cjs/src/decorator.js.map +1 -1
- package/dist/cjs/src/filter.js +1 -1
- package/dist/cjs/src/filter.js.map +1 -1
- package/dist/cjs/src/http.d.ts +1 -3
- package/dist/cjs/src/http.js +18 -13
- package/dist/cjs/src/http.js.map +1 -1
- package/dist/cjs/src/kernel.d.ts +43 -3
- package/dist/cjs/src/kernel.js +57 -7
- package/dist/cjs/src/kernel.js.map +1 -1
- package/dist/cjs/src/middleware.js +1 -1
- package/dist/cjs/src/middleware.js.map +1 -1
- package/dist/cjs/src/model.d.ts +6 -6
- package/dist/cjs/src/model.js +59 -39
- package/dist/cjs/src/model.js.map +1 -1
- package/dist/cjs/src/module.config.d.ts +1 -2
- package/dist/cjs/src/module.config.js +1 -1
- package/dist/cjs/src/module.js +11 -12
- package/dist/cjs/src/module.js.map +1 -1
- package/dist/cjs/src/request-parser.js +56 -66
- package/dist/cjs/src/request-parser.js.map +1 -1
- package/dist/cjs/src/router.js +17 -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 +40 -41
- 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/src/decorator.js +29 -32
- package/dist/esm/src/decorator.js.map +1 -1
- package/dist/esm/src/filter.js +1 -1
- package/dist/esm/src/filter.js.map +1 -1
- package/dist/esm/src/http.d.ts +1 -3
- package/dist/esm/src/http.js +14 -6
- package/dist/esm/src/http.js.map +1 -1
- package/dist/esm/src/kernel.d.ts +43 -3
- package/dist/esm/src/kernel.js +58 -8
- package/dist/esm/src/kernel.js.map +1 -1
- package/dist/esm/src/middleware.js +1 -1
- package/dist/esm/src/middleware.js.map +1 -1
- package/dist/esm/src/model.d.ts +6 -6
- package/dist/esm/src/model.js +28 -11
- package/dist/esm/src/model.js.map +1 -1
- package/dist/esm/src/module.config.d.ts +1 -2
- package/dist/esm/src/module.config.js +1 -1
- package/dist/esm/src/module.js +12 -12
- package/dist/esm/src/module.js.map +1 -1
- package/dist/esm/src/request-parser.js +48 -52
- package/dist/esm/src/request-parser.js.map +1 -1
- package/dist/esm/src/router.js +3 -4
- 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 +33 -31
- package/dist/esm/src/static-serving.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +16 -15
- package/src/http.ts +20 -4
- package/src/kernel.ts +80 -8
- package/src/model.ts +23 -7
- package/src/module.config.ts +1 -1
- package/src/module.ts +12 -11
- package/src/request-parser.ts +45 -50
- package/src/static-serving.ts +44 -33
- package/tsconfig.json +1 -1
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) {
|
|
@@ -276,13 +276,13 @@ export type HttpQueries<T, Options extends { name?: string } = {}> = T & TypeAnn
|
|
|
276
276
|
* }
|
|
277
277
|
* ```
|
|
278
278
|
*/
|
|
279
|
-
export type HttpRegExp<T, Pattern extends string | RegExp> = T &
|
|
279
|
+
export type HttpRegExp<T, Pattern extends string | RegExp> = T & TypeAnnotation<'httpRegExp', Pattern>;
|
|
280
280
|
|
|
281
281
|
export function getRegExp(type: Type): string | RegExp | undefined {
|
|
282
|
-
const options =
|
|
283
|
-
if (!options
|
|
284
|
-
if (options
|
|
285
|
-
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;
|
|
286
286
|
return;
|
|
287
287
|
}
|
|
288
288
|
|
|
@@ -383,6 +383,8 @@ export class HttpRequest extends IncomingMessage {
|
|
|
383
383
|
|
|
384
384
|
public uploadedFiles: { [name: string]: UploadedFile } = {};
|
|
385
385
|
|
|
386
|
+
public throwErrorOnNotFound: boolean = false;
|
|
387
|
+
|
|
386
388
|
/**
|
|
387
389
|
* The router sets the body when it was read.
|
|
388
390
|
*/
|
|
@@ -442,6 +444,20 @@ export class HttpRequest extends IncomingMessage {
|
|
|
442
444
|
}
|
|
443
445
|
}
|
|
444
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
|
+
|
|
445
461
|
export class MemoryHttpResponse extends HttpResponse {
|
|
446
462
|
public body: Buffer = Buffer.alloc(0);
|
|
447
463
|
public headers: { [name: string]: number | string | string[] | undefined } = Object.create(null);
|
package/src/module.config.ts
CHANGED
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,20 +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
|
-
||
|
|
20
|
-
||
|
|
21
|
-
||
|
|
22
|
-
||
|
|
23
|
-
||
|
|
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'));
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
|
|
27
|
+
export class HttpModule extends createModuleClass({
|
|
27
28
|
config: HttpConfig,
|
|
28
29
|
providers: [
|
|
29
30
|
HttpRouter,
|
|
@@ -71,7 +72,7 @@ export class HttpModule extends createModule({
|
|
|
71
72
|
const params = listener.reflection.getParameters().slice(1);
|
|
72
73
|
|
|
73
74
|
for (const parameter of params) {
|
|
74
|
-
if (
|
|
75
|
+
if (typeAnnotation.getType(parameter.type, 'httpBody')) needsAsync = true;
|
|
75
76
|
if (parameterRequiresRequest(parameter)) requiresHttpRequest = true;
|
|
76
77
|
}
|
|
77
78
|
|
|
@@ -89,7 +90,7 @@ export class HttpModule extends createModule({
|
|
|
89
90
|
//change the reflection type so that we create a unique injection token for that type.
|
|
90
91
|
const unique = Symbol('event.parameter:' + parameter.name);
|
|
91
92
|
const uniqueType: Type = { kind: ReflectionKind.literal, literal: unique };
|
|
92
|
-
|
|
93
|
+
typeAnnotation.registerType(parameter.type, { name: 'inject', options: uniqueType });
|
|
93
94
|
let i = index;
|
|
94
95
|
|
|
95
96
|
this.addProvider({
|
package/src/request-parser.ts
CHANGED
|
@@ -7,67 +7,61 @@ import {
|
|
|
7
7
|
getValidatorFunction,
|
|
8
8
|
hasDefaultValue,
|
|
9
9
|
isOptional,
|
|
10
|
-
metaAnnotation,
|
|
11
10
|
ReflectionKind,
|
|
12
11
|
ReflectionParameter,
|
|
13
12
|
resolveReceiveType,
|
|
14
13
|
serializer,
|
|
15
14
|
stringifyType,
|
|
16
15
|
Type,
|
|
16
|
+
typeAnnotation,
|
|
17
17
|
typeToObject,
|
|
18
18
|
ValidationError,
|
|
19
19
|
} from '@deepkit/type';
|
|
20
20
|
import { BodyValidationError, createRequestWithCachedBody, getRegExp, HttpRequest, ValidatedBody } from './model.js';
|
|
21
21
|
import { getRouteActionLabel, RouteConfig, UploadedFile, UploadedFileSymbol } from './router.js';
|
|
22
|
-
|
|
23
|
-
//@ts-ignore
|
|
24
22
|
import qs from 'qs';
|
|
25
|
-
|
|
26
|
-
// @ts-ignore
|
|
27
|
-
import formidable from 'formidable';
|
|
23
|
+
import formidable, { Fields, Files, Options } from 'formidable';
|
|
28
24
|
import { HttpParserOptions } from './module.config.js';
|
|
29
25
|
|
|
30
|
-
|
|
31
26
|
function parseBody(
|
|
32
27
|
options: HttpParserOptions,
|
|
33
28
|
req: HttpRequest, foundFiles: { [name: string]: UploadedFile }) {
|
|
34
|
-
|
|
29
|
+
|
|
30
|
+
const form = formidable(Object.assign(options as Options, {
|
|
35
31
|
multiples: true,
|
|
36
32
|
}));
|
|
37
|
-
return asyncOperation((resolve, reject) => {
|
|
38
|
-
function parseData(
|
|
39
|
-
|
|
40
|
-
reject(err);
|
|
41
|
-
} else {
|
|
42
|
-
const fileEntries = Object.entries(files);
|
|
33
|
+
return asyncOperation(async (resolve, reject) => {
|
|
34
|
+
function parseData(fields: Fields, files: Files) {
|
|
35
|
+
const fileEntries = Object.entries(files);
|
|
43
36
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
37
|
+
// formidable turns JSON arrays into numerically keyed objects, so we convert them back
|
|
38
|
+
if ('0' in fields && fileEntries.length === 0 && Object.keys(fields).every((key, idx) => parseInt(key) === idx)) {
|
|
39
|
+
return resolve(Object.values(fields));
|
|
40
|
+
}
|
|
48
41
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
resolve(body);
|
|
42
|
+
for (const [name, files] of fileEntries) {
|
|
43
|
+
if (!files) continue;
|
|
44
|
+
const file = files[0];
|
|
45
|
+
if (!file) continue;
|
|
46
|
+
if (!file.filepath || 'string' !== typeof file.filepath) continue;
|
|
47
|
+
if (!file.size || 'number' !== typeof file.size) continue;
|
|
48
|
+
|
|
49
|
+
foundFiles[name] = {
|
|
50
|
+
validator: UploadedFileSymbol,
|
|
51
|
+
size: file.size,
|
|
52
|
+
path: file.filepath,
|
|
53
|
+
name: file.originalFilename || null,
|
|
54
|
+
type: file.mimetype || null,
|
|
55
|
+
lastModifiedDate: null,
|
|
56
|
+
};
|
|
65
57
|
}
|
|
58
|
+
const body = { ...fields, ...foundFiles };
|
|
59
|
+
resolve(body);
|
|
66
60
|
}
|
|
67
61
|
|
|
68
62
|
if (req.body) {
|
|
69
|
-
form.parse(createRequestWithCachedBody(req, req.body)
|
|
70
|
-
|
|
63
|
+
const [fields, files] = await form.parse(createRequestWithCachedBody(req, req.body));
|
|
64
|
+
parseData(fields, files);
|
|
71
65
|
}
|
|
72
66
|
|
|
73
67
|
const chunks: Buffer[] = [];
|
|
@@ -83,7 +77,8 @@ function parseBody(
|
|
|
83
77
|
});
|
|
84
78
|
req.once('error', () => req.off('data', read));
|
|
85
79
|
|
|
86
|
-
form.parse(req
|
|
80
|
+
const [fields, files] = await form.parse(req);
|
|
81
|
+
parseData(fields, files);
|
|
87
82
|
});
|
|
88
83
|
}
|
|
89
84
|
|
|
@@ -96,21 +91,21 @@ export class ParameterForRequestParser {
|
|
|
96
91
|
}
|
|
97
92
|
|
|
98
93
|
get body() {
|
|
99
|
-
return
|
|
94
|
+
return typeAnnotation.getType(this.parameter.type, 'httpBody') !== undefined;
|
|
100
95
|
}
|
|
101
96
|
|
|
102
97
|
get requestParser() {
|
|
103
|
-
return
|
|
98
|
+
return typeAnnotation.getType(this.parameter.type, 'httpRequestParser') !== undefined;
|
|
104
99
|
}
|
|
105
100
|
|
|
106
101
|
get bodyValidation() {
|
|
107
|
-
return
|
|
102
|
+
return typeAnnotation.getType(this.parameter.type, 'httpBodyValidation') !== undefined;
|
|
108
103
|
}
|
|
109
104
|
|
|
110
105
|
getType(): Type {
|
|
111
|
-
const parser =
|
|
112
|
-
if (parser
|
|
113
|
-
return parser
|
|
106
|
+
const parser = typeAnnotation.getType(this.parameter.type, 'httpRequestParser');
|
|
107
|
+
if (parser) {
|
|
108
|
+
return parser;
|
|
114
109
|
}
|
|
115
110
|
|
|
116
111
|
if (this.bodyValidation) {
|
|
@@ -123,22 +118,22 @@ export class ParameterForRequestParser {
|
|
|
123
118
|
}
|
|
124
119
|
|
|
125
120
|
get header() {
|
|
126
|
-
return
|
|
121
|
+
return typeAnnotation.getType(this.parameter.type, 'httpHeader') !== undefined;
|
|
127
122
|
}
|
|
128
123
|
|
|
129
124
|
get query() {
|
|
130
|
-
return
|
|
125
|
+
return typeAnnotation.getType(this.parameter.type, 'httpQuery') !== undefined;
|
|
131
126
|
}
|
|
132
127
|
|
|
133
128
|
get queries() {
|
|
134
|
-
return
|
|
129
|
+
return typeAnnotation.getType(this.parameter.type, 'httpQueries') !== undefined;
|
|
135
130
|
}
|
|
136
131
|
|
|
137
132
|
get typePath(): string | undefined {
|
|
138
|
-
const typeOptions =
|
|
139
|
-
||
|
|
133
|
+
const typeOptions = typeAnnotation.getType(this.parameter.type, 'httpQueries') || typeAnnotation.getType(this.parameter.type, 'httpQuery')
|
|
134
|
+
|| typeAnnotation.getType(this.parameter.type, 'httpPath') || typeAnnotation.getType(this.parameter.type, 'httpHeader');
|
|
140
135
|
if (!typeOptions) return;
|
|
141
|
-
const options = typeToObject(typeOptions
|
|
136
|
+
const options = typeToObject(typeOptions);
|
|
142
137
|
if (isObject(options)) return options.name;
|
|
143
138
|
return;
|
|
144
139
|
}
|
|
@@ -148,7 +143,7 @@ export class ParameterForRequestParser {
|
|
|
148
143
|
}
|
|
149
144
|
|
|
150
145
|
isPartOfPath(): boolean {
|
|
151
|
-
return
|
|
146
|
+
return typeAnnotation.getType(this.parameter.type, 'httpPath') !== undefined || this.regexPosition !== undefined;
|
|
152
147
|
}
|
|
153
148
|
}
|
|
154
149
|
|
package/src/static-serving.ts
CHANGED
|
@@ -20,42 +20,53 @@ import send from 'send';
|
|
|
20
20
|
import { eventDispatcher } from '@deepkit/event';
|
|
21
21
|
import { HttpRouter, RouteConfig } from './router.js';
|
|
22
22
|
|
|
23
|
+
export function staticServe(
|
|
24
|
+
localPath: string,
|
|
25
|
+
path: string,
|
|
26
|
+
request: HttpRequest,
|
|
27
|
+
response: HttpResponse,
|
|
28
|
+
) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
const res = send(request, path, { root: localPath });
|
|
31
|
+
res.pipe(response);
|
|
32
|
+
res.on('end', resolve);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function staticOnRoute(
|
|
37
|
+
event: typeof httpWorkflow.onRoute.event,
|
|
38
|
+
path: string,
|
|
39
|
+
localPath: string,
|
|
40
|
+
) {
|
|
41
|
+
if (event.sent) return;
|
|
42
|
+
if (event.route) return;
|
|
43
|
+
|
|
44
|
+
if (!event.request.url?.startsWith(path)) return;
|
|
45
|
+
|
|
46
|
+
const relativePath = urlJoin('/', event.url.substr(path.length));
|
|
47
|
+
const finalLocalPath = join(localPath, relativePath);
|
|
48
|
+
|
|
49
|
+
return new Promise(resolve => {
|
|
50
|
+
stat(finalLocalPath, (err, stat) => {
|
|
51
|
+
if (stat && stat.isFile()) {
|
|
52
|
+
event.routeFound(
|
|
53
|
+
new RouteConfig('static', ['GET'], event.url, {
|
|
54
|
+
type: 'function',
|
|
55
|
+
fn: staticServe,
|
|
56
|
+
}),
|
|
57
|
+
() => ({ arguments: [localPath, relativePath, event.request, event.response], parameters: {} }),
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
resolve(undefined);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
23
65
|
export function serveStaticListener(module: AppModule<any>, path: string, localPath: string = path): ClassType {
|
|
24
66
|
class HttpRequestStaticServingListener {
|
|
25
|
-
serve(path: string, request: HttpRequest, response: HttpResponse) {
|
|
26
|
-
return new Promise((resolve, reject) => {
|
|
27
|
-
const res = send(request, path, { root: localPath });
|
|
28
|
-
res.pipe(response);
|
|
29
|
-
res.on('end', resolve);
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
|
|
33
67
|
@eventDispatcher.listen(httpWorkflow.onRoute, 101) //after default route listener at 100
|
|
34
68
|
onRoute(event: typeof httpWorkflow.onRoute.event) {
|
|
35
|
-
|
|
36
|
-
if (event.route) return;
|
|
37
|
-
|
|
38
|
-
if (!event.request.url?.startsWith(path)) return;
|
|
39
|
-
|
|
40
|
-
const relativePath = urlJoin('/', event.url.substr(path.length));
|
|
41
|
-
const finalLocalPath = join(localPath, relativePath);
|
|
42
|
-
|
|
43
|
-
return new Promise(resolve => {
|
|
44
|
-
stat(finalLocalPath, (err, stat) => {
|
|
45
|
-
if (stat && stat.isFile()) {
|
|
46
|
-
event.routeFound(
|
|
47
|
-
new RouteConfig('static', ['GET'], event.url, {
|
|
48
|
-
type: 'controller',
|
|
49
|
-
controller: HttpRequestStaticServingListener,
|
|
50
|
-
module,
|
|
51
|
-
methodName: 'serve',
|
|
52
|
-
}),
|
|
53
|
-
() => ({ arguments: [relativePath, event.request, event.response], parameters: {} }),
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
resolve(undefined);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
69
|
+
return staticOnRoute(event, path, localPath);
|
|
59
70
|
}
|
|
60
71
|
}
|
|
61
72
|
|
|
@@ -108,7 +119,7 @@ export function registerStaticHttpController(module: AppModule<any>, options: St
|
|
|
108
119
|
const groups = options.groups || [];
|
|
109
120
|
|
|
110
121
|
class StaticController {
|
|
111
|
-
@http.GET().group(...groups)
|
|
122
|
+
@(http.GET().group(...groups))
|
|
112
123
|
serveIndex(request: HttpRequest, response: HttpResponse) {
|
|
113
124
|
if (!indexHtml) {
|
|
114
125
|
indexHtml = loadHtml(options.localPath, normalizeDirectory(options.path));
|