@deepkit/http 1.0.1-alpha.91 → 1.0.1-alpha.93

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 (59) hide show
  1. package/dist/cjs/src/http.d.ts +7 -6
  2. package/dist/cjs/src/http.js +22 -12
  3. package/dist/cjs/src/http.js.map +1 -1
  4. package/dist/cjs/src/kernel.js +18 -1
  5. package/dist/cjs/src/kernel.js.map +1 -1
  6. package/dist/cjs/src/logger.js +1 -1
  7. package/dist/cjs/src/logger.js.map +1 -1
  8. package/dist/cjs/src/model.d.ts +17 -0
  9. package/dist/cjs/src/model.js +10 -2
  10. package/dist/cjs/src/model.js.map +1 -1
  11. package/dist/cjs/src/module.d.ts +4 -1
  12. package/dist/cjs/src/module.js +57 -1
  13. package/dist/cjs/src/module.js.map +1 -1
  14. package/dist/cjs/src/request-parser.d.ts +35 -0
  15. package/dist/cjs/src/request-parser.js +302 -0
  16. package/dist/cjs/src/request-parser.js.map +1 -0
  17. package/dist/cjs/src/resolver.d.ts +27 -0
  18. package/dist/cjs/src/resolver.js +246 -0
  19. package/dist/cjs/src/resolver.js.map +1 -0
  20. package/dist/cjs/src/router.d.ts +7 -20
  21. package/dist/cjs/src/router.js +23 -247
  22. package/dist/cjs/src/router.js.map +1 -1
  23. package/dist/cjs/src/static-serving.js +1 -1
  24. package/dist/cjs/src/static-serving.js.map +1 -1
  25. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  26. package/dist/esm/src/http.d.ts +7 -6
  27. package/dist/esm/src/http.js +22 -12
  28. package/dist/esm/src/http.js.map +1 -1
  29. package/dist/esm/src/kernel.js +19 -2
  30. package/dist/esm/src/kernel.js.map +1 -1
  31. package/dist/esm/src/logger.js +1 -1
  32. package/dist/esm/src/logger.js.map +1 -1
  33. package/dist/esm/src/model.d.ts +17 -0
  34. package/dist/esm/src/model.js +9 -1
  35. package/dist/esm/src/model.js.map +1 -1
  36. package/dist/esm/src/module.d.ts +4 -1
  37. package/dist/esm/src/module.js +59 -3
  38. package/dist/esm/src/module.js.map +1 -1
  39. package/dist/esm/src/request-parser.d.ts +35 -0
  40. package/dist/esm/src/request-parser.js +292 -0
  41. package/dist/esm/src/request-parser.js.map +1 -0
  42. package/dist/esm/src/resolver.d.ts +27 -0
  43. package/dist/esm/src/resolver.js +238 -0
  44. package/dist/esm/src/resolver.js.map +1 -0
  45. package/dist/esm/src/router.d.ts +7 -20
  46. package/dist/esm/src/router.js +26 -250
  47. package/dist/esm/src/router.js.map +1 -1
  48. package/dist/esm/src/static-serving.js +1 -1
  49. package/dist/esm/src/static-serving.js.map +1 -1
  50. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  51. package/package.json +11 -11
  52. package/src/http.ts +15 -8
  53. package/src/kernel.ts +22 -3
  54. package/src/logger.ts +1 -1
  55. package/src/model.ts +6 -1
  56. package/src/module.ts +59 -2
  57. package/src/request-parser.ts +340 -0
  58. package/src/router.ts +26 -290
  59. package/src/static-serving.ts +1 -1
package/src/router.ts CHANGED
@@ -7,31 +7,10 @@
7
7
  *
8
8
  * You should have received a copy of the MIT License along with this program.
9
9
  */
10
- import { asyncOperation, ClassType, CompilerContext, getClassName, isArray, isClass, isObject, urlJoin } from '@deepkit/core';
11
- import {
12
- assertType,
13
- entity,
14
- findMember,
15
- getSerializeFunction,
16
- getValidatorFunction,
17
- metaAnnotation,
18
- ReflectionClass,
19
- ReflectionFunction,
20
- ReflectionKind,
21
- ReflectionParameter,
22
- resolveReceiveType,
23
- SerializationOptions,
24
- serializer,
25
- Serializer,
26
- stringifyType,
27
- Type,
28
- typeToObject,
29
- ValidationError
30
- } from '@deepkit/type';
31
- // @ts-ignore
32
- import formidable from 'formidable';
10
+ import { ClassType, CompilerContext, getClassName, isArray, isClass, urlJoin } from '@deepkit/core';
11
+ import { entity, ReflectionClass, ReflectionFunction, ReflectionKind, ReflectionParameter, SerializationOptions, Serializer, Type, ValidationError } from '@deepkit/type';
33
12
  import { HttpAction, httpClass, HttpController, HttpDecorator } from './decorator.js';
34
- import { BodyValidationError, createRequestWithCachedBody, getRegExp, HttpRequest, HttpRequestQuery, HttpRequestResolvedParameters, ValidatedBody } from './model.js';
13
+ import { HttpRequest, HttpRequestPositionedParameters, HttpRequestQuery, HttpRequestResolvedParameters } from './model.js';
35
14
  import { InjectorContext, InjectorModule, TagRegistry } from '@deepkit/injector';
36
15
  import { Logger, LoggerInterface } from '@deepkit/logger';
37
16
  import { HttpControllers } from './controllers.js';
@@ -41,8 +20,9 @@ import { HttpMiddlewareConfig, HttpMiddlewareFn } from './middleware.js';
41
20
  //@ts-ignore
42
21
  import qs from 'qs';
43
22
  import { HtmlResponse, JSONResponse, Response } from './http.js';
23
+ import { getRequestParserCodeForParameters, ParameterForRequestParser, parseRoutePathToRegex } from './request-parser.js';
44
24
 
45
- export type RouteParameterResolverForInjector = ((injector: InjectorContext) => any[] | Promise<any[]>);
25
+ export type RouteParameterResolverForInjector = ((injector: InjectorContext) => HttpRequestPositionedParameters | Promise<HttpRequestPositionedParameters>);
46
26
 
47
27
  interface ResolvedController {
48
28
  parameters: RouteParameterResolverForInjector;
@@ -165,22 +145,22 @@ class ParsedRoute {
165
145
 
166
146
  public pathParameterNames: { [name: string]: number } = {};
167
147
 
168
- protected parameters: ParsedRouteParameter[] = [];
148
+ protected parameters: ParameterForRequestParser[] = [];
169
149
 
170
150
  constructor(public routeConfig: RouteConfig) {
171
151
  }
172
152
 
173
- addParameter(property: ReflectionParameter): ParsedRouteParameter {
174
- const parameter = new ParsedRouteParameter(property);
153
+ addParameter(property: ReflectionParameter): ParameterForRequestParser {
154
+ const parameter = new ParameterForRequestParser(property);
175
155
  this.parameters.push(parameter);
176
156
  return parameter;
177
157
  }
178
158
 
179
- getParameters(): ParsedRouteParameter[] {
159
+ getParameters(): ParameterForRequestParser[] {
180
160
  return this.parameters;
181
161
  }
182
162
 
183
- getParameter(name: string): ParsedRouteParameter {
163
+ getParameter(name: string): ParameterForRequestParser {
184
164
  for (const parameter of this.parameters) {
185
165
  if (parameter.getName() === name) return parameter;
186
166
  }
@@ -188,91 +168,15 @@ class ParsedRoute {
188
168
  }
189
169
  }
190
170
 
191
- class ParsedRouteParameter {
192
- regexPosition?: number;
193
-
194
- constructor(
195
- public parameter: ReflectionParameter,
196
- ) {
197
- }
198
-
199
- get body() {
200
- return metaAnnotation.getForName(this.parameter.type, 'httpBody') !== undefined;
201
- }
202
-
203
- get bodyValidation() {
204
- return metaAnnotation.getForName(this.parameter.type, 'httpBodyValidation') !== undefined;
205
- }
206
-
207
- getType(): Type {
208
- if (this.bodyValidation) {
209
- assertType(this.parameter.type, ReflectionKind.class);
210
- const valueType = findMember('value', this.parameter.type.types);
211
- if (!valueType || valueType.kind !== ReflectionKind.property) throw new Error(`No property value found at ${stringifyType(this.parameter.type)}`);
212
- return valueType.type as Type;
213
- }
214
- return this.parameter.type;
215
- }
216
-
217
- get query() {
218
- return metaAnnotation.getForName(this.parameter.type, 'httpQuery') !== undefined;
219
- }
220
-
221
- get queries() {
222
- return metaAnnotation.getForName(this.parameter.type, 'httpQueries') !== undefined;
223
- }
224
-
225
- get typePath(): string | undefined {
226
- const typeOptions = metaAnnotation.getForName(this.parameter.type, 'httpQueries') || metaAnnotation.getForName(this.parameter.type, 'httpQuery');
227
- if (!typeOptions) return;
228
- const options = typeToObject(typeOptions[0]);
229
- if (isObject(options)) return options.name;
230
- return;
231
- }
232
-
233
- getName() {
234
- return this.parameter.name;
235
- }
236
-
237
- isPartOfPath(): boolean {
238
- return this.regexPosition !== undefined;
239
- }
240
- }
241
-
242
- function parseRoutePathToRegex(routeConfig: RouteConfig): { regex: string, parameterNames: { [name: string]: number } } {
243
- const parameterNames: { [name: string]: number } = {};
244
- let path = routeConfig.getFullPath();
245
-
246
- const fn = routeConfig.getReflectionFunction();
247
-
248
- let argumentIndex = 0;
249
- path = path.replace(/:(\w+)/g, (a, name) => {
250
- parameterNames[name] = argumentIndex;
251
- argumentIndex++;
252
- const parameter = fn.getParameterOrUndefined(name);
253
- if (parameter) {
254
- const regExp = getRegExp(parameter.type);
255
- if (regExp instanceof RegExp) {
256
- return '(' + regExp.source + ')';
257
- } else if (regExp) {
258
- return '(' + regExp + ')';
259
- }
260
- }
261
- return String.raw`([^/]+)`;
262
- });
263
-
264
- return { regex: path, parameterNames };
265
- }
266
-
267
171
  export function parseRouteControllerAction(routeConfig: RouteConfig): ParsedRoute {
268
172
  const parsedRoute = new ParsedRoute(routeConfig);
269
173
 
270
- const methodArgumentProperties = routeConfig.getReflectionFunction().getParameters();
271
- const parsedPath = parseRoutePathToRegex(routeConfig);
174
+ const parameters = routeConfig.getReflectionFunction().getParameters();
175
+ const parsedPath = parseRoutePathToRegex(routeConfig.getFullPath(), parameters);
272
176
  parsedRoute.regex = parsedPath.regex;
273
177
  parsedRoute.pathParameterNames = parsedPath.parameterNames;
274
178
 
275
- for (const property of methodArgumentProperties) {
179
+ for (const property of parameters) {
276
180
  const parsedParameter = parsedRoute.addParameter(property);
277
181
  parsedParameter.regexPosition = parsedPath.parameterNames[property.name];
278
182
  }
@@ -622,52 +526,6 @@ export class HttpRouter {
622
526
  protected buildId: number = 0;
623
527
  protected resolveFn?: (name: string, parameters: { [name: string]: any }) => string;
624
528
 
625
- private parseBody(req: HttpRequest, foundFiles: { [name: string]: UploadedFile }) {
626
- const form = formidable({
627
- multiples: true,
628
- hash: 'sha1',
629
- });
630
- return asyncOperation((resolve, reject) => {
631
- function parseData(err: any, fields: any, files: any) {
632
- if (err) {
633
- reject(err);
634
- } else {
635
- for (const [name, file] of Object.entries(files) as any) {
636
- if (file.size === 0) continue;
637
- foundFiles[name] = {
638
- size: file.size,
639
- path: file.filepath,
640
- name: file.originalFilename,
641
- type: file.mimetype,
642
- lastModifiedDate: file.lastModifiedDate,
643
- };
644
- }
645
- const body = { ...fields, ...foundFiles };
646
- resolve(body);
647
- }
648
- }
649
-
650
- if (req.body) {
651
- form.parse(createRequestWithCachedBody(req, req.body), parseData);
652
- return;
653
- }
654
-
655
- const chunks: Buffer[] = [];
656
-
657
- function read(chunk: Buffer) {
658
- chunks.push(chunk);
659
- }
660
-
661
- req.on('data', read);
662
- req.once('end', () => {
663
- req.body = Buffer.concat(chunks);
664
- req.off('data', read);
665
- });
666
- req.once('error', () => req.off('data', read));
667
-
668
- form.parse(req, parseData);
669
- });
670
- }
671
529
 
672
530
  constructor(
673
531
  controllers: HttpControllers,
@@ -697,138 +555,22 @@ export class HttpRouter {
697
555
  }
698
556
 
699
557
  protected getRouteCode(compiler: CompilerContext, routeConfig: RouteConfig): string {
700
- const routeConfigVar = compiler.reserveVariable('routeConfigVar', routeConfig);
701
558
  const parsedRoute = parseRouteControllerAction(routeConfig);
702
559
  const path = routeConfig.getFullPath();
703
560
  const pathParamStarter = path.indexOf(':');
704
561
  const prefix = path.substr(0, pathParamStarter);
705
562
 
706
563
  const regexVar = compiler.reserveVariable('regex', new RegExp('^' + parsedRoute.regex + '$'));
707
- const setParameters: string[] = [];
708
- const parameterNames: string[] = [];
709
- const parameterValidator: string[] = [];
710
- let bodyValidationErrorHandling = `if (bodyErrors.length) throw ValidationError.from(bodyErrors);`;
711
564
 
712
- let enableParseBody = false;
713
565
  const hasParameters = pathParamStarter !== -1 || parsedRoute.getParameters().length > 0;
714
- let requiresAsyncParameters = false;
715
- let setParametersFromPath = '';
716
566
 
717
567
  const fullPath = routeConfig.getFullPath();
718
568
  const middlewareConfigs = filterMiddlewaresForRoute(this.middlewareRegistry.configs, routeConfig, fullPath);
719
569
 
720
- for (const parameter of parsedRoute.getParameters()) {
721
- if (parameter.body || parameter.bodyValidation) {
722
- const type = parameter.getType();
723
- const validatorVar = compiler.reserveVariable('argumentValidator', getValidatorFunction(undefined, type));
724
- const converterVar = compiler.reserveVariable('argumentConverter', getSerializeFunction(type, serializer.deserializeRegistry));
725
-
726
- enableParseBody = true;
727
- setParameters.push(`parameters.${parameter.parameter.name} = ${converterVar}(bodyFields, {loosely: true});`);
728
- parameterValidator.push(`${validatorVar}(parameters.${parameter.parameter.name}, {errors: bodyErrors});`);
729
- if (parameter.bodyValidation) {
730
- compiler.context.set('BodyValidation', ValidatedBody);
731
- compiler.context.set('BodyValidationError', BodyValidationError);
732
- parameterNames.push(`new BodyValidation(new BodyValidationError(bodyErrors), bodyErrors.length === 0 ? parameters.${parameter.parameter.name} : undefined)`);
733
- bodyValidationErrorHandling = '';
734
- } else {
735
- parameterNames.push(`parameters.${parameter.parameter.name}`);
736
- }
737
- } else if (parameter.query || parameter.queries) {
738
- const converted = getSerializeFunction(parameter.parameter.parameter, serializer.deserializeRegistry, undefined, parameter.getName());
739
- const validator = getValidatorFunction(undefined, parameter.parameter.parameter,);
740
- const converterVar = compiler.reserveVariable('argumentConverter', converted);
741
- const validatorVar = compiler.reserveVariable('argumentValidator', validator);
742
-
743
- const queryPath = parameter.typePath === undefined && parameter.query ? parameter.parameter.name : parameter.typePath;
744
- const accessor = queryPath ? `['` + (queryPath.replace(/\./g, `']['`)) + `']` : '';
745
- const queryAccessor = queryPath ? `_query${accessor}` : '_query';
746
-
747
- setParameters.push(`parameters.${parameter.parameter.name} = ${converterVar}(${queryAccessor}, {loosely: true});`);
748
- parameterNames.push(`parameters.${parameter.parameter.name}`);
749
- parameterValidator.push(`${validatorVar}(parameters.${parameter.parameter.name}, {errors: validationErrors}, ${JSON.stringify(parameter.typePath || parameter.getName())});`);
750
- } else {
751
- parameterNames.push(`parameters.${parameter.parameter.name}`);
752
-
753
- if (parameter.isPartOfPath()) {
754
- if (parameter.parameter.type.kind !== ReflectionKind.class) {
755
- const converted = getSerializeFunction(parameter.parameter.parameter, serializer.deserializeRegistry, undefined, parameter.getName());
756
- const converterVar = compiler.reserveVariable('argumentConverter', converted);
757
- setParameters.push(`parameters.${parameter.parameter.name} = ${converterVar}(_match[${1 + (parameter.regexPosition || 0)}], {loosely: true});`);
758
-
759
- const validator = getValidatorFunction(undefined, parameter.parameter.parameter);
760
- const validatorVar = compiler.reserveVariable('argumentValidator', validator);
761
- parameterValidator.push(`${validatorVar}(parameters.${parameter.parameter.name}, {errors: validationErrors}, ${JSON.stringify(parameter.getName())});`);
762
- } else {
763
- setParameters.push(`parameters.${parameter.parameter.name} = _match[${1 + (parameter.regexPosition || 0)}];`);
764
- }
765
- }
766
-
767
- const injectorToken = parameter.parameter.type.kind === ReflectionKind.class ? parameter.parameter.type.classType : undefined;
768
- const injectorTokenVar = compiler.reserveVariable('classType', injectorToken);
769
- const parameterResolverFoundVar = compiler.reserveVariable('parameterResolverFound', false);
770
-
771
- setParameters.push(`${parameterResolverFoundVar} = false;`);
570
+ const parameters = [...parsedRoute.getParameters()];
772
571
 
773
- const resolverType = routeConfig.resolverForParameterName.get(parameter.getName()) || routeConfig.resolverForToken.get(injectorToken);
774
-
775
- //make sure all parameter values from the path are available
776
- if (resolverType && !setParametersFromPath) {
777
- for (const i in parsedRoute.pathParameterNames) {
778
- setParametersFromPath += `parameters.${i} = _match[${1 + parsedRoute.pathParameterNames[i]}];`;
779
- }
780
- }
781
-
782
- let injector = '_injector';
783
- const moduleVar = routeConfig.module ? ', ' + compiler.reserveConst(routeConfig.module, 'module') : '';
784
-
785
- if (resolverType) {
786
- requiresAsyncParameters = true;
787
- let instanceFetcher = '';
788
- if (routeConfig.module && routeConfig.module.injector) {
789
- const resolverResolverVar = compiler.reserveVariable('resolverProvideToken', routeConfig.module.injector.createResolver(resolveReceiveType(resolverType)));
790
- instanceFetcher = `${resolverResolverVar}(${injector}.scope)`;
791
- } else {
792
- const resolverProvideTokenVar = compiler.reserveVariable('resolverProvideToken', resolverType);
793
- instanceFetcher = `${injector}.get(${resolverProvideTokenVar}${moduleVar})`;
794
- }
795
- const instance = compiler.reserveVariable('resolverInstance');
796
-
797
- setParameters.push(`
798
- //resolver ${getClassName(resolverType)} for ${parameter.getName()}
799
- ${instance} = ${instanceFetcher};
800
- if (!${parameterResolverFoundVar}) {
801
- ${parameterResolverFoundVar} = true;
802
- parameters.${parameter.parameter.name} = await ${instance}.resolve({
803
- token: ${injectorTokenVar},
804
- route: ${routeConfigVar},
805
- request: request,
806
- name: ${JSON.stringify(parameter.parameter.name)},
807
- value: parameters.${parameter.parameter.name},
808
- query: _query,
809
- parameters: parameters,
810
- type: ${compiler.reserveVariable('parameterType', parameter.parameter)}
811
- });
812
- }`);
813
- }
814
-
815
- if (!parameter.isPartOfPath()) {
816
- let injectorGet = `parameters.${parameter.parameter.name} = ${injector}.get(${injectorTokenVar});`;
817
- if (parameter.parameter.isOptional()) {
818
- injectorGet = `try {parameters.${parameter.parameter.name} = ${injector}.get(${injectorTokenVar}); } catch (e) {}`;
819
- }
820
- setParameters.push(`if (!${parameterResolverFoundVar}) ${injectorGet}`);
821
- }
822
- }
823
- }
824
-
825
- let parseBodyLoading = '';
826
- if (enableParseBody) {
827
- const parseBodyVar = compiler.reserveVariable('parseBody', this.parseBody.bind(this));
828
- parseBodyLoading = `
829
- const bodyFields = (await ${parseBodyVar}(request, uploadedFiles));`;
830
- requiresAsyncParameters = true;
831
- }
572
+ //look through all http listeners and check if they need any http parameters.
573
+ //the router then parses
832
574
 
833
575
  let matcher = `_path.startsWith(${JSON.stringify(prefix)}) && (_match = _path.match(${regexVar}))`;
834
576
  if (!hasParameters) {
@@ -858,21 +600,13 @@ export class HttpRouter {
858
600
  `;
859
601
  }
860
602
 
861
- let parameters = '() => []';
862
- if (setParameters.length) {
863
- parameters = `${requiresAsyncParameters ? 'async' : ''} function(_injector){
864
- const validationErrors = [];
865
- const bodyErrors = [];
866
- const parameters = {};
867
- ${setParametersFromPath}
868
- ${parseBodyLoading}
869
- ${setParameters.join('\n')}
870
- ${parameterValidator.join('\n')}
871
- ${bodyValidationErrorHandling}
872
- if (validationErrors.length) throw new ValidationError(validationErrors);
873
- return [${parameterNames.join(',')}];
874
- }`;
875
- }
603
+ const parametersLoader = getRequestParserCodeForParameters(compiler, parameters, {
604
+ module: routeConfig.module,
605
+ resolverForToken: routeConfig.resolverForToken,
606
+ resolverForParameterName: routeConfig.resolverForParameterName,
607
+ pathParameterNames: parsedRoute.pathParameterNames,
608
+ routeConfig
609
+ });
876
610
 
877
611
  let methodCheck = '';
878
612
  if (routeConfig.httpMethods.length) {
@@ -881,10 +615,11 @@ export class HttpRouter {
881
615
  }).join(' || ') + ') && ';
882
616
  }
883
617
 
618
+ const routeConfigVar = compiler.reserveVariable('routeConfigVar', routeConfig);
884
619
  return `
885
620
  //=> ${routeConfig.httpMethods.join(',')} ${path}
886
621
  if (${methodCheck}${matcher}) {
887
- return {routeConfig: ${routeConfigVar}, parameters: ${parameters}, uploadedFiles: uploadedFiles, middlewares: ${middlewares}};
622
+ return {routeConfig: ${routeConfigVar}, parameters: ${parametersLoader}, uploadedFiles: uploadedFiles, middlewares: ${middlewares}};
888
623
  }
889
624
  `;
890
625
  }
@@ -949,6 +684,7 @@ export class HttpRouter {
949
684
  let _match;
950
685
  const _method = request.method || 'GET';
951
686
  const _url = request.url || '/';
687
+ const _headers = request.headers || {};
952
688
  const _qPosition = _url.indexOf('?');
953
689
  let uploadedFiles = {};
954
690
  const _path = _qPosition === -1 ? _url : _url.substr(0, _qPosition);
@@ -50,7 +50,7 @@ export function serveStaticListener(module: AppModule<any>, path: string, localP
50
50
  module,
51
51
  methodName: 'serve'
52
52
  }),
53
- () => [relativePath, event.request, event.response]
53
+ () => ({arguments: [relativePath, event.request, event.response], parameters: {}})
54
54
  );
55
55
  }
56
56
  resolve(undefined);