@deepkit/http 1.0.1-alpha.59 → 1.0.1-alpha.61

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 (142) hide show
  1. package/coverage/clover.xml +1086 -0
  2. package/coverage/coverage-final.json +12 -0
  3. package/coverage/lcov-report/base.css +224 -0
  4. package/coverage/lcov-report/block-navigation.js +79 -0
  5. package/coverage/lcov-report/favicon.png +0 -0
  6. package/coverage/lcov-report/index.html +126 -0
  7. package/coverage/lcov-report/prettify.css +1 -0
  8. package/coverage/lcov-report/prettify.js +2 -0
  9. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  10. package/coverage/lcov-report/sorter.js +170 -0
  11. package/coverage/lcov-report/src/controllers.ts.html +113 -0
  12. package/coverage/lcov-report/src/decorator.ts.html +1280 -0
  13. package/coverage/lcov-report/src/filter.ts.html +734 -0
  14. package/coverage/lcov-report/src/http.ts.html +2192 -0
  15. package/coverage/lcov-report/src/index.html +246 -0
  16. package/coverage/lcov-report/src/kernel.ts.html +260 -0
  17. package/coverage/lcov-report/src/middleware.ts.html +428 -0
  18. package/coverage/lcov-report/src/model.ts.html +937 -0
  19. package/coverage/lcov-report/src/module.config.ts.html +89 -0
  20. package/coverage/lcov-report/src/module.ts.html +245 -0
  21. package/coverage/lcov-report/src/router.ts.html +2318 -0
  22. package/coverage/lcov-report/tests/index.html +111 -0
  23. package/coverage/lcov-report/tests/utils.ts.html +176 -0
  24. package/coverage/lcov.info +2225 -0
  25. package/dist/cjs/src/controllers.d.ts +4 -4
  26. package/dist/cjs/src/controllers.js +3 -0
  27. package/dist/cjs/src/controllers.js.map +1 -1
  28. package/dist/cjs/src/decorator.d.ts +29 -94
  29. package/dist/cjs/src/decorator.js +53 -118
  30. package/dist/cjs/src/decorator.js.map +1 -1
  31. package/dist/cjs/src/filter.d.ts +1 -0
  32. package/dist/cjs/src/filter.js +11 -17
  33. package/dist/cjs/src/filter.js.map +1 -1
  34. package/dist/cjs/src/http.d.ts +5 -3
  35. package/dist/cjs/src/http.js +91 -55
  36. package/dist/cjs/src/http.js.map +1 -1
  37. package/dist/cjs/src/kernel.d.ts +3 -3
  38. package/dist/cjs/src/kernel.js +5 -25
  39. package/dist/cjs/src/kernel.js.map +1 -1
  40. package/dist/cjs/src/logger.d.ts +2 -2
  41. package/dist/cjs/src/logger.js +4 -8
  42. package/dist/cjs/src/logger.js.map +1 -1
  43. package/dist/cjs/src/middleware.d.ts +5 -1
  44. package/dist/cjs/src/middleware.js +15 -3
  45. package/dist/cjs/src/middleware.js.map +1 -1
  46. package/dist/cjs/src/model.d.ts +60 -0
  47. package/dist/cjs/src/model.js +95 -24
  48. package/dist/cjs/src/model.js.map +1 -1
  49. package/dist/cjs/src/module.config.d.ts +3 -4
  50. package/dist/cjs/src/module.config.js +8 -6
  51. package/dist/cjs/src/module.config.js.map +1 -1
  52. package/dist/cjs/src/module.d.ts +2 -3
  53. package/dist/cjs/src/module.js +4 -2
  54. package/dist/cjs/src/module.js.map +1 -1
  55. package/dist/cjs/src/router.d.ts +24 -53
  56. package/dist/cjs/src/router.js +140 -159
  57. package/dist/cjs/src/router.js.map +1 -1
  58. package/dist/cjs/src/static-serving.d.ts +1 -0
  59. package/dist/cjs/src/static-serving.js +38 -34
  60. package/dist/cjs/src/static-serving.js.map +1 -1
  61. package/dist/cjs/src/utils.js +1 -0
  62. package/dist/cjs/tests/filter.spec.js +61 -47
  63. package/dist/cjs/tests/filter.spec.js.map +1 -1
  64. package/dist/cjs/tests/middleware.spec.d.ts +1 -1
  65. package/dist/cjs/tests/middleware.spec.js +157 -145
  66. package/dist/cjs/tests/middleware.spec.js.map +1 -1
  67. package/dist/cjs/tests/model.spec.js +4 -4
  68. package/dist/cjs/tests/model.spec.js.map +1 -1
  69. package/dist/cjs/tests/module.spec.js +4 -3
  70. package/dist/cjs/tests/module.spec.js.map +1 -1
  71. package/dist/cjs/tests/router.spec.d.ts +1 -1
  72. package/dist/cjs/tests/router.spec.js +298 -333
  73. package/dist/cjs/tests/router.spec.js.map +1 -1
  74. package/dist/cjs/tests/utils.js +5 -2
  75. package/dist/cjs/tests/utils.js.map +1 -1
  76. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  77. package/dist/esm/src/controllers.d.ts +4 -4
  78. package/dist/esm/src/controllers.js +3 -0
  79. package/dist/esm/src/controllers.js.map +1 -1
  80. package/dist/esm/src/decorator.d.ts +29 -94
  81. package/dist/esm/src/decorator.js +49 -114
  82. package/dist/esm/src/decorator.js.map +1 -1
  83. package/dist/esm/src/filter.d.ts +1 -0
  84. package/dist/esm/src/filter.js +11 -18
  85. package/dist/esm/src/filter.js.map +1 -1
  86. package/dist/esm/src/http.d.ts +5 -3
  87. package/dist/esm/src/http.js +89 -55
  88. package/dist/esm/src/http.js.map +1 -1
  89. package/dist/esm/src/kernel.d.ts +3 -3
  90. package/dist/esm/src/kernel.js +6 -27
  91. package/dist/esm/src/kernel.js.map +1 -1
  92. package/dist/esm/src/logger.d.ts +2 -2
  93. package/dist/esm/src/logger.js +4 -9
  94. package/dist/esm/src/logger.js.map +1 -1
  95. package/dist/esm/src/middleware.d.ts +5 -1
  96. package/dist/esm/src/middleware.js +12 -0
  97. package/dist/esm/src/middleware.js.map +1 -1
  98. package/dist/esm/src/model.d.ts +60 -0
  99. package/dist/esm/src/model.js +91 -23
  100. package/dist/esm/src/model.js.map +1 -1
  101. package/dist/esm/src/module.config.d.ts +3 -4
  102. package/dist/esm/src/module.config.js +6 -5
  103. package/dist/esm/src/module.config.js.map +1 -1
  104. package/dist/esm/src/module.d.ts +2 -3
  105. package/dist/esm/src/module.js +5 -3
  106. package/dist/esm/src/module.js.map +1 -1
  107. package/dist/esm/src/router.d.ts +24 -53
  108. package/dist/esm/src/router.js +136 -155
  109. package/dist/esm/src/router.js.map +1 -1
  110. package/dist/esm/src/static-serving.d.ts +1 -0
  111. package/dist/esm/src/static-serving.js +29 -25
  112. package/dist/esm/src/static-serving.js.map +1 -1
  113. package/dist/esm/src/utils.js +1 -0
  114. package/dist/esm/tests/filter.spec.js +14 -0
  115. package/dist/esm/tests/filter.spec.js.map +1 -1
  116. package/dist/esm/tests/middleware.spec.d.ts +1 -1
  117. package/dist/esm/tests/middleware.spec.js +60 -48
  118. package/dist/esm/tests/middleware.spec.js.map +1 -1
  119. package/dist/esm/tests/module.spec.js +1 -0
  120. package/dist/esm/tests/module.spec.js.map +1 -1
  121. package/dist/esm/tests/router.spec.d.ts +1 -1
  122. package/dist/esm/tests/router.spec.js +167 -202
  123. package/dist/esm/tests/router.spec.js.map +1 -1
  124. package/dist/esm/tests/utils.js +4 -1
  125. package/dist/esm/tests/utils.js.map +1 -1
  126. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  127. package/package.json +14 -15
  128. package/src/controllers.ts +3 -3
  129. package/src/decorator.ts +42 -131
  130. package/src/filter.ts +0 -2
  131. package/src/http.ts +35 -36
  132. package/src/kernel.ts +4 -5
  133. package/src/logger.ts +2 -4
  134. package/src/middleware.ts +2 -1
  135. package/src/model.ts +77 -5
  136. package/src/module.config.ts +3 -6
  137. package/src/module.ts +2 -2
  138. package/src/router.ts +121 -161
  139. package/src/static-serving.ts +2 -4
  140. package/tests/middleware.spec.ts +0 -1
  141. package/tests/router.spec.ts +131 -133
  142. package/tsconfig.json +2 -1
package/src/decorator.ts CHANGED
@@ -11,27 +11,25 @@
11
11
  import { ClassType, isClass, urlJoin } from '@deepkit/core';
12
12
  import {
13
13
  ClassDecoratorResult,
14
- ClassSchema,
15
14
  createClassDecoratorContext,
16
15
  createPropertyDecoratorContext,
17
- FieldDecoratorResult,
18
- getClassSchema,
16
+ DecoratorAndFetchSignature,
17
+ DualDecorator,
18
+ ExtractApiDataType,
19
+ ExtractClass,
19
20
  isDecoratorContext,
20
- isFieldDecorator,
21
- JitConverterOptions,
22
21
  mergeDecorator,
23
- PropertyDecoratorResult,
24
- PropertySchema,
22
+ PropertyDecoratorFn,
23
+ ReceiveType,
24
+ resolveReceiveType,
25
+ SerializationOptions,
25
26
  Serializer,
26
- t
27
+ Type,
28
+ UnionToIntersection
27
29
  } from '@deepkit/type';
28
30
  import { RouteParameterResolver } from './router';
29
31
  import { httpMiddleware, HttpMiddleware, HttpMiddlewareConfig, HttpMiddlewareFn } from './middleware';
30
32
 
31
- export interface ControllerOptions {
32
- name: string;
33
- }
34
-
35
33
  type HttpActionMiddleware = (() => HttpMiddlewareConfig) | ClassType<HttpMiddleware> | HttpMiddlewareFn;
36
34
 
37
35
  function isMiddlewareClassTypeOrFn(v: HttpActionMiddleware): v is ClassType<HttpMiddleware> | HttpMiddlewareFn {
@@ -99,24 +97,17 @@ export class HttpAction {
99
97
  groups: string[] = [];
100
98
  serializer?: Serializer;
101
99
  middlewares: (() => HttpMiddlewareConfig)[] = [];
102
- serializationOptions?: JitConverterOptions;
103
-
104
- parameterRegularExpressions: { [name: string]: any } = {};
100
+ serializationOptions?: SerializationOptions;
105
101
 
106
102
  resolverForToken: Map<any, ClassType> = new Map();
107
103
  resolverForParameterName: Map<string, ClassType> = new Map();
108
104
 
109
- /**
110
- * This is only filled when the user used @http.body() for example on an method argument.
111
- */
112
- parameters: { [name: string]: HttpActionParameter } = {};
113
-
114
105
  /**
115
106
  * An arbitrary data container the user can use to store app specific settings/values.
116
107
  */
117
108
  data = new Map<any, any>();
118
109
 
119
- responses: { statusCode: number, description: string, type?: PropertySchema }[] = [];
110
+ responses: { statusCode: number, description: string, type?: Type }[] = [];
120
111
  }
121
112
 
122
113
  export class HttpDecorator {
@@ -196,7 +187,8 @@ export const httpClass: ClassDecoratorResult<typeof HttpDecorator> = createClass
196
187
  export class HttpActionDecorator {
197
188
  t = new HttpAction;
198
189
 
199
- onDecorator(target: ClassType, property: string) {
190
+ onDecorator(target: ClassType, property: string | undefined, parameterIndexOrDescriptor?: any) {
191
+ if (!property) return;
200
192
  this.t.methodName = property;
201
193
  httpClass.setAction(this.t)(target);
202
194
  }
@@ -205,15 +197,11 @@ export class HttpActionDecorator {
205
197
  this.t.name = name;
206
198
  }
207
199
 
208
- setParameter(name: string, parameter: HttpActionParameter) {
209
- this.t.parameters[name] = parameter;
210
- }
211
-
212
200
  description(description: string) {
213
201
  this.t.description = description;
214
202
  }
215
203
 
216
- serialization(options: JitConverterOptions) {
204
+ serialization(options: SerializationOptions) {
217
205
  this.t.serializationOptions = options;
218
206
  }
219
207
 
@@ -298,12 +286,12 @@ export class HttpActionDecorator {
298
286
  }
299
287
 
300
288
  OPTIONS(path: string = '') {
301
- this.t.httpMethods.push('HEAD');
289
+ this.t.httpMethods.push('OPTIONS');
302
290
  if (path) this.t.path = path;
303
291
  }
304
292
 
305
293
  TRACE(path: string = '') {
306
- this.t.httpMethods.push('HEAD');
294
+ this.t.httpMethods.push('TRACE');
307
295
  if (path) this.t.path = path;
308
296
  }
309
297
 
@@ -319,31 +307,29 @@ export class HttpActionDecorator {
319
307
 
320
308
  /**
321
309
  * Adds additional information about what HTTP status codes are available in this route.
322
- * You can add additionally a description and a class type.
310
+ * You can add additionally a description and a response body type.
323
311
  *
324
- * The class type is used for serialization for responses with the given statusCode.
312
+ * The type is used for serialization for responses with the given statusCode.
325
313
  *
326
- * Those information are available in Deepkit API console.
327
- *
328
- * classType can be a schema, class, or deepkit/type t.
314
+ * This information is available in Deepkit API console.
329
315
  *
330
316
  * ```typescript
331
317
  *
332
- * @http.GET().response(200, 'All ok', t.boolean)
318
+ * @http.GET().response<boolean>(200, 'All ok')
333
319
  *
334
- * class User {
335
- * @t username: string = '';
320
+ * interface User {
321
+ * username: string;
336
322
  * }
337
- * @http.GET().response(200, 'User object', User)
338
- *
323
+ * @http.GET().response<User>(200, 'User object')
339
324
  *
340
- * const error = t.schema({error: t.string});
341
- * @http.GET().response(500, 'Error', error)
325
+ * interface HttpErrorMessage {
326
+ * error: string;
327
+ * }
328
+ * @http.GET().response<HttpErrorMessage>(500, 'Error')
342
329
  * ```
343
330
  */
344
- response(statusCode: number, description: string = '', classType?: FieldDecoratorResult<any> | ClassType | ClassSchema,) {
345
- const type = classType ? (isFieldDecorator(classType) ? classType.buildPropertySchema() : t.type(getClassSchema(classType)).buildPropertySchema()) : undefined;
346
- this.t.responses.push({ statusCode, description, type });
331
+ response<T>(statusCode: number, description: string = '', type?: ReceiveType<T>) {
332
+ this.t.responses.push({ statusCode, description, type: type ? resolveReceiveType(type) : undefined });
347
333
  }
348
334
 
349
335
  /**
@@ -395,95 +381,20 @@ export class HttpActionDecorator {
395
381
  resolveParameterByName(name: string, resolver: ClassType<RouteParameterResolver>) {
396
382
  this.t.resolverForParameterName.set(name, resolver);
397
383
  }
398
-
399
- regexp(parameterName: string, regex: any) {
400
- this.t.parameterRegularExpressions[parameterName] = regex;
401
- }
402
384
  }
403
385
 
404
- class HttpActionParameterDecorator {
405
- t = new HttpActionParameter();
406
-
407
- onDecorator(target: ClassType, propertyName?: string, parameterIndex?: number) {
408
- if (!propertyName) throw new Error('@http action parameter decorator can only be used on method arguments.');
409
- if (parameterIndex === undefined) throw new Error('@http action parameter decorator can only be used on method arguments.');
410
- const schema = getClassSchema(target);
411
- const property = schema.getMethodProperties(propertyName)[parameterIndex];
412
- this.t.name = property.name;
413
- if (this.t.typePath === undefined) {
414
- this.t.typePath = property.name;
415
- }
416
- httpAction.setParameter(property.name, this.t)(target.prototype, propertyName);
417
- }
418
-
419
- /**
420
- * Marks the argument as body parameter. Data from the client sent in the body
421
- * will be tried to parsed (JSON/form data) and deserialized to the defined type.
422
- * Make sure the class type as a schema assigned.
423
- *
424
- * @example
425
- * ```typescript
426
- * class MyActionBody {
427
- * @t name!: string;
428
- * }
429
- *
430
- * class Controller {
431
- * @http.GET()
432
- * myAction(@http.body() body: MyActionBody) {
433
- * console.log('body', body.name);
434
- * }
435
- * }
436
- * ```
437
- */
438
- body() {
439
- this.t.type = 'body';
440
- this.t.typePath = ''; //root
441
- }
442
-
443
- query(path?: string) {
444
- this.t.typePath = path; //undefined === propertyName
445
- this.t.type = 'query';
446
- }
447
-
448
- get optional() {
449
- this.t.optional = true;
450
- return;
451
- }
452
-
453
- /**
454
- * Marks the argument as query parameter. Data from the query string is parsed
455
- * and deserialized to the defined type.
456
- * Define a `path` if you want to parse a subset of the query string only.
457
- *
458
- * Note: Make sure the defined parameter type has optional properties,
459
- * otherwise it's always required to pass a query string.
460
- *
461
- * @example
462
- * ```typescript
463
- * class MyActionQueries {
464
- * @t.optional name?: string;
465
- * }
466
- *
467
- * class Controller {
468
- * @http.GET('my-action')
469
- * myAction(@http.queries() query: MyActionQueries) {
470
- * console.log('query', query.name);
471
- * }
472
- * }
473
- *
474
- * // Open via, e.g.
475
- * // -> /my-action?name=Peter
476
- * ```
477
- *
478
- */
479
- queries(path: string = '') {
480
- this.t.typePath = path; //'' === root
481
- this.t.type = 'queries';
482
- }
483
- }
386
+ //this workaround is necessary since generic functions (necessary for response<T>) are lost during a mapped type and changed ReturnType
387
+ type HttpActionFluidDecorator<T, D extends Function> = {
388
+ [name in keyof T]: name extends 'response' ? <T2>(statusCode: number, description?: string, type?: ReceiveType<T2>) => D & HttpActionFluidDecorator<T, D>
389
+ : T[name] extends (...args: infer K) => any ? (...args: K) => D & HttpActionFluidDecorator<T, D>
390
+ : D & HttpActionFluidDecorator<T, D> & { _data: ExtractApiDataType<T> };
391
+ };
392
+ type HttpActionPropertyDecoratorResult = HttpActionFluidDecorator<ExtractClass<typeof HttpActionDecorator>, PropertyDecoratorFn> & DecoratorAndFetchSignature<typeof HttpActionDecorator, PropertyDecoratorFn>;
484
393
 
485
- export const httpAction: PropertyDecoratorResult<typeof HttpActionDecorator> = createPropertyDecoratorContext(HttpActionDecorator);
394
+ export const httpAction: HttpActionPropertyDecoratorResult = createPropertyDecoratorContext(HttpActionDecorator);
486
395
 
487
- export const httpActionParameter: PropertyDecoratorResult<typeof HttpActionParameterDecorator> = createPropertyDecoratorContext(HttpActionParameterDecorator);
396
+ //this workaround is necessary since generic functions are lost during a mapped type and changed ReturnType
397
+ type HttpMerge<U> = { [K in keyof U]: K extends 'response' ? <T2>(statusCode: number, description?: string, type?: ReceiveType<T2>) => PropertyDecoratorFn & U : U[K] extends ((...a: infer A) => infer R) ? R extends DualDecorator ? (...a: A) => PropertyDecoratorFn & R & U : (...a: A) => R : never };
398
+ type MergedHttp<T extends any[]> = HttpMerge<Omit<UnionToIntersection<T[number]>, '_fetch' | 't'>>
488
399
 
489
- export const http = mergeDecorator(httpClass, httpAction, httpActionParameter);
400
+ export const http: MergedHttp<[typeof httpClass, typeof httpAction]> = mergeDecorator(httpClass, httpAction) as any as MergedHttp<[typeof httpClass, typeof httpAction]>;
package/src/filter.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ClassType } from '@deepkit/core';
2
2
  import { AppModule } from '@deepkit/app';
3
3
  import { RouteConfig, Router } from './router';
4
- import { injectable } from '@deepkit/injector';
5
4
 
6
5
  export interface HttpRouteFilterRoute {
7
6
  path?: string;
@@ -57,7 +56,6 @@ function match(routeConfig: RouteConfig, route: HttpRouteFilterRoute): boolean {
57
56
  return false;
58
57
  }
59
58
 
60
- @injectable
61
59
  export class HttpRouterFilterResolver {
62
60
  constructor(protected router: Router) {
63
61
  }
package/src/http.ts CHANGED
@@ -8,17 +8,17 @@
8
8
  * You should have received a copy of the MIT License along with this program.
9
9
  */
10
10
 
11
- import { asyncOperation, ClassType, CustomError, getClassName } from '@deepkit/core';
12
- import { getClassTypeFromInstance, getPropertyClassToXFunction, isClassInstance, isRegisteredEntity, jsonSerializer, ValidationFailed } from '@deepkit/type';
11
+ import { asyncOperation, ClassType, CustomError, getClassName, getClassTypeFromInstance, isClassInstance } from '@deepkit/core';
13
12
  import { OutgoingHttpHeaders, ServerResponse } from 'http';
14
13
  import { eventDispatcher } from '@deepkit/event';
15
14
  import { HttpRequest, HttpResponse } from './model';
16
- import { inject, injectable, InjectorContext } from '@deepkit/injector';
17
- import { Logger } from '@deepkit/logger';
15
+ import { InjectorContext } from '@deepkit/injector';
16
+ import { LoggerInterface } from '@deepkit/logger';
18
17
  import { RouteConfig, RouteParameterResolverForInjector, Router } from './router';
19
18
  import { createWorkflow, WorkflowEvent } from '@deepkit/workflow';
20
19
  import type { ElementStruct, render } from '@deepkit/template';
21
20
  import { FrameCategory, Stopwatch } from '@deepkit/stopwatch';
21
+ import { getSerializeFunction, hasTypeInformation, ReflectionKind, resolveReceiveType, SerializationError, serialize, serializer, ValidationError } from '@deepkit/type';
22
22
 
23
23
  export function isElementStruct(v: any): v is ElementStruct {
24
24
  return 'object' === typeof v && v.hasOwnProperty('render') && v.hasOwnProperty('attributes') && !v.slice;
@@ -350,7 +350,6 @@ export interface HttpResultFormatterContext {
350
350
  route?: RouteConfig;
351
351
  }
352
352
 
353
- @injectable
354
353
  export class HttpResultFormatter {
355
354
  protected jsonContentType: string = 'application/json; charset=utf-8';
356
355
  protected htmlContentType: string = 'text/html; charset=utf-8';
@@ -406,14 +405,9 @@ export class HttpResultFormatter {
406
405
  handleTypeEntity<T>(classType: ClassType<T>, instance: T, context: HttpResultFormatterContext, route?: RouteConfig): void {
407
406
  this.setContentTypeIfNotSetAlready(context.response, this.jsonContentType);
408
407
 
409
- context.response.end(JSON.stringify(
410
- (route && route?.serializer ? route.serializer : jsonSerializer)
411
- .for(classType).serialize(
412
- instance,
413
- route ? route.serializationOptions : undefined
414
- )
415
- )
416
- );
408
+ const serializerToUse = route && route?.serializer ? route.serializer : serializer;
409
+
410
+ context.response.end(JSON.stringify(serialize(instance, route ? route.serializationOptions : undefined, serializerToUse, resolveReceiveType(classType))));
417
411
  }
418
412
 
419
413
  handleBinary(result: Uint8Array, context: HttpResultFormatterContext): void {
@@ -441,7 +435,7 @@ export class HttpResultFormatter {
441
435
  } else {
442
436
  if (isClassInstance(result)) {
443
437
  const classType = getClassTypeFromInstance(result);
444
- if (isRegisteredEntity(classType)) {
438
+ if (hasTypeInformation(classType)) {
445
439
  this.handleTypeEntity(classType, result, context);
446
440
  return;
447
441
  }
@@ -452,13 +446,12 @@ export class HttpResultFormatter {
452
446
  }
453
447
  }
454
448
 
455
- @injectable
456
449
  export class HttpListener {
457
450
  constructor(
458
451
  protected router: Router,
459
- protected logger: Logger,
452
+ protected logger: LoggerInterface,
460
453
  protected resultFormatter: HttpResultFormatter,
461
- @inject().optional protected stopwatch?: Stopwatch,
454
+ protected stopwatch?: Stopwatch,
462
455
  ) {
463
456
  }
464
457
 
@@ -573,7 +566,7 @@ export class HttpListener {
573
566
  try {
574
567
  event.parameters = await event.parameterResolver(event.injectorContext);
575
568
  event.next('controller', new HttpControllerEvent(event.injectorContext, event.request, event.response, event.parameters, event.route));
576
- } catch (error) {
569
+ } catch (error: any) {
577
570
  event.next('parametersFailed', new HttpControllerErrorEvent(event.injectorContext, event.request, event.response, event.route, error));
578
571
  }
579
572
  }
@@ -606,7 +599,7 @@ export class HttpListener {
606
599
  const responseEvent = new HttpResponseEvent(event.injectorContext, event.request, event.response, result, event.route);
607
600
  responseEvent.controllerActionTime = Date.now() - start;
608
601
  event.next('response', responseEvent);
609
- } catch (error) {
602
+ } catch (error: any) {
610
603
  if (frame) frame.end();
611
604
  if (error instanceof HttpAccessDeniedError) {
612
605
  event.next('accessDenied', new HttpAccessDeniedEvent(event.injectorContext, event.request, event.response, event.route));
@@ -625,7 +618,16 @@ export class HttpListener {
625
618
  if (event.response.finished) return;
626
619
  if (event.sent) return;
627
620
 
628
- if (event.error instanceof ValidationFailed) {
621
+ if (event.error instanceof SerializationError) {
622
+ event.send(new JSONResponse({
623
+ message: event.error.message,
624
+ errors: [{
625
+ path: event.error.path,
626
+ code: 'serialization',
627
+ message: event.error.originalMessage,
628
+ }]
629
+ }, 400).disableAutoSerializing());
630
+ } else if (event.error instanceof ValidationError) {
629
631
  event.send(new JSONResponse({
630
632
  message: event.error.message,
631
633
  errors: event.error.errors
@@ -642,7 +644,7 @@ export class HttpListener {
642
644
  if (event.response.finished) return;
643
645
  if (event.sent) return;
644
646
 
645
- if (event.error instanceof ValidationFailed) {
647
+ if (event.error instanceof ValidationError) {
646
648
  event.send(new JSONResponse({
647
649
  message: event.error.message,
648
650
  errors: event.error.errors
@@ -672,28 +674,25 @@ export class HttpListener {
672
674
  if (event.result instanceof HtmlResponse || event.result instanceof ServerResponse || event.result instanceof Redirect) {
673
675
  // don't do anything
674
676
  } else if (event.result instanceof JSONResponse) {
675
- const schema = (event.result._statusCode && event.route.getSchemaForResponse(event.result._statusCode)) || event.route.returnSchema;
677
+ const schema = (event.result._statusCode && event.route.getSchemaForResponse(event.result._statusCode)) || event.route.returnType;
676
678
 
677
679
  if (!schema || !event.result.autoSerializing) return;
678
680
 
679
- event.result.json = getPropertyClassToXFunction(
680
- schema,
681
- event.route && event.route.serializer ? event.route.serializer : jsonSerializer
682
- )(event.result.json, event.route.serializationOptions);
683
- } else if (event.route.returnSchema && (!event.route.returnSchema.isPromise || event.route.returnSchema.getSubType())) {
684
- event.result = getPropertyClassToXFunction(
685
- event.route.returnSchema,
686
- event.route && event.route.serializer ? event.route.serializer : jsonSerializer
687
- )(event.result, event.route.serializationOptions);
681
+ const serializerToUse = event.route && event.route.serializer ? event.route.serializer : serializer;
682
+ const serialize = getSerializeFunction(schema, serializerToUse.serializeRegistry);
683
+ event.result.json = serialize(event.result.json, event.route.serializationOptions);
684
+ } else if (event.route.returnType && event.route.returnType.kind !== ReflectionKind.any) {
685
+ const serializerToUse = event.route && event.route.serializer ? event.route.serializer : serializer;
686
+ const serialize = getSerializeFunction(event.route.returnType, serializerToUse.serializeRegistry);
687
+ event.result = serialize(event.result, event.route.serializationOptions);
688
688
  } else {
689
689
  const schema = event.route.getSchemaForResponse(200);
690
690
  if (!schema) return;
691
- event.result = getPropertyClassToXFunction(
692
- schema,
693
- event.route && event.route.serializer ? event.route.serializer : jsonSerializer
694
- )(event.result, event.route.serializationOptions);
695
- }
696
691
 
692
+ const serializerToUse = event.route && event.route.serializer ? event.route.serializer : serializer;
693
+ const serialize = getSerializeFunction(schema, serializerToUse.serializeRegistry);
694
+ event.result = serialize(event.result, event.route.serializationOptions);
695
+ }
697
696
  }
698
697
 
699
698
  @eventDispatcher.listen(httpWorkflow.onResponse, 100)
package/src/kernel.ts CHANGED
@@ -1,20 +1,19 @@
1
- import { inject, injectable, InjectorContext } from '@deepkit/injector';
1
+ import { InjectorContext } from '@deepkit/injector';
2
2
  import { Router } from './router';
3
3
  import { EventDispatcher } from '@deepkit/event';
4
- import { Logger } from '@deepkit/logger';
4
+ import { LoggerInterface } from '@deepkit/logger';
5
5
  import { HttpRequest, HttpResponse, MemoryHttpResponse, RequestBuilder } from './model';
6
6
  import { HttpRequestEvent, httpWorkflow } from './http';
7
7
  import { FrameCategory, Stopwatch } from '@deepkit/stopwatch';
8
8
  import { unlink } from 'fs';
9
9
 
10
- @injectable
11
10
  export class HttpKernel {
12
11
  constructor(
13
12
  protected router: Router,
14
13
  protected eventDispatcher: EventDispatcher,
15
14
  protected injectorContext: InjectorContext,
16
- protected logger: Logger,
17
- @inject().optional protected stopwatch?: Stopwatch,
15
+ protected logger: LoggerInterface,
16
+ protected stopwatch?: Stopwatch,
18
17
  ) {
19
18
 
20
19
  }
package/src/logger.ts CHANGED
@@ -1,11 +1,9 @@
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
3
  import { httpWorkflow } from './http';
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
package/src/middleware.ts CHANGED
@@ -2,6 +2,7 @@ import { ClassType, isClass } from '@deepkit/core';
2
2
  import { AppModule } from '@deepkit/app';
3
3
  import { createFreeDecoratorContext } from '@deepkit/type';
4
4
  import { HttpRequest, HttpResponse } from './model';
5
+ import { InjectorModule } from '@deepkit/injector';
5
6
 
6
7
  export type HttpMiddlewareFn = (req: HttpRequest, res: HttpResponse, next: (err?: any) => void) => void | Promise<void>;
7
8
 
@@ -36,7 +37,7 @@ export class HttpMiddlewareConfig {
36
37
 
37
38
  timeout: number = 4_000;
38
39
 
39
- modules: AppModule<any>[] = [];
40
+ modules: InjectorModule<any>[] = [];
40
41
 
41
42
  selfModule: boolean = false;
42
43
 
package/src/model.ts CHANGED
@@ -12,6 +12,7 @@ import { IncomingMessage, ServerResponse } from 'http';
12
12
  import { UploadedFile } from './router';
13
13
  import * as querystring from 'querystring';
14
14
  import { Writable } from 'stream';
15
+ import { metaAnnotation, ReflectionKind, Type, ValidationErrorItem } from '@deepkit/type';
15
16
 
16
17
  export class HttpResponse extends ServerResponse {
17
18
  status(code: number) {
@@ -23,6 +24,68 @@ export class HttpResponse extends ServerResponse {
23
24
  export type HttpRequestQuery = { [name: string]: string };
24
25
  export type HttpRequestResolvedParameters = { [name: string]: any };
25
26
 
27
+ export class BodyValidationError {
28
+ constructor(
29
+ public readonly errors: ValidationErrorItem[] = []
30
+ ) {
31
+ }
32
+
33
+ hasErrors(prefix?: string): boolean {
34
+ return this.getErrors(prefix).length > 0;
35
+ }
36
+
37
+ getErrors(prefix?: string): ValidationErrorItem[] {
38
+ if (prefix) return this.errors.filter(v => v.path.startsWith(prefix));
39
+
40
+ return this.errors;
41
+ }
42
+
43
+ getErrorsForPath(path: string): ValidationErrorItem[] {
44
+ return this.errors.filter(v => v.path === path);
45
+ }
46
+
47
+ getErrorMessageForPath(path: string): string {
48
+ return this.getErrorsForPath(path).map(v => v.message).join(', ');
49
+ }
50
+ }
51
+
52
+ export class ValidatedBody<T> {
53
+ constructor(public error: BodyValidationError, public value?: T) {
54
+ }
55
+
56
+ valid(): this is { value: T } {
57
+ return this.value !== undefined;
58
+ }
59
+ }
60
+
61
+ export type HttpBody<T> = T & { __meta?: ['httpBody'] };
62
+ export type HttpBodyValidation<T> = ValidatedBody<T> & { __meta?: ['httpBodyValidation'] };
63
+ export type HttpQuery<T, Options extends { name?: string } = {}> = T & { __meta?: ['httpQuery', Options] };
64
+ export type HttpQueries<T, Options extends { name?: string } = {}> = T & { __meta?: ['httpQueries', Options] };
65
+
66
+ /**
67
+ * For all parameters used in the URL path, a regular expression of /[^/]+/ is used. To change that, use getRegExp.
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ *
72
+ * class Controller {
73
+ * @http.GET('/user/:username')
74
+ * route(username: HttpRegExp<string, '.*'>) {
75
+ * //username is string
76
+ * }
77
+ * }
78
+ * ```
79
+ */
80
+ export type HttpRegExp<T, Pattern extends string> = T & { __meta?: ['httpRegExp', Pattern] };
81
+
82
+ export function getRegExp(type: Type): string | undefined {
83
+ const options = metaAnnotation.getForName(type, 'httpRegExp');
84
+ if (!options || !options[0]) return;
85
+ if (options[0].kind !== ReflectionKind.literal || 'string' !== typeof options[0].literal) return;
86
+ return options[0].literal;
87
+ }
88
+
26
89
  export class RequestBuilder {
27
90
  protected contentBuffer: Buffer = Buffer.alloc(0);
28
91
  protected _headers: { [name: string]: string } = {};
@@ -48,8 +111,9 @@ export class RequestBuilder {
48
111
  const bodyContent = this.contentBuffer;
49
112
 
50
113
  const writable = new Writable({
51
- write(chunk, encoding, callback) {-
52
- callback();
114
+ write(chunk, encoding, callback) {
115
+ -
116
+ callback();
53
117
  },
54
118
  writev(chunks, callback) {
55
119
  callback();
@@ -69,7 +133,7 @@ export class RequestBuilder {
69
133
  this.push(bodyContent);
70
134
  process.nextTick(() => {
71
135
  this.emit('end');
72
- })
136
+ });
73
137
  this.done = true;
74
138
  }
75
139
  }
@@ -136,6 +200,14 @@ export class HttpRequest extends IncomingMessage {
136
200
  return new RequestBuilder(path, 'OPTIONS');
137
201
  }
138
202
 
203
+ static TRACE(path: string): RequestBuilder {
204
+ return new RequestBuilder(path, 'TRACE');
205
+ }
206
+
207
+ static HEAD(path: string): RequestBuilder {
208
+ return new RequestBuilder(path, 'HEAD');
209
+ }
210
+
139
211
  static PATCH(path: string): RequestBuilder {
140
212
  return new RequestBuilder(path, 'PATCH');
141
213
  }
@@ -157,7 +229,7 @@ export class HttpRequest extends IncomingMessage {
157
229
  }
158
230
 
159
231
  getRemoteAddress(): string {
160
- return this.connection.remoteAddress || '';
232
+ return this.socket.remoteAddress || '';
161
233
  }
162
234
  }
163
235
 
@@ -168,7 +240,7 @@ export class MemoryHttpResponse extends HttpResponse {
168
240
  const json = this.bodyString;
169
241
  try {
170
242
  return JSON.parse(json);
171
- } catch (error) {
243
+ } catch (error: any) {
172
244
  throw new Error(`Could not parse JSON: ${error.message}, body: ${json}`);
173
245
  }
174
246
  }
@@ -1,6 +1,3 @@
1
- import { t } from '@deepkit/type';
2
- import { AppModuleConfig } from '@deepkit/app';
3
-
4
- export const config = new AppModuleConfig({
5
- debug: t.boolean.default(false),
6
- });
1
+ export class HttpConfig {
2
+ debug: boolean = false;
3
+ }
package/src/module.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { HttpListener, HttpResultFormatter, httpWorkflow } from './http';
2
- import { config } from './module.config';
2
+ import { HttpConfig } from './module.config';
3
3
  import { AppModule, createModule } from '@deepkit/app';
4
4
  import { Router } from './router';
5
5
  import { HttpKernel } from './kernel';
@@ -12,7 +12,7 @@ import { ClassType } from '@deepkit/core';
12
12
  import { httpClass } from './decorator';
13
13
 
14
14
  export class HttpModule extends createModule({
15
- config: config,
15
+ config: HttpConfig,
16
16
  providers: [
17
17
  Router,
18
18
  HttpKernel,