@deepkit/http 1.0.1-alpha.156 → 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.
Files changed (66) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/src/decorator.js +35 -42
  3. package/dist/cjs/src/decorator.js.map +1 -1
  4. package/dist/cjs/src/filter.js +1 -1
  5. package/dist/cjs/src/filter.js.map +1 -1
  6. package/dist/cjs/src/http.d.ts +1 -3
  7. package/dist/cjs/src/http.js +18 -13
  8. package/dist/cjs/src/http.js.map +1 -1
  9. package/dist/cjs/src/kernel.d.ts +43 -3
  10. package/dist/cjs/src/kernel.js +57 -7
  11. package/dist/cjs/src/kernel.js.map +1 -1
  12. package/dist/cjs/src/middleware.js +1 -1
  13. package/dist/cjs/src/middleware.js.map +1 -1
  14. package/dist/cjs/src/model.d.ts +6 -6
  15. package/dist/cjs/src/model.js +59 -39
  16. package/dist/cjs/src/model.js.map +1 -1
  17. package/dist/cjs/src/module.config.d.ts +1 -2
  18. package/dist/cjs/src/module.config.js +1 -1
  19. package/dist/cjs/src/module.js +11 -12
  20. package/dist/cjs/src/module.js.map +1 -1
  21. package/dist/cjs/src/request-parser.js +56 -66
  22. package/dist/cjs/src/request-parser.js.map +1 -1
  23. package/dist/cjs/src/router.js +17 -28
  24. package/dist/cjs/src/router.js.map +1 -1
  25. package/dist/cjs/src/static-serving.d.ts +4 -0
  26. package/dist/cjs/src/static-serving.js +40 -41
  27. package/dist/cjs/src/static-serving.js.map +1 -1
  28. package/dist/cjs/src/utils.js +1 -2
  29. package/dist/cjs/src/utils.js.map +1 -1
  30. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  31. package/dist/esm/src/decorator.js +29 -32
  32. package/dist/esm/src/decorator.js.map +1 -1
  33. package/dist/esm/src/filter.js +1 -1
  34. package/dist/esm/src/filter.js.map +1 -1
  35. package/dist/esm/src/http.d.ts +1 -3
  36. package/dist/esm/src/http.js +14 -6
  37. package/dist/esm/src/http.js.map +1 -1
  38. package/dist/esm/src/kernel.d.ts +43 -3
  39. package/dist/esm/src/kernel.js +58 -8
  40. package/dist/esm/src/kernel.js.map +1 -1
  41. package/dist/esm/src/middleware.js +1 -1
  42. package/dist/esm/src/middleware.js.map +1 -1
  43. package/dist/esm/src/model.d.ts +6 -6
  44. package/dist/esm/src/model.js +28 -11
  45. package/dist/esm/src/model.js.map +1 -1
  46. package/dist/esm/src/module.config.d.ts +1 -2
  47. package/dist/esm/src/module.config.js +1 -1
  48. package/dist/esm/src/module.js +12 -12
  49. package/dist/esm/src/module.js.map +1 -1
  50. package/dist/esm/src/request-parser.js +48 -52
  51. package/dist/esm/src/request-parser.js.map +1 -1
  52. package/dist/esm/src/router.js +3 -4
  53. package/dist/esm/src/router.js.map +1 -1
  54. package/dist/esm/src/static-serving.d.ts +4 -0
  55. package/dist/esm/src/static-serving.js +33 -31
  56. package/dist/esm/src/static-serving.js.map +1 -1
  57. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  58. package/package.json +16 -15
  59. package/src/http.ts +20 -4
  60. package/src/kernel.ts +80 -8
  61. package/src/model.ts +23 -7
  62. package/src/module.config.ts +1 -1
  63. package/src/module.ts +12 -11
  64. package/src/request-parser.ts +45 -50
  65. package/src/static-serving.ts +44 -33
  66. 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 { metaAnnotation, ReflectionKind, Type, TypeAnnotation, ValidationErrorItem } from '@deepkit/type';
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 & { __meta?: never & ['httpRegExp', Pattern] };
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 = metaAnnotation.getForName(type, 'httpRegExp');
283
- if (!options || !options[0]) return;
284
- if (options[0].kind === ReflectionKind.literal && 'string' === typeof options[0].literal) return options[0].literal;
285
- if (options[0].kind === ReflectionKind.literal && options[0].literal instanceof RegExp) return options[0].literal;
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);
@@ -4,7 +4,7 @@ export interface HttpParserOptions {
4
4
  *
5
5
  * @default 'utf-8'
6
6
  */
7
- encoding?: BufferEncoding | undefined;
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()
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, createModule, stringifyListener } from '@deepkit/app';
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 { metaAnnotation, ReflectionKind, ReflectionParameter, Type } from '@deepkit/type';
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(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'));
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
- export class HttpModule extends createModule({
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 (metaAnnotation.getForName(parameter.type, 'httpBody')) needsAsync = true;
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
- metaAnnotation.registerType(parameter.type, { name: 'inject', options: [uniqueType] });
93
+ typeAnnotation.registerType(parameter.type, { name: 'inject', options: uniqueType });
93
94
  let i = index;
94
95
 
95
96
  this.addProvider({
@@ -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
- const form = formidable(Object.assign(options, {
29
+
30
+ const form = formidable(Object.assign(options as Options, {
35
31
  multiples: true,
36
32
  }));
37
- return asyncOperation((resolve, reject) => {
38
- function parseData(err: any, fields: any, files: any) {
39
- if (err) {
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
- // formidable turns JSON arrays into numerically keyed objects, so we convert them back
45
- if ('0' in fields && fileEntries.length === 0 && Object.keys(fields).every((key, idx) => parseInt(key) === idx)) {
46
- return resolve(Object.values(fields));
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
- for (const [name, file] of fileEntries as any) {
50
- if (!file.filepath || 'string' !== typeof file.filepath) continue;
51
- if (!file.size || 'number' !== typeof file.size) continue;
52
- if (file.lastModifiedDate && !(file.lastModifiedDate instanceof Date)) continue;
53
-
54
- foundFiles[name] = {
55
- validator: UploadedFileSymbol,
56
- size: file.size,
57
- path: file.filepath,
58
- name: file.originalFilename || null,
59
- type: file.mimetype || null,
60
- lastModifiedDate: file.lastModifiedDate || null,
61
- };
62
- }
63
- const body = { ...fields, ...foundFiles };
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), parseData);
70
- return;
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, parseData);
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 metaAnnotation.getForName(this.parameter.type, 'httpBody') !== undefined;
94
+ return typeAnnotation.getType(this.parameter.type, 'httpBody') !== undefined;
100
95
  }
101
96
 
102
97
  get requestParser() {
103
- return metaAnnotation.getForName(this.parameter.type, 'httpRequestParser') !== undefined;
98
+ return typeAnnotation.getType(this.parameter.type, 'httpRequestParser') !== undefined;
104
99
  }
105
100
 
106
101
  get bodyValidation() {
107
- return metaAnnotation.getForName(this.parameter.type, 'httpBodyValidation') !== undefined;
102
+ return typeAnnotation.getType(this.parameter.type, 'httpBodyValidation') !== undefined;
108
103
  }
109
104
 
110
105
  getType(): Type {
111
- const parser = metaAnnotation.getForName(this.parameter.type, 'httpRequestParser');
112
- if (parser && parser[0]) {
113
- return parser[0];
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 metaAnnotation.getForName(this.parameter.type, 'httpHeader') !== undefined;
121
+ return typeAnnotation.getType(this.parameter.type, 'httpHeader') !== undefined;
127
122
  }
128
123
 
129
124
  get query() {
130
- return metaAnnotation.getForName(this.parameter.type, 'httpQuery') !== undefined;
125
+ return typeAnnotation.getType(this.parameter.type, 'httpQuery') !== undefined;
131
126
  }
132
127
 
133
128
  get queries() {
134
- return metaAnnotation.getForName(this.parameter.type, 'httpQueries') !== undefined;
129
+ return typeAnnotation.getType(this.parameter.type, 'httpQueries') !== undefined;
135
130
  }
136
131
 
137
132
  get typePath(): string | undefined {
138
- const typeOptions = metaAnnotation.getForName(this.parameter.type, 'httpQueries') || metaAnnotation.getForName(this.parameter.type, 'httpQuery')
139
- || metaAnnotation.getForName(this.parameter.type, 'httpPath') || metaAnnotation.getForName(this.parameter.type, 'httpHeader');
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[0]);
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 metaAnnotation.getForName(this.parameter.type, 'httpPath') !== undefined || this.regexPosition !== undefined;
146
+ return typeAnnotation.getType(this.parameter.type, 'httpPath') !== undefined || this.regexPosition !== undefined;
152
147
  }
153
148
  }
154
149
 
@@ -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
- if (event.sent) return;
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));
package/tsconfig.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "experimentalDecorators": true,
9
9
  "emitDecoratorMetadata": true,
10
10
  "moduleResolution": "node",
11
- "target": "es2018",
11
+ "target": "es2020",
12
12
  "module": "CommonJS",
13
13
  "esModuleInterop": true,
14
14
  "outDir": "./dist/cjs",