@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/src/kernel.ts CHANGED
@@ -1,103 +1,151 @@
1
- import { inject, injectable, InjectorContext, MemoryInjector } from '@deepkit/injector';
2
- import { Router } from './router';
1
+ import { InjectorContext } from '@deepkit/injector';
2
+ import { HttpRouter } from './router.js';
3
3
  import { EventDispatcher } from '@deepkit/event';
4
- import { Logger } from '@deepkit/logger';
5
- import { HttpRequest, HttpResponse } from './model';
6
- import { Socket } from 'net';
7
- import { HttpRequestEvent, httpWorkflow } from './http';
8
- import { config } from './module.config';
4
+ import { LoggerInterface } from '@deepkit/logger';
5
+ import {
6
+ HttpRequest,
7
+ HttpResponse,
8
+ incomingMessageToHttpRequest,
9
+ MemoryHttpResponse,
10
+ RequestBuilder,
11
+ serverResponseToHttpResponse,
12
+ } from './model.js';
13
+ import { HttpError, HttpRequestEvent, HttpResultFormatter, httpWorkflow, JSONResponse } from './http.js';
14
+ import { FrameCategory, Stopwatch } from '@deepkit/stopwatch';
15
+ import { unlink } from 'fs';
16
+ import { ValidationError } from '@deepkit/type';
17
+ import { IncomingMessage, ServerResponse } from 'http';
9
18
 
19
+ interface HttpKernelHandleOptions {
20
+ /**
21
+ * Makes the kernel throw a HttpNotFoundError when the route is not found
22
+ * or a controller threw a HttpNotFoundError. This allows you to catch
23
+ * the error and handle it yourself.
24
+ */
25
+ throwOnNotFound?: boolean;
26
+ }
27
+
28
+ interface HttpKernelMiddlewareOptions extends HttpKernelHandleOptions {
29
+ /**
30
+ * If true, a HttpNotFoundError or missing route is not automatically
31
+ * sent to the client. Instead, the request is passed to the next middleware.
32
+ */
33
+ fallThroughOnNotFound?: boolean;
34
+ }
10
35
 
11
- @injectable()
12
36
  export class HttpKernel {
13
37
  constructor(
14
- protected router: Router,
38
+ protected router: HttpRouter,
15
39
  protected eventDispatcher: EventDispatcher,
16
40
  protected injectorContext: InjectorContext,
17
- protected logger: Logger,
18
- @inject(config.token('debug')) protected debug: boolean = false,
41
+ protected logger: LoggerInterface,
42
+ protected stopwatch?: Stopwatch,
19
43
  ) {
20
-
21
44
  }
22
45
 
23
- async handleRequestFor(method: string, url: string, jsonBody?: any): Promise<any> {
24
- const body = Buffer.from(jsonBody ? JSON.stringify(jsonBody) : '');
46
+ /**
47
+ * Creates a request handler function that can be used with http.createServer
48
+ * or any other http server library based on the node.js http module.
49
+ *
50
+ * When `fallThroughOnNotFound` is set to true, the handler will call `next()`
51
+ * when the route is not found, allowing the request to fall through to the next
52
+ * middleware in the chain.
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * import { createServer } from 'http';
57
+ *
58
+ * const app = new App({
59
+ * imports: [new HttpModule({})],
60
+ * });
61
+ *
62
+ * const handler = app.get(HttpKernel).createMiddleware({ fallThroughOnNotFound: true });
63
+ * const server = createServer(handler);
64
+ *
65
+ * server.listen(3000);
66
+ * ```
67
+ */
68
+ public createMiddleware(options: HttpKernelMiddlewareOptions = {}) {
69
+ if (options.fallThroughOnNotFound && 'undefined' === typeof options.throwOnNotFound) {
70
+ options.throwOnNotFound = true;
71
+ }
72
+ return (req: IncomingMessage, res: ServerResponse, next: (error?: any) => void) => {
73
+ return this.handleRequest(req, res, options).then(() => {
74
+ next();
75
+ }).catch((error) => {
76
+ if (options.fallThroughOnNotFound && error instanceof HttpError && error.httpCode === 404) {
77
+ next();
78
+ return;
79
+ }
80
+ next(error);
81
+ });
82
+ };
83
+ }
25
84
 
26
- const request = new (class extends HttpRequest {
27
- url = url;
28
- method = method;
29
- position = 0;
85
+ public async request(requestBuilder: RequestBuilder, options: HttpKernelHandleOptions = {}): Promise<MemoryHttpResponse> {
86
+ const request = requestBuilder.build();
87
+ const response = new MemoryHttpResponse(request);
88
+ response.assignSocket(request.socket);
89
+ await this.handleRequest(request, response, options);
90
+ return response;
91
+ }
30
92
 
31
- headers = {
32
- 'content-type': 'application/json',
33
- 'content-length': String(body.byteLength),
34
- };
93
+ async handleRequest(
94
+ _req: IncomingMessage,
95
+ _res: ServerResponse,
96
+ options: HttpKernelHandleOptions = {},
97
+ ): Promise<void> {
98
+ const httpInjectorContext = this.injectorContext.createChildScope('http');
99
+ const req = incomingMessageToHttpRequest(_req);
100
+ const res = serverResponseToHttpResponse(_res);
101
+ httpInjectorContext.set(HttpRequest, req);
102
+ httpInjectorContext.set(HttpResponse, res);
103
+ httpInjectorContext.set(InjectorContext, httpInjectorContext);
104
+ req.throwErrorOnNotFound = options.throwOnNotFound || false;
35
105
 
36
- done = false;
106
+ const frame = this.stopwatch ? this.stopwatch.start(req.method + ' ' + req.getUrl(), FrameCategory.http, true) : undefined;
107
+ const workflow = httpWorkflow.create('start', this.eventDispatcher, httpInjectorContext, this.stopwatch);
37
108
 
38
- _read(size: number) {
39
- if (this.done) {
40
- this.push(null);
41
- } else {
42
- this.push(body);
43
- this.done = true;
44
- }
109
+ try {
110
+ if (frame) {
111
+ frame.data({ url: req.getUrl(), method: req.getMethod(), clientIp: req.getRemoteAddress() });
112
+ await frame.run(() => workflow.apply('request', new HttpRequestEvent(httpInjectorContext, req, res)));
113
+ } else {
114
+ await workflow.apply('request', new HttpRequestEvent(httpInjectorContext, req, res));
45
115
  }
46
- })(new Socket());
116
+ } catch (error: any) {
117
+ if (!res.headersSent) {
118
+ const resultFormatter = httpInjectorContext.get(HttpResultFormatter);
119
+ if (error instanceof ValidationError) {
120
+ resultFormatter.handle(new JSONResponse({
121
+ message: error.message,
122
+ errors: error.errors,
123
+ }, 400).disableAutoSerializing(), { request: req, response: res });
124
+ return;
125
+ } else if (error instanceof HttpError) {
126
+ if (req.throwErrorOnNotFound && error.httpCode === 404) {
127
+ throw error;
128
+ }
129
+ resultFormatter.handle(new JSONResponse({
130
+ message: error.message,
131
+ }, error.httpCode).disableAutoSerializing(), { request: req, response: res });
132
+ return;
133
+ }
47
134
 
48
- let result: any = 'nothing';
49
- const response = new (class extends HttpResponse {
50
- end(chunk: any) {
51
- result = chunk ? chunk.toString() : chunk;
135
+ res.status(500);
52
136
  }
53
137
 
54
- write(chunk: any): boolean {
55
- result = chunk ? chunk.toString() : chunk;
56
- return true;
138
+ this.logger.error('HTTP kernel request failed', error);
139
+ } finally {
140
+ for (const file of Object.values(req.uploadedFiles || [])) {
141
+ unlink(file.path, () => {
142
+ });
57
143
  }
58
- })(request);
59
-
60
- await this.handleRequest(request, response);
61
- if (result === '' || result === undefined || result === null) return result;
62
- try {
63
- return JSON.parse(result);
64
- } catch (error) {
65
- return result;
66
- }
67
- }
68
-
69
- async handleRequest(req: HttpRequest, res: HttpResponse) {
70
- const httpInjectorContext = this.injectorContext.createChildScope('http', new MemoryInjector([
71
- { provide: HttpRequest, useValue: req },
72
- { provide: HttpResponse, useValue: res },
73
- ]));
74
144
 
75
- //todo: How should we do that?
76
- // const collector = this.debug ? httpInjectorContext.get(HttpRequestDebugCollector) : undefined;
77
-
78
- // const workflow = httpWorkflow.create('start', this.eventDispatcher, httpInjectorContext, collector?.stopwatch);
79
- const workflow = httpWorkflow.create('start', this.eventDispatcher, httpInjectorContext);
80
-
81
- try {
82
- // if (collector) {
83
- // await collector.init();
84
- // try {
85
- // collector.stopwatch.start('http');
86
- //
87
- // //todo: How should we do that?
88
- // await Zone.run({ collector: collector }, async () => {
89
- // await workflow.apply('request', new HttpRequestEvent(httpInjectorContext, req, res));
90
- // });
91
- // collector.stopwatch.end('http');
92
- // } finally {
93
- // await collector.save();
94
- // }
95
- // } else {
96
- await workflow.apply('request', new HttpRequestEvent(httpInjectorContext, req, res));
97
- // }
98
- } catch (error) {
99
- this.logger.error('HTTP kernel request failed', error);
100
- throw error;
145
+ if (frame) {
146
+ frame.data({ responseStatus: res.statusCode });
147
+ frame.end();
148
+ }
101
149
  }
102
150
  }
103
151
  }
package/src/logger.ts CHANGED
@@ -1,17 +1,15 @@
1
- import { injectable } from '@deepkit/injector';
2
- import { Logger } from '@deepkit/logger';
1
+ import { LoggerInterface } from '@deepkit/logger';
3
2
  import { eventDispatcher } from '@deepkit/event';
4
- import { httpWorkflow } from './http';
3
+ import { httpWorkflow } from './http.js';
5
4
 
6
- @injectable()
7
5
  export class HttpLogger {
8
- constructor(private logger: Logger) {
6
+ constructor(private logger: LoggerInterface) {
9
7
  }
10
8
 
11
9
  @eventDispatcher.listen(httpWorkflow.onResponse, 101) //101 is right after 100 default listener
12
10
  onHttpRequest(event: typeof httpWorkflow.onResponse.event) {
13
11
  this.logger.log(
14
- event.request.connection.remoteAddress, '-',
12
+ event.request.connection.remoteAddress || '0.0.0.0', '-',
15
13
  event.request.method,
16
14
  `"${event.request.url}"`,
17
15
  event.response.statusCode,
@@ -0,0 +1,116 @@
1
+ import { ClassType, isClass } from '@deepkit/core';
2
+ import { AppModule } from '@deepkit/app';
3
+ import { createFreeDecoratorContext } from '@deepkit/type';
4
+ import { HttpRequest, HttpResponse } from './model.js';
5
+ import { InjectorModule } from '@deepkit/injector';
6
+
7
+ export type HttpMiddlewareFn = (req: HttpRequest, res: HttpResponse, next: (err?: any) => void) => void | Promise<void>;
8
+
9
+ export interface HttpMiddleware {
10
+ execute: HttpMiddlewareFn;
11
+ }
12
+
13
+ export interface HttpMiddlewareRoute {
14
+ path?: string;
15
+ pathRegExp?: RegExp;
16
+ httpMethod?: 'GET' | 'HEAD' | 'POST' | 'PATCH' | 'PUT' | 'DELETE' | 'OPTIONS' | 'TRACE';
17
+ category?: string;
18
+ excludeCategory?: string;
19
+ group?: string;
20
+ excludeGroup?: string;
21
+ }
22
+
23
+ export class HttpMiddlewareConfig {
24
+ name?: string;
25
+ middlewares: (HttpMiddlewareFn | ClassType<HttpMiddleware>)[] = [];
26
+
27
+ routes: HttpMiddlewareRoute[] = [];
28
+ excludeRoutes: HttpMiddlewareRoute[] = [];
29
+
30
+ order: number = 0;
31
+
32
+ controllers: ClassType[] = [];
33
+ excludeControllers: ClassType[] = [];
34
+
35
+ routeNames: string[] = [];
36
+ excludeRouteNames: string[] = [];
37
+
38
+ timeout?: number;
39
+
40
+ modules: InjectorModule<any>[] = [];
41
+
42
+ selfModule: boolean = false;
43
+
44
+ getClassTypes(): ClassType[] {
45
+ const classTypes: ClassType[] = [];
46
+ for (const middleware of this.middlewares) {
47
+ if (isClass(middleware)) classTypes.push(middleware);
48
+ }
49
+
50
+ return classTypes;
51
+ }
52
+ }
53
+
54
+ export class HttpMiddlewareApi {
55
+ t = new HttpMiddlewareConfig;
56
+
57
+ name(name: string) {
58
+ this.t.name = name;
59
+ }
60
+
61
+ for(...middlewares: (HttpMiddlewareFn | ClassType<HttpMiddleware>)[]) {
62
+ this.t.middlewares = middlewares;
63
+ }
64
+
65
+ forRoutes(...routes: HttpMiddlewareRoute[]) {
66
+ this.t.routes = routes;
67
+ }
68
+
69
+ excludeRoutes(...routes: HttpMiddlewareRoute[]) {
70
+ this.t.excludeRoutes = routes;
71
+ }
72
+
73
+ forRouteNames(...names: string[]) {
74
+ this.t.routeNames = names;
75
+ }
76
+
77
+ excludeRouteNames(...names: string[]) {
78
+ this.t.excludeRouteNames = names;
79
+ }
80
+
81
+ /**
82
+ * When the middleware does not respond (either calling next() or sending headers) withing <timeout> milliseconds,
83
+ * automatically the next is executed and warning printed.
84
+ */
85
+ timeout(timeout: number) {
86
+ this.t.timeout = timeout;
87
+ }
88
+
89
+ forControllers(...controllers: ClassType[]) {
90
+ this.t.controllers = controllers;
91
+ }
92
+
93
+ excludeControllers(...controllers: ClassType[]) {
94
+ this.t.excludeControllers = controllers;
95
+ }
96
+
97
+ /**
98
+ * Per default middlewares are executed in the order they were registered. The default order is 0. A lower order means the middleware is executed earlier.
99
+ */
100
+ order(order: number) {
101
+ this.t.order = order;
102
+ }
103
+
104
+ forModules(...modules: AppModule<any>[]) {
105
+ this.t.modules = modules;
106
+ }
107
+
108
+ /**
109
+ * Limit the middleware to the module where this middleware is defined.
110
+ */
111
+ forSelfModules() {
112
+ this.t.selfModule = true;
113
+ }
114
+ }
115
+
116
+ export const httpMiddleware = createFreeDecoratorContext(HttpMiddlewareApi);